xref: /reactos/win32ss/gdi/ntgdi/dcobjs.c (revision 9d3c3a75)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS Win32k subsystem
4  * PURPOSE:           Functions for creation and destruction of DCs
5  * FILE:              win32ss/gdi/ntgdi/dcobjs.c
6  * PROGRAMER:         Timo Kreuzer (timo.kreuzer@rectos.org)
7  */
8 
9 #include <win32k.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 VOID
15 FASTCALL
16 DC_vUpdateFillBrush(PDC pdc)
17 {
18     PDC_ATTR pdcattr = pdc->pdcattr;
19     PBRUSH pbrFill;
20 
21     /* Check if the brush handle has changed */
22     if (pdcattr->hbrush != pdc->dclevel.pbrFill->BaseObject.hHmgr)
23     {
24         /* Try to lock the new brush */
25         pbrFill = BRUSH_ShareLockBrush(pdcattr->hbrush);
26         if (pbrFill)
27         {
28             /* Unlock old brush, set new brush */
29             BRUSH_ShareUnlockBrush(pdc->dclevel.pbrFill);
30             pdc->dclevel.pbrFill = pbrFill;
31 
32             /* Mark eboFill as dirty */
33             pdcattr->ulDirty_ |= DIRTY_FILL;
34         }
35         else
36         {
37             /* Invalid brush handle, restore old one */
38             pdcattr->hbrush = pdc->dclevel.pbrFill->BaseObject.hHmgr;
39         }
40     }
41 
42     /* Check if the EBRUSHOBJ needs update */
43     if (pdcattr->ulDirty_ & DIRTY_FILL)
44     {
45         /* Update eboFill */
46         EBRUSHOBJ_vUpdateFromDC(&pdc->eboFill, pdc->dclevel.pbrFill, pdc);
47     }
48 
49     /* Check for DC brush */
50     if (pdcattr->hbrush == StockObjects[DC_BRUSH])
51     {
52         /* Update the eboFill's solid color */
53         EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboFill, pdcattr->crBrushClr);
54     }
55 
56     /* Clear flags */
57     pdcattr->ulDirty_ &= ~(DIRTY_FILL | DC_BRUSH_DIRTY);
58 }
59 
60 VOID
61 FASTCALL
62 DC_vUpdateLineBrush(PDC pdc)
63 {
64     PDC_ATTR pdcattr = pdc->pdcattr;
65     PBRUSH pbrLine;
66 
67     /* Check if the pen handle has changed */
68     if (pdcattr->hpen != pdc->dclevel.pbrLine->BaseObject.hHmgr)
69     {
70         /* Try to lock the new pen */
71         pbrLine = PEN_ShareLockPen(pdcattr->hpen);
72         if (pbrLine)
73         {
74             /* Unlock old brush, set new brush */
75             BRUSH_ShareUnlockBrush(pdc->dclevel.pbrLine);
76             pdc->dclevel.pbrLine = pbrLine;
77 
78             /* Mark eboLine as dirty */
79             pdcattr->ulDirty_ |= DIRTY_LINE;
80         }
81         else
82         {
83             /* Invalid pen handle, restore old one */
84             pdcattr->hpen = pdc->dclevel.pbrLine->BaseObject.hHmgr;
85         }
86     }
87 
88     /* Check if the EBRUSHOBJ needs update */
89     if (pdcattr->ulDirty_ & DIRTY_LINE)
90     {
91         /* Update eboLine */
92         EBRUSHOBJ_vUpdateFromDC(&pdc->eboLine, pdc->dclevel.pbrLine, pdc);
93     }
94 
95     /* Check for DC pen */
96     if (pdcattr->hpen == StockObjects[DC_PEN])
97     {
98         /* Update the eboLine's solid color */
99         EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboLine, pdcattr->crPenClr);
100     }
101 
102     /* Clear flags */
103     pdcattr->ulDirty_ &= ~(DIRTY_LINE | DC_PEN_DIRTY);
104 }
105 
106 VOID
107 FASTCALL
108 DC_vUpdateTextBrush(PDC pdc)
109 {
110     PDC_ATTR pdcattr = pdc->pdcattr;
111 
112     /* Timo : The text brush should never be changed.
113      * Jérôme : Yeah, but its palette must be updated anyway! */
114     if(pdcattr->ulDirty_ & DIRTY_TEXT)
115         EBRUSHOBJ_vUpdateFromDC(&pdc->eboText, pbrDefaultBrush, pdc);
116 
117     /* Update the eboText's solid color */
118     EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboText, pdcattr->crForegroundClr);
119 
120     /* Clear flag */
121     pdcattr->ulDirty_ &= ~DIRTY_TEXT;
122 }
123 
124 VOID
125 FASTCALL
126 DC_vUpdateBackgroundBrush(PDC pdc)
127 {
128     PDC_ATTR pdcattr = pdc->pdcattr;
129 
130     if(pdcattr->ulDirty_ & DIRTY_BACKGROUND)
131         EBRUSHOBJ_vUpdateFromDC(&pdc->eboBackground, pbrDefaultBrush, pdc);
132 
133     /* Update the eboBackground's solid color */
134     EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboBackground, pdcattr->crBackgroundClr);
135 
136     /* Clear flag */
137     pdcattr->ulDirty_ &= ~DIRTY_BACKGROUND;
138 }
139 
140 VOID
141 NTAPI
142 DC_vSetBrushOrigin(PDC pdc, LONG x, LONG y)
143 {
144     /* Set the brush origin */
145     pdc->dclevel.ptlBrushOrigin.x = x;
146     pdc->dclevel.ptlBrushOrigin.y = y;
147 
148     /* Set the fill origin */
149     pdc->ptlFillOrigin.x = x + pdc->ptlDCOrig.x;
150     pdc->ptlFillOrigin.y = y + pdc->ptlDCOrig.y;
151 }
152 
153 /**
154  * \name NtGdiSetBrushOrg
155  *
156  * \brief Sets the brush origin that GDI uses when drawing with pattern
157  *     brushes. The brush origin is relative to the DC origin.
158  *
159  * @implemented
160  */
161 _Success_(return!=FALSE)
162 __kernel_entry
163 BOOL
164 APIENTRY
165 NtGdiSetBrushOrg(
166     _In_ HDC hdc,
167     _In_ INT x,
168     _In_ INT y,
169     _Out_opt_ LPPOINT pptOut)
170 {
171 
172     POINT ptOut;
173                 /* Call the internal function */
174     BOOL  Ret = GreSetBrushOrg( hdc, x, y, &ptOut);
175     if (Ret)
176     {
177        /* Check if the old origin was requested */
178        if (pptOut != NULL)
179        {
180            /* Enter SEH for buffer transfer */
181            _SEH2_TRY
182            {
183                /* Probe and copy the old origin */
184                ProbeForWrite(pptOut, sizeof(POINT), 1);
185                *pptOut = ptOut;
186            }
187            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
188            {
189                _SEH2_YIELD(return FALSE);
190            }
191            _SEH2_END;
192        }
193     }
194     return Ret;
195 }
196 
197 HPALETTE
198 NTAPI
199 GdiSelectPalette(
200     HDC hDC,
201     HPALETTE hpal,
202     BOOL ForceBackground)
203 {
204     PDC pdc;
205     HPALETTE oldPal = NULL;
206     PPALETTE ppal;
207 
208     // FIXME: Mark the palette as a [fore\back]ground pal
209     pdc = DC_LockDc(hDC);
210     if (!pdc)
211     {
212         return NULL;
213     }
214 
215     /* Check if this is a valid palette handle */
216     ppal = PALETTE_ShareLockPalette(hpal);
217     if (!ppal)
218     {
219         DC_UnlockDc(pdc);
220         return NULL;
221     }
222 
223     /// FIXME: we shouldn't dereference pSurface when the PDEV is not locked
224     /* Is this a valid palette for this depth? */
225 	if ((!pdc->dclevel.pSurface) ||
226         (BitsPerFormat(pdc->dclevel.pSurface->SurfObj.iBitmapFormat) <= 8
227             && (ppal->flFlags & PAL_INDEXED)) ||
228         (BitsPerFormat(pdc->dclevel.pSurface->SurfObj.iBitmapFormat) > 8))
229     {
230         /* Get old palette, set new one */
231         oldPal = pdc->dclevel.hpal;
232         pdc->dclevel.hpal = hpal;
233         DC_vSelectPalette(pdc, ppal);
234 
235         /* Mark the brushes invalid */
236         pdc->pdcattr->ulDirty_ |= DIRTY_FILL | DIRTY_LINE |
237                                   DIRTY_BACKGROUND | DIRTY_TEXT;
238     }
239 
240     if(pdc->dctype == DCTYPE_MEMORY)
241     {
242         // This didn't work anyway
243         //IntGdiRealizePalette(hDC);
244     }
245 
246     PALETTE_ShareUnlockPalette(ppal);
247     DC_UnlockDc(pdc);
248 
249     return oldPal;
250 }
251 
252  /*
253  * @implemented
254  */
255 HBRUSH
256 APIENTRY
257 NtGdiSelectBrush(
258     IN HDC hDC,
259     IN HBRUSH hBrush)
260 {
261     PDC pDC;
262     HBRUSH hOrgBrush;
263 
264     if (hDC == NULL || hBrush == NULL) return NULL;
265 
266     pDC = DC_LockDc(hDC);
267     if (!pDC)
268     {
269         return NULL;
270     }
271 
272     /* Simply return the user mode value, without checking */
273     hOrgBrush = pDC->pdcattr->hbrush;
274     pDC->pdcattr->hbrush = hBrush;
275     DC_vUpdateFillBrush(pDC);
276 
277     DC_UnlockDc(pDC);
278 
279     return hOrgBrush;
280 }
281 
282  /*
283  * @implemented
284  */
285 HPEN
286 APIENTRY
287 NtGdiSelectPen(
288     IN HDC hDC,
289     IN HPEN hPen)
290 {
291     PDC pDC;
292     HPEN hOrgPen;
293 
294     if (hDC == NULL || hPen == NULL) return NULL;
295 
296     pDC = DC_LockDc(hDC);
297     if (!pDC)
298     {
299         return NULL;
300     }
301 
302     /* Simply return the user mode value, without checking */
303     hOrgPen = pDC->pdcattr->hpen;
304     pDC->pdcattr->hpen = hPen;
305     DC_vUpdateLineBrush(pDC);
306 
307     DC_UnlockDc(pDC);
308 
309     return hOrgPen;
310 }
311 
312 BOOL
313 NTAPI
314 DC_bIsBitmapCompatible(PDC pdc, PSURFACE psurf)
315 {
316     ULONG cBitsPixel;
317 
318     /* Must be an API bitmap */
319     if (!(psurf->flags & API_BITMAP)) return FALSE;
320 
321     /* DIB sections are always compatible */
322     if (psurf->hSecure != NULL) return TRUE;
323 
324     /* See if this is the same PDEV */
325     if (psurf->SurfObj.hdev == (HDEV)pdc->ppdev)
326         return TRUE;
327 
328     /* Get the bit depth of the bitmap */
329     cBitsPixel = gajBitsPerFormat[psurf->SurfObj.iBitmapFormat];
330 
331     /* 1 BPP is compatible */
332     if ((cBitsPixel == 1) || (cBitsPixel == pdc->ppdev->gdiinfo.cBitsPixel))
333         return TRUE;
334 
335     return FALSE;
336 }
337 
338 /*
339  * @implemented
340  */
341 HBITMAP
342 APIENTRY
343 NtGdiSelectBitmap(
344     IN HDC hdc,
345     IN HBITMAP hbmp)
346 {
347     PDC pdc;
348     HBITMAP hbmpOld;
349     PSURFACE psurfNew, psurfOld;
350     HDC hdcOld;
351     ASSERT_NOGDILOCKS();
352 
353     /* Verify parameters */
354     if (hdc == NULL || hbmp == NULL) return NULL;
355 
356     /* First lock the DC */
357     pdc = DC_LockDc(hdc);
358     if (!pdc)
359     {
360         return NULL;
361     }
362 
363     /* Must be a memory dc to select a bitmap */
364     if (pdc->dctype != DCTYPE_MEMORY)
365     {
366         DC_UnlockDc(pdc);
367         return NULL;
368     }
369 
370     /* Save the old bitmap */
371     psurfOld = pdc->dclevel.pSurface;
372 
373     /* Check if there is a bitmap selected */
374     if (psurfOld)
375     {
376         /* Get the old bitmap's handle */
377         hbmpOld = psurfOld->BaseObject.hHmgr;
378     }
379     else
380     {
381         /* Use the default bitmap */
382         hbmpOld = StockObjects[DEFAULT_BITMAP];
383     }
384 
385     /* Check if the new bitmap is already selected */
386     if (hbmp == hbmpOld)
387     {
388         /* Unlock the DC and return the old bitmap */
389         DC_UnlockDc(pdc);
390         return hbmpOld;
391     }
392 
393     /* Check if the default bitmap was passed */
394     if (hbmp == StockObjects[DEFAULT_BITMAP])
395     {
396         psurfNew = NULL;
397 
398         /* Default bitmap is 1x1 pixel */
399         pdc->dclevel.sizl.cx = 1;
400         pdc->dclevel.sizl.cy = 1;
401     }
402     else
403     {
404         /* Reference the new bitmap and check if it's valid */
405         psurfNew = SURFACE_ShareLockSurface(hbmp);
406         if (!psurfNew)
407         {
408             DC_UnlockDc(pdc);
409             return NULL;
410         }
411 
412         /* Check if the bitmap is compatible with the dc */
413         if (!DC_bIsBitmapCompatible(pdc, psurfNew))
414         {
415             /* Dereference the bitmap, unlock the DC and fail. */
416             SURFACE_ShareUnlockSurface(psurfNew);
417             DC_UnlockDc(pdc);
418             return NULL;
419         }
420 
421         /* Set the bitmap's hdc and check if it was set before */
422         hdcOld = InterlockedCompareExchangePointer((PVOID*)&psurfNew->hdc, hdc, 0);
423         if (hdcOld != NULL)
424         {
425             /* The bitmap is already selected into a different DC */
426             ASSERT(hdcOld != hdc);
427 
428             /* Dereference the bitmap, unlock the DC and fail. */
429             SURFACE_ShareUnlockSurface(psurfNew);
430             DC_UnlockDc(pdc);
431             return NULL;
432         }
433 
434         /* Copy the bitmap size */
435         pdc->dclevel.sizl = psurfNew->SurfObj.sizlBitmap;
436 
437         /* Check if the bitmap is a dibsection */
438         if (psurfNew->hSecure)
439         {
440             /* Set DIBSECTION attribute */
441             pdc->pdcattr->ulDirty_ |= DC_DIBSECTION;
442         }
443         else
444         {
445             /* Remove DIBSECTION attribute */
446             pdc->pdcattr->ulDirty_ &= ~DC_DIBSECTION;
447         }
448     }
449 
450     /* Select the new bitmap */
451     pdc->dclevel.pSurface = psurfNew;
452 
453     /* Check if there was a bitmap selected before */
454     if (psurfOld)
455     {
456         /* Reset hdc of the old bitmap, it isn't selected anymore */
457         psurfOld->hdc = NULL;
458 
459         /* Dereference the old bitmap */
460         SURFACE_ShareUnlockSurface(psurfOld);
461     }
462 
463     /* Mark the DC brushes and the RAO region invalid */
464     pdc->pdcattr->ulDirty_ |= DIRTY_FILL | DIRTY_LINE;
465     pdc->fs |= DC_DIRTY_RAO;
466 
467     /* Update the system region */
468     REGION_SetRectRgn(pdc->prgnVis,
469                       0,
470                       0,
471                       pdc->dclevel.sizl.cx,
472                       pdc->dclevel.sizl.cy);
473 
474     /* Unlock the DC */
475     DC_UnlockDc(pdc);
476 
477     /* Return the old bitmap handle */
478     return hbmpOld;
479 }
480 
481 
482 BOOL
483 APIENTRY
484 NtGdiSelectClipPath(
485     HDC hDC,
486     int Mode)
487 {
488     PREGION  RgnPath;
489     PPATH pPath, pNewPath;
490     BOOL  success = FALSE;
491     PDC_ATTR pdcattr;
492     PDC pdc;
493 
494     pdc = DC_LockDc(hDC);
495     if (!pdc)
496     {
497         EngSetLastError(ERROR_INVALID_PARAMETER);
498         return FALSE;
499     }
500     pdcattr = pdc->pdcattr;
501 
502     pPath = PATH_LockPath(pdc->dclevel.hPath);
503     if (!pPath)
504     {
505         DC_UnlockDc(pdc);
506         return FALSE;
507     }
508 
509     /* Check that path is closed */
510     if (pPath->state != PATH_Closed)
511     {
512         EngSetLastError(ERROR_CAN_NOT_COMPLETE);
513         success = FALSE;
514         goto Exit;
515     }
516 
517     /* Construct a region from the path */
518     RgnPath = IntSysCreateRectpRgn(0, 0, 0, 0);
519     if (!RgnPath)
520     {
521         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
522         DC_UnlockDc(pdc);
523         return FALSE;
524     }
525 
526     pNewPath = PATH_FlattenPath(pPath);
527 
528     success = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, RgnPath);
529 
530     PATH_UnlockPath(pNewPath);
531     PATH_Delete(pNewPath->BaseObject.hHmgr);
532 
533     if (success) success = IntGdiExtSelectClipRgn(pdc, RgnPath, Mode) != ERROR;
534 
535     REGION_Delete(RgnPath);
536 
537 Exit:
538     PATH_UnlockPath(pPath);
539     PATH_Delete(pdc->dclevel.hPath);
540     pdc->dclevel.flPath &= ~DCPATH_ACTIVE;
541     pdc->dclevel.hPath = NULL;
542 
543     DC_UnlockDc(pdc);
544 
545     return success;
546 }
547 
548 HFONT
549 NTAPI
550 DC_hSelectFont(
551     _In_ PDC pdc,
552     _In_ HFONT hlfntNew)
553 {
554     PLFONT plfntNew;
555     HFONT hlfntOld;
556 
557     // Legacy crap that will die with font engine rewrite
558     if (!NT_SUCCESS(TextIntRealizeFont(hlfntNew, NULL)))
559     {
560         return NULL;
561     }
562 
563     /* Get the current selected font */
564     hlfntOld = pdc->dclevel.plfnt->BaseObject.hHmgr;
565 
566     /* Check if a new font should be selected */
567     if (hlfntNew != hlfntOld)
568     {
569         /* Lock the new font */
570         plfntNew = LFONT_ShareLockFont(hlfntNew);
571         if (plfntNew)
572         {
573             /* Success, dereference the old font */
574             LFONT_ShareUnlockFont(pdc->dclevel.plfnt);
575 
576             /* Select the new font */
577             pdc->dclevel.plfnt = plfntNew;
578             pdc->pdcattr->hlfntNew = hlfntNew;
579 
580             /* Update dirty flags */
581             pdc->pdcattr->ulDirty_ |= DIRTY_CHARSET;
582             pdc->pdcattr->ulDirty_ &= ~SLOW_WIDTHS;
583         }
584         else
585         {
586             /* Failed, restore old, return NULL */
587             pdc->pdcattr->hlfntNew = hlfntOld;
588             hlfntOld = NULL;
589         }
590     }
591 
592     return hlfntOld;
593 }
594 
595 HFONT
596 APIENTRY
597 NtGdiSelectFont(
598     _In_ HDC hdc,
599     _In_ HFONT hfont)
600 {
601     HFONT hfontOld;
602     PDC pdc;
603 
604     /* Check parameters */
605     if ((hdc == NULL) || (hfont == NULL))
606     {
607         return NULL;
608     }
609 
610     /* Lock the DC */
611     pdc = DC_LockDc(hdc);
612     if (!pdc)
613     {
614         return NULL;
615     }
616 
617     /* Call the internal function */
618     hfontOld = DC_hSelectFont(pdc, hfont);
619 
620     /* Unlock the DC */
621     DC_UnlockDc(pdc);
622 
623     /* Return the previously selected font */
624     return hfontOld;
625 }
626 
627 HANDLE
628 APIENTRY
629 NtGdiGetDCObject(HDC hDC, INT ObjectType)
630 {
631     HGDIOBJ SelObject;
632     DC *pdc;
633     PDC_ATTR pdcattr;
634 
635     /* From Wine: GetCurrentObject does not SetLastError() on a null object */
636     if(!hDC) return NULL;
637 
638     if(!(pdc = DC_LockDc(hDC)))
639     {
640         return NULL;
641     }
642     pdcattr = pdc->pdcattr;
643 
644     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
645         DC_vUpdateFillBrush(pdc);
646 
647     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
648         DC_vUpdateLineBrush(pdc);
649 
650     switch(ObjectType)
651     {
652         case GDI_OBJECT_TYPE_EXTPEN:
653         case GDI_OBJECT_TYPE_PEN:
654             SelObject = pdcattr->hpen;
655             break;
656 
657         case GDI_OBJECT_TYPE_BRUSH:
658             SelObject = pdcattr->hbrush;
659             break;
660 
661         case GDI_OBJECT_TYPE_PALETTE:
662             SelObject = pdc->dclevel.hpal;
663             break;
664 
665         case GDI_OBJECT_TYPE_FONT:
666             SelObject = pdcattr->hlfntNew;
667             break;
668 
669         case GDI_OBJECT_TYPE_BITMAP:
670         {
671             SURFACE *psurf = pdc->dclevel.pSurface;
672             SelObject = psurf ? psurf->BaseObject.hHmgr : StockObjects[DEFAULT_BITMAP];
673             break;
674         }
675 
676         case GDI_OBJECT_TYPE_COLORSPACE:
677             DPRINT1("FIXME: NtGdiGetCurrentObject() ObjectType OBJ_COLORSPACE not supported yet!\n");
678             // SelObject = dc->dclevel.pColorSpace.BaseObject.hHmgr; ?
679             SelObject = NULL;
680             break;
681 
682         default:
683             SelObject = NULL;
684             EngSetLastError(ERROR_INVALID_PARAMETER);
685             break;
686     }
687 
688     DC_UnlockDc(pdc);
689     return SelObject;
690 }
691 
692 /* See WINE, MSDN, OSR and Feng Yuan - Windows Graphics Programming Win32 GDI and DirectDraw
693  *
694  * 1st: http://www.codeproject.com/gdi/cliprgnguide.asp is wrong!
695  *
696  * The intersection of the clip with the meta region is not Rao it's API!
697  * Go back and read 7.2 Clipping pages 418-19:
698  * Rao = API & Vis:
699  * 1) The Rao region is the intersection of the API region and the system region,
700  *    named after the Microsoft engineer who initially proposed it.
701  * 2) The Rao region can be calculated from the API region and the system region.
702  *
703  * API:
704  *    API region is the intersection of the meta region and the clipping region,
705  *    clearly named after the fact that it is controlled by GDI API calls.
706  */
707 INT
708 APIENTRY
709 NtGdiGetRandomRgn(
710     HDC hdc,
711     HRGN hrgnDest,
712     INT iCode)
713 {
714     INT ret = 0;
715     PDC pdc;
716     PREGION prgnSrc = NULL;
717 
718     pdc = DC_LockDc(hdc);
719     if (!pdc)
720     {
721         EngSetLastError(ERROR_INVALID_HANDLE);
722         return -1;
723     }
724 
725     switch (iCode)
726     {
727         case CLIPRGN:
728             prgnSrc = pdc->dclevel.prgnClip;
729             break;
730 
731         case METARGN:
732             prgnSrc = pdc->dclevel.prgnMeta;
733             break;
734 
735         case APIRGN:
736             if (pdc->fs & DC_DIRTY_RAO)
737                 CLIPPING_UpdateGCRegion(pdc);
738             if (pdc->prgnAPI)
739             {
740                 prgnSrc = pdc->prgnAPI;
741             }
742             else if (pdc->dclevel.prgnClip)
743             {
744                 prgnSrc = pdc->dclevel.prgnClip;
745             }
746             else if (pdc->dclevel.prgnMeta)
747             {
748                 prgnSrc = pdc->dclevel.prgnMeta;
749             }
750             break;
751 
752         case SYSRGN:
753             prgnSrc = pdc->prgnVis;
754             break;
755 
756         default:
757             break;
758     }
759 
760     if (prgnSrc)
761     {
762         PREGION prgnDest = REGION_LockRgn(hrgnDest);
763         if (prgnDest)
764         {
765             ret = IntGdiCombineRgn(prgnDest, prgnSrc, 0, RGN_COPY) == ERROR ? -1 : 1;
766             if ((ret == 1) && (iCode == SYSRGN))
767             {
768                 /// \todo FIXME This is not really correct, since we already modified the region
769                 ret = REGION_bOffsetRgn(prgnDest, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
770             }
771             REGION_UnlockRgn(prgnDest);
772         }
773         else
774             ret = -1;
775     }
776 
777     DC_UnlockDc(pdc);
778 
779     return ret;
780 }
781 
782 ULONG
783 APIENTRY
784 NtGdiEnumObjects(
785     IN HDC hdc,
786     IN INT iObjectType,
787     IN ULONG cjBuf,
788     OUT OPTIONAL PVOID pvBuf)
789 {
790     UNIMPLEMENTED;
791     return 0;
792 }
793 
794 /* EOF */
795