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
BitmapPainter(const Painter * painter,const ServerBitmap * bitmap,uint32 options)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
Draw(const BRect & sourceRect,const BRect & destinationRect)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 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
177 bool
_DetermineTransform(BRect sourceRect,const BRect & destinationRect)178 Painter::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
237 bool
_HasScale()238 Painter::BitmapPainter::_HasScale()
239 {
240 return fScaleX != 1.0 || fScaleY != 1.0;
241 }
242
243
244 bool
_HasAffineTransform()245 Painter::BitmapPainter::_HasAffineTransform()
246 {
247 return !fPainter->fIdentityTransform;
248 }
249
250
251 bool
_HasAlphaMask()252 Painter::BitmapPainter::_HasAlphaMask()
253 {
254 return fPainter->fInternal.fMaskedUnpackedScanline != NULL;
255 }
256
257
258 void
_ConvertColorSpace(ObjectDeleter<BBitmap> & convertedBitmapDeleter)259 Painter::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
332 template<typename sourcePixel>
333 void
_TransparentMagicToAlpha(sourcePixel * buffer,uint32 width,uint32 height,uint32 sourceBytesPerRow,sourcePixel transparentMagic,BBitmap * output)334 Painter::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