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