[WIN32K]
[reactos.git] / reactos / 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 ROP4 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 if (Rop4 == ROP4_NOOP)
136 {
137 /* Copy destination onto itself: nop */
138 return TRUE;
139 }
140
141
142 /* Determine clipping type */
143 if (ClipRegion == (CLIPOBJ *) NULL)
144 {
145 clippingType = DC_TRIVIAL;
146 }
147 else
148 {
149 clippingType = ClipRegion->iDComplexity;
150 }
151
152 OutputRect = *prclDest;
153 if (OutputRect.right < OutputRect.left)
154 {
155 OutputRect.left = prclDest->right;
156 OutputRect.right = prclDest->left;
157 }
158 if (OutputRect.bottom < OutputRect.top)
159 {
160 OutputRect.top = prclDest->bottom;
161 OutputRect.bottom = prclDest->top;
162 }
163
164 if (UsesSource)
165 {
166 if (NULL == prclSrc)
167 {
168 return FALSE;
169 }
170 InputRect = *prclSrc;
171
172 if (! IntEngEnter(&EnterLeaveSource, psoSource, &InputRect, TRUE,
173 &Translate, &psoInput))
174 {
175 return FALSE;
176 }
177
178 InputRect.left += Translate.x;
179 InputRect.right += Translate.x;
180 InputRect.top += Translate.y;
181 InputRect.bottom += Translate.y;
182 }
183 else
184 {
185 InputRect.left = 0;
186 InputRect.right = OutputRect.right - OutputRect.left;
187 InputRect.top = 0;
188 InputRect.bottom = OutputRect.bottom - OutputRect.top;
189 }
190
191 if (NULL != ClipRegion)
192 {
193 if (OutputRect.left < ClipRegion->rclBounds.left)
194 {
195 InputRect.left += ClipRegion->rclBounds.left - OutputRect.left;
196 OutputRect.left = ClipRegion->rclBounds.left;
197 }
198 if (ClipRegion->rclBounds.right < OutputRect.right)
199 {
200 InputRect.right -= OutputRect.right - ClipRegion->rclBounds.right;
201 OutputRect.right = ClipRegion->rclBounds.right;
202 }
203 if (OutputRect.top < ClipRegion->rclBounds.top)
204 {
205 InputRect.top += ClipRegion->rclBounds.top - OutputRect.top;
206 OutputRect.top = ClipRegion->rclBounds.top;
207 }
208 if (ClipRegion->rclBounds.bottom < OutputRect.bottom)
209 {
210 InputRect.bottom -= OutputRect.bottom - ClipRegion->rclBounds.bottom;
211 OutputRect.bottom = ClipRegion->rclBounds.bottom;
212 }
213 }
214
215 /* Check for degenerate case: if height or width of OutputRect is 0 pixels there's
216 nothing to do */
217 if (OutputRect.right <= OutputRect.left || OutputRect.bottom <= OutputRect.top)
218 {
219 if (UsesSource)
220 {
221 IntEngLeave(&EnterLeaveSource);
222 }
223 return TRUE;
224 }
225
226 if (! IntEngEnter(&EnterLeaveDest, psoDest, &OutputRect, FALSE, &Translate, &psoOutput))
227 {
228 if (UsesSource)
229 {
230 IntEngLeave(&EnterLeaveSource);
231 }
232 return FALSE;
233 }
234
235 OutputRect.left += Translate.x;
236 OutputRect.right += Translate.x;
237 OutputRect.top += Translate.y;
238 OutputRect.bottom += Translate.y;
239
240 if (BrushOrigin)
241 {
242 AdjustedBrushOrigin.x = BrushOrigin->x + Translate.x;
243 AdjustedBrushOrigin.y = BrushOrigin->y + Translate.y;
244 }
245 else
246 {
247 AdjustedBrushOrigin = Translate;
248 }
249
250 BltRectFunc = CallDibStretchBlt;
251
252 DstHeight = OutputRect.bottom - OutputRect.top;
253 DstWidth = OutputRect.right - OutputRect.left;
254 SrcHeight = InputRect.bottom - InputRect.top;
255 SrcWidth = InputRect.right - InputRect.left;
256 switch (clippingType)
257 {
258 case DC_TRIVIAL:
259 Ret = (*BltRectFunc)(psoOutput, psoInput, Mask,
260 ColorTranslation, &OutputRect, &InputRect, MaskOrigin,
261 pbo, &AdjustedBrushOrigin, Rop4);
262 break;
263 case DC_RECT:
264 // Clip the blt to the clip rectangle
265 ClipRect.left = ClipRegion->rclBounds.left + Translate.x;
266 ClipRect.right = ClipRegion->rclBounds.right + Translate.x;
267 ClipRect.top = ClipRegion->rclBounds.top + Translate.y;
268 ClipRect.bottom = ClipRegion->rclBounds.bottom + Translate.y;
269 if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
270 {
271 InputToCombinedRect.top = InputRect.top + (CombinedRect.top - OutputRect.top) * SrcHeight / DstHeight;
272 InputToCombinedRect.bottom = InputRect.top + (CombinedRect.bottom - OutputRect.top) * SrcHeight / DstHeight;
273 InputToCombinedRect.left = InputRect.left + (CombinedRect.left - OutputRect.left) * SrcWidth / DstWidth;
274 InputToCombinedRect.right = InputRect.left + (CombinedRect.right - OutputRect.left) * SrcWidth / DstWidth;
275 Ret = (*BltRectFunc)(psoOutput, psoInput, Mask,
276 ColorTranslation,
277 &CombinedRect,
278 &InputToCombinedRect,
279 MaskOrigin,
280 pbo,
281 &AdjustedBrushOrigin,
282 Rop4);
283 }
284 break;
285 case DC_COMPLEX:
286 if (psoOutput == psoInput)
287 {
288 if (OutputRect.top < InputRect.top)
289 {
290 Direction = OutputRect.left < InputRect.left ?
291 CD_RIGHTDOWN : CD_LEFTDOWN;
292 }
293 else
294 {
295 Direction = OutputRect.left < InputRect.left ?
296 CD_RIGHTUP : CD_LEFTUP;
297 }
298 }
299 else
300 {
301 Direction = CD_ANY;
302 }
303 CLIPOBJ_cEnumStart(ClipRegion, FALSE, CT_RECTANGLES, Direction, 0);
304 do
305 {
306 EnumMore = CLIPOBJ_bEnum(ClipRegion,(ULONG) sizeof(RectEnum),
307 (PVOID) &RectEnum);
308 for (i = 0; i < RectEnum.c; i++)
309 {
310 ClipRect.left = RectEnum.arcl[i].left + Translate.x;
311 ClipRect.right = RectEnum.arcl[i].right + Translate.x;
312 ClipRect.top = RectEnum.arcl[i].top + Translate.y;
313 ClipRect.bottom = RectEnum.arcl[i].bottom + Translate.y;
314 if (RECTL_bIntersectRect(&CombinedRect, &OutputRect, &ClipRect))
315 {
316 InputToCombinedRect.top = InputRect.top + (CombinedRect.top - OutputRect.top) * SrcHeight / DstHeight;
317 InputToCombinedRect.bottom = InputRect.top + (CombinedRect.bottom - OutputRect.top) * SrcHeight / DstHeight;
318 InputToCombinedRect.left = InputRect.left + (CombinedRect.left - OutputRect.left) * SrcWidth / DstWidth;
319 InputToCombinedRect.right = InputRect.left + (CombinedRect.right - OutputRect.left) * SrcWidth / DstWidth;
320 Ret = (*BltRectFunc)(psoOutput, psoInput, Mask,
321 ColorTranslation,
322 &CombinedRect,
323 &InputToCombinedRect,
324 MaskOrigin,
325 pbo,
326 &AdjustedBrushOrigin,
327 Rop4);
328 }
329 }
330 }
331 while (EnumMore);
332 break;
333 }
334
335 IntEngLeave(&EnterLeaveDest);
336 if (UsesSource)
337 {
338 IntEngLeave(&EnterLeaveSource);
339 }
340
341 return Ret;
342 }
343
344 /*
345 * @implemented
346 */
347 BOOL
348 APIENTRY
349 EngStretchBlt(
350 IN SURFOBJ *psoDest,
351 IN SURFOBJ *psoSource,
352 IN SURFOBJ *Mask,
353 IN CLIPOBJ *ClipRegion,
354 IN XLATEOBJ *ColorTranslation,
355 IN COLORADJUSTMENT *pca,
356 IN POINTL *BrushOrigin,
357 IN RECTL *prclDest,
358 IN RECTL *prclSrc,
359 IN POINTL *MaskOrigin,
360 IN ULONG Mode)
361 {
362 return EngStretchBltROP(
363 psoDest,
364 psoSource,
365 Mask,
366 ClipRegion,
367 ColorTranslation,
368 pca,
369 BrushOrigin,
370 prclDest,
371 prclSrc,
372 MaskOrigin,
373 Mode,
374 NULL,
375 ROP4_FROM_INDEX(R3_OPINDEX_SRCCOPY));
376 }
377
378 BOOL APIENTRY
379 IntEngStretchBlt(SURFOBJ *psoDest,
380 SURFOBJ *psoSource,
381 SURFOBJ *MaskSurf,
382 CLIPOBJ *ClipRegion,
383 XLATEOBJ *ColorTranslation,
384 RECTL *DestRect,
385 RECTL *SourceRect,
386 POINTL *pMaskOrigin,
387 BRUSHOBJ *pbo,
388 POINTL *BrushOrigin,
389 DWORD Rop4)
390 {
391 BOOLEAN ret;
392 COLORADJUSTMENT ca;
393 POINTL MaskOrigin = {0, 0};
394 SURFACE *psurfDest;
395 SURFACE *psurfSource = NULL;
396 RECTL InputClippedRect;
397 RECTL InputRect;
398 RECTL OutputRect;
399 BOOL UsesSource = ROP4_USES_SOURCE(Rop4);
400 LONG InputClWidth, InputClHeight, InputWidth, InputHeight;
401
402 ASSERT(psoDest);
403 psurfDest = CONTAINING_RECORD(psoDest, SURFACE, SurfObj);
404 ASSERT(psurfDest);
405 ASSERT(DestRect);
406
407 /* Sanity check */
408 ASSERT(IS_VALID_ROP4(Rop4));
409
410 InputClippedRect = *DestRect;
411 if (InputClippedRect.right < InputClippedRect.left)
412 {
413 InputClippedRect.left = DestRect->right;
414 InputClippedRect.right = DestRect->left;
415 }
416 if (InputClippedRect.bottom < InputClippedRect.top)
417 {
418 InputClippedRect.top = DestRect->bottom;
419 InputClippedRect.bottom = DestRect->top;
420 }
421
422 if (UsesSource)
423 {
424 if (NULL == SourceRect || NULL == psoSource)
425 {
426 return FALSE;
427 }
428 InputRect = *SourceRect;
429
430 if (InputRect.right < InputRect.left ||
431 InputRect.bottom < InputRect.top)
432 {
433 /* Everything clipped away, nothing to do */
434 return TRUE;
435 }
436 }
437
438 if (ClipRegion)
439 {
440 if (!RECTL_bIntersectRect(&OutputRect, &InputClippedRect,
441 &ClipRegion->rclBounds))
442 {
443 return TRUE;
444 }
445 /* Update source rect */
446 InputClWidth = InputClippedRect.right - InputClippedRect.left;
447 InputClHeight = InputClippedRect.bottom - InputClippedRect.top;
448 InputWidth = InputRect.right - InputRect.left;
449 InputHeight = InputRect.bottom - InputRect.top;
450
451 InputRect.left += (InputWidth * (OutputRect.left - InputClippedRect.left)) / InputClWidth;
452 InputRect.right -= (InputWidth * (InputClippedRect.right - OutputRect.right)) / InputClWidth;
453 InputRect.top += (InputHeight * (OutputRect.top - InputClippedRect.top)) / InputClHeight;
454 InputRect.bottom -= (InputHeight * (InputClippedRect.bottom - OutputRect.bottom)) / InputClHeight;
455 }
456 else
457 {
458 OutputRect = InputClippedRect;
459 }
460
461 if (pMaskOrigin != NULL)
462 {
463 MaskOrigin.x = pMaskOrigin->x;
464 MaskOrigin.y = pMaskOrigin->y;
465 }
466
467 /* No success yet */
468 ret = FALSE;
469
470 if (UsesSource)
471 {
472 psurfSource = CONTAINING_RECORD(psoSource, SURFACE, SurfObj);
473 }
474
475 /* Prepare color adjustment */
476
477 /* Call the driver's DrvStretchBlt if available */
478 if (psurfDest->flags & HOOK_STRETCHBLTROP)
479 {
480 /* Drv->StretchBltROP (look at http://www.osronline.com/ddkx/graphics/ddifncs_0z3b.htm ) */
481 ret = GDIDEVFUNCS(psoDest).StretchBltROP(psoDest,
482 (UsesSource) ? psoSource : NULL,
483 MaskSurf,
484 ClipRegion,
485 ColorTranslation,
486 &ca, BrushOrigin,
487 &OutputRect,
488 &InputRect,
489 &MaskOrigin,
490 COLORONCOLOR,
491 pbo,
492 Rop4);
493 }
494
495 if (! ret)
496 {
497 ret = EngStretchBltROP(psoDest,
498 (UsesSource) ? psoSource : NULL,
499 MaskSurf,
500 ClipRegion,
501 ColorTranslation,
502 &ca,
503 BrushOrigin,
504 &OutputRect,
505 &InputRect,
506 &MaskOrigin,
507 COLORONCOLOR,
508 pbo,
509 Rop4);
510 }
511
512 return ret;
513 }
514
515 BOOL
516 APIENTRY
517 NtGdiEngStretchBlt(
518 IN SURFOBJ *psoDest,
519 IN SURFOBJ *psoSource,
520 IN SURFOBJ *Mask,
521 IN CLIPOBJ *ClipRegion,
522 IN XLATEOBJ *ColorTranslation,
523 IN COLORADJUSTMENT *pca,
524 IN POINTL *BrushOrigin,
525 IN RECTL *prclDest,
526 IN RECTL *prclSrc,
527 IN POINTL *MaskOrigin,
528 IN ULONG Mode)
529 {
530 COLORADJUSTMENT ca;
531 POINTL lBrushOrigin;
532 RECTL rclDest;
533 RECTL rclSrc;
534 POINTL lMaskOrigin;
535
536 _SEH2_TRY
537 {
538 ProbeForRead(pca, sizeof(COLORADJUSTMENT), 1);
539 RtlCopyMemory(&ca,pca, sizeof(COLORADJUSTMENT));
540
541 ProbeForRead(BrushOrigin, sizeof(POINTL), 1);
542 RtlCopyMemory(&lBrushOrigin, BrushOrigin, sizeof(POINTL));
543
544 ProbeForRead(prclDest, sizeof(RECTL), 1);
545 RtlCopyMemory(&rclDest, prclDest, sizeof(RECTL));
546
547 ProbeForRead(prclSrc, sizeof(RECTL), 1);
548 RtlCopyMemory(&rclSrc, prclSrc, sizeof(RECTL));
549
550 ProbeForRead(MaskOrigin, sizeof(POINTL), 1);
551 RtlCopyMemory(&lMaskOrigin, MaskOrigin, sizeof(POINTL));
552
553 }
554 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
555 {
556 _SEH2_YIELD(return FALSE);
557 }
558 _SEH2_END;
559
560 return EngStretchBlt(psoDest, psoSource, Mask, ClipRegion, ColorTranslation, &ca, &lBrushOrigin, &rclDest, &rclSrc, &lMaskOrigin, Mode);
561 }
562