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.IsSet())
61		return B_NO_MEMORY;
62
63	return B_OK;
64}
65
66
67void
68Canvas::PushState()
69{
70	DrawState* previous = fDrawState.Detach();
71	DrawState* newState = previous->PushState();
72	if (newState == NULL)
73		newState = previous;
74
75	fDrawState.SetTo(newState);
76}
77
78
79void
80Canvas::PopState()
81{
82	if (fDrawState->PreviousState() == NULL)
83		return;
84
85	bool rebuildClipping = fDrawState->HasAdditionalClipping();
86
87	fDrawState.SetTo(fDrawState->PopState());
88
89	// rebuild clipping
90	// (the clipping from the popped state is not effective anymore)
91	if (rebuildClipping)
92		RebuildClipping(false);
93}
94
95
96void
97Canvas::SetDrawState(DrawState* newState)
98{
99	fDrawState.SetTo(newState);
100}
101
102
103void
104Canvas::SetDrawingOrigin(BPoint origin)
105{
106	fDrawState->SetOrigin(origin);
107
108	// rebuild clipping
109	if (fDrawState->HasClipping())
110		RebuildClipping(false);
111}
112
113
114BPoint
115Canvas::DrawingOrigin() const
116{
117	return fDrawState->Origin();
118}
119
120
121void
122Canvas::SetScale(float scale)
123{
124	fDrawState->SetScale(scale);
125
126	// rebuild clipping
127	if (fDrawState->HasClipping())
128		RebuildClipping(false);
129}
130
131
132float
133Canvas::Scale() const
134{
135	return fDrawState->Scale();
136}
137
138
139void
140Canvas::SetUserClipping(const BRegion* region)
141{
142	fDrawState->SetClippingRegion(region);
143
144	// rebuild clipping (for just this canvas)
145	RebuildClipping(false);
146}
147
148
149bool
150Canvas::ClipToRect(BRect rect, bool inverse)
151{
152	bool needDrawStateUpdate = fDrawState->ClipToRect(rect, inverse);
153	RebuildClipping(false);
154	return needDrawStateUpdate;
155}
156
157
158void
159Canvas::ClipToShape(shape_data* shape, bool inverse)
160{
161	fDrawState->ClipToShape(shape, inverse);
162}
163
164
165void
166Canvas::SetAlphaMask(AlphaMask* mask)
167{
168	fDrawState->SetAlphaMask(mask);
169}
170
171
172AlphaMask*
173Canvas::GetAlphaMask() const
174{
175	return fDrawState->GetAlphaMask();
176}
177
178
179SimpleTransform
180Canvas::LocalToScreenTransform() const GCC_2_NRV(transform)
181{
182#if __GNUC__ >= 3
183	SimpleTransform transform;
184#endif
185	_LocalToScreenTransform(transform);
186	return transform;
187}
188
189
190SimpleTransform
191Canvas::ScreenToLocalTransform() const GCC_2_NRV(transform)
192{
193#if __GNUC__ >= 3
194	SimpleTransform transform;
195#endif
196	_ScreenToLocalTransform(transform);
197	return transform;
198}
199
200
201SimpleTransform
202Canvas::PenToScreenTransform() const GCC_2_NRV(transform)
203{
204#if __GNUC__ >= 3
205	SimpleTransform transform;
206#endif
207	fDrawState->Transform(transform);
208	_LocalToScreenTransform(transform);
209	return transform;
210}
211
212
213SimpleTransform
214Canvas::PenToLocalTransform() const GCC_2_NRV(transform)
215{
216#if __GNUC__ >= 3
217	SimpleTransform transform;
218#endif
219	fDrawState->Transform(transform);
220	return transform;
221}
222
223
224SimpleTransform
225Canvas::ScreenToPenTransform() const GCC_2_NRV(transform)
226{
227#if __GNUC__ >= 3
228	SimpleTransform transform;
229#endif
230	_ScreenToLocalTransform(transform);
231	fDrawState->InverseTransform(transform);
232	return transform;
233}
234
235
236void
237Canvas::BlendLayer(Layer* layerPtr)
238{
239	BReference<Layer> layer(layerPtr, true);
240
241	if (layer->Opacity() == 255) {
242		layer->Play(this);
243		return;
244	}
245
246	BReference <UtilityBitmap> layerBitmap(layer->RenderToBitmap(this), true);
247	if (layerBitmap == NULL)
248		return;
249
250	BRect destination = layerBitmap->Bounds();
251	destination.OffsetBy(layer->LeftTopOffset());
252	LocalToScreenTransform().Apply(&destination);
253
254	PushState();
255
256	fDrawState->SetDrawingMode(B_OP_ALPHA);
257	fDrawState->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
258	fDrawState->SetTransformEnabled(false);
259
260	BReference<AlphaMask> mask(new(std::nothrow) UniformAlphaMask(layer->Opacity()), true);
261	if (mask == NULL)
262		return;
263
264	SetAlphaMask(mask);
265	ResyncDrawState();
266
267	GetDrawingEngine()->DrawBitmap(layerBitmap, layerBitmap->Bounds(),
268		destination, 0);
269
270	fDrawState->SetTransformEnabled(true);
271
272	PopState();
273	ResyncDrawState();
274}
275
276
277// #pragma mark - OffscreenCanvas
278
279
280OffscreenCanvas::OffscreenCanvas(DrawingEngine* engine,
281		const DrawState& state, const IntRect& bounds)
282	:
283	Canvas(state),
284	fDrawingEngine(engine),
285	fBounds(bounds)
286{
287	ResyncDrawState();
288}
289
290
291OffscreenCanvas::~OffscreenCanvas()
292{
293}
294
295
296void
297OffscreenCanvas::ResyncDrawState()
298{
299	fDrawingEngine->SetDrawState(fDrawState.Get());
300}
301
302
303void
304OffscreenCanvas::UpdateCurrentDrawingRegion()
305{
306	if (fDrawState->HasClipping()) {
307		fDrawState->GetCombinedClippingRegion(&fCurrentDrawingRegion);
308		fDrawingEngine->ConstrainClippingRegion(&fCurrentDrawingRegion);
309	}
310}
311
312
313IntRect
314OffscreenCanvas::Bounds() const
315{
316	return fBounds;
317}
318