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