Sync with trunk (r47116), hopefully without breaking anything.
[reactos.git] / subsystems / win32 / win32k / eng / alphablend.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: GDI alpha blending functions
5 * FILE: subsystems/win32/win32k/eng/alphablend.c
6 * PROGRAMER: Jason Filby
7 */
8
9 #include <win32k.h>
10
11 #define NDEBUG
12 #include <debug.h>
13
14
15 /*
16 * @implemented
17 */
18 BOOL
19 APIENTRY
20 EngAlphaBlend(IN SURFOBJ *psoDest,
21 IN SURFOBJ *psoSource,
22 IN CLIPOBJ *ClipRegion,
23 IN XLATEOBJ *ColorTranslation,
24 IN PRECTL DestRect,
25 IN PRECTL SourceRect,
26 IN BLENDOBJ *BlendObj)
27 {
28 RECTL SourceStretchedRect;
29 SIZEL SourceStretchedSize;
30 HBITMAP SourceStretchedBitmap = 0;
31 SURFOBJ* SourceStretchedObj = NULL;
32 RECTL InputRect;
33 RECTL OutputRect;
34 RECTL ClipRect;
35 RECTL CombinedRect;
36 RECTL Rect;
37 POINTL Translate;
38 INTENG_ENTER_LEAVE EnterLeaveSource;
39 INTENG_ENTER_LEAVE EnterLeaveDest;
40 SURFOBJ* InputObj;
41 SURFOBJ* OutputObj;
42 LONG Width;
43 LONG ClippingType;
44 RECT_ENUM RectEnum;
45 BOOL EnumMore;
46 INT i;
47 BOOLEAN Ret;
48
49 DPRINT("EngAlphaBlend(psoDest:0x%p, psoSource:0x%p, ClipRegion:0x%p, ColorTranslation:0x%p,\n", psoDest, psoSource, ClipRegion, ColorTranslation);
50 DPRINT(" DestRect:{0x%x, 0x%x, 0x%x, 0x%x}, SourceRect:{0x%x, 0x%x, 0x%x, 0x%x},\n",
51 DestRect->left, DestRect->top, DestRect->right, DestRect->bottom,
52 SourceRect->left, SourceRect->top, SourceRect->right, SourceRect->bottom);
53 DPRINT(" BlendObj:{0x%x, 0x%x, 0x%x, 0x%x}\n", BlendObj->BlendFunction.BlendOp,
54 BlendObj->BlendFunction.BlendFlags, BlendObj->BlendFunction.SourceConstantAlpha,
55 BlendObj->BlendFunction.AlphaFormat);
56
57 /* Validate output */
58 OutputRect = *DestRect;
59 if (OutputRect.right < OutputRect.left)
60 {
61 OutputRect.left = DestRect->right;
62 OutputRect.right = DestRect->left;
63 }
64 if (OutputRect.bottom < OutputRect.top)
65 {
66 OutputRect.left = DestRect->right;
67 OutputRect.right = DestRect->left;
68 }
69
70 /* Validate input */
71 InputRect = *SourceRect;
72 if ( (InputRect.top < 0) || (InputRect.bottom < 0) ||
73 (InputRect.left < 0) || (InputRect.right < 0) ||
74 InputRect.right > psoSource->sizlBitmap.cx ||
75 InputRect.bottom > psoSource->sizlBitmap.cy )
76 {
77 SetLastWin32Error(ERROR_INVALID_PARAMETER);
78 return FALSE;
79 }
80
81 if (psoDest == psoSource &&
82 !(OutputRect.left >= SourceRect->right || InputRect.left >= OutputRect.right ||
83 OutputRect.top >= SourceRect->bottom || InputRect.top >= OutputRect.bottom))
84 {
85 DPRINT1("Source and destination rectangles overlap!\n");
86 return FALSE;
87 }
88
89 if (BlendObj->BlendFunction.BlendOp != AC_SRC_OVER)
90 {
91 DPRINT1("BlendOp != AC_SRC_OVER (0x%x)\n", BlendObj->BlendFunction.BlendOp);
92 return FALSE;
93 }
94 if (BlendObj->BlendFunction.BlendFlags != 0)
95 {
96 DPRINT1("BlendFlags != 0 (0x%x)\n", BlendObj->BlendFunction.BlendFlags);
97 return FALSE;
98 }
99 if ((BlendObj->BlendFunction.AlphaFormat & ~AC_SRC_ALPHA) != 0)
100 {
101 DPRINT1("Unsupported AlphaFormat (0x%x)\n", BlendObj->BlendFunction.AlphaFormat);
102 return FALSE;
103 }
104
105 /* Check if there is anything to draw */
106 if (ClipRegion != NULL &&
107 (ClipRegion->rclBounds.left >= ClipRegion->rclBounds.right ||
108 ClipRegion->rclBounds.top >= ClipRegion->rclBounds.bottom))
109 {
110 /* Nothing to do */
111 return TRUE;
112 }
113
114 /* Stretch source if needed */
115 if (OutputRect.right - OutputRect.left != InputRect.right - InputRect.left ||
116 OutputRect.bottom - OutputRect.top != InputRect.bottom - InputRect.top)
117 {
118 SourceStretchedSize.cx = OutputRect.right - OutputRect.left;
119 SourceStretchedSize.cy = OutputRect.bottom - OutputRect.top;
120 Width = DIB_GetDIBWidthBytes(SourceStretchedSize.cx, BitsPerFormat(psoSource->iBitmapFormat));
121 /* FIXME: Maybe it is a good idea to use EngCreateDeviceBitmap and IntEngStretchBlt
122 if possible to get a HW accelerated stretch. */
123 SourceStretchedBitmap = EngCreateBitmap(SourceStretchedSize, Width, psoSource->iBitmapFormat,
124 BMF_TOPDOWN | BMF_NOZEROINIT, NULL);
125 if (SourceStretchedBitmap == 0)
126 {
127 DPRINT1("EngCreateBitmap failed!\n");
128 return FALSE;
129 }
130 SourceStretchedObj = EngLockSurface((HSURF)SourceStretchedBitmap);
131 if (SourceStretchedObj == NULL)
132 {
133 DPRINT1("EngLockSurface failed!\n");
134 EngDeleteSurface((HSURF)SourceStretchedBitmap);
135 return FALSE;
136 }
137
138 SourceStretchedRect.left = 0;
139 SourceStretchedRect.right = SourceStretchedSize.cx;
140 SourceStretchedRect.top = 0;
141 SourceStretchedRect.bottom = SourceStretchedSize.cy;
142 if (!IntEngStretchBlt(SourceStretchedObj, psoSource, NULL, NULL, NULL,
143 &SourceStretchedRect, SourceRect,
144 NULL, NULL, NULL, ROP3_TO_ROP4(SRCCOPY)))
145 {
146 DPRINT1("EngStretchBlt failed!\n");
147 EngFreeMem(SourceStretchedObj->pvBits);
148 EngUnlockSurface(SourceStretchedObj);
149 EngDeleteSurface((HSURF)SourceStretchedBitmap);
150 return FALSE;
151 }
152 InputRect.top = SourceStretchedRect.top;
153 InputRect.bottom = SourceStretchedRect.bottom;
154 InputRect.left = SourceStretchedRect.left;
155 InputRect.right = SourceStretchedRect.right;
156 psoSource = SourceStretchedObj;
157 }
158
159 /* Now call the DIB function */
160 if (!IntEngEnter(&EnterLeaveSource, psoSource, &InputRect, TRUE, &Translate, &InputObj))
161 {
162 if (SourceStretchedObj != NULL)
163 {
164 EngFreeMem(SourceStretchedObj->pvBits);
165 EngUnlockSurface(SourceStretchedObj);
166 }
167 if (SourceStretchedBitmap != 0)
168 {
169 EngDeleteSurface((HSURF)SourceStretchedBitmap);
170 }
171 return FALSE;
172 }
173 InputRect.left += Translate.x;
174 InputRect.right += Translate.x;
175 InputRect.top += Translate.y;
176 InputRect.bottom += Translate.y;
177
178 if (!IntEngEnter(&EnterLeaveDest, psoDest, &OutputRect, FALSE, &Translate, &OutputObj))
179 {
180 IntEngLeave(&EnterLeaveSource);
181 if (SourceStretchedObj != NULL)
182 {
183 EngFreeMem(SourceStretchedObj->pvBits);
184 EngUnlockSurface(SourceStretchedObj);
185 }
186 if (SourceStretchedBitmap != 0)
187 {
188 EngDeleteSurface((HSURF)SourceStretchedBitmap);
189 }
190 return FALSE;
191 }
192 OutputRect.left += Translate.x;
193 OutputRect.right += Translate.x;
194 OutputRect.top += Translate.y;
195 OutputRect.bottom += Translate.y;
196
197 Ret = FALSE;
198 ClippingType = (ClipRegion == NULL) ? DC_TRIVIAL : ClipRegion->iDComplexity;
199 switch (ClippingType)
200 {
201 case DC_TRIVIAL:
202 Ret = DibFunctionsForBitmapFormat[OutputObj->iBitmapFormat].DIB_AlphaBlend(
203 OutputObj, InputObj, &OutputRect, &InputRect, ClipRegion, ColorTranslation, BlendObj);
204 break;
205
206 case DC_RECT:
207 ClipRect.left = ClipRegion->rclBounds.left + Translate.x;
208 ClipRect.right = ClipRegion->rclBounds.right + Translate.x;
209 ClipRect.top = ClipRegion->rclBounds.top + Translate.y;
210 ClipRect.bottom = ClipRegion->rclBounds.bottom + Translate.y;
211 if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
212 {
213 Rect.left = InputRect.left + CombinedRect.left - OutputRect.left;
214 Rect.right = InputRect.right + CombinedRect.right - OutputRect.right;
215 Rect.top = InputRect.top + CombinedRect.top - OutputRect.top;
216 Rect.bottom = InputRect.bottom + CombinedRect.bottom - OutputRect.bottom;
217 Ret = DibFunctionsForBitmapFormat[OutputObj->iBitmapFormat].DIB_AlphaBlend(
218 OutputObj, InputObj, &CombinedRect, &Rect, ClipRegion, ColorTranslation, BlendObj);
219 }
220 break;
221
222 case DC_COMPLEX:
223 Ret = TRUE;
224 CLIPOBJ_cEnumStart(ClipRegion, FALSE, CT_RECTANGLES, CD_ANY, 0);
225 do
226 {
227 EnumMore = CLIPOBJ_bEnum(ClipRegion,(ULONG) sizeof(RectEnum),
228 (PVOID) &RectEnum);
229
230 for (i = 0; i < RectEnum.c; i++)
231 {
232 ClipRect.left = RectEnum.arcl[i].left + Translate.x;
233 ClipRect.right = RectEnum.arcl[i].right + Translate.x;
234 ClipRect.top = RectEnum.arcl[i].top + Translate.y;
235 ClipRect.bottom = RectEnum.arcl[i].bottom + Translate.y;
236 if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
237 {
238 Rect.left = InputRect.left + CombinedRect.left - OutputRect.left;
239 Rect.right = InputRect.right + CombinedRect.right - OutputRect.right;
240 Rect.top = InputRect.top + CombinedRect.top - OutputRect.top;
241 Rect.bottom = InputRect.bottom + CombinedRect.bottom - OutputRect.bottom;
242 Ret = DibFunctionsForBitmapFormat[OutputObj->iBitmapFormat].DIB_AlphaBlend(
243 OutputObj, InputObj, &CombinedRect, &Rect, ClipRegion, ColorTranslation, BlendObj) && Ret;
244 }
245 }
246 }
247 while (EnumMore);
248 break;
249
250 default:
251 UNIMPLEMENTED;
252 ASSERT(FALSE);
253 break;
254 }
255
256 IntEngLeave(&EnterLeaveDest);
257 IntEngLeave(&EnterLeaveSource);
258
259 if (SourceStretchedObj != NULL)
260 {
261 EngFreeMem(SourceStretchedObj->pvBits);
262 EngUnlockSurface(SourceStretchedObj);
263 }
264 if (SourceStretchedBitmap != 0)
265 {
266 EngDeleteSurface((HSURF)SourceStretchedBitmap);
267 }
268
269 return Ret;
270 }
271
272 BOOL APIENTRY
273 IntEngAlphaBlend(IN SURFOBJ *psoDest,
274 IN SURFOBJ *psoSource,
275 IN CLIPOBJ *ClipRegion,
276 IN XLATEOBJ *ColorTranslation,
277 IN PRECTL DestRect,
278 IN PRECTL SourceRect,
279 IN BLENDOBJ *BlendObj)
280 {
281 BOOL ret = FALSE;
282 SURFACE *psurfDest;
283 SURFACE *psurfSource;
284
285 ASSERT(psoDest);
286 psurfDest = CONTAINING_RECORD(psoDest, SURFACE, SurfObj);
287
288 ASSERT(psoSource);
289 psurfSource = CONTAINING_RECORD(psoSource, SURFACE, SurfObj);
290
291 ASSERT(DestRect);
292 ASSERT(SourceRect);
293
294 /* Check if there is anything to draw */
295 if (ClipRegion != NULL &&
296 (ClipRegion->rclBounds.left >= ClipRegion->rclBounds.right ||
297 ClipRegion->rclBounds.top >= ClipRegion->rclBounds.bottom))
298 {
299 /* Nothing to do */
300 return TRUE;
301 }
302
303 /* Call the driver's DrvAlphaBlend if available */
304 if (psurfDest->flHooks & HOOK_ALPHABLEND)
305 {
306 ret = GDIDEVFUNCS(psoDest).AlphaBlend(
307 psoDest, psoSource, ClipRegion, ColorTranslation,
308 DestRect, SourceRect, BlendObj);
309 }
310
311 if (! ret)
312 {
313 ret = EngAlphaBlend(psoDest, psoSource, ClipRegion, ColorTranslation,
314 DestRect, SourceRect, BlendObj);
315 }
316
317 return ret;
318 }
319
320 /*
321 * @implemented
322 */
323 BOOL
324 APIENTRY
325 NtGdiEngAlphaBlend(IN SURFOBJ *psoDest,
326 IN SURFOBJ *psoSource,
327 IN CLIPOBJ *ClipRegion,
328 IN XLATEOBJ *ColorTranslation,
329 IN PRECTL upDestRect,
330 IN PRECTL upSourceRect,
331 IN BLENDOBJ *BlendObj)
332 {
333 RECTL DestRect;
334 RECTL SourceRect;
335
336 _SEH2_TRY
337 {
338 ProbeForRead(upDestRect, sizeof(RECTL), 1);
339 RtlCopyMemory(&DestRect,upDestRect, sizeof(RECTL));
340
341 ProbeForRead(upSourceRect, sizeof(RECTL), 1);
342 RtlCopyMemory(&SourceRect, upSourceRect, sizeof(RECTL));
343
344 }
345 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
346 {
347 _SEH2_YIELD(return FALSE);
348 }
349 _SEH2_END;
350
351 return EngAlphaBlend(psoDest, psoSource, ClipRegion, ColorTranslation, &DestRect, &SourceRect, BlendObj);
352 }
353