xref: /haiku/src/servers/app/drawing/Painter/bitmap_painter/BitmapPainter.cpp (revision 79a483ebbfdbf0bdf39c99b12c128a4fd088aa1d)
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 
33 Painter::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 
57 void
58 Painter::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 		bitmapBounds.left, bitmapBounds.top,
69 		bitmapBounds.right, bitmapBounds.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 	// optimized version for no scale in CMAP8 or RGB32 OP_OVER
82 	if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) {
83 		if (fColorSpace == B_CMAP8) {
84 			if (fPainter->fDrawingMode == B_OP_COPY) {
85 				DrawBitmapNoScale<CMap8Copy> drawNoScale;
86 				drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
87 					fDestinationRect);
88 				return;
89 			}
90 			if (fPainter->fDrawingMode == B_OP_OVER) {
91 				DrawBitmapNoScale<CMap8Over> drawNoScale;
92 				drawNoScale.Draw(fPainter->fInternal, fBitmap, 1, fOffset,
93 					fDestinationRect);
94 				return;
95 			}
96 		} else if (fColorSpace == B_RGB32) {
97 			if (fPainter->fDrawingMode == B_OP_OVER) {
98 				DrawBitmapNoScale<Bgr32Over> drawNoScale;
99 				drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
100 					fDestinationRect);
101 				return;
102 			}
103 		}
104 	}
105 
106 	ObjectDeleter<BBitmap> convertedBitmapDeleter;
107 	_ConvertColorSpace(convertedBitmapDeleter);
108 
109 	// optimized version if there is no scale
110 	if (!_HasScale() && !_HasAffineTransform() && !_HasAlphaMask()) {
111 		if (fPainter->fDrawingMode == B_OP_COPY) {
112 			DrawBitmapNoScale<Bgr32Copy> drawNoScale;
113 			drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
114 				fDestinationRect);
115 			return;
116 		}
117 		if (fPainter->fDrawingMode == B_OP_OVER
118 			|| (fPainter->fDrawingMode == B_OP_ALPHA
119 				 && fPainter->fAlphaSrcMode == B_PIXEL_ALPHA
120 				 && fPainter->fAlphaFncMode == B_ALPHA_OVERLAY)) {
121 			DrawBitmapNoScale<Bgr32Alpha> drawNoScale;
122 			drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
123 				fDestinationRect);
124 			return;
125 		}
126 	}
127 
128 	if (!_HasScale() && !_HasAffineTransform() && _HasAlphaMask()) {
129 		if (fPainter->fDrawingMode == B_OP_COPY) {
130 			DrawBitmapNoScale<Bgr32CopyMasked> drawNoScale;
131 			drawNoScale.Draw(fPainter->fInternal, fBitmap, 4, fOffset,
132 				fDestinationRect);
133 			return;
134 		}
135 	}
136 
137 	// bilinear and nearest-neighbor scaled, OP_COPY only
138 	if (fPainter->fDrawingMode == B_OP_COPY
139 		&& !_HasAffineTransform() && !_HasAlphaMask()) {
140 		if ((fOptions & B_FILTER_BITMAP_BILINEAR) != 0) {
141 			DrawBitmapBilinearCopy drawBilinear;
142 			drawBilinear.Draw(fPainter, fPainter->fInternal,
143 				fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
144 		}
145 		else {
146 			DrawBitmapNearestNeighborCopy::Draw(fPainter, fPainter->fInternal,
147 				fBitmap, fOffset, fScaleX, fScaleY, fDestinationRect);
148 		}
149 		return;
150 	}
151 
152 	// for all other cases (non-optimized drawing mode or scaled drawing)
153 	DrawBitmapGeneric::Draw(fPainter, fPainter->fInternal, fBitmap, fOffset,
154 		fScaleX, fScaleY, fDestinationRect, fOptions);
155 }
156 
157 
158 bool
159 Painter::BitmapPainter::_DetermineTransform(BRect sourceRect,
160 	const BRect& destinationRect)
161 {
162 	if (!fPainter->fValidClipping
163 		|| !sourceRect.IsValid()
164 		|| !sourceRect.Intersects(fBitmapBounds)
165 		|| !destinationRect.IsValid()) {
166 		return false;
167 	}
168 
169 	fDestinationRect = destinationRect;
170 
171 	if (!fPainter->fSubpixelPrecise) {
172 		align_rect_to_pixels(&sourceRect);
173 		align_rect_to_pixels(&fDestinationRect);
174 	}
175 
176 	fScaleX = (fDestinationRect.Width() + 1) / (sourceRect.Width() + 1);
177 	fScaleY = (fDestinationRect.Height() + 1) / (sourceRect.Height() + 1);
178 
179 	if (fScaleX == 0.0 || fScaleY == 0.0)
180 		return false;
181 
182 	// constrain source rect to bitmap bounds and transfer the changes to
183 	// the destination rect with the right scale
184 	if (sourceRect.left < fBitmapBounds.left) {
185 		float diff = fBitmapBounds.left - sourceRect.left;
186 		fDestinationRect.left += diff * fScaleX;
187 		sourceRect.left = fBitmapBounds.left;
188 	}
189 	if (sourceRect.top < fBitmapBounds.top) {
190 		float diff = fBitmapBounds.top - sourceRect.top;
191 		fDestinationRect.top += diff * fScaleY;
192 		sourceRect.top = fBitmapBounds.top;
193 	}
194 	if (sourceRect.right > fBitmapBounds.right) {
195 		float diff = sourceRect.right - fBitmapBounds.right;
196 		fDestinationRect.right -= diff * fScaleX;
197 		sourceRect.right = fBitmapBounds.right;
198 	}
199 	if (sourceRect.bottom > fBitmapBounds.bottom) {
200 		float diff = sourceRect.bottom - fBitmapBounds.bottom;
201 		fDestinationRect.bottom -= diff * fScaleY;
202 		sourceRect.bottom = fBitmapBounds.bottom;
203 	}
204 
205 	fOffset.x = fDestinationRect.left - sourceRect.left;
206 	fOffset.y = fDestinationRect.top - sourceRect.top;
207 
208 	return true;
209 }
210 
211 
212 bool
213 Painter::BitmapPainter::_HasScale()
214 {
215 	return fScaleX != 1.0 || fScaleY != 1.0;
216 }
217 
218 
219 bool
220 Painter::BitmapPainter::_HasAffineTransform()
221 {
222 	return !fPainter->fIdentityTransform;
223 }
224 
225 
226 bool
227 Painter::BitmapPainter::_HasAlphaMask()
228 {
229 	return fPainter->fInternal.fMaskedUnpackedScanline != NULL;
230 }
231 
232 
233 void
234 Painter::BitmapPainter::_ConvertColorSpace(
235 	ObjectDeleter<BBitmap>& convertedBitmapDeleter)
236 {
237 	if (fColorSpace == B_RGBA32)
238 		return;
239 
240 	if (fColorSpace == B_RGB32
241 		&& (fPainter->fDrawingMode == B_OP_COPY
242 #if 1
243 // Enabling this would make the behavior compatible to BeOS, which
244 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in
245 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled.
246 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually
247 // use this color, unless the alpha channel contains 255 for all other
248 // pixels, which is inconsistent.
249 		|| fPainter->fDrawingMode == B_OP_ALPHA
250 #endif
251 		)) {
252 		return;
253 	}
254 
255 	BBitmap* conversionBitmap = new(nothrow) BBitmap(fBitmapBounds,
256 		B_BITMAP_NO_SERVER_LINK, B_RGBA32);
257 	if (conversionBitmap == NULL) {
258 		fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - "
259 			"out of memory for creating temporary conversion bitmap\n");
260 		return;
261 	}
262 	convertedBitmapDeleter.SetTo(conversionBitmap);
263 
264 	status_t err = conversionBitmap->ImportBits(fBitmap.buf(),
265 		fBitmap.height() * fBitmap.stride(),
266 		fBitmap.stride(), 0, fColorSpace);
267 	if (err < B_OK) {
268 		fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - "
269 			"colorspace conversion failed: %s\n", strerror(err));
270 		return;
271 	}
272 
273 	// the original bitmap might have had some of the
274 	// transaparent magic colors set that we now need to
275 	// make transparent in our RGBA32 bitmap again.
276 	switch (fColorSpace) {
277 		case B_RGB32:
278 			_TransparentMagicToAlpha((uint32 *)fBitmap.buf(),
279 				fBitmap.width(), fBitmap.height(),
280 				fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA32,
281 				conversionBitmap);
282 			break;
283 
284 		// TODO: not sure if this applies to B_RGBA15 too. It
285 		// should not because B_RGBA15 actually has an alpha
286 		// channel itself and it should have been preserved
287 		// when importing the bitmap. Maybe it applies to
288 		// B_RGB16 though?
289 		case B_RGB15:
290 			_TransparentMagicToAlpha((uint16 *)fBitmap.buf(),
291 				fBitmap.width(), fBitmap.height(),
292 				fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA15,
293 				conversionBitmap);
294 			break;
295 
296 		default:
297 			break;
298 	}
299 
300 	fBitmap.attach((uint8*)conversionBitmap->Bits(),
301 		(uint32)fBitmapBounds.IntegerWidth() + 1,
302 		(uint32)fBitmapBounds.IntegerHeight() + 1,
303 		conversionBitmap->BytesPerRow());
304 }
305 
306 
307 template<typename sourcePixel>
308 void
309 Painter::BitmapPainter::_TransparentMagicToAlpha(sourcePixel* buffer,
310 	uint32 width, uint32 height, uint32 sourceBytesPerRow,
311 	sourcePixel transparentMagic, BBitmap* output)
312 {
313 	uint8* sourceRow = (uint8*)buffer;
314 	uint8* destRow = (uint8*)output->Bits();
315 	uint32 destBytesPerRow = output->BytesPerRow();
316 
317 	for (uint32 y = 0; y < height; y++) {
318 		sourcePixel* pixel = (sourcePixel*)sourceRow;
319 		uint32* destPixel = (uint32*)destRow;
320 		for (uint32 x = 0; x < width; x++, pixel++, destPixel++) {
321 			if (*pixel == transparentMagic)
322 				*destPixel &= 0x00ffffff;
323 		}
324 
325 		sourceRow += sourceBytesPerRow;
326 		destRow += destBytesPerRow;
327 	}
328 }
329