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
17class LayerCanvas : public Canvas {
18public:
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
68protected:
69	virtual void _LocalToScreenTransform(SimpleTransform&) const
70	{
71	}
72
73	virtual void _ScreenToLocalTransform(SimpleTransform&) const
74	{
75	}
76
77private:
78	DrawingEngine*	fDrawingEngine;
79	BRegion			fCurrentDrawingRegion;
80	BRect			fBitmapBounds;
81};
82
83
84Layer::Layer(uint8 opacity)
85	:
86	fOpacity(opacity),
87	fLeftTopOffset(0, 0)
88{
89}
90
91
92Layer::~Layer()
93{
94}
95
96
97void
98Layer::PushLayer(Layer* layer)
99{
100	PushPicture(layer);
101}
102
103
104Layer*
105Layer::PopLayer()
106{
107	Layer* const previousLayer = static_cast<Layer*>(PopPicture());
108	if (previousLayer != NULL)
109		previousLayer->ReleaseReference();
110	return previousLayer;
111}
112
113
114UtilityBitmap*
115Layer::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
188IntPoint
189Layer::LeftTopOffset() const
190{
191	return fLeftTopOffset;
192}
193
194
195uint8
196Layer::Opacity() const
197{
198	return fOpacity;
199}
200
201
202BRect
203Layer::_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
225UtilityBitmap*
226Layer::_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