Sync with trunk (r47116), hopefully without breaking anything.
[reactos.git] / subsystems / win32 / win32k / eng / stretchblt.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32 kernelmode subsystem
4 * PURPOSE: GDI stretch blt functions
5 * FILE: subsystems/win32/win32k/eng/stretchblt.c
6 * PROGRAMER: Jason Filby
7 */
8
9 #include <win32k.h>
10
11 #define NDEBUG
12 #include <debug.h>
13
14 typedef BOOLEAN (APIENTRY *PSTRETCHRECTFUNC)(SURFOBJ* OutputObj,
15 SURFOBJ* InputObj,
16 SURFOBJ* Mask,
17 XLATEOBJ* ColorTranslation,
18 RECTL* OutputRect,
19 RECTL* InputRect,
20 POINTL* MaskOrigin,
21 BRUSHOBJ* pbo,
22 POINTL* BrushOrigin,
23 ROP4 Rop4);
24
25 static BOOLEAN APIENTRY
26 CallDibStretchBlt(SURFOBJ* psoDest,
27 SURFOBJ* psoSource,
28 SURFOBJ* Mask,
29 XLATEOBJ* ColorTranslation,
30 RECTL* OutputRect,
31 RECTL* InputRect,
32 POINTL* MaskOrigin,
33 BRUSHOBJ* pbo,
34 POINTL* BrushOrigin,
35 ROP4 Rop4)
36 {
37 POINTL RealBrushOrigin;
38 SURFACE* psurfPattern;
39 PEBRUSHOBJ GdiBrush = NULL;
40 SURFOBJ* PatternSurface = NULL;
41 BOOL bResult;
42 HBITMAP hbmPattern;
43
44 if (BrushOrigin == NULL)
45 {
46 RealBrushOrigin.x = RealBrushOrigin.y = 0;
47 }
48 else
49 {
50 RealBrushOrigin = *BrushOrigin;
51 }
52
53 /* Pattern brush */
54 if (ROP4_USES_PATTERN(Rop4) && pbo && pbo->iSolidColor == 0xFFFFFFFF)
55 {
56 GdiBrush = CONTAINING_RECORD(pbo, EBRUSHOBJ, BrushObject);
57 hbmPattern = EBRUSHOBJ_pvGetEngBrush(GdiBrush);
58 psurfPattern = SURFACE_LockSurface(hbmPattern);
59 if (psurfPattern)
60 {
61 PatternSurface = &psurfPattern->SurfObj;
62 }
63 else
64 {
65 /* FIXME - What to do here? */
66 }
67 }
68 else
69 {
70 psurfPattern = NULL;
71 }
72
73 bResult = DibFunctionsForBitmapFormat[psoDest->iBitmapFormat].DIB_StretchBlt(
74 psoDest, psoSource, Mask, PatternSurface,
75 OutputRect, InputRect, MaskOrigin, pbo, &RealBrushOrigin,
76 ColorTranslation, Rop4);
77
78 /* Pattern brush */
79 if (psurfPattern)
80 {
81 SURFACE_UnlockSurface(psurfPattern);
82 }
83
84 return bResult;
85 }
86
87
88
89 /*
90 * @implemented
91 */
92 BOOL
93 APIENTRY
94 EngStretchBltROP(
95 IN SURFOBJ *psoDest,
96 IN SURFOBJ *psoSource,
97 IN SURFOBJ *Mask,
98 IN CLIPOBJ *ClipRegion,
99 IN XLATEOBJ *ColorTranslation,
100 IN COLORADJUSTMENT *pca,
101 IN POINTL *BrushOrigin,
102 IN RECTL *prclDest,
103 IN RECTL *prclSrc,
104 IN POINTL *MaskOrigin,
105 IN ULONG Mode,
106 IN BRUSHOBJ *pbo,
107 IN DWORD ROP4)
108 {
109 RECTL InputRect;
110 RECTL OutputRect;
111 POINTL Translate;
112 INTENG_ENTER_LEAVE EnterLeaveSource;
113 INTENG_ENTER_LEAVE EnterLeaveDest;
114 SURFOBJ* psoInput;
115 SURFOBJ* psoOutput;
116 PSTRETCHRECTFUNC BltRectFunc;
117 BOOLEAN Ret = TRUE;
118 POINTL AdjustedBrushOrigin;
119 BOOL UsesSource = ROP4_USES_SOURCE(ROP4);
120
121 BYTE clippingType;
122 RECTL ClipRect;
123 RECT_ENUM RectEnum;
124 BOOL EnumMore;
125 ULONG Direction;
126 RECTL CombinedRect;
127 RECTL InputToCombinedRect;
128 unsigned i;
129
130 LONG DstHeight;
131 LONG DstWidth;
132 LONG SrcHeight;
133 LONG SrcWidth;
134
135 /* Determine clipping type */
136 if (ClipRegion == (CLIPOBJ *) NULL)
137 {
138 clippingType = DC_TRIVIAL;
139 }
140 else
141 {
142 clippingType = ClipRegion->iDComplexity;
143 }
144
145 if (ROP4 == R4_NOOP)
146 {
147 /* Copy destination onto itself: nop */
148 return TRUE;
149 }
150
151 OutputRect = *prclDest;
152 if (OutputRect.right < OutputRect.left)
153 {
154 OutputRect.left = prclDest->right;
155 OutputRect.right = prclDest->left;
156 }
157 if (OutputRect.bottom < OutputRect.top)
158 {
159 OutputRect.top = prclDest->bottom;
160 OutputRect.bottom = prclDest->top;
161 }
162
163 if (UsesSource)
164 {
165 if (NULL == prclSrc)
166 {
167 return FALSE;
168 }
169 InputRect = *prclSrc;
170
171 if (! IntEngEnter(&EnterLeaveSource, psoSource, &InputRect, TRUE,
172 &Translate, &psoInput))
173 {
174 return FALSE;
175 }
176
177 InputRect.left += Translate.x;
178 InputRect.right += Translate.x;
179 InputRect.top += Translate.y;
180 InputRect.bottom += Translate.y;
181 }
182 else
183 {
184 InputRect.left = 0;
185 InputRect.right = OutputRect.right - OutputRect.left;
186 InputRect.top = 0;
187 InputRect.bottom = OutputRect.bottom - OutputRect.top;
188 }
189
190 if (NULL != ClipRegion)
191 {
192 if (OutputRect.left < ClipRegion->rclBounds.left)
193 {
194 InputRect.left += ClipRegion->rclBounds.left - OutputRect.left;
195 OutputRect.left = ClipRegion->rclBounds.left;
196 }
197 if (ClipRegion->rclBounds.right < OutputRect.right)
198 {
199 InputRect.right -= OutputRect.right - ClipRegion->rclBounds.right;
200 OutputRect.right = ClipRegion->rclBounds.right;
201 }
202 if (OutputRect.top < ClipRegion->rclBounds.top)
203 {
204 InputRect.top += ClipRegion->rclBounds.top - OutputRect.top;
205 OutputRect.top = ClipRegion->rclBounds.top;
206 }
207 if (ClipRegion->rclBounds.bottom < OutputRect.bottom)
208 {
209 InputRect.bottom -= OutputRect.bottom - ClipRegion->rclBounds.bottom;
210 OutputRect.bottom = ClipRegion->rclBounds.bottom;
211 }
212 }
213
214 /* Check for degenerate case: if height or width of OutputRect is 0 pixels there's
215 nothing to do */
216 if (OutputRect.right <= OutputRect.left || OutputRect.bottom <= OutputRect.top)
217 {
218 if (UsesSource)
219 {
220 IntEngLeave(&EnterLeaveSource);
221 }
222 return TRUE;
223 }
224
225 if (! IntEngEnter(&EnterLeaveDest, psoDest, &OutputRect, FALSE, &Translate, &psoOutput))
226 {
227 if (UsesSource)
228 {
229 IntEngLeave(&EnterLeaveSource);
230 }
231 return FALSE;
232 }
233
234 OutputRect.left += Translate.x;
235 OutputRect.right += Translate.x;
236 OutputRect.top += Translate.y;
237 OutputRect.bottom += Translate.y;
238
239 if (BrushOrigin)
240 {
241 AdjustedBrushOrigin.x = BrushOrigin->x + Translate.x;
242 AdjustedBrushOrigin.y = BrushOrigin->y + Translate.y;
243 }
244 else
245 {
246 AdjustedBrushOrigin = Translate;
247 }
248
249 BltRectFunc = CallDibStretchBlt;
250
251 DstHeight = OutputRect.bottom - OutputRect.top;
252 DstWidth = OutputRect.right - OutputRect.left;
253 SrcHeight = InputRect.bottom - InputRect.top;
254 SrcWidth = InputRect.right - InputRect.left;
255 switch (clippingType)
256 {
257 case DC_TRIVIAL:
258 Ret = (*BltRectFunc)(psoOutput, psoInput, Mask,
259 ColorTranslation, &OutputRect, &InputRect, MaskOrigin,
260 pbo, &AdjustedBrushOrigin, ROP4);
261 break;
262 case DC_RECT:
263 // Clip the blt to the clip rectangle
264 ClipRect.left = ClipRegion->rclBounds.left + Translate.x;
265 ClipRect.right = ClipRegion->rclBounds.right + Translate.x;
266 ClipRect.top = ClipRegion->rclBounds.top + Translate.y;
267 ClipRect.bottom = ClipRegion->rclBounds.bottom + Translate.y;
268 if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
269 {
270 InputToCombinedRect.top = InputRect.top + (CombinedRect.top - OutputRect.top) * SrcHeight / DstHeight;
271 InputToCombinedRect.bottom = InputRect.top + (CombinedRect.bottom - OutputRect.top) * SrcHeight / DstHeight;
272 InputToCombinedRect.left = InputRect.left + (CombinedRect.left - OutputRect.left) * SrcWidth / DstWidth;
273 InputToCombinedRect.right = InputRect.left + (CombinedRect.right - OutputRect.left) * SrcWidth / DstWidth;
274 Ret = (*BltRectFunc)(psoOutput, psoInput, Mask,
275 ColorTranslation,
276 &CombinedRect,
277 &InputToCombinedRect,
278 MaskOrigin,
279 pbo,
280 &AdjustedBrushOrigin,
281 ROP4);
282 }
283 break;
284 case DC_COMPLEX:
285 if (psoOutput == psoInput)
286 {
287 if (OutputRect.top < InputRect.top)
288 {
289 Direction = OutputRect.left < InputRect.left ?
290 CD_RIGHTDOWN : CD_LEFTDOWN;
291 }
292 else
293 {
294 Direction = OutputRect.left < InputRect.left ?
295 CD_RIGHTUP : CD_LEFTUP;
296 }
297 }
298 else
299 {
300 Direction = CD_ANY;
301 }
302 CLIPOBJ_cEnumStart(ClipRegion, FALSE, CT_RECTANGLES, Direction, 0);
303 do
304 {
305 EnumMore = CLIPOBJ_bEnum(ClipRegion,(ULONG) sizeof(RectEnum),
306 (PVOID) &RectEnum);
307 for (i = 0; i < RectEnum.c; i++)
308 {
309 ClipRect.left = RectEnum.arcl[i].left + Translate.x;
310 ClipRect.right = RectEnum.arcl[i].right + Translate.x;
311 ClipRect.top = RectEnum.arcl[i].top + Translate.y;
312 ClipRect.bottom = RectEnum.arcl[i].bottom + Translate.y;
313 if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
314 {
315 InputToCombinedRect.top = InputRect.top + (CombinedRect.top - OutputRect.top) * SrcHeight / DstHeight;
316 InputToCombinedRect.bottom = InputRect.top + (CombinedRect.bottom - OutputRect.top) * SrcHeight / DstHeight;
317 InputToCombinedRect.left = InputRect.left + (CombinedRect.left - OutputRect.left) * SrcWidth / DstWidth;
318 InputToCombinedRect.right = InputRect.left + (CombinedRect.right - OutputRect.left) * SrcWidth / DstWidth;
319 Ret = (*BltRectFunc)(psoOutput, psoInput, Mask,
320 ColorTranslation,
321 &CombinedRect,
322 &InputToCombinedRect,
323 MaskOrigin,
324 pbo,
325 &AdjustedBrushOrigin,
326 ROP4);
327 }
328 }
329 }
330 while (EnumMore);
331 break;
332 }
333
334 IntEngLeave(&EnterLeaveDest);
335 if (UsesSource)
336 {
337 IntEngLeave(&EnterLeaveSource);
338 }
339
340 return Ret;
341 }
342
343 /*
344 * @implemented
345 */
346 BOOL
347 APIENTRY
348 EngStretchBlt(
349 IN SURFOBJ *psoDest,
350 IN SURFOBJ *psoSource,
351 IN SURFOBJ *Mask,
352 IN CLIPOBJ *ClipRegion,
353 IN XLATEOBJ *ColorTranslation,
354 IN COLORADJUSTMENT *pca,
355 IN POINTL *BrushOrigin,
356 IN RECTL *prclDest,
357 IN RECTL *prclSrc,
358 IN POINTL *MaskOrigin,
359 IN ULONG Mode)
360 {
361 return EngStretchBltROP(
362 psoDest,
363 psoSource,
364 Mask,
365 ClipRegion,
366 ColorTranslation,
367 pca,
368 BrushOrigin,
369 prclDest,
370 prclSrc,
371 MaskOrigin,
372 Mode,
373 NULL,
374 ROP3_TO_ROP4(SRCCOPY));
375 }
376
377 BOOL APIENTRY
378 IntEngStretchBlt(SURFOBJ *psoDest,
379 SURFOBJ *psoSource,
380 SURFOBJ *MaskSurf,
381 CLIPOBJ *ClipRegion,
382 XLATEOBJ *ColorTranslation,
383 RECTL *DestRect,
384 RECTL *SourceRect,
385 POINTL *pMaskOrigin,
386 BRUSHOBJ *pbo,
387 POINTL *BrushOrigin,
388 ROP4 ROP)
389 {
390 BOOLEAN ret;
391 COLORADJUSTMENT ca;
392 POINTL MaskOrigin = {0, 0};
393 SURFACE *psurfDest;
394 SURFACE *psurfSource = NULL;
395 RECTL InputClippedRect;
396 RECTL InputRect;
397 RECTL OutputRect;
398 BOOL UsesSource = ROP4_USES_SOURCE(ROP);
399 LONG InputClWidth, InputClHeight, InputWidth, InputHeight;
400
401 ASSERT(psoDest);
402 psurfDest = CONTAINING_RECORD(psoDest, SURFACE, SurfObj);
403 ASSERT(psurfDest);
404 ASSERT(DestRect);
405
406 InputClippedRect = *DestRect;
407 if (InputClippedRect.right < InputClippedRect.left)
408 {
409 InputClippedRect.left = DestRect->right;
410 InputClippedRect.right = DestRect->left;
411 }
412 if (InputClippedRect.bottom < InputClippedRect.top)
413 {
414 InputClippedRect.top = DestRect->bottom;
415 InputClippedRect.bottom = DestRect->top;
416 }
417
418 if (UsesSource)
419 {
420 if (NULL == SourceRect || NULL == psoSource)
421 {
422 return FALSE;
423 }
424 InputRect = *SourceRect;
425
426 if (InputRect.right < InputRect.left ||
427 InputRect.bottom < InputRect.top)
428 {
429 /* Everything clipped away, nothing to do */
430 return TRUE;
431 }
432 }
433
434 if (ClipRegion)
435 {
436 if (!RECTL_bIntersectRect(&OutputRect, &InputClippedRect,
437 &ClipRegion->rclBounds))
438 {
439 return TRUE;
440 }
441 /* Update source rect */
442 InputClWidth = InputClippedRect.right - InputClippedRect.left;
443 InputClHeight = InputClippedRect.bottom - InputClippedRect.top;
444 InputWidth = InputRect.right - InputRect.left;
445 InputHeight = InputRect.bottom - InputRect.top;
446
447 InputRect.left += (InputWidth * (OutputRect.left - InputClippedRect.left)) / InputClWidth;
448 InputRect.right -= (InputWidth * (InputClippedRect.right - OutputRect.right)) / InputClWidth;
449 InputRect.top += (InputHeight * (OutputRect.top - InputClippedRect.top)) / InputClHeight;
450 InputRect.bottom -= (InputHeight * (InputClippedRect.bottom - OutputRect.bottom)) / InputClHeight;
451 }
452 else
453 {
454 OutputRect = InputClippedRect;
455 }
456
457 if (pMaskOrigin != NULL)
458 {
459 MaskOrigin.x = pMaskOrigin->x;
460 MaskOrigin.y = pMaskOrigin->y;
461 }
462
463 /* No success yet */
464 ret = FALSE;
465
466 if (UsesSource)
467 {
468 psurfSource = CONTAINING_RECORD(psoSource, SURFACE, SurfObj);
469 }
470
471 /* Prepare color adjustment */
472
473 /* Call the driver's DrvStretchBlt if available */
474 if (psurfDest->flHooks & HOOK_STRETCHBLTROP)
475 {
476 /* Drv->StretchBltROP (look at http://www.osronline.com/ddkx/graphics/ddifncs_0z3b.htm ) */
477 ret = GDIDEVFUNCS(psoDest).StretchBltROP(psoDest,
478 (UsesSource) ? psoSource : NULL,
479 MaskSurf,
480 ClipRegion,
481 ColorTranslation,
482 &ca, BrushOrigin,
483 &OutputRect,
484 &InputRect,
485 &MaskOrigin,
486 COLORONCOLOR,
487 pbo,
488 ROP);
489 }
490
491 if (! ret)
492 {
493 ret = EngStretchBltROP(psoDest,
494 (UsesSource) ? psoSource : NULL,
495 MaskSurf,
496 ClipRegion,
497 ColorTranslation,
498 &ca,
499 BrushOrigin,
500 &OutputRect,
501 &InputRect,
502 &MaskOrigin,
503 COLORONCOLOR,
504 pbo,
505 ROP);
506 }
507
508 return ret;
509 }
510
511 BOOL
512 APIENTRY
513 NtGdiEngStretchBlt(
514 IN SURFOBJ *psoDest,
515 IN SURFOBJ *psoSource,
516 IN SURFOBJ *Mask,
517 IN CLIPOBJ *ClipRegion,
518 IN XLATEOBJ *ColorTranslation,
519 IN COLORADJUSTMENT *pca,
520 IN POINTL *BrushOrigin,
521 IN RECTL *prclDest,
522 IN RECTL *prclSrc,
523 IN POINTL *MaskOrigin,
524 IN ULONG Mode)
525 {
526 COLORADJUSTMENT ca;
527 POINTL lBrushOrigin;
528 RECTL rclDest;
529 RECTL rclSrc;
530 POINTL lMaskOrigin;
531
532 _SEH2_TRY
533 {
534 ProbeForRead(pca, sizeof(COLORADJUSTMENT), 1);
535 RtlCopyMemory(&ca,pca, sizeof(COLORADJUSTMENT));
536
537 ProbeForRead(BrushOrigin, sizeof(POINTL), 1);
538 RtlCopyMemory(&lBrushOrigin, BrushOrigin, sizeof(POINTL));
539
540 ProbeForRead(prclDest, sizeof(RECTL), 1);
541 RtlCopyMemory(&rclDest, prclDest, sizeof(RECTL));
542
543 ProbeForRead(prclSrc, sizeof(RECTL), 1);
544 RtlCopyMemory(&rclSrc, prclSrc, sizeof(RECTL));
545
546 ProbeForRead(MaskOrigin, sizeof(POINTL), 1);
547 RtlCopyMemory(&lMaskOrigin, MaskOrigin, sizeof(POINTL));
548
549 }
550 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
551 {
552 _SEH2_YIELD(return FALSE);
553 }
554 _SEH2_END;
555
556 return EngStretchBlt(psoDest, psoSource, Mask, ClipRegion, ColorTranslation, &ca, &lBrushOrigin, &rclDest, &rclSrc, &lMaskOrigin, Mode);
557 }
558