Sync with trunk (r48144)
[reactos.git] / subsystems / win32 / win32k / objects / brush.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS win32 subsystem
4 * PURPOSE: Functions for brushes
5 * FILE: subsystem/win32/win32k/objects/brush.c
6 * PROGRAMER:
7 */
8
9 #include <win32k.h>
10
11 #define NDEBUG
12 #include <debug.h>
13
14 #define GDIOBJATTRFREE 170
15
16 typedef struct _GDI_OBJ_ATTR_FREELIST
17 {
18 LIST_ENTRY Entry;
19 DWORD nEntries;
20 PVOID AttrList[GDIOBJATTRFREE];
21 } GDI_OBJ_ATTR_FREELIST, *PGDI_OBJ_ATTR_FREELIST;
22
23 typedef struct _GDI_OBJ_ATTR_ENTRY
24 {
25 RGN_ATTR Attr[GDIOBJATTRFREE];
26 } GDI_OBJ_ATTR_ENTRY, *PGDI_OBJ_ATTR_ENTRY;
27
28 static const USHORT HatchBrushes[NB_HATCH_STYLES][8] =
29 {
30 {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}, /* HS_HORIZONTAL */
31 {0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7}, /* HS_VERTICAL */
32 {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F}, /* HS_FDIAGONAL */
33 {0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE}, /* HS_BDIAGONAL */
34 {0xF7, 0xF7, 0xF7, 0xF7, 0x00, 0xF7, 0xF7, 0xF7}, /* HS_CROSS */
35 {0x7E, 0xBD, 0xDB, 0xE7, 0xE7, 0xDB, 0xBD, 0x7E} /* HS_DIAGCROSS */
36 };
37
38
39 PVOID
40 FASTCALL
41 AllocateObjectAttr(VOID)
42 {
43 PTHREADINFO pti;
44 PPROCESSINFO ppi;
45 PVOID pAttr;
46 PGDI_OBJ_ATTR_FREELIST pGdiObjAttrFreeList;
47 PGDI_OBJ_ATTR_ENTRY pGdiObjAttrEntry;
48 int i;
49
50 pti = PsGetCurrentThreadWin32Thread();
51 if (pti->pgdiBrushAttr)
52 {
53 pAttr = pti->pgdiBrushAttr; // Get the free one.
54 pti->pgdiBrushAttr = NULL;
55 return pAttr;
56 }
57
58 ppi = PsGetCurrentProcessWin32Process();
59
60 if (!ppi->pBrushAttrList) // If set point is null, allocate new group.
61 {
62 pGdiObjAttrEntry = EngAllocUserMem(sizeof(GDI_OBJ_ATTR_ENTRY), 0);
63
64 if (!pGdiObjAttrEntry)
65 {
66 DPRINT1("Attr Failed User Allocation!\n");
67 return NULL;
68 }
69
70 DPRINT("AllocObjectAttr User 0x%x\n",pGdiObjAttrEntry);
71
72 pGdiObjAttrFreeList = ExAllocatePoolWithTag( PagedPool,
73 sizeof(GDI_OBJ_ATTR_FREELIST),
74 GDITAG_BRUSH_FREELIST);
75 if ( !pGdiObjAttrFreeList )
76 {
77 EngFreeUserMem(pGdiObjAttrEntry);
78 return NULL;
79 }
80
81 RtlZeroMemory(pGdiObjAttrFreeList, sizeof(GDI_OBJ_ATTR_FREELIST));
82
83 DPRINT("AllocObjectAttr Ex 0x%x\n",pGdiObjAttrFreeList);
84
85 InsertHeadList( &ppi->GDIBrushAttrFreeList, &pGdiObjAttrFreeList->Entry);
86
87 pGdiObjAttrFreeList->nEntries = GDIOBJATTRFREE;
88 // Start at the bottom up and set end of free list point.
89 ppi->pBrushAttrList = &pGdiObjAttrEntry->Attr[GDIOBJATTRFREE-1];
90 // Build the free attr list.
91 for ( i = 0; i < GDIOBJATTRFREE; i++)
92 {
93 pGdiObjAttrFreeList->AttrList[i] = &pGdiObjAttrEntry->Attr[i];
94 }
95 }
96
97 pAttr = ppi->pBrushAttrList;
98 pGdiObjAttrFreeList = (PGDI_OBJ_ATTR_FREELIST)ppi->GDIBrushAttrFreeList.Flink;
99
100 // Free the list when it is full!
101 if ( pGdiObjAttrFreeList->nEntries-- == 1)
102 { // No more free entries, so yank the list.
103 RemoveEntryList( &pGdiObjAttrFreeList->Entry );
104
105 ExFreePoolWithTag( pGdiObjAttrFreeList, GDITAG_BRUSH_FREELIST );
106
107 if ( IsListEmpty( &ppi->GDIBrushAttrFreeList ) )
108 {
109 ppi->pBrushAttrList = NULL;
110 return pAttr;
111 }
112
113 pGdiObjAttrFreeList = (PGDI_OBJ_ATTR_FREELIST)ppi->GDIBrushAttrFreeList.Flink;
114 }
115
116 ppi->pBrushAttrList = pGdiObjAttrFreeList->AttrList[pGdiObjAttrFreeList->nEntries-1];
117
118 return pAttr;
119 }
120
121 VOID
122 FASTCALL
123 FreeObjectAttr(PVOID pAttr)
124 {
125 PTHREADINFO pti;
126 PPROCESSINFO ppi;
127 PGDI_OBJ_ATTR_FREELIST pGdiObjAttrFreeList;
128
129 pti = PsGetCurrentThreadWin32Thread();
130
131 if (!pti) return;
132
133 if (!pti->pgdiBrushAttr)
134 { // If it is null, just cache it for the next time.
135 pti->pgdiBrushAttr = pAttr;
136 return;
137 }
138
139 ppi = PsGetCurrentProcessWin32Process();
140
141 pGdiObjAttrFreeList = (PGDI_OBJ_ATTR_FREELIST)ppi->GDIBrushAttrFreeList.Flink;
142
143 // We add to the list of free entries, so this will grows!
144 if ( IsListEmpty(&ppi->GDIBrushAttrFreeList) ||
145 pGdiObjAttrFreeList->nEntries == GDIOBJATTRFREE )
146 {
147 pGdiObjAttrFreeList = ExAllocatePoolWithTag( PagedPool,
148 sizeof(GDI_OBJ_ATTR_FREELIST),
149 GDITAG_BRUSH_FREELIST);
150 if ( !pGdiObjAttrFreeList )
151 {
152 return;
153 }
154 InsertHeadList( &ppi->GDIBrushAttrFreeList, &pGdiObjAttrFreeList->Entry);
155 pGdiObjAttrFreeList->nEntries = 0;
156 }
157 // Up count, save the entry and set end of free list point.
158 ++pGdiObjAttrFreeList->nEntries; // Top Down...
159 pGdiObjAttrFreeList->AttrList[pGdiObjAttrFreeList->nEntries-1] = pAttr;
160 ppi->pBrushAttrList = pAttr;
161
162 return;
163 }
164
165
166 BOOL
167 INTERNAL_CALL
168 BRUSH_Cleanup(PVOID ObjectBody)
169 {
170 PBRUSH pbrush = (PBRUSH)ObjectBody;
171 if (pbrush->flAttrs & (GDIBRUSH_IS_HATCH | GDIBRUSH_IS_BITMAP))
172 {
173 ASSERT(pbrush->hbmPattern);
174 GDIOBJ_SetOwnership(pbrush->hbmPattern, PsGetCurrentProcess());
175 GreDeleteObject(pbrush->hbmPattern);
176 }
177
178 /* Free the kmode styles array of EXTPENS */
179 if (pbrush->pStyle)
180 {
181 ExFreePool(pbrush->pStyle);
182 }
183
184 return TRUE;
185 }
186
187 INT
188 FASTCALL
189 BRUSH_GetObject(PBRUSH pbrush, INT Count, LPLOGBRUSH Buffer)
190 {
191 if (Buffer == NULL) return sizeof(LOGBRUSH);
192 if (Count == 0) return 0;
193
194 /* Set colour */
195 Buffer->lbColor = pbrush->BrushAttr.lbColor;
196
197 /* Set Hatch */
198 if ((pbrush->flAttrs & GDIBRUSH_IS_HATCH)!=0)
199 {
200 /* FIXME : this is not the right value */
201 Buffer->lbHatch = (LONG)pbrush->hbmPattern;
202 }
203 else
204 {
205 Buffer->lbHatch = 0;
206 }
207
208 Buffer->lbStyle = 0;
209
210 /* Get the type of style */
211 if ((pbrush->flAttrs & GDIBRUSH_IS_SOLID)!=0)
212 {
213 Buffer->lbStyle = BS_SOLID;
214 }
215 else if ((pbrush->flAttrs & GDIBRUSH_IS_NULL)!=0)
216 {
217 Buffer->lbStyle = BS_NULL; // BS_HOLLOW
218 }
219 else if ((pbrush->flAttrs & GDIBRUSH_IS_HATCH)!=0)
220 {
221 Buffer->lbStyle = BS_HATCHED;
222 }
223 else if ((pbrush->flAttrs & GDIBRUSH_IS_BITMAP)!=0)
224 {
225 Buffer->lbStyle = BS_PATTERN;
226 }
227 else if ((pbrush->flAttrs & GDIBRUSH_IS_DIB)!=0)
228 {
229 Buffer->lbStyle = BS_DIBPATTERN;
230 }
231
232 /* FIXME
233 else if ((pbrush->flAttrs & )!=0)
234 {
235 Buffer->lbStyle = BS_INDEXED;
236 }
237 else if ((pbrush->flAttrs & )!=0)
238 {
239 Buffer->lbStyle = BS_DIBPATTERNPT;
240 }
241 */
242
243 /* FIXME */
244 return sizeof(LOGBRUSH);
245 }
246
247 /**
248 * @name CalculateColorTableSize
249 *
250 * Internal routine to calculate the number of color table entries.
251 *
252 * @param BitmapInfoHeader
253 * Input bitmap information header, can be any version of
254 * BITMAPINFOHEADER or BITMAPCOREHEADER.
255 *
256 * @param ColorSpec
257 * Pointer to variable which specifiing the color mode (DIB_RGB_COLORS
258 * or DIB_RGB_COLORS). On successful return this value is normalized
259 * according to the bitmap info.
260 *
261 * @param ColorTableSize
262 * On successful return this variable is filled with number of
263 * entries in color table for the image with specified parameters.
264 *
265 * @return
266 * TRUE if the input values together form a valid image, FALSE otherwise.
267 */
268 BOOL
269 APIENTRY
270 CalculateColorTableSize(
271 CONST BITMAPINFOHEADER *BitmapInfoHeader,
272 UINT *ColorSpec,
273 UINT *ColorTableSize)
274 {
275 WORD BitCount;
276 DWORD ClrUsed;
277 DWORD Compression;
278
279 /*
280 * At first get some basic parameters from the passed BitmapInfoHeader
281 * structure. It can have one of the following formats:
282 * - BITMAPCOREHEADER (the oldest one with totally different layout
283 * from the others)
284 * - BITMAPINFOHEADER (the standard and most common header)
285 * - BITMAPV4HEADER (extension of BITMAPINFOHEADER)
286 * - BITMAPV5HEADER (extension of BITMAPV4HEADER)
287 */
288 if (BitmapInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
289 {
290 BitCount = ((LPBITMAPCOREHEADER)BitmapInfoHeader)->bcBitCount;
291 ClrUsed = 0;
292 Compression = BI_RGB;
293 }
294 else
295 {
296 BitCount = BitmapInfoHeader->biBitCount;
297 ClrUsed = BitmapInfoHeader->biClrUsed;
298 Compression = BitmapInfoHeader->biCompression;
299 }
300
301 switch (Compression)
302 {
303 case BI_BITFIELDS:
304 if (*ColorSpec == DIB_PAL_COLORS)
305 *ColorSpec = DIB_RGB_COLORS;
306
307 if (BitCount != 16 && BitCount != 32)
308 return FALSE;
309
310 /* For BITMAPV4HEADER/BITMAPV5HEADER the masks are included in
311 * the structure itself (bV4RedMask, bV4GreenMask, and bV4BlueMask).
312 * For BITMAPINFOHEADER the color masks are stored in the palette. */
313 if (BitmapInfoHeader->biSize > sizeof(BITMAPINFOHEADER))
314 *ColorTableSize = 0;
315 else
316 *ColorTableSize = 3;
317
318 return TRUE;
319
320 case BI_RGB:
321 switch (BitCount)
322 {
323 case 1:
324 *ColorTableSize = ClrUsed ? min(ClrUsed, 2) : 2;
325 return TRUE;
326
327 case 4:
328 *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
329 return TRUE;
330
331 case 8:
332 *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
333 return TRUE;
334
335 default:
336 if (*ColorSpec == DIB_PAL_COLORS)
337 *ColorSpec = DIB_RGB_COLORS;
338 if (BitCount != 16 && BitCount != 24 && BitCount != 32)
339 return FALSE;
340 *ColorTableSize = ClrUsed;
341 return TRUE;
342 }
343
344 case BI_RLE4:
345 if (BitCount == 4)
346 {
347 *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
348 return TRUE;
349 }
350 return FALSE;
351
352 case BI_RLE8:
353 if (BitCount == 8)
354 {
355 *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
356 return TRUE;
357 }
358 return FALSE;
359
360 case BI_JPEG:
361 case BI_PNG:
362 *ColorTableSize = ClrUsed;
363 return TRUE;
364
365 default:
366 return FALSE;
367 }
368 }
369
370 HBRUSH
371 APIENTRY
372 IntGdiCreateDIBBrush(
373 CONST BITMAPINFO *BitmapInfo,
374 UINT ColorSpec,
375 UINT BitmapInfoSize,
376 CONST VOID *PackedDIB)
377 {
378 HBRUSH hBrush;
379 PBRUSH pbrush;
380 HBITMAP hPattern;
381 ULONG_PTR DataPtr;
382 UINT PaletteEntryCount;
383 PSURFACE psurfPattern;
384 INT PaletteType;
385 HPALETTE hpal ;
386
387 if (BitmapInfo->bmiHeader.biSize < sizeof(BITMAPINFOHEADER))
388 {
389 SetLastWin32Error(ERROR_INVALID_PARAMETER);
390 return NULL;
391 }
392
393 if (!CalculateColorTableSize(&BitmapInfo->bmiHeader,
394 &ColorSpec,
395 &PaletteEntryCount))
396 {
397 SetLastWin32Error(ERROR_INVALID_PARAMETER);
398 return NULL;
399 }
400
401 // FIXME: What about BI_BITFIELDS
402 DataPtr = (ULONG_PTR)BitmapInfo + BitmapInfo->bmiHeader.biSize;
403 if (ColorSpec == DIB_RGB_COLORS)
404 DataPtr += PaletteEntryCount * sizeof(RGBQUAD);
405 else
406 DataPtr += PaletteEntryCount * sizeof(USHORT);
407
408 hPattern = GreCreateBitmap(BitmapInfo->bmiHeader.biWidth,
409 BitmapInfo->bmiHeader.biHeight,
410 BitmapInfo->bmiHeader.biPlanes,
411 BitmapInfo->bmiHeader.biBitCount,
412 (PVOID)DataPtr);
413 if (hPattern == NULL)
414 {
415 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
416 return NULL;
417 }
418
419 psurfPattern = SURFACE_LockSurface(hPattern);
420 ASSERT(psurfPattern != NULL);
421 hpal = BuildDIBPalette(BitmapInfo, &PaletteType);
422 psurfPattern->ppal = PALETTE_ShareLockPalette(hpal);
423 /* Lazy delete palette, it will be freed when its shared reference is zeroed */
424 GreDeleteObject(hpal);
425 SURFACE_UnlockSurface(psurfPattern);
426
427 pbrush = BRUSH_AllocBrushWithHandle();
428 if (pbrush == NULL)
429 {
430 GreDeleteObject(hPattern);
431 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
432 return NULL;
433 }
434 hBrush = pbrush->BaseObject.hHmgr;
435
436 pbrush->flAttrs |= GDIBRUSH_IS_BITMAP | GDIBRUSH_IS_DIB;
437 pbrush->hbmPattern = hPattern;
438 /* FIXME: Fill in the rest of fields!!! */
439
440 GDIOBJ_SetOwnership(hPattern, NULL);
441
442 BRUSH_UnlockBrush(pbrush);
443
444 return hBrush;
445 }
446
447 HBRUSH
448 APIENTRY
449 IntGdiCreateHatchBrush(
450 INT Style,
451 COLORREF Color)
452 {
453 HBRUSH hBrush;
454 PBRUSH pbrush;
455 HBITMAP hPattern;
456
457 if (Style < 0 || Style >= NB_HATCH_STYLES)
458 {
459 return 0;
460 }
461
462 hPattern = GreCreateBitmap(8, 8, 1, 1, (LPBYTE)HatchBrushes[Style]);
463 if (hPattern == NULL)
464 {
465 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
466 return NULL;
467 }
468
469 pbrush = BRUSH_AllocBrushWithHandle();
470 if (pbrush == NULL)
471 {
472 GreDeleteObject(hPattern);
473 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
474 return NULL;
475 }
476 hBrush = pbrush->BaseObject.hHmgr;
477
478 pbrush->flAttrs |= GDIBRUSH_IS_HATCH;
479 pbrush->hbmPattern = hPattern;
480 pbrush->BrushAttr.lbColor = Color & 0xFFFFFF;
481
482 GDIOBJ_SetOwnership(hPattern, NULL);
483
484 BRUSH_UnlockBrush(pbrush);
485
486 return hBrush;
487 }
488
489 HBRUSH
490 APIENTRY
491 IntGdiCreatePatternBrush(
492 HBITMAP hBitmap)
493 {
494 HBRUSH hBrush;
495 PBRUSH pbrush;
496 HBITMAP hPattern;
497
498 hPattern = BITMAP_CopyBitmap(hBitmap);
499 if (hPattern == NULL)
500 {
501 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
502 return NULL;
503 }
504
505 pbrush = BRUSH_AllocBrushWithHandle();
506 if (pbrush == NULL)
507 {
508 GreDeleteObject(hPattern);
509 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
510 return NULL;
511 }
512 hBrush = pbrush->BaseObject.hHmgr;
513
514 pbrush->flAttrs |= GDIBRUSH_IS_BITMAP;
515 pbrush->hbmPattern = hPattern;
516 /* FIXME: Fill in the rest of fields!!! */
517
518 GDIOBJ_SetOwnership(hPattern, NULL);
519
520 BRUSH_UnlockBrush(pbrush);
521
522 return hBrush;
523 }
524
525 HBRUSH
526 APIENTRY
527 IntGdiCreateSolidBrush(
528 COLORREF Color)
529 {
530 HBRUSH hBrush;
531 PBRUSH pbrush;
532
533 pbrush = BRUSH_AllocBrushWithHandle();
534 if (pbrush == NULL)
535 {
536 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
537 return NULL;
538 }
539 hBrush = pbrush->BaseObject.hHmgr;
540
541 pbrush->flAttrs |= GDIBRUSH_IS_SOLID;
542
543 pbrush->BrushAttr.lbColor = Color;
544 /* FIXME: Fill in the rest of fields!!! */
545
546 BRUSH_UnlockBrush(pbrush);
547
548 return hBrush;
549 }
550
551 HBRUSH
552 APIENTRY
553 IntGdiCreateNullBrush(VOID)
554 {
555 HBRUSH hBrush;
556 PBRUSH pbrush;
557
558 pbrush = BRUSH_AllocBrushWithHandle();
559 if (pbrush == NULL)
560 {
561 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
562 return NULL;
563 }
564 hBrush = pbrush->BaseObject.hHmgr;
565
566 pbrush->flAttrs |= GDIBRUSH_IS_NULL;
567 BRUSH_UnlockBrush(pbrush);
568
569 return hBrush;
570 }
571
572 VOID
573 FASTCALL
574 IntGdiSetSolidBrushColor(HBRUSH hBrush, COLORREF Color)
575 {
576 PBRUSH pbrush;
577
578 pbrush = BRUSH_LockBrush(hBrush);
579 if (pbrush->flAttrs & GDIBRUSH_IS_SOLID)
580 {
581 pbrush->BrushAttr.lbColor = Color & 0xFFFFFF;
582 }
583 BRUSH_UnlockBrush(pbrush);
584 }
585
586
587 /* PUBLIC FUNCTIONS ***********************************************************/
588
589 HBRUSH
590 APIENTRY
591 NtGdiCreateDIBBrush(
592 IN PVOID BitmapInfoAndData,
593 IN FLONG ColorSpec,
594 IN UINT BitmapInfoSize,
595 IN BOOL b8X8,
596 IN BOOL bPen,
597 IN PVOID PackedDIB)
598 {
599 BITMAPINFO *SafeBitmapInfoAndData;
600 NTSTATUS Status = STATUS_SUCCESS;
601 HBRUSH hBrush;
602
603 SafeBitmapInfoAndData = EngAllocMem(FL_ZERO_MEMORY, BitmapInfoSize, TAG_DIB);
604 if (SafeBitmapInfoAndData == NULL)
605 {
606 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
607 return NULL;
608 }
609
610 _SEH2_TRY
611 {
612 ProbeForRead(BitmapInfoAndData, BitmapInfoSize, 1);
613 RtlCopyMemory(SafeBitmapInfoAndData, BitmapInfoAndData, BitmapInfoSize);
614 }
615 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
616 {
617 Status = _SEH2_GetExceptionCode();
618 }
619 _SEH2_END;
620
621 if (!NT_SUCCESS(Status))
622 {
623 EngFreeMem(SafeBitmapInfoAndData);
624 SetLastNtError(Status);
625 return 0;
626 }
627
628 hBrush = IntGdiCreateDIBBrush(SafeBitmapInfoAndData,
629 ColorSpec,
630 BitmapInfoSize,
631 PackedDIB);
632
633 EngFreeMem(SafeBitmapInfoAndData);
634
635 return hBrush;
636 }
637
638 HBRUSH
639 APIENTRY
640 NtGdiCreateHatchBrushInternal(
641 ULONG Style,
642 COLORREF Color,
643 BOOL bPen)
644 {
645 return IntGdiCreateHatchBrush(Style, Color);
646 }
647
648 HBRUSH
649 APIENTRY
650 NtGdiCreatePatternBrushInternal(
651 HBITMAP hBitmap,
652 BOOL bPen,
653 BOOL b8x8)
654 {
655 return IntGdiCreatePatternBrush(hBitmap);
656 }
657
658 HBRUSH
659 APIENTRY
660 NtGdiCreateSolidBrush(COLORREF Color,
661 IN OPTIONAL HBRUSH hbr)
662 {
663 return IntGdiCreateSolidBrush(Color);
664 }
665
666 /**
667 * \name NtGdiSetBrushOrg
668 *
669 * \brief Sets the brush origin that GDI assigns to
670 * the next brush an application selects into the specified device context.
671 *
672 * @implemented
673 */
674 BOOL
675 APIENTRY
676 NtGdiSetBrushOrg(HDC hDC, INT XOrg, INT YOrg, LPPOINT Point)
677 {
678 PDC dc;
679 PDC_ATTR pdcattr;
680
681 dc = DC_LockDc(hDC);
682 if (dc == NULL)
683 {
684 SetLastWin32Error(ERROR_INVALID_HANDLE);
685 return FALSE;
686 }
687 pdcattr = dc->pdcattr;
688
689 if (Point != NULL)
690 {
691 NTSTATUS Status = STATUS_SUCCESS;
692 POINT SafePoint;
693 SafePoint.x = pdcattr->ptlBrushOrigin.x;
694 SafePoint.y = pdcattr->ptlBrushOrigin.y;
695 _SEH2_TRY
696 {
697 ProbeForWrite(Point, sizeof(POINT), 1);
698 *Point = SafePoint;
699 }
700 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
701 {
702 Status = _SEH2_GetExceptionCode();
703 }
704 _SEH2_END;
705
706 if (!NT_SUCCESS(Status))
707 {
708 DC_UnlockDc(dc);
709 SetLastNtError(Status);
710 return FALSE;
711 }
712 }
713
714 pdcattr->ptlBrushOrigin.x = XOrg;
715 pdcattr->ptlBrushOrigin.y = YOrg;
716 IntptlBrushOrigin(dc, XOrg, YOrg );
717 DC_UnlockDc(dc);
718
719 return TRUE;
720 }
721
722 /* EOF */