1/*
2 * Copyright (c) 2001-2015, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 *		Adi Oanca <adioanca@gmail.com>
8 *		Axel Dörfler, axeld@pinc-software.de
9 *		Stephan Aßmus <superstippi@gmx.de>
10 *		Marcus Overhagen <marcus@overhagen.de>
11 *		Adrien Destugues <pulkomandy@pulkomandy.tk
12 *		Julian Harnath <julian.harnath@rwth-aachen.de>
13 */
14
15
16#include "Canvas.h"
17
18#include <new>
19
20#include <Region.h>
21
22#include "AlphaMask.h"
23#include "DrawingEngine.h"
24#include "DrawState.h"
25#include "Layer.h"
26
27
28#if __GNUC__ >= 3
29#	define GCC_2_NRV(x)
30	// GCC >= 3.1 doesn't need it anymore
31#else
32#	define GCC_2_NRV(x) return x;
33	// GCC 2 named return value syntax
34	// see http://gcc.gnu.org/onlinedocs/gcc-2.95.2/gcc_5.html#SEC106
35#endif
36
37
38Canvas::Canvas()
39	:
40	fDrawState(new(std::nothrow) DrawState())
41{
42}
43
44
45Canvas::Canvas(const DrawState& state)
46	:
47	fDrawState(new(std::nothrow) DrawState(state))
48{
49}
50
51
52Canvas::~Canvas()
53{
54}
55
56
57status_t
58Canvas::InitCheck() const
59{
60	if (fDrawState == NULL)
61		return B_NO_MEMORY;
62
63	return B_OK;
64}
65
66
67void
68Canvas::PushState()
69{
70	DrawState* newState = fDrawState->PushState();
71	if (newState)
72		fDrawState = newState;
73}
74
75
76void
77Canvas::PopState()
78{
79	if (fDrawState->PreviousState() == NULL)
80		return;
81
82	bool rebuildClipping = fDrawState->HasAdditionalClipping();
83
84	fDrawState = fDrawState->PopState();
85
86	// rebuild clipping
87	// (the clipping from the popped state is not effective anymore)
88	if (rebuildClipping)
89		RebuildClipping(false);
90}
91
92
93void
94Canvas::SetDrawState(DrawState* newState)
95{
96	fDrawState = newState;
97}
98
99
100void
101Canvas::SetDrawingOrigin(BPoint origin)
102{
103	fDrawState->SetOrigin(origin);
104
105	// rebuild clipping
106	if (fDrawState->HasClipping())
107		RebuildClipping(false);
108}
109
110
111BPoint
112Canvas::DrawingOrigin() const
113{
114	BPoint origin(fDrawState->Origin());
115	float scale = Scale();
116
117	origin.x *= scale;
118	origin.y *= scale;
119
120	return origin;
121}
122
123
124void
125Canvas::SetScale(float scale)
126{
127	fDrawState->SetScale(scale);
128
129	// rebuild clipping
130	if (fDrawState->HasClipping())
131		RebuildClipping(false);
132}
133
134
135float
136Canvas::Scale() const
137{
138	return fDrawState->Scale();
139}
140
141
142void
143Canvas::SetUserClipping(const BRegion* region)
144{
145	fDrawState->SetClippingRegion(region);
146
147	// rebuild clipping (for just this canvas)
148	RebuildClipping(false);
149}
150
151
152bool
153Canvas::ClipToRect(BRect rect, bool inverse)
154{
155	bool needDrawStateUpdate = fDrawState->ClipToRect(rect, inverse);
156	RebuildClipping(false);
157	return needDrawStateUpdate;
158}
159
160
161void
162Canvas::ClipToShape(shape_data* shape, bool inverse)
163{
164	fDrawState->ClipToShape(shape, inverse);
165}
166
167
168void
169Canvas::SetAlphaMask(AlphaMask* mask)
170{
171	fDrawState->SetAlphaMask(mask);
172}
173
174
175AlphaMask*
176Canvas::GetAlphaMask() const
177{
178	return fDrawState->GetAlphaMask();
179}
180
181
182SimpleTransform
183Canvas::LocalToScreenTransform() const GCC_2_NRV(transform)
184{
185#if __GNUC__ >= 3
186	SimpleTransform transform;
187#endif
188	_LocalToScreenTransform(transform);
189	return transform;
190}
191
192
193SimpleTransform
194Canvas::ScreenToLocalTransform() const GCC_2_NRV(transform)
195{
196#if __GNUC__ >= 3
197	SimpleTransform transform;
198#endif
199	_ScreenToLocalTransform(transform);
200	return transform;
201}
202
203
204SimpleTransform
205Canvas::PenToScreenTransform() const GCC_2_NRV(transform)
206{
207#if __GNUC__ >= 3
208	SimpleTransform transform;
209#endif
210	fDrawState->Transform(transform);
211	_LocalToScreenTransform(transform);
212	return transform;
213}
214
215
216SimpleTransform
217Canvas::PenToLocalTransform() const GCC_2_NRV(transform)
218{
219#if __GNUC__ >= 3
220	SimpleTransform transform;
221#endif
222	fDrawState->Transform(transform);
223	return transform;
224}
225
226
227SimpleTransform
228Canvas::ScreenToPenTransform() const GCC_2_NRV(transform)
229{
230#if __GNUC__ >= 3
231	SimpleTransform transform;
232#endif
233	_ScreenToLocalTransform(transform);
234	fDrawState->InverseTransform(transform);
235	return transform;
236}
237
238
239void
240Canvas::BlendLayer(Layer* layer)
241{
242	if (layer->Opacity() == 255) {
243		layer->Play(this);
244		layer->ReleaseReference();
245		return;
246	}
247
248	UtilityBitmap* layerBitmap = layer->RenderToBitmap(this);
249	if (layerBitmap == NULL)
250		return;
251
252	BRect destination = layerBitmap->Bounds();
253	destination.OffsetBy(layer->LeftTopOffset());
254	LocalToScreenTransform().Apply(&destination);
255
256	PushState();
257
258	fDrawState->SetDrawingMode(B_OP_ALPHA);
259	fDrawState->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
260	fDrawState->SetTransformEnabled(false);
261
262	AlphaMask* mask = new(std::nothrow) UniformAlphaMask(layer->Opacity());
263	if (mask == NULL) {
264		layerBitmap->ReleaseReference();
265		layer->ReleaseReference();
266		return;
267	}
268
269	SetAlphaMask(mask);
270	mask->ReleaseReference();
271	ResyncDrawState();
272
273	GetDrawingEngine()->DrawBitmap(layerBitmap, layerBitmap->Bounds(),
274		destination, 0);
275
276	fDrawState->SetTransformEnabled(true);
277
278	PopState();
279	ResyncDrawState();
280
281	layerBitmap->ReleaseReference();
282	layer->ReleaseReference();
283}
284
285
286// #pragma mark - OffscreenCanvas
287
288
289OffscreenCanvas::OffscreenCanvas(DrawingEngine* engine,
290		const DrawState& state, const IntRect& bounds)
291	:
292	Canvas(state),
293	fDrawingEngine(engine),
294	fBounds(bounds)
295{
296	ResyncDrawState();
297}
298
299
300OffscreenCanvas::~OffscreenCanvas()
301{
302	delete fDrawState;
303}
304
305
306void
307OffscreenCanvas::ResyncDrawState()
308{
309	fDrawingEngine->SetDrawState(fDrawState);
310}
311
312
313void
314OffscreenCanvas::UpdateCurrentDrawingRegion()
315{
316	if (fDrawState->HasClipping()) {
317		fDrawState->GetCombinedClippingRegion(&fCurrentDrawingRegion);
318		fDrawingEngine->ConstrainClippingRegion(&fCurrentDrawingRegion);
319	}
320}
321
322
323IntRect
324OffscreenCanvas::Bounds() const
325{
326	return fBounds;
327}
328