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