1/*
2 * Copyright 2009, Christian Packmann.
3 * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
4 * Copyright 2005-2014, Stephan A��mus <superstippi@gmx.de>.
5 * Copyright 2015, Julian Harnath <julian.harnath@rwth-aachen.de>
6 * All rights reserved. Distributed under the terms of the MIT License.
7 */
8#include "BitmapPainter.h"
9
10#include <Bitmap.h>
11
12#include <agg_image_accessors.h>
13#include <agg_pixfmt_rgba.h>
14#include <agg_span_image_filter_rgba.h>
15
16#include "DrawBitmapBilinear.h"
17#include "DrawBitmapGeneric.h"
18#include "DrawBitmapNearestNeighbor.h"
19#include "DrawBitmapNoScale.h"
20#include "drawing_support.h"
21#include "ServerBitmap.h"
22#include "SystemPalette.h"
23
24
25// #define TRACE_BITMAP_PAINTER
26#ifdef TRACE_BITMAP_PAINTER
27#	define TRACE(x...)		printf(x)
28#else
29#	define TRACE(x...)
30#endif
31
32
33Painter::BitmapPainter::BitmapPainter(const Painter* painter,
34	const ServerBitmap* bitmap, uint32 options)
35	:
36	fPainter(painter),
37	fStatus(B_NO_INIT),
38	fOptions(options)
39{
40	if (bitmap == NULL || !bitmap->IsValid())
41		return;
42
43	fBitmapBounds = bitmap->Bounds();
44	fBitmapBounds.OffsetBy(-fBitmapBounds.left, -fBitmapBounds.top);
45		// Compensate for the lefttop offset the bitmap bounds might have
46		// It has the right size, but put it at B_ORIGIN
47
48	fColorSpace = bitmap->ColorSpace();
49
50	fBitmap.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(),
51		bitmap->BytesPerRow());
52
53	fStatus = B_OK;
54}
55
56
57void
58Painter::BitmapPainter::Draw(const BRect& sourceRect,
59	const BRect& destinationRect)
60{
61	using namespace BitmapPainterPrivate;
62
63	if (fStatus != B_OK)
64		return;
65
66	TRACE("BitmapPainter::Draw()\n");
67	TRACE("   bitmapBounds = (%.1f, %.1f) - (%.1f, %.1f)\n",
68		fBitmapBounds.left, fBitmapBounds.top,
69		fBitmapBounds.right, fBitmapBounds.bottom);
70	TRACE("   sourceRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
71		sourceRect.left, sourceRect.top,
72		sourceRect.right, sourceRect.bottom);
73	TRACE("   destinationRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
74		destinationRect.left, destinationRect.top,
75		destinationRect.right, destinationRect.bottom);
76
77	bool success = _DetermineTransform(sourceRect, destinationRect);
78	if (!success)
79		return;
80
81	if ((fOptions & B_TILE_BITMAP) == 0) {
82		// optimized version for no scale in CMAP8 or RGB32 OP_OVER
83		if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) {
84			if (fColorSpace == B_CMAP8) {
85				if (fPainter->fDrawingMode == B_OP_COPY) {
86					DrawBitmapNoScale<CMap8Copy> drawNoScale;
87					drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
88						fDestinationRect);
89					return;
90				}
91				if (fPainter->fDrawingMode == B_OP_OVER) {
92					DrawBitmapNoScale<CMap8Over> drawNoScale;
93					drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
94						fDestinationRect);
95					return;
96				}
97			} else if (fColorSpace == B_RGB32) {
98				if (fPainter->fDrawingMode == B_OP_OVER) {
99					DrawBitmapNoScale<Bgr32Over> drawNoScale;
100					drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
101						fDestinationRect);
102					return;
103				}
104			}
105		}
106	}
107
108	ObjectDeleter<BBitmap> convertedBitmapDeleter;
109	_ConvertColorSpace(convertedBitmapDeleter);
110
111	if ((fOptions & B_TILE_BITMAP) == 0) {
112		// optimized version if there is no scale
113		if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) {
114			if (fPainter->fDrawingMode == B_OP_COPY) {
115				DrawBitmapNoScale<Bgr32Copy> drawNoScale;
116				drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
117					fDestinationRect);
118				return;
119			}
120			if (fPainter->fDrawingMode == B_OP_OVER
121				|| (fPainter->fDrawingMode == B_OP_ALPHA
122					 && fPainter->fAlphaSrcMode == B_PIXEL_ALPHA
123					 && fPainter->fAlphaFncMode == B_ALPHA_OVERLAY)) {
124				DrawBitmapNoScale<Bgr32Alpha> drawNoScale;
125				drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
126					fDestinationRect);
127				return;
128			}
129		}
130
131		if (!_HasScale() && !_HasAffineTransform() && _HasAlphaMask()) {
132			if (fPainter->fDrawingMode == B_OP_COPY) {
133				DrawBitmapNoScale<Bgr32CopyMasked> drawNoScale;
134				drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
135					fDestinationRect);
136				return;
137			}
138		}
139
140		// bilinear and nearest-neighbor scaled, OP_COPY only
141		if (fPainter->fDrawingMode == B_OP_COPY
142			&& !_HasAffineTransform() && !_HasAlphaMask()) {
143			if ((fOptions & B_FILTER_BITMAP_BILINEAR) != 0) {
144				DrawBitmapBilinear<ColorTypeRgb, DrawModeCopy> drawBilinear;
145				drawBilinear.Draw(fPainter, fPainter->fInternal,
146					fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
147			} else {
148				DrawBitmapNearestNeighborCopy::Draw(fPainter, fPainter->fInternal,
149					fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
150			}
151			return;
152		}
153
154		if (fPainter->fDrawingMode == B_OP_ALPHA
155			&& fPainter->fAlphaSrcMode == B_PIXEL_ALPHA
156			&& fPainter->fAlphaFncMode == B_ALPHA_OVERLAY
157			&& !_HasAffineTransform() && !_HasAlphaMask()
158			&& (fOptions & B_FILTER_BITMAP_BILINEAR) != 0) {
159			DrawBitmapBilinear<ColorTypeRgba, DrawModeAlphaOverlay> drawBilinear;
160			drawBilinear.Draw(fPainter, fPainter->fInternal,
161				fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
162			return;
163		}
164	}
165
166	if ((fOptions & B_TILE_BITMAP) != 0) {
167		DrawBitmapGeneric<Tile>::Draw(fPainter, fPainter->fInternal, fBitmap,
168			fOffset, fScaleX, fScaleY, fDestinationRect, fOptions);
169	} else {
170		// for all other cases (non-optimized drawing mode or scaled drawing)
171		DrawBitmapGeneric<Fill>::Draw(fPainter, fPainter->fInternal, fBitmap,
172			fOffset, fScaleX, fScaleY, fDestinationRect, fOptions);
173	}
174}
175
176
177bool
178Painter::BitmapPainter::_DetermineTransform(BRect sourceRect,
179	const BRect& destinationRect)
180{
181	if (!fPainter->fValidClipping
182		|| !sourceRect.IsValid()
183		|| ((fOptions & B_TILE_BITMAP) == 0
184			&& !sourceRect.Intersects(fBitmapBounds))
185		|| !destinationRect.IsValid()) {
186		return false;
187	}
188
189	fDestinationRect = destinationRect;
190
191	if (!fPainter->fSubpixelPrecise) {
192		align_rect_to_pixels(&sourceRect);
193		align_rect_to_pixels(&fDestinationRect);
194	}
195
196	if((fOptions & B_TILE_BITMAP) == 0) {
197		fScaleX = (fDestinationRect.Width() + 1) / (sourceRect.Width() + 1);
198		fScaleY = (fDestinationRect.Height() + 1) / (sourceRect.Height() + 1);
199
200		if (fScaleX == 0.0 || fScaleY == 0.0)
201			return false;
202
203		// constrain source rect to bitmap bounds and transfer the changes to
204		// the destination rect with the right scale
205		if (sourceRect.left < fBitmapBounds.left) {
206			float diff = fBitmapBounds.left - sourceRect.left;
207			fDestinationRect.left += diff * fScaleX;
208			sourceRect.left = fBitmapBounds.left;
209		}
210		if (sourceRect.top < fBitmapBounds.top) {
211			float diff = fBitmapBounds.top - sourceRect.top;
212			fDestinationRect.top += diff * fScaleY;
213			sourceRect.top = fBitmapBounds.top;
214		}
215		if (sourceRect.right > fBitmapBounds.right) {
216			float diff = sourceRect.right - fBitmapBounds.right;
217			fDestinationRect.right -= diff * fScaleX;
218			sourceRect.right = fBitmapBounds.right;
219		}
220		if (sourceRect.bottom > fBitmapBounds.bottom) {
221			float diff = sourceRect.bottom - fBitmapBounds.bottom;
222			fDestinationRect.bottom -= diff * fScaleY;
223			sourceRect.bottom = fBitmapBounds.bottom;
224		}
225	} else {
226		fScaleX = 1.0;
227		fScaleY = 1.0;
228	}
229
230	fOffset.x = fDestinationRect.left - sourceRect.left;
231	fOffset.y = fDestinationRect.top - sourceRect.top;
232
233	return true;
234}
235
236
237bool
238Painter::BitmapPainter::_HasScale()
239{
240	return fScaleX != 1.0 || fScaleY != 1.0;
241}
242
243
244bool
245Painter::BitmapPainter::_HasAffineTransform()
246{
247	return !fPainter->fIdentityTransform;
248}
249
250
251bool
252Painter::BitmapPainter::_HasAlphaMask()
253{
254	return fPainter->fInternal.fMaskedUnpackedScanline != NULL;
255}
256
257
258void
259Painter::BitmapPainter::_ConvertColorSpace(
260	ObjectDeleter<BBitmap>& convertedBitmapDeleter)
261{
262	if (fColorSpace == B_RGBA32)
263		return;
264
265	if (fColorSpace == B_RGB32
266		&& (fPainter->fDrawingMode == B_OP_COPY
267#if 1
268// Enabling this would make the behavior compatible to BeOS, which
269// treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in
270// all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled.
271// B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually
272// use this color, unless the alpha channel contains 255 for all other
273// pixels, which is inconsistent.
274		|| fPainter->fDrawingMode == B_OP_ALPHA
275#endif
276		)) {
277		return;
278	}
279
280	BBitmap* conversionBitmap = new(std::nothrow) BBitmap(fBitmapBounds,
281		B_BITMAP_NO_SERVER_LINK, B_RGBA32);
282	if (conversionBitmap == NULL) {
283		fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - "
284			"out of memory for creating temporary conversion bitmap\n");
285		return;
286	}
287	convertedBitmapDeleter.SetTo(conversionBitmap);
288
289	status_t err = conversionBitmap->ImportBits(fBitmap.buf(),
290		fBitmap.height() * fBitmap.stride(),
291		fBitmap.stride(), 0, fColorSpace);
292	if (err < B_OK) {
293		fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - "
294			"colorspace conversion failed: %s\n", strerror(err));
295		return;
296	}
297
298	// the original bitmap might have had some of the
299	// transaparent magic colors set that we now need to
300	// make transparent in our RGBA32 bitmap again.
301	switch (fColorSpace) {
302		case B_RGB32:
303			_TransparentMagicToAlpha((uint32 *)fBitmap.buf(),
304				fBitmap.width(), fBitmap.height(),
305				fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA32,
306				conversionBitmap);
307			break;
308
309		// TODO: not sure if this applies to B_RGBA15 too. It
310		// should not because B_RGBA15 actually has an alpha
311		// channel itself and it should have been preserved
312		// when importing the bitmap. Maybe it applies to
313		// B_RGB16 though?
314		case B_RGB15:
315			_TransparentMagicToAlpha((uint16 *)fBitmap.buf(),
316				fBitmap.width(), fBitmap.height(),
317				fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA15,
318				conversionBitmap);
319			break;
320
321		default:
322			break;
323	}
324
325	fBitmap.attach((uint8*)conversionBitmap->Bits(),
326		(uint32)fBitmapBounds.IntegerWidth() + 1,
327		(uint32)fBitmapBounds.IntegerHeight() + 1,
328		conversionBitmap->BytesPerRow());
329}
330
331
332template<typename sourcePixel>
333void
334Painter::BitmapPainter::_TransparentMagicToAlpha(sourcePixel* buffer,
335	uint32 width, uint32 height, uint32 sourceBytesPerRow,
336	sourcePixel transparentMagic, BBitmap* output)
337{
338	uint8* sourceRow = (uint8*)buffer;
339	uint8* destRow = (uint8*)output->Bits();
340	uint32 destBytesPerRow = output->BytesPerRow();
341
342	for (uint32 y = 0; y < height; y++) {
343		sourcePixel* pixel = (sourcePixel*)sourceRow;
344		uint32* destPixel = (uint32*)destRow;
345		for (uint32 x = 0; x < width; x++, pixel++, destPixel++) {
346			if (*pixel == transparentMagic)
347				*destPixel &= 0x00ffffff;
348		}
349
350		sourceRow += sourceBytesPerRow;
351		destRow += destBytesPerRow;
352	}
353}
354