xref: /haiku/src/servers/app/drawing/AlphaMask.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
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