Prevent painting outside surface
[reactos.git] / reactos / subsys / win32k / eng / bitblt.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: bitblt.c,v 1.22 2003/07/09 07:00:00 gvg Exp $
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: GDI BitBlt Functions
24 * FILE: subsys/win32k/eng/bitblt.c
25 * PROGRAMER: Jason Filby
26 * REVISION HISTORY:
27 * 2/10/1999: Created
28 */
29
30 #include <ddk/winddi.h>
31 #include <ddk/ntddk.h>
32 #include <ddk/ntddmou.h>
33 #include <ntos/minmax.h>
34 #include "brush.h"
35 #include "clip.h"
36 #include "objects.h"
37 #include "../dib/dib.h"
38 #include "misc.h"
39 #include <include/mouse.h>
40 #include <include/object.h>
41 #include <include/dib.h>
42 #include <include/surface.h>
43 #include <include/copybits.h>
44 #include <include/inteng.h>
45
46 #define NDEBUG
47 #include <win32k/debug1.h>
48
49 typedef BOOLEAN STDCALL (*PBLTRECTFUNC)(PSURFOBJ OutputObj,
50 PSURFGDI OutputGDI,
51 PSURFOBJ InputObj,
52 PSURFGDI InputGDI,
53 PSURFOBJ Mask,
54 PXLATEOBJ ColorTranslation,
55 PRECTL OutputRect,
56 PPOINTL InputPoint,
57 PPOINTL MaskOrigin,
58 PBRUSHOBJ Brush,
59 PPOINTL BrushOrigin,
60 ROP4 Rop4);
61
62 BOOL STDCALL EngIntersectRect(PRECTL prcDst, PRECTL prcSrc1, PRECTL prcSrc2)
63 {
64 static const RECTL rclEmpty = { 0, 0, 0, 0 };
65
66 prcDst->left = max(prcSrc1->left, prcSrc2->left);
67 prcDst->right = min(prcSrc1->right, prcSrc2->right);
68
69 if (prcDst->left < prcDst->right)
70 {
71 prcDst->top = max(prcSrc1->top, prcSrc2->top);
72 prcDst->bottom = min(prcSrc1->bottom, prcSrc2->bottom);
73
74 if (prcDst->top < prcDst->bottom)
75 {
76 return TRUE;
77 }
78 }
79
80 *prcDst = rclEmpty;
81
82 return FALSE;
83 }
84
85 static BOOLEAN STDCALL
86 BltMask(PSURFOBJ Dest,
87 PSURFGDI DestGDI,
88 PSURFOBJ Source,
89 PSURFGDI SourceGDI,
90 PSURFOBJ Mask,
91 PXLATEOBJ ColorTranslation,
92 PRECTL DestRect,
93 PPOINTL SourcePoint,
94 PPOINTL MaskPoint,
95 PBRUSHOBJ Brush,
96 PPOINTL BrushPoint,
97 ROP4 Rop4)
98 {
99 LONG i, j, dx, dy, c8;
100 BYTE *tMask, *lMask;
101 static BYTE maskbit[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
102
103 dx = DestRect->right - DestRect->left;
104 dy = DestRect->bottom - DestRect->top;
105
106 if (Mask != NULL)
107 {
108 tMask = Mask->pvBits;
109 for (j = 0; j < dy; j++)
110 {
111 lMask = tMask;
112 c8 = 0;
113 for (i = 0; i < dx; i++)
114 {
115 if (0 != (*lMask & maskbit[c8]))
116 {
117 DestGDI->DIB_PutPixel(Dest, DestRect->left + i, DestRect->top + j, Brush->iSolidColor);
118 }
119 c8++;
120 if (8 == c8)
121 {
122 lMask++;
123 c8=0;
124 }
125 }
126 tMask += Mask->lDelta;
127 }
128 return TRUE;
129 }
130 else
131 {
132 return FALSE;
133 }
134 }
135
136 static BOOLEAN STDCALL
137 BltPatCopy(PSURFOBJ Dest,
138 PSURFGDI DestGDI,
139 PSURFOBJ Source,
140 PSURFGDI SourceGDI,
141 PSURFOBJ Mask,
142 PXLATEOBJ ColorTranslation,
143 PRECTL DestRect,
144 PPOINTL SourcePoint,
145 PPOINTL MaskPoint,
146 PBRUSHOBJ Brush,
147 PPOINTL BrushPoint,
148 ROP4 Rop4)
149 {
150 // These functions are assigned if we're working with a DIB
151 // The assigned functions depend on the bitsPerPixel of the DIB
152 LONG y;
153 ULONG LineWidth;
154
155 LineWidth = DestRect->right - DestRect->left;
156 for (y = DestRect->top; y < DestRect->bottom; y++)
157 {
158 DestGDI->DIB_HLine(Dest, DestRect->left, DestRect->right, y, Brush->iSolidColor);
159 }
160
161 return TRUE;
162 }
163
164 static BOOLEAN STDCALL
165 CallDibBitBlt(PSURFOBJ OutputObj,
166 PSURFGDI OutputGDI,
167 PSURFOBJ InputObj,
168 PSURFGDI InputGDI,
169 PSURFOBJ Mask,
170 PXLATEOBJ ColorTranslation,
171 PRECTL OutputRect,
172 PPOINTL InputPoint,
173 PPOINTL MaskOrigin,
174 PBRUSHOBJ Brush,
175 PPOINTL BrushOrigin,
176 ROP4 Rop4)
177 {
178 return OutputGDI->DIB_BitBlt(OutputObj, InputObj, OutputGDI, InputGDI, OutputRect, InputPoint, ColorTranslation);
179 }
180
181 INT abs(INT nm);
182
183 BOOL STDCALL
184 EngBitBlt(SURFOBJ *DestObj,
185 SURFOBJ *SourceObj,
186 SURFOBJ *Mask,
187 CLIPOBJ *ClipRegion,
188 XLATEOBJ *ColorTranslation,
189 RECTL *DestRect,
190 POINTL *SourcePoint,
191 POINTL *MaskOrigin,
192 BRUSHOBJ *Brush,
193 POINTL *BrushOrigin,
194 ROP4 Rop4)
195 {
196 BYTE clippingType;
197 RECTL CombinedRect;
198 RECT_ENUM RectEnum;
199 BOOL EnumMore;
200 PSURFGDI OutputGDI, InputGDI;
201 POINTL InputPoint;
202 RECTL InputRect;
203 RECTL OutputRect;
204 POINTL Translate;
205 INTENG_ENTER_LEAVE EnterLeaveSource;
206 INTENG_ENTER_LEAVE EnterLeaveDest;
207 PSURFOBJ InputObj;
208 PSURFOBJ OutputObj;
209 PBLTRECTFUNC BltRectFunc;
210 BOOLEAN Ret;
211 RECTL ClipRect;
212 unsigned i;
213
214 if (NULL != SourcePoint)
215 {
216 InputRect.left = SourcePoint->x;
217 InputRect.right = SourcePoint->x + (DestRect->right - DestRect->left);
218 InputRect.top = SourcePoint->y;
219 InputRect.bottom = SourcePoint->y + (DestRect->bottom - DestRect->top);
220 }
221 else
222 {
223 InputRect.left = 0;
224 InputRect.right = DestRect->right - DestRect->left;
225 InputRect.top = 0;
226 InputRect.bottom = DestRect->bottom - DestRect->top;
227 }
228
229 if (! IntEngEnter(&EnterLeaveSource, SourceObj, &InputRect, TRUE, &Translate, &InputObj))
230 {
231 return FALSE;
232 }
233
234 if (NULL != SourcePoint)
235 {
236 InputPoint.x = SourcePoint->x + Translate.x;
237 InputPoint.y = SourcePoint->y + Translate.y;
238 }
239 else
240 {
241 InputPoint.x = 0;
242 InputPoint.y = 0;
243 }
244
245 if (NULL != InputObj)
246 {
247 InputGDI = (PSURFGDI) AccessInternalObjectFromUserObject(InputObj);
248 }
249
250 OutputRect = *DestRect;
251 if (NULL != ClipRegion)
252 {
253 if (OutputRect.left < ClipRegion->rclBounds.left)
254 {
255 InputRect.left += ClipRegion->rclBounds.left - OutputRect.left;
256 InputPoint.x += ClipRegion->rclBounds.left - OutputRect.left;
257 OutputRect.left = ClipRegion->rclBounds.left;
258 }
259 if (ClipRegion->rclBounds.right < OutputRect.right)
260 {
261 InputRect.right -= OutputRect.right - ClipRegion->rclBounds.right;
262 OutputRect.right = ClipRegion->rclBounds.right;
263 }
264 if (OutputRect.top < ClipRegion->rclBounds.top)
265 {
266 InputRect.top += ClipRegion->rclBounds.top - OutputRect.top;
267 InputPoint.y += ClipRegion->rclBounds.top - OutputRect.top;
268 OutputRect.top = ClipRegion->rclBounds.top;
269 }
270 if (ClipRegion->rclBounds.bottom < OutputRect.bottom)
271 {
272 InputRect.bottom -= OutputRect.bottom - ClipRegion->rclBounds.bottom;
273 OutputRect.bottom = ClipRegion->rclBounds.bottom;
274 }
275 }
276
277 /* Check for degenerate case: if height or width of OutputRect is 0 pixels there's
278 nothing to do */
279 if (OutputRect.right <= OutputRect.left || OutputRect.bottom <= OutputRect.top)
280 {
281 IntEngLeave(&EnterLeaveSource);
282 return TRUE;
283 }
284
285 if (! IntEngEnter(&EnterLeaveDest, DestObj, &OutputRect, FALSE, &Translate, &OutputObj))
286 {
287 IntEngLeave(&EnterLeaveSource);
288 return FALSE;
289 }
290
291 OutputRect.left = DestRect->left + Translate.x;
292 OutputRect.right = DestRect->right + Translate.x;
293 OutputRect.top = DestRect->top + Translate.y;
294 OutputRect.bottom = DestRect->bottom + Translate.y;
295
296 if (NULL != OutputObj)
297 {
298 OutputGDI = (PSURFGDI)AccessInternalObjectFromUserObject(OutputObj);
299 }
300
301 /* The code currently assumes there will be a source bitmap. This is not true when, for example, using this function to
302 * paint a brush pattern on the destination. */
303 if (NULL == InputObj && 0xaacc != Rop4 && PATCOPY != Rop4)
304 {
305 DbgPrint("EngBitBlt: A source is currently required, even though not all operations require one (FIXME)\n");
306 return FALSE;
307 }
308
309 // Determine clipping type
310 if (ClipRegion == (CLIPOBJ *) NULL)
311 {
312 clippingType = DC_TRIVIAL;
313 } else {
314 clippingType = ClipRegion->iDComplexity;
315 }
316
317 if (0xaacc == Rop4)
318 {
319 BltRectFunc = BltMask;
320 }
321 else if (PATCOPY == Rop4)
322 {
323 BltRectFunc = BltPatCopy;
324 }
325 else
326 {
327 BltRectFunc = CallDibBitBlt;
328 }
329
330
331 switch(clippingType)
332 {
333 case DC_TRIVIAL:
334 Ret = (*BltRectFunc)(OutputObj, OutputGDI, InputObj, InputGDI, Mask, ColorTranslation,
335 &OutputRect, &InputPoint, MaskOrigin, Brush, BrushOrigin, Rop4);
336 break;
337 case DC_RECT:
338 // Clip the blt to the clip rectangle
339 ClipRect.left = ClipRegion->rclBounds.left + Translate.x;
340 ClipRect.right = ClipRegion->rclBounds.right + Translate.x;
341 ClipRect.top = ClipRegion->rclBounds.top + Translate.y;
342 ClipRect.bottom = ClipRegion->rclBounds.bottom + Translate.y;
343 EngIntersectRect(&CombinedRect, &OutputRect, &ClipRect);
344 Ret = (*BltRectFunc)(OutputObj, OutputGDI, InputObj, InputGDI, Mask, ColorTranslation,
345 &CombinedRect, &InputPoint, MaskOrigin, Brush, BrushOrigin, Rop4);
346 break;
347 case DC_COMPLEX:
348 Ret = TRUE;
349 CLIPOBJ_cEnumStart(ClipRegion, FALSE, CT_RECTANGLES, CD_ANY, ENUM_RECT_LIMIT);
350 do
351 {
352 EnumMore = CLIPOBJ_bEnum(ClipRegion,(ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
353
354 for (i = 0; i < RectEnum.c; i++)
355 {
356 ClipRect.left = RectEnum.arcl[i].left + Translate.x;
357 ClipRect.right = RectEnum.arcl[i].right + Translate.x;
358 ClipRect.top = RectEnum.arcl[i].top + Translate.y;
359 ClipRect.bottom = RectEnum.arcl[i].bottom + Translate.y;
360 EngIntersectRect(&CombinedRect, &OutputRect, &ClipRect);
361 Ret = (*BltRectFunc)(OutputObj, OutputGDI, InputObj, InputGDI, Mask, ColorTranslation,
362 &CombinedRect, &InputPoint, MaskOrigin, Brush, BrushOrigin, Rop4) &&
363 Ret;
364 }
365 }
366 while(EnumMore);
367 break;
368 }
369
370
371 IntEngLeave(&EnterLeaveDest);
372 IntEngLeave(&EnterLeaveSource);
373
374 return Ret;
375 }
376
377 BOOL STDCALL
378 IntEngBitBlt(SURFOBJ *DestObj,
379 SURFOBJ *SourceObj,
380 SURFOBJ *Mask,
381 CLIPOBJ *ClipRegion,
382 XLATEOBJ *ColorTranslation,
383 RECTL *DestRect,
384 POINTL *SourcePoint,
385 POINTL *MaskOrigin,
386 BRUSHOBJ *Brush,
387 POINTL *BrushOrigin,
388 ROP4 Rop4)
389 {
390 BOOLEAN ret;
391 SURFGDI *DestGDI;
392 SURFGDI *SourceGDI;
393 RECTL OutputRect;
394 POINTL InputPoint;
395
396 if (NULL != SourcePoint)
397 {
398 InputPoint = *SourcePoint;
399 }
400
401 /* Clip against the bounds of the clipping region so we won't try to write
402 * outside the surface */
403 if (NULL != ClipRegion)
404 {
405 if (! EngIntersectRect(&OutputRect, DestRect, &ClipRegion->rclBounds))
406 {
407 return TRUE;
408 }
409 InputPoint.x += OutputRect.left - DestRect->left;
410 InputPoint.y += OutputRect.top - DestRect->top;
411 }
412 else
413 {
414 OutputRect = *DestRect;
415 }
416
417 if (NULL != SourceObj)
418 {
419 SourceGDI = (PSURFGDI) AccessInternalObjectFromUserObject(SourceObj);
420 MouseSafetyOnDrawStart(SourceObj, SourceGDI, InputPoint.x, InputPoint.y,
421 (InputPoint.x + abs(DestRect->right - DestRect->left)),
422 (InputPoint.y + abs(DestRect->bottom - DestRect->top)));
423 }
424
425 /* No success yet */
426 ret = FALSE;
427 DestGDI = (SURFGDI*)AccessInternalObjectFromUserObject(DestObj);
428 MouseSafetyOnDrawStart(DestObj, DestGDI, OutputRect.left, OutputRect.top,
429 OutputRect.right, OutputRect.bottom);
430
431 /* Call the driver's DrvBitBlt if available */
432 if (NULL != DestGDI->BitBlt)
433 {
434 ret = DestGDI->BitBlt(DestObj, SourceObj, Mask, ClipRegion, ColorTranslation,
435 &OutputRect, &InputPoint, MaskOrigin, Brush, BrushOrigin,
436 Rop4);
437 }
438
439 if (! ret)
440 {
441 ret = EngBitBlt(DestObj, SourceObj, Mask, ClipRegion, ColorTranslation,
442 &OutputRect, &InputPoint, MaskOrigin, Brush, BrushOrigin,
443 Rop4);
444 }
445
446 MouseSafetyOnDrawEnd(DestObj, DestGDI);
447 if (NULL != SourceObj)
448 {
449 MouseSafetyOnDrawEnd(SourceObj, SourceGDI);
450 }
451
452 return ret;
453 }
454 /* EOF */