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 || !sourceRect.Intersects(fBitmapBounds) 184 || !destinationRect.IsValid()) { 185 return false; 186 } 187 188 fDestinationRect = destinationRect; 189 190 if (!fPainter->fSubpixelPrecise) { 191 align_rect_to_pixels(&sourceRect); 192 align_rect_to_pixels(&fDestinationRect); 193 } 194 195 if((fOptions & B_TILE_BITMAP) == 0) { 196 fScaleX = (fDestinationRect.Width() + 1) / (sourceRect.Width() + 1); 197 fScaleY = (fDestinationRect.Height() + 1) / (sourceRect.Height() + 1); 198 199 if (fScaleX == 0.0 || fScaleY == 0.0) 200 return false; 201 202 // constrain source rect to bitmap bounds and transfer the changes to 203 // the destination rect with the right scale 204 if (sourceRect.left < fBitmapBounds.left) { 205 float diff = fBitmapBounds.left - sourceRect.left; 206 fDestinationRect.left += diff * fScaleX; 207 sourceRect.left = fBitmapBounds.left; 208 } 209 if (sourceRect.top < fBitmapBounds.top) { 210 float diff = fBitmapBounds.top - sourceRect.top; 211 fDestinationRect.top += diff * fScaleY; 212 sourceRect.top = fBitmapBounds.top; 213 } 214 if (sourceRect.right > fBitmapBounds.right) { 215 float diff = sourceRect.right - fBitmapBounds.right; 216 fDestinationRect.right -= diff * fScaleX; 217 sourceRect.right = fBitmapBounds.right; 218 } 219 if (sourceRect.bottom > fBitmapBounds.bottom) { 220 float diff = sourceRect.bottom - fBitmapBounds.bottom; 221 fDestinationRect.bottom -= diff * fScaleY; 222 sourceRect.bottom = fBitmapBounds.bottom; 223 } 224 } else { 225 fScaleX = 1.0; 226 fScaleY = 1.0; 227 } 228 229 fOffset.x = fDestinationRect.left - sourceRect.left; 230 fOffset.y = fDestinationRect.top - sourceRect.top; 231 232 return true; 233 } 234 235 236 bool 237 Painter::BitmapPainter::_HasScale() 238 { 239 return fScaleX != 1.0 || fScaleY != 1.0; 240 } 241 242 243 bool 244 Painter::BitmapPainter::_HasAffineTransform() 245 { 246 return !fPainter->fIdentityTransform; 247 } 248 249 250 bool 251 Painter::BitmapPainter::_HasAlphaMask() 252 { 253 return fPainter->fInternal.fMaskedUnpackedScanline != NULL; 254 } 255 256 257 void 258 Painter::BitmapPainter::_ConvertColorSpace( 259 ObjectDeleter<BBitmap>& convertedBitmapDeleter) 260 { 261 if (fColorSpace == B_RGBA32) 262 return; 263 264 if (fColorSpace == B_RGB32 265 && (fPainter->fDrawingMode == B_OP_COPY 266 #if 1 267 // Enabling this would make the behavior compatible to BeOS, which 268 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in 269 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. 270 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually 271 // use this color, unless the alpha channel contains 255 for all other 272 // pixels, which is inconsistent. 273 || fPainter->fDrawingMode == B_OP_ALPHA 274 #endif 275 )) { 276 return; 277 } 278 279 BBitmap* conversionBitmap = new(std::nothrow) BBitmap(fBitmapBounds, 280 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 281 if (conversionBitmap == NULL) { 282 fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - " 283 "out of memory for creating temporary conversion bitmap\n"); 284 return; 285 } 286 convertedBitmapDeleter.SetTo(conversionBitmap); 287 288 status_t err = conversionBitmap->ImportBits(fBitmap.buf(), 289 fBitmap.height() * fBitmap.stride(), 290 fBitmap.stride(), 0, fColorSpace); 291 if (err < B_OK) { 292 fprintf(stderr, "BitmapPainter::_ConvertColorSpace() - " 293 "colorspace conversion failed: %s\n", strerror(err)); 294 return; 295 } 296 297 // the original bitmap might have had some of the 298 // transaparent magic colors set that we now need to 299 // make transparent in our RGBA32 bitmap again. 300 switch (fColorSpace) { 301 case B_RGB32: 302 _TransparentMagicToAlpha((uint32 *)fBitmap.buf(), 303 fBitmap.width(), fBitmap.height(), 304 fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA32, 305 conversionBitmap); 306 break; 307 308 // TODO: not sure if this applies to B_RGBA15 too. It 309 // should not because B_RGBA15 actually has an alpha 310 // channel itself and it should have been preserved 311 // when importing the bitmap. Maybe it applies to 312 // B_RGB16 though? 313 case B_RGB15: 314 _TransparentMagicToAlpha((uint16 *)fBitmap.buf(), 315 fBitmap.width(), fBitmap.height(), 316 fBitmap.stride(), B_TRANSPARENT_MAGIC_RGBA15, 317 conversionBitmap); 318 break; 319 320 default: 321 break; 322 } 323 324 fBitmap.attach((uint8*)conversionBitmap->Bits(), 325 (uint32)fBitmapBounds.IntegerWidth() + 1, 326 (uint32)fBitmapBounds.IntegerHeight() + 1, 327 conversionBitmap->BytesPerRow()); 328 } 329 330 331 template<typename sourcePixel> 332 void 333 Painter::BitmapPainter::_TransparentMagicToAlpha(sourcePixel* buffer, 334 uint32 width, uint32 height, uint32 sourceBytesPerRow, 335 sourcePixel transparentMagic, BBitmap* output) 336 { 337 uint8* sourceRow = (uint8*)buffer; 338 uint8* destRow = (uint8*)output->Bits(); 339 uint32 destBytesPerRow = output->BytesPerRow(); 340 341 for (uint32 y = 0; y < height; y++) { 342 sourcePixel* pixel = (sourcePixel*)sourceRow; 343 uint32* destPixel = (uint32*)destRow; 344 for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { 345 if (*pixel == transparentMagic) 346 *destPixel &= 0x00ffffff; 347 } 348 349 sourceRow += sourceBytesPerRow; 350 destRow += destBytesPerRow; 351 } 352 } 353