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