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 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 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 238 Painter::BitmapPainter::_HasScale() 239 { 240 return fScaleX != 1.0 || fScaleY != 1.0; 241 } 242 243 244 bool 245 Painter::BitmapPainter::_HasAffineTransform() 246 { 247 return !fPainter->fIdentityTransform; 248 } 249 250 251 bool 252 Painter::BitmapPainter::_HasAlphaMask() 253 { 254 return fPainter->fInternal.fMaskedUnpackedScanline != NULL; 255 } 256 257 258 void 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 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