Copy wininet to branch
[reactos.git] / reactos / subsys / win32k / objects / brush.c
1 /*
2 * ReactOS Win32 Subsystem
3 *
4 * Copyright (C) 1998 - 2004 ReactOS Team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * $Id$
21 */
22 #include <w32k.h>
23
24 static const USHORT HatchBrushes[NB_HATCH_STYLES][8] =
25 {
26 {0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00}, /* HS_HORIZONTAL */
27 {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, /* HS_VERTICAL */
28 {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}, /* HS_FDIAGONAL */
29 {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, /* HS_BDIAGONAL */
30 {0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08}, /* HS_CROSS */
31 {0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81} /* HS_DIAGCROSS */
32 };
33
34 BOOL INTERNAL_CALL
35 BRUSH_Cleanup(PVOID ObjectBody)
36 {
37 PGDIBRUSHOBJ pBrush = (PGDIBRUSHOBJ)ObjectBody;
38 if(pBrush->flAttrs & (GDIBRUSH_IS_HATCH | GDIBRUSH_IS_BITMAP))
39 {
40 ASSERT(pBrush->hbmPattern);
41 GDIOBJ_SetOwnership(pBrush->hbmPattern, PsGetCurrentProcess());
42 NtGdiDeleteObject(pBrush->hbmPattern);
43 }
44
45 return TRUE;
46 }
47
48 XLATEOBJ* FASTCALL
49 IntGdiCreateBrushXlate(PDC Dc, GDIBRUSHOBJ *BrushObj, BOOLEAN *Failed)
50 {
51 XLATEOBJ *Result = NULL;
52
53 if (BrushObj->flAttrs & GDIBRUSH_IS_NULL)
54 {
55 Result = NULL;
56 *Failed = FALSE;
57 }
58 else if (BrushObj->flAttrs & GDIBRUSH_IS_SOLID)
59 {
60 Result = IntEngCreateXlate(0, PAL_RGB, Dc->w.hPalette, NULL);
61 *Failed = FALSE;
62 }
63 else
64 {
65 BITMAPOBJ *Pattern = BITMAPOBJ_LockBitmap(BrushObj->hbmPattern);
66 if (Pattern == NULL)
67 return NULL;
68
69 /* Special case: 1bpp pattern */
70 if (Pattern->SurfObj.iBitmapFormat == BMF_1BPP)
71 {
72 if (Dc->w.bitsPerPixel != 1)
73 Result = IntEngCreateSrcMonoXlate(Dc->w.hPalette, Dc->w.textColor, Dc->w.backgroundColor);
74 }
75 else if (BrushObj->flAttrs & GDIBRUSH_IS_DIB)
76 {
77 Result = IntEngCreateXlate(0, 0, Dc->w.hPalette, Pattern->hDIBPalette);
78 }
79
80 BITMAPOBJ_UnlockBitmap(BrushObj->hbmPattern);
81 *Failed = FALSE;
82 }
83
84 return Result;
85 }
86
87 VOID FASTCALL
88 IntGdiInitBrushInstance(GDIBRUSHINST *BrushInst, PGDIBRUSHOBJ BrushObj, XLATEOBJ *XlateObj)
89 {
90 ASSERT(BrushInst);
91 ASSERT(BrushObj);
92 if (BrushObj->flAttrs & GDIBRUSH_IS_NULL)
93 BrushInst->BrushObject.iSolidColor = 0;
94 else if (BrushObj->flAttrs & GDIBRUSH_IS_SOLID)
95 BrushInst->BrushObject.iSolidColor = XLATEOBJ_iXlate(XlateObj, BrushObj->BrushAttr.lbColor);
96 else
97 BrushInst->BrushObject.iSolidColor = 0xFFFFFFFF;
98 BrushInst->BrushObject.pvRbrush = BrushObj->ulRealization;
99 BrushInst->BrushObject.flColorType = 0;
100 BrushInst->GdiBrushObject = BrushObj;
101 BrushInst->XlateObject = XlateObj;
102 }
103
104 /**
105 * @name CalculateColorTableSize
106 *
107 * Internal routine to calculate the number of color table entries.
108 *
109 * @param BitmapInfoHeader
110 * Input bitmap information header, can be any version of
111 * BITMAPINFOHEADER or BITMAPCOREHEADER.
112 *
113 * @param ColorSpec
114 * Pointer to variable which specifiing the color mode (DIB_RGB_COLORS
115 * or DIB_RGB_COLORS). On successful return this value is normalized
116 * according to the bitmap info.
117 *
118 * @param ColorTableSize
119 * On successful return this variable is filled with number of
120 * entries in color table for the image with specified parameters.
121 *
122 * @return
123 * TRUE if the input values together form a valid image, FALSE otherwise.
124 */
125
126 BOOL STDCALL
127 CalculateColorTableSize(
128 CONST BITMAPINFOHEADER *BitmapInfoHeader,
129 UINT *ColorSpec,
130 UINT *ColorTableSize)
131 {
132 WORD BitCount;
133 DWORD ClrUsed;
134 DWORD Compression;
135
136 /*
137 * At first get some basic parameters from the passed BitmapInfoHeader
138 * structure. It can have one of the following formats:
139 * - BITMAPCOREHEADER (the oldest one with totally different layout
140 * from the others)
141 * - BITMAPINFOHEADER (the standard and most common header)
142 * - BITMAPV4HEADER (extension of BITMAPINFOHEADER)
143 * - BITMAPV5HEADER (extension of BITMAPV4HEADER)
144 */
145
146 if (BitmapInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
147 {
148 BitCount = ((LPBITMAPCOREHEADER)BitmapInfoHeader)->bcBitCount;
149 ClrUsed = 0;
150 Compression = BI_RGB;
151 }
152 else
153 {
154 BitCount = BitmapInfoHeader->biBitCount;
155 ClrUsed = BitmapInfoHeader->biClrUsed;
156 Compression = BitmapInfoHeader->biCompression;
157 }
158
159 switch (Compression)
160 {
161 case BI_BITFIELDS:
162 if (*ColorSpec == DIB_PAL_COLORS)
163 *ColorSpec = DIB_RGB_COLORS;
164
165 if (BitCount != 16 && BitCount != 32)
166 return FALSE;
167
168 /*
169 * For BITMAPV4HEADER/BITMAPV5HEADER the masks are included in
170 * the structure itself (bV4RedMask, bV4GreenMask, and bV4BlueMask).
171 * For BITMAPINFOHEADER the color masks are stored in the palette.
172 */
173
174 if (BitmapInfoHeader->biSize > sizeof(BITMAPINFOHEADER))
175 *ColorTableSize = 0;
176 else
177 *ColorTableSize = 3;
178
179 return TRUE;
180
181 case BI_RGB:
182 switch (BitCount)
183 {
184 case 1:
185 *ColorTableSize = ClrUsed ? min(ClrUsed, 2) : 2;
186 return TRUE;
187
188 case 4:
189 *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
190 return TRUE;
191
192 case 8:
193 *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
194 return TRUE;
195
196 default:
197 if (*ColorSpec == DIB_PAL_COLORS)
198 *ColorSpec = DIB_RGB_COLORS;
199 if (BitCount != 16 && BitCount != 24 && BitCount != 32)
200 return FALSE;
201 *ColorTableSize = ClrUsed;
202 return TRUE;
203 }
204
205 case BI_RLE4:
206 if (BitCount == 4)
207 {
208 *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
209 return TRUE;
210 }
211 return FALSE;
212
213 case BI_RLE8:
214 if (BitCount == 8)
215 {
216 *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
217 return TRUE;
218 }
219 return FALSE;
220
221 case BI_JPEG:
222 case BI_PNG:
223 *ColorTableSize = ClrUsed;
224 return TRUE;
225
226 default:
227 return FALSE;
228 }
229 }
230
231 HBRUSH STDCALL
232 IntGdiCreateDIBBrush(
233 CONST BITMAPINFO *BitmapInfo,
234 UINT ColorSpec,
235 UINT BitmapInfoSize,
236 CONST VOID *PackedDIB)
237 {
238 HBRUSH hBrush;
239 PGDIBRUSHOBJ BrushObject;
240 HBITMAP hPattern;
241 ULONG_PTR DataPtr;
242 UINT PaletteEntryCount;
243 PBITMAPOBJ BitmapObject;
244 UINT PaletteType;
245
246 if (BitmapInfo->bmiHeader.biSize < sizeof(BITMAPINFOHEADER))
247 {
248 SetLastWin32Error(ERROR_INVALID_PARAMETER);
249 return NULL;
250 }
251
252 if (!CalculateColorTableSize(&BitmapInfo->bmiHeader, &ColorSpec,
253 &PaletteEntryCount))
254 {
255 SetLastWin32Error(ERROR_INVALID_PARAMETER);
256 return NULL;
257 }
258
259 DataPtr = (ULONG_PTR)BitmapInfo + BitmapInfo->bmiHeader.biSize;
260 if (ColorSpec == DIB_RGB_COLORS)
261 DataPtr += PaletteEntryCount * sizeof(RGBQUAD);
262 else
263 DataPtr += PaletteEntryCount * sizeof(USHORT);
264
265 hPattern = NtGdiCreateBitmap(BitmapInfo->bmiHeader.biWidth,
266 BitmapInfo->bmiHeader.biHeight,
267 BitmapInfo->bmiHeader.biPlanes,
268 BitmapInfo->bmiHeader.biBitCount,
269 (PVOID)DataPtr);
270 if (hPattern == NULL)
271 {
272 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
273 return NULL;
274 }
275
276 BitmapObject = BITMAPOBJ_LockBitmap(hPattern);
277 ASSERT(BitmapObject != NULL);
278 BitmapObject->hDIBPalette = BuildDIBPalette(BitmapInfo, &PaletteType);
279 BITMAPOBJ_UnlockBitmap(hPattern);
280
281 hBrush = BRUSHOBJ_AllocBrush();
282 if (hBrush == NULL)
283 {
284 NtGdiDeleteObject(hPattern);
285 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
286 return NULL;
287 }
288
289 BrushObject = BRUSHOBJ_LockBrush(hBrush);
290 ASSERT(BrushObject != NULL);
291
292 BrushObject->flAttrs |= GDIBRUSH_IS_BITMAP | GDIBRUSH_IS_DIB;
293 BrushObject->hbmPattern = hPattern;
294 /* FIXME: Fill in the rest of fields!!! */
295
296 GDIOBJ_SetOwnership(hPattern, NULL);
297
298 BRUSHOBJ_UnlockBrush(hBrush);
299
300 return hBrush;
301 }
302
303 HBRUSH STDCALL
304 IntGdiCreateHatchBrush(
305 INT Style,
306 COLORREF Color)
307 {
308 HBRUSH hBrush;
309 PGDIBRUSHOBJ BrushObject;
310 HBITMAP hPattern;
311
312 if (Style < 0 || Style >= NB_HATCH_STYLES)
313 {
314 return 0;
315 }
316
317 hPattern = NtGdiCreateBitmap(8, 8, 1, 1, HatchBrushes[Style]);
318 if (hPattern == NULL)
319 {
320 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
321 return NULL;
322 }
323
324 hBrush = BRUSHOBJ_AllocBrush();
325 if (hBrush == NULL)
326 {
327 NtGdiDeleteObject(hPattern);
328 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
329 return NULL;
330 }
331
332 BrushObject = BRUSHOBJ_LockBrush(hBrush);
333 ASSERT(BrushObject != NULL);
334
335 BrushObject->flAttrs |= GDIBRUSH_IS_HATCH;
336 BrushObject->hbmPattern = hPattern;
337 BrushObject->BrushAttr.lbColor = Color & 0xFFFFFF;
338
339 GDIOBJ_SetOwnership(hPattern, NULL);
340
341 BRUSHOBJ_UnlockBrush(hBrush);
342
343 return hBrush;
344 }
345
346 HBRUSH STDCALL
347 IntGdiCreatePatternBrush(
348 HBITMAP hBitmap)
349 {
350 HBRUSH hBrush;
351 PGDIBRUSHOBJ BrushObject;
352 HBITMAP hPattern;
353
354 hPattern = BITMAPOBJ_CopyBitmap(hBitmap);
355 if (hPattern == NULL)
356 {
357 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
358 return NULL;
359 }
360
361 hBrush = BRUSHOBJ_AllocBrush();
362 if (hBrush == NULL)
363 {
364 NtGdiDeleteObject(hPattern);
365 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
366 return NULL;
367 }
368
369 BrushObject = BRUSHOBJ_LockBrush(hBrush);
370 ASSERT(BrushObject != NULL);
371
372 BrushObject->flAttrs |= GDIBRUSH_IS_BITMAP;
373 BrushObject->hbmPattern = hPattern;
374 /* FIXME: Fill in the rest of fields!!! */
375
376 GDIOBJ_SetOwnership(hPattern, NULL);
377
378 BRUSHOBJ_UnlockBrush(hBrush);
379
380 return hBrush;
381 }
382
383 HBRUSH STDCALL
384 IntGdiCreateSolidBrush(
385 COLORREF Color)
386 {
387 HBRUSH hBrush;
388 PGDIBRUSHOBJ BrushObject;
389
390 hBrush = BRUSHOBJ_AllocBrush();
391 if (hBrush == NULL)
392 {
393 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
394 return NULL;
395 }
396
397 BrushObject = BRUSHOBJ_LockBrush(hBrush);
398 ASSERT(BrushObject != NULL);
399
400 BrushObject->flAttrs |= GDIBRUSH_IS_SOLID;
401 BrushObject->BrushAttr.lbColor = Color & 0xFFFFFF;
402 /* FIXME: Fill in the rest of fields!!! */
403
404 BRUSHOBJ_UnlockBrush(hBrush);
405
406 return hBrush;
407 }
408
409 HBRUSH STDCALL
410 IntGdiCreateNullBrush(VOID)
411 {
412 HBRUSH hBrush;
413 PGDIBRUSHOBJ BrushObject;
414
415 hBrush = BRUSHOBJ_AllocBrush();
416 if (hBrush == NULL)
417 {
418 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
419 return NULL;
420 }
421
422 BrushObject = BRUSHOBJ_LockBrush(hBrush);
423 ASSERT(BrushObject != NULL);
424 BrushObject->flAttrs |= GDIBRUSH_IS_NULL;
425 BRUSHOBJ_UnlockBrush(hBrush);
426
427 return hBrush;
428 }
429
430 BOOL FASTCALL
431 IntPatBlt(
432 PDC dc,
433 INT XLeft,
434 INT YLeft,
435 INT Width,
436 INT Height,
437 DWORD ROP,
438 PGDIBRUSHOBJ BrushObj)
439 {
440 RECTL DestRect;
441 BITMAPOBJ *BitmapObj;
442 GDIBRUSHINST BrushInst;
443 POINTL BrushOrigin;
444 BOOL ret = TRUE;
445
446 ASSERT(BrushObj);
447
448 BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
449 if (BitmapObj == NULL)
450 {
451 SetLastWin32Error(ERROR_INVALID_HANDLE);
452 return FALSE;
453 }
454
455 if (!(BrushObj->flAttrs & GDIBRUSH_IS_NULL))
456 {
457 if (Width > 0)
458 {
459 DestRect.left = XLeft + dc->w.DCOrgX;
460 DestRect.right = XLeft + Width + dc->w.DCOrgX;
461 }
462 else
463 {
464 DestRect.left = XLeft + Width + 1 + dc->w.DCOrgX;
465 DestRect.right = XLeft + dc->w.DCOrgX + 1;
466 }
467
468 if (Height > 0)
469 {
470 DestRect.top = YLeft + dc->w.DCOrgY;
471 DestRect.bottom = YLeft + Height + dc->w.DCOrgY;
472 }
473 else
474 {
475 DestRect.top = YLeft + Height + dc->w.DCOrgY + 1;
476 DestRect.bottom = YLeft + dc->w.DCOrgY + 1;
477 }
478
479 BrushOrigin.x = BrushObj->ptOrigin.x + dc->w.DCOrgX;
480 BrushOrigin.y = BrushObj->ptOrigin.y + dc->w.DCOrgY;
481
482 IntGdiInitBrushInstance(&BrushInst, BrushObj, dc->XlateBrush);
483
484 ret = IntEngBitBlt(
485 BitmapObj,
486 NULL,
487 NULL,
488 dc->CombinedClip,
489 NULL,
490 &DestRect,
491 NULL,
492 NULL,
493 &BrushInst.BrushObject,
494 &BrushOrigin,
495 ROP3_TO_ROP4(ROP));
496 }
497
498 BITMAPOBJ_UnlockBitmap(dc->w.hBitmap);
499
500 return ret;
501 }
502
503 BOOL FASTCALL
504 IntGdiPolyPatBlt(
505 HDC hDC,
506 DWORD dwRop,
507 PPATRECT pRects,
508 int cRects,
509 ULONG Reserved)
510 {
511 int i;
512 PPATRECT r;
513 PGDIBRUSHOBJ BrushObj;
514 DC *dc;
515
516 dc = DC_LockDc(hDC);
517 if (dc == NULL)
518 {
519 SetLastWin32Error(ERROR_INVALID_HANDLE);
520 return FALSE;
521 }
522 if (dc->IsIC)
523 {
524 DC_UnlockDc(hDC);
525 /* Yes, Windows really returns TRUE in this case */
526 return TRUE;
527 }
528
529 for (r = pRects, i = 0; i < cRects; i++)
530 {
531 BrushObj = BRUSHOBJ_LockBrush(r->hBrush);
532 if(BrushObj != NULL)
533 {
534 IntPatBlt(
535 dc,
536 r->r.left,
537 r->r.top,
538 r->r.right,
539 r->r.bottom,
540 dwRop,
541 BrushObj);
542 BRUSHOBJ_UnlockBrush(r->hBrush);
543 }
544 r++;
545 }
546
547 DC_UnlockDc( hDC );
548
549 return TRUE;
550 }
551
552 /* PUBLIC FUNCTIONS ***********************************************************/
553
554 HBRUSH STDCALL
555 NtGdiCreateDIBBrush(
556 CONST BITMAPINFO *BitmapInfoAndData,
557 UINT ColorSpec,
558 UINT BitmapInfoSize,
559 CONST VOID *PackedDIB)
560 {
561 BITMAPINFO *SafeBitmapInfoAndData;
562 NTSTATUS Status;
563 HBRUSH hBrush;
564
565 SafeBitmapInfoAndData = EngAllocMem(0, BitmapInfoSize, 0);
566 if (SafeBitmapInfoAndData == NULL)
567 {
568 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
569 return NULL;
570 }
571
572 Status = MmCopyFromCaller(SafeBitmapInfoAndData, BitmapInfoAndData,
573 BitmapInfoSize);
574 if (!NT_SUCCESS(Status))
575 {
576 SetLastNtError(Status);
577 return 0;
578 }
579
580 hBrush = IntGdiCreateDIBBrush(SafeBitmapInfoAndData, ColorSpec,
581 BitmapInfoSize, PackedDIB);
582
583 EngFreeMem(SafeBitmapInfoAndData);
584
585 return hBrush;
586 }
587
588 HBRUSH STDCALL
589 NtGdiCreateHatchBrush(
590 INT Style,
591 COLORREF Color)
592 {
593 return IntGdiCreateHatchBrush(Style, Color);
594 }
595
596 HBRUSH STDCALL
597 NtGdiCreatePatternBrush(
598 HBITMAP hBitmap)
599 {
600 return IntGdiCreatePatternBrush(hBitmap);
601 }
602
603 HBRUSH STDCALL
604 NtGdiCreateSolidBrush(COLORREF Color)
605 {
606 return IntGdiCreateSolidBrush(Color);
607 }
608
609 /*
610 * NtGdiSetBrushOrgEx
611 *
612 * The NtGdiSetBrushOrgEx function sets the brush origin that GDI assigns to
613 * the next brush an application selects into the specified device context.
614 *
615 * Status
616 * @implemented
617 */
618
619 BOOL STDCALL
620 NtGdiSetBrushOrgEx(HDC hDC, INT XOrg, INT YOrg, LPPOINT Point)
621 {
622 PDC dc = DC_LockDc(hDC);
623 if (dc == NULL)
624 {
625 SetLastWin32Error(ERROR_INVALID_HANDLE);
626 return FALSE;
627 }
628
629 if (Point != NULL)
630 {
631 NTSTATUS Status;
632 POINT SafePoint;
633 SafePoint.x = dc->w.brushOrgX;
634 SafePoint.y = dc->w.brushOrgY;
635 Status = MmCopyToCaller(Point, &SafePoint, sizeof(POINT));
636 if(!NT_SUCCESS(Status))
637 {
638 DC_UnlockDc(hDC);
639 SetLastNtError(Status);
640 return FALSE;
641 }
642 }
643
644 dc->w.brushOrgX = XOrg;
645 dc->w.brushOrgY = YOrg;
646 DC_UnlockDc(hDC);
647
648 return TRUE;
649 }
650
651 BOOL STDCALL
652 NtGdiPolyPatBlt(
653 HDC hDC,
654 DWORD dwRop,
655 PPATRECT pRects,
656 INT cRects,
657 ULONG Reserved)
658 {
659 PPATRECT rb = NULL;
660 NTSTATUS Status;
661 BOOL Ret;
662
663 if (cRects > 0)
664 {
665 rb = ExAllocatePoolWithTag(PagedPool, sizeof(PATRECT) * cRects, TAG_PATBLT);
666 if (!rb)
667 {
668 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
669 return FALSE;
670 }
671 Status = MmCopyFromCaller(rb, pRects, sizeof(PATRECT) * cRects);
672 if (!NT_SUCCESS(Status))
673 {
674 ExFreePool(rb);
675 SetLastNtError(Status);
676 return FALSE;
677 }
678 }
679
680 Ret = IntGdiPolyPatBlt(hDC, dwRop, pRects, cRects, Reserved);
681
682 if (cRects > 0)
683 ExFreePool(rb);
684
685 return Ret;
686 }
687
688 BOOL STDCALL
689 NtGdiPatBlt(
690 HDC hDC,
691 INT XLeft,
692 INT YLeft,
693 INT Width,
694 INT Height,
695 DWORD ROP)
696 {
697 PGDIBRUSHOBJ BrushObj;
698 DC *dc = DC_LockDc(hDC);
699 BOOL ret;
700
701 if (dc == NULL)
702 {
703 SetLastWin32Error(ERROR_INVALID_HANDLE);
704 return FALSE;
705 }
706 if (dc->IsIC)
707 {
708 DC_UnlockDc(hDC);
709 /* Yes, Windows really returns TRUE in this case */
710 return TRUE;
711 }
712
713 BrushObj = BRUSHOBJ_LockBrush(dc->w.hBrush);
714 if (BrushObj == NULL)
715 {
716 SetLastWin32Error(ERROR_INVALID_HANDLE);
717 DC_UnlockDc(hDC);
718 return FALSE;
719 }
720
721 ret = IntPatBlt(
722 dc,
723 XLeft,
724 YLeft,
725 Width,
726 Height,
727 ROP,
728 BrushObj);
729
730 BRUSHOBJ_UnlockBrush(dc->w.hBrush);
731 DC_UnlockDc(hDC);
732
733 return ret;
734 }
735
736 /* EOF */