xref: /haiku/src/servers/app/Layer.cpp (revision 779ab335dd81d47f9aa7ef06822de37c8fec1c6e)
1 /*
2  * Copyright 2015 Julian Harnath <julian.harnath@rwth-aachen.de>
3  * All rights reserved. Distributed under the terms of the MIT license.
4  */
5 #include "Layer.h"
6 
7 #include "AlphaMask.h"
8 #include "BitmapHWInterface.h"
9 #include "DrawingEngine.h"
10 #include "DrawState.h"
11 #include "IntRect.h"
12 #include "PictureBoundingBoxPlayer.h"
13 #include "ServerBitmap.h"
14 #include "View.h"
15 
16 
17 class LayerCanvas : public Canvas {
18 public:
LayerCanvas(DrawingEngine * drawingEngine,DrawState * drawState,BRect bitmapBounds)19 	LayerCanvas(DrawingEngine* drawingEngine, DrawState* drawState,
20 		BRect bitmapBounds)
21 		:
22 		Canvas(),
23 		fDrawingEngine(drawingEngine),
24 		fBitmapBounds(bitmapBounds)
25 	{
26 		fDrawState.SetTo(drawState);
27 	}
28 
GetDrawingEngine() const29 	virtual DrawingEngine* GetDrawingEngine() const
30 	{
31 		return fDrawingEngine;
32 	}
33 
GetPicture(int32 token) const34 	virtual ServerPicture* GetPicture(int32 token) const
35 	{
36 		return NULL;
37 	}
38 
RebuildClipping(bool)39 	virtual void RebuildClipping(bool)
40 	{
41 	}
42 
ResyncDrawState()43 	virtual void ResyncDrawState()
44 	{
45 		fDrawingEngine->SetDrawState(fDrawState.Get());
46 	}
47 
UpdateCurrentDrawingRegion()48 	virtual void UpdateCurrentDrawingRegion()
49 	{
50 		bool hasDrawStateClipping = fDrawState->GetCombinedClippingRegion(
51 			&fCurrentDrawingRegion);
52 
53 		BRegion bitmapRegion(fBitmapBounds);
54 		if (hasDrawStateClipping)
55 			fCurrentDrawingRegion.IntersectWith(&bitmapRegion);
56 		else
57 			fCurrentDrawingRegion = bitmapRegion;
58 
59 		fDrawingEngine->ConstrainClippingRegion(&fCurrentDrawingRegion);
60 	}
61 
Bounds() const62 	virtual	IntRect Bounds() const
63 	{
64 		return fBitmapBounds;
65 	}
66 
67 protected:
_LocalToScreenTransform(SimpleTransform &) const68 	virtual void _LocalToScreenTransform(SimpleTransform&) const
69 	{
70 	}
71 
_ScreenToLocalTransform(SimpleTransform &) const72 	virtual void _ScreenToLocalTransform(SimpleTransform&) const
73 	{
74 	}
75 
76 private:
77 	DrawingEngine*	fDrawingEngine;
78 	BRegion			fCurrentDrawingRegion;
79 	BRect			fBitmapBounds;
80 };
81 
82 
Layer(uint8 opacity)83 Layer::Layer(uint8 opacity)
84 	:
85 	fOpacity(opacity),
86 	fLeftTopOffset(0, 0)
87 {
88 }
89 
90 
~Layer()91 Layer::~Layer()
92 {
93 }
94 
95 
96 void
PushLayer(Layer * layer)97 Layer::PushLayer(Layer* layer)
98 {
99 	PushPicture(layer);
100 }
101 
102 
103 Layer*
PopLayer()104 Layer::PopLayer()
105 {
106 	return static_cast<Layer*>(PopPicture());
107 }
108 
109 
110 UtilityBitmap*
RenderToBitmap(Canvas * canvas)111 Layer::RenderToBitmap(Canvas* canvas)
112 {
113 	BRect boundingBox = _DetermineBoundingBox(canvas);
114 	if (!boundingBox.IsValid())
115 		return NULL;
116 
117 	fLeftTopOffset = boundingBox.LeftTop();
118 
119 	BReference<UtilityBitmap> layerBitmap(_AllocateBitmap(boundingBox), true);
120 	if (layerBitmap == NULL)
121 		return NULL;
122 
123 	BitmapHWInterface layerInterface(layerBitmap);
124 	ObjectDeleter<DrawingEngine> const layerEngine(layerInterface.CreateDrawingEngine());
125 	if (!layerEngine.IsSet())
126 		return NULL;
127 
128 	layerEngine->SetRendererOffset(boundingBox.left, boundingBox.top);
129 		// Drawing commands of the layer's picture use coordinates in the
130 		// coordinate space of the underlying canvas. The coordinate origin
131 		// of the layer bitmap is at boundingBox.LeftTop(). So all the drawing
132 		// from the picture needs to be offset to be moved into the bitmap.
133 		// We use a low-level offsetting via the AGG renderer here because the
134 		// offset needs to be processed independently, after all other
135 		// transforms, even after the BAffineTransforms (which are processed in
136 		// Painter), to prevent this origin from being further transformed by
137 		// e.g. scaling.
138 
139 	LayerCanvas layerCanvas(layerEngine.Get(), canvas->DetachDrawState(), boundingBox);
140 
141 	AlphaMask* const mask = layerCanvas.GetAlphaMask();
142 	IntPoint oldOffset;
143 	if (mask != NULL) {
144 		// Move alpha mask to bitmap origin
145 		oldOffset = mask->SetCanvasGeometry(IntPoint(0, 0), boundingBox);
146 	}
147 
148 	layerCanvas.CurrentState()->SetDrawingMode(B_OP_ALPHA);
149 	layerCanvas.CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
150 
151 	layerCanvas.ResyncDrawState();
152 		// Apply state to the new drawing engine of the layer canvas
153 
154 	if (layerEngine->LockParallelAccess()) {
155 		layerCanvas.UpdateCurrentDrawingRegion();
156 
157 		// Draw recorded picture into bitmap
158 		Play(&layerCanvas);
159 		layerEngine->UnlockParallelAccess();
160 	}
161 
162 	if (mask != NULL) {
163 		// Move alpha mask back to its old position
164 		// Note: this needs to be adapted if setting alpha masks is
165 		// implemented as BPicture command (the mask now might be a different
166 		// one than before).
167 		layerCanvas.CurrentState()->CombinedTransform().Apply(oldOffset);
168 		mask->SetCanvasGeometry(oldOffset, boundingBox);
169 		layerCanvas.ResyncDrawState();
170 	}
171 
172 	canvas->SetDrawState(layerCanvas.DetachDrawState());
173 		// Update state in canvas (the top-of-stack state could be a different
174 		// state instance now, if the picture commands contained push/pop
175 		// commands)
176 
177 	return layerBitmap.Detach();
178 }
179 
180 
181 IntPoint
LeftTopOffset() const182 Layer::LeftTopOffset() const
183 {
184 	return fLeftTopOffset;
185 }
186 
187 
188 uint8
Opacity() const189 Layer::Opacity() const
190 {
191 	return fOpacity;
192 }
193 
194 
195 BRect
_DetermineBoundingBox(Canvas * canvas)196 Layer::_DetermineBoundingBox(Canvas* canvas)
197 {
198 	BRect boundingBox;
199 	PictureBoundingBoxPlayer::Play(this, canvas->CurrentState(), &boundingBox);
200 
201 	if (!boundingBox.IsValid())
202 		return boundingBox;
203 
204 	// Round up and add an additional 2 pixels on the bottom/right to
205 	// compensate for the various types of rounding used in Painter.
206 	boundingBox.left = floorf(boundingBox.left);
207 	boundingBox.right = ceilf(boundingBox.right) + 2;
208 	boundingBox.top = floorf(boundingBox.top);
209 	boundingBox.bottom = ceilf(boundingBox.bottom) + 2;
210 
211 	// TODO: for optimization, crop the bounding box to the underlying
212 	// view bounds here
213 
214 	return boundingBox;
215 }
216 
217 
218 UtilityBitmap*
_AllocateBitmap(const BRect & bounds)219 Layer::_AllocateBitmap(const BRect& bounds)
220 {
221 	BReference<UtilityBitmap> layerBitmap(new(std::nothrow) UtilityBitmap(bounds,
222 		B_RGBA32, 0), true);
223 	if (layerBitmap == NULL)
224 		return NULL;
225 	if (!layerBitmap->IsValid())
226 		return NULL;
227 
228 	memset(layerBitmap->Bits(), 0, layerBitmap->BitsLength());
229 
230 	return layerBitmap.Detach();
231 }
232