1 /* 2 * Copyright 2014, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Adrien Destugues <pulkomandy@pulkomandy.tk> 7 * Stephan Aßmus <superstippi@gmx.de> 8 */ 9 10 11 #include "AlphaMask.h" 12 13 #include "BitmapHWInterface.h" 14 #include "BitmapManager.h" 15 #include "DrawingContext.h" 16 #include "DrawingEngine.h" 17 #include "ServerBitmap.h" 18 #include "ServerPicture.h" 19 20 21 AlphaMask::AlphaMask(ServerPicture* picture, bool inverse, BPoint origin, 22 const DrawState& drawState) 23 : 24 fPreviousMask(NULL), 25 26 fPicture(picture), 27 fInverse(inverse), 28 fOrigin(origin), 29 fDrawState(drawState), 30 31 fViewBounds(), 32 fViewOffset(), 33 34 fCachedBitmap(NULL), 35 fCachedBounds(), 36 fCachedOffset(), 37 38 fBuffer(), 39 fCachedMask(), 40 fScanline(fCachedMask) 41 { 42 fPicture->AcquireReference(); 43 } 44 45 46 AlphaMask::~AlphaMask() 47 { 48 fPicture->ReleaseReference(); 49 delete[] fCachedBitmap; 50 SetPrevious(NULL); 51 } 52 53 54 void 55 AlphaMask::Update(BRect bounds, BPoint offset) 56 { 57 fViewBounds = bounds; 58 fViewOffset = offset; 59 60 if (fPreviousMask != NULL) 61 fPreviousMask->Update(bounds, offset); 62 } 63 64 65 void 66 AlphaMask::SetPrevious(AlphaMask* mask) 67 { 68 // Since multiple DrawStates can point to the same AlphaMask, 69 // don't accept ourself as the "previous" mask on the state stack. 70 if (mask == this || mask == fPreviousMask) 71 return; 72 73 if (mask != NULL) 74 mask->AcquireReference(); 75 if (fPreviousMask != NULL) 76 fPreviousMask->ReleaseReference(); 77 fPreviousMask = mask; 78 } 79 80 81 scanline_unpacked_masked_type* 82 AlphaMask::Generate() 83 { 84 if (fPicture == NULL || !fViewBounds.IsValid()) 85 return NULL; 86 87 // See if a cached bitmap can be used. Don't use it when the view offset 88 // or bounds have changed. 89 if (fCachedBitmap != NULL 90 && fViewBounds == fCachedBounds && fViewOffset == fCachedOffset) { 91 return &fScanline; 92 } 93 94 uint32 width = fViewBounds.IntegerWidth() + 1; 95 uint32 height = fViewBounds.IntegerHeight() + 1; 96 97 if (fViewBounds != fCachedBounds || fCachedBitmap == NULL) { 98 delete[] fCachedBitmap; 99 fCachedBitmap = new(std::nothrow) uint8[width * height]; 100 } 101 102 // If rendering the picture fails, we will draw without any clipping. 103 ServerBitmap* bitmap = _RenderPicture(); 104 if (bitmap == NULL || fCachedBitmap == NULL) { 105 fBuffer.attach(NULL, 0, 0, 0); 106 return NULL; 107 } 108 109 uint8* bits = bitmap->Bits(); 110 uint32 bytesPerRow = bitmap->BytesPerRow(); 111 uint8* row = bits; 112 uint8* pixel = fCachedBitmap; 113 114 // Let any previous masks also regenerate themselves. Updating the cached 115 // mask bitmap is only necessary after the view size changed or the 116 // scrolling offset, which definitely affects any masks of lower states 117 // as well, so it works recursively until the bottom mask is regenerated. 118 bool transferBitmap = true; 119 if (fPreviousMask != NULL) { 120 fPreviousMask->Generate(); 121 if (fPreviousMask->fCachedBitmap != NULL) { 122 uint8* previousBits = fPreviousMask->fCachedBitmap; 123 for (uint32 y = 0; y < height; y++) { 124 for (uint32 x = 0; x < width; x++) { 125 if (previousBits[0] != 0) { 126 if (fInverse) 127 pixel[0] = 255 - row[3]; 128 else 129 pixel[0] = row[3]; 130 pixel[0] = pixel[0] * previousBits[0] / 255; 131 } else 132 pixel[0] = 0; 133 previousBits++; 134 pixel++; 135 row += 4; 136 } 137 bits += bytesPerRow; 138 row = bits; 139 } 140 transferBitmap = false; 141 } 142 } 143 144 if (transferBitmap) { 145 for (uint32 y = 0; y < height; y++) { 146 for (uint32 x = 0; x < width; x++) { 147 if (fInverse) 148 pixel[0] = 255 - row[3]; 149 else 150 pixel[0] = row[3]; 151 pixel++; 152 row += 4; 153 } 154 bits += bytesPerRow; 155 row = bits; 156 } 157 } 158 159 bitmap->ReleaseReference(); 160 161 fCachedBounds = fViewBounds; 162 fCachedOffset = fViewOffset; 163 164 fBuffer.attach(fCachedBitmap, width, height, width); 165 166 fCachedMask.attach(fBuffer, fViewOffset.x + fOrigin.x, 167 fViewOffset.y + fOrigin.y, fInverse ? 255 : 0); 168 169 return &fScanline; 170 } 171 172 173 ServerBitmap* 174 AlphaMask::_RenderPicture() const 175 { 176 UtilityBitmap* bitmap = new(std::nothrow) UtilityBitmap(fViewBounds, 177 B_RGBA32, 0); 178 if (bitmap == NULL) 179 return NULL; 180 181 if (!bitmap->IsValid()) { 182 delete bitmap; 183 return NULL; 184 } 185 186 // Clear the bitmap with the transparent color 187 memset(bitmap->Bits(), 0, bitmap->BitsLength()); 188 189 // Render the picture to the bitmap 190 BitmapHWInterface interface(bitmap); 191 DrawingEngine* engine = interface.CreateDrawingEngine(); 192 if (engine == NULL) { 193 delete bitmap; 194 return NULL; 195 } 196 197 OffscreenContext context(engine, fDrawState); 198 context.PushState(); 199 200 if (engine->LockParallelAccess()) { 201 // FIXME ConstrainClippingRegion docs says passing NULL disables 202 // all clipping. This doesn't work and will crash in Painter. 203 BRegion clipping; 204 clipping.Include(fViewBounds); 205 engine->ConstrainClippingRegion(&clipping); 206 fPicture->Play(&context); 207 engine->UnlockParallelAccess(); 208 } 209 210 context.PopState(); 211 delete engine; 212 213 return bitmap; 214 } 215 216