36dc12612015c1c7756340fcbd22bf2cd0556353
[reactos.git] / subsys / win32k / objects / region.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$ */
20 #undef WIN32_LEAN_AND_MEAN
21 #include <windows.h>
22 #include <ddk/ntddk.h>
23 #include <internal/safe.h>
24 #include <win32k/float.h>
25 #include <win32k/dc.h>
26 #include <win32k/bitmaps.h>
27 #include <win32k/region.h>
28 #include <win32k/cliprgn.h>
29 #include <win32k/brush.h>
30 #include <include/rect.h>
31 #include <include/object.h>
32 #include <include/inteng.h>
33 #include <include/error.h>
34
35 #define NDEBUG
36 #include <win32k/debug1.h>
37
38 BOOL STDCALL
39 IntEngPaint(IN SURFOBJ *Surface,IN CLIPOBJ *ClipRegion,IN BRUSHOBJ *Brush,IN POINTL *BrushOrigin,
40          IN MIX  Mix);
41
42 // Internal Functions
43
44 #define EMPTY_REGION(pReg) { \
45   (pReg)->rdh.nCount = 0; \
46   (pReg)->rdh.rcBound.left = (pReg)->rdh.rcBound.top = 0; \
47   (pReg)->rdh.rcBound.right = (pReg)->rdh.rcBound.bottom = 0; \
48   (pReg)->rdh.iType = NULLREGION; \
49 }
50
51 #define REGION_NOT_EMPTY(pReg) pReg->rdh.nCount
52
53 #define INRECT(r, x, y) \
54       ( ( ((r).right >  x)) && \
55         ( ((r).left <= x)) && \
56         ( ((r).bottom >  y)) && \
57         ( ((r).top <= y)) )
58
59 /*  1 if two RECTs overlap.
60  *  0 if two RECTs do not overlap.
61  */
62 #define EXTENTCHECK(r1, r2) \
63         ((r1)->right > (r2)->left && \
64          (r1)->left < (r2)->right && \
65          (r1)->bottom > (r2)->top && \
66          (r1)->top < (r2)->bottom)
67
68 /*
69  *   Check to see if there is enough memory in the present region.
70  */
71 static inline int xmemcheck(ROSRGNDATA *reg, LPRECT *rect, LPRECT *firstrect ) {
72         if ( (reg->rdh.nCount+1)*sizeof( RECT ) >= reg->rdh.nRgnSize ) {
73                 PRECT temp;
74                 temp = ExAllocatePool( PagedPool, (2 * (reg->rdh.nRgnSize)));
75
76                 if (temp == 0)
77                     return 0;
78                 RtlCopyMemory( temp, *firstrect, reg->rdh.nRgnSize );
79                 reg->rdh.nRgnSize *= 2;
80                 ExFreePool( *firstrect );
81                 *firstrect = temp;
82                 *rect = (*firstrect)+reg->rdh.nCount;
83     }
84     return 1;
85 }
86
87 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),(LPRECT *)&(firstrect))
88
89 typedef void FASTCALL (*overlapProcp)(PROSRGNDATA, PRECT, PRECT, PRECT, PRECT, INT, INT);
90 typedef void FASTCALL (*nonOverlapProcp)(PROSRGNDATA, PRECT, PRECT, INT, INT);
91
92 // Number of points to buffer before sending them off to scanlines() :  Must be an even number
93 #define NUMPTSTOBUFFER 200
94
95 #define RGN_DEFAULT_RECTS       2
96
97 // used to allocate buffers for points and link the buffers together
98
99 typedef struct _POINTBLOCK {
100   POINT pts[NUMPTSTOBUFFER];
101   struct _POINTBLOCK *next;
102 } POINTBLOCK;
103
104 static BOOL FASTCALL REGION_CopyRegion(PROSRGNDATA dst, PROSRGNDATA src)
105 {
106   if(dst != src) //  don't want to copy to itself
107   {
108     if (dst->rdh.nRgnSize < src->rdh.nCount * sizeof(RECT))
109     {
110           PCHAR temp;
111
112           temp = ExAllocatePool(PagedPool, src->rdh.nCount * sizeof(RECT) );
113           if( !temp )
114                 return FALSE;
115
116           if( dst->Buffer )
117                 ExFreePool( dst->Buffer );      //free the old buffer
118           dst->Buffer = temp;
119       dst->rdh.nRgnSize = src->rdh.nCount * sizeof(RECT);  //size of region buffer
120     }
121     dst->rdh.nCount = src->rdh.nCount;                 //number of rectangles present in Buffer
122     dst->rdh.rcBound.left = src->rdh.rcBound.left;
123     dst->rdh.rcBound.top = src->rdh.rcBound.top;
124     dst->rdh.rcBound.right = src->rdh.rcBound.right;
125     dst->rdh.rcBound.bottom = src->rdh.rcBound.bottom;
126     dst->rdh.iType = src->rdh.iType;
127     RtlCopyMemory(dst->Buffer, src->Buffer, (int)(src->rdh.nCount * sizeof(RECT)));
128   }
129   return TRUE;
130 }
131
132 static void FASTCALL REGION_SetExtents (ROSRGNDATA *pReg)
133 {
134     RECT *pRect, *pRectEnd, *pExtents;
135
136     if (pReg->rdh.nCount == 0)
137     {
138                 pReg->rdh.rcBound.left = 0;
139                 pReg->rdh.rcBound.top = 0;
140                 pReg->rdh.rcBound.right = 0;
141                 pReg->rdh.rcBound.bottom = 0;
142                 pReg->rdh.iType = NULLREGION;
143                 return;
144     }
145
146     pExtents = &pReg->rdh.rcBound;
147     pRect = (PRECT)pReg->Buffer;
148     pRectEnd = (PRECT)pReg->Buffer + pReg->rdh.nCount - 1;
149
150     /*
151      * Since pRect is the first rectangle in the region, it must have the
152      * smallest top and since pRectEnd is the last rectangle in the region,
153      * it must have the largest bottom, because of banding. Initialize left and
154      * right from pRect and pRectEnd, resp., as good things to initialize them
155      * to...
156      */
157     pExtents->left = pRect->left;
158     pExtents->top = pRect->top;
159     pExtents->right = pRectEnd->right;
160     pExtents->bottom = pRectEnd->bottom;
161
162     while (pRect <= pRectEnd)
163     {
164                 if (pRect->left < pExtents->left)
165                     pExtents->left = pRect->left;
166                 if (pRect->right > pExtents->right)
167                     pExtents->right = pRect->right;
168                 pRect++;
169     }
170     pReg->rdh.iType = (1 == pReg->rdh.nCount ? SIMPLEREGION : COMPLEXREGION);
171 }
172
173
174 /***********************************************************************
175  *           REGION_CropAndOffsetRegion
176  */
177 static BOOL FASTCALL REGION_CropAndOffsetRegion(const PPOINT off, const PRECT rect, PROSRGNDATA rgnSrc, PROSRGNDATA rgnDst)
178 {
179   if(!rect) // just copy and offset
180   {
181     PRECT xrect;
182     if(rgnDst == rgnSrc)
183     {
184       if(off->x || off->y)
185         xrect = (PRECT)rgnDst->Buffer;
186       else
187         return TRUE;
188     }
189     else{
190       xrect = ExAllocatePool(PagedPool, rgnSrc->rdh.nCount * sizeof(RECT));
191           if( rgnDst->Buffer )
192                 ExFreePool( rgnDst->Buffer ); //free the old buffer. will be assigned to xrect below.
193         }
194
195     if(xrect)
196     {
197       ULONG i;
198
199       if(rgnDst != rgnSrc)
200                 RtlCopyMemory(rgnDst, rgnSrc, sizeof(ROSRGNDATA));
201
202       if(off->x || off->y)
203       {
204         for(i = 0; i < rgnDst->rdh.nCount; i++)
205         {
206           xrect[i].left = ((PRECT)rgnSrc->Buffer + i)->left + off->x;
207           xrect[i].right = ((PRECT)rgnSrc->Buffer + i)->right + off->x;
208           xrect[i].top = ((PRECT)rgnSrc->Buffer + i)->top + off->y;
209           xrect[i].bottom = ((PRECT)rgnSrc->Buffer + i)->bottom + off->y;
210         }
211         rgnDst->rdh.rcBound.left   += off->x;
212         rgnDst->rdh.rcBound.right  += off->x;
213         rgnDst->rdh.rcBound.top    += off->y;
214         rgnDst->rdh.rcBound.bottom += off->y;
215       }
216       else
217         RtlCopyMemory(xrect, rgnSrc->Buffer, rgnDst->rdh.nCount * sizeof(RECT));
218
219           rgnDst->Buffer = (char*)xrect;
220     } else
221       return FALSE;
222   }
223   else if ((rect->left >= rect->right) ||
224            (rect->top >= rect->bottom) ||
225             !EXTENTCHECK(rect, &rgnSrc->rdh.rcBound))
226   {
227         goto empty;
228   }
229   else // region box and clipping rect appear to intersect
230   {
231     PRECT lpr, rpr;
232     ULONG i, j, clipa, clipb;
233     INT left = rgnSrc->rdh.rcBound.right + off->x;
234     INT right = rgnSrc->rdh.rcBound.left + off->x;
235
236     for(clipa = 0; ((PRECT)rgnSrc->Buffer + clipa)->bottom <= rect->top; clipa++)
237         //region and rect intersect so we stop before clipa > rgnSrc->rdh.nCount
238       ; // skip bands above the clipping rectangle
239
240     for(clipb = clipa; clipb < rgnSrc->rdh.nCount; clipb++)
241       if(((PRECT)rgnSrc->Buffer + clipb)->top >= rect->bottom)
242         break;    // and below it
243
244     // clipa - index of the first rect in the first intersecting band
245     // clipb - index of the last rect in the last intersecting band
246
247     if((rgnDst != rgnSrc) && (rgnDst->rdh.nCount < (i = (clipb - clipa))))
248     {
249           PCHAR temp;
250           temp = ExAllocatePool( PagedPool, i * sizeof(RECT) );
251       if(!temp)
252               return FALSE;
253
254           if( rgnDst->Buffer )
255                 ExFreePool( rgnDst->Buffer ); //free the old buffer
256       (PRECT)rgnDst->Buffer = temp;
257       rgnDst->rdh.nCount = i;
258           rgnDst->rdh.nRgnSize = i * sizeof(RECT);
259     }
260
261     for(i = clipa, j = 0; i < clipb ; i++)
262     {
263       // i - src index, j - dst index, j is always <= i for obvious reasons
264
265       lpr = (PRECT)rgnSrc->Buffer + i;
266
267       if(lpr->left < rect->right && lpr->right > rect->left)
268       {
269             rpr = (PRECT)rgnDst->Buffer + j;
270
271         rpr->top = lpr->top + off->y;
272         rpr->bottom = lpr->bottom + off->y;
273         rpr->left = ((lpr->left > rect->left) ? lpr->left : rect->left) + off->x;
274         rpr->right = ((lpr->right < rect->right) ? lpr->right : rect->right) + off->x;
275
276         if(rpr->left < left) left = rpr->left;
277         if(rpr->right > right) right = rpr->right;
278
279         j++;
280       }
281     }
282
283     if(j == 0) goto empty;
284
285     rgnDst->rdh.rcBound.left = left;
286     rgnDst->rdh.rcBound.right = right;
287
288     left = rect->top + off->y;
289     right = rect->bottom + off->y;
290
291     rgnDst->rdh.nCount = j--;
292     for(i = 0; i <= j; i++) // fixup top band
293       if(((PRECT)rgnDst->Buffer + i)->top < left)
294         ((PRECT)rgnDst->Buffer + i)->top = left;
295       else
296         break;
297
298     for(i = j; i >= 0; i--) // fixup bottom band
299       if(((PRECT)rgnDst->Buffer + i)->bottom > right)
300         ((PRECT)rgnDst->Buffer + i)->bottom = right;
301       else
302         break;
303
304     rgnDst->rdh.rcBound.top = ((PRECT)rgnDst->Buffer)->top;
305     rgnDst->rdh.rcBound.bottom = ((PRECT)rgnDst->Buffer + j)->bottom;
306
307     rgnDst->rdh.iType = (j >= 1) ? COMPLEXREGION : SIMPLEREGION;
308   }
309
310   return TRUE;
311
312 empty:
313         if(!rgnDst->Buffer)
314         {
315           rgnDst->Buffer = (char*)ExAllocatePool(PagedPool, RGN_DEFAULT_RECTS * sizeof(RECT));
316           if(rgnDst->Buffer){
317             rgnDst->rdh.nCount = RGN_DEFAULT_RECTS;
318                 rgnDst->rdh.nRgnSize = RGN_DEFAULT_RECTS * sizeof(RECT);
319           }
320           else
321             return FALSE;
322         }
323         EMPTY_REGION(rgnDst);
324         return TRUE;
325 }
326
327 /*!
328  * \param
329  * hSrc:        Region to crop and offset.
330  * lpRect:      Clipping rectangle. Can be NULL (no clipping).
331  * lpPt:        Points to offset the cropped region. Can be NULL (no offset).
332  *
333  * hDst: Region to hold the result (a new region is created if it's 0).
334  *       Allowed to be the same region as hSrc in which case everything
335  *       will be done in place, with no memory reallocations.
336  *
337  * \return      hDst if success, 0 otherwise.
338  */
339 HRGN STDCALL REGION_CropRgn(HRGN hDst, HRGN hSrc, const PRECT lpRect, PPOINT lpPt)
340 {
341   PROSRGNDATA objSrc, rgnDst;
342   HRGN hNewDst, hRet = NULL;
343   GDIMULTILOCK Lock[2] = {{hDst, 0, GDI_OBJECT_TYPE_REGION}, {hSrc, 0, GDI_OBJECT_TYPE_REGION}};
344
345   if( !hDst )
346     {
347       if( !( hNewDst = RGNDATA_AllocRgn(1) ) )
348         {
349           return 0;
350         }
351       Lock[0].hObj = hNewDst;
352     }
353
354   if ( !GDIOBJ_LockMultipleObj(Lock, sizeof(Lock)/sizeof(Lock[0])) )
355     {
356       DPRINT1("GDIOBJ_LockMultipleObj() failed\n" );
357       return 0;
358     }
359   rgnDst = Lock[0].pObj;
360   objSrc = Lock[1].pObj;
361
362   if( objSrc && rgnDst )
363   {
364     if(rgnDst)
365     {
366       POINT pt = { 0, 0 };
367
368           if(!lpPt)
369                 lpPt = &pt;
370
371       if(REGION_CropAndOffsetRegion(lpPt, lpRect, objSrc, rgnDst) == FALSE)
372           { // ve failed cleanup and return
373                 hRet = NULL;
374       }
375       else{ // ve are fine. unlock the correct pointer and return correct handle
376                 hRet = Lock[0].hObj;
377           }
378     }
379   }
380   GDIOBJ_UnlockMultipleObj(Lock, sizeof(Lock)/sizeof(Lock[0]));
381   return hRet;
382 }
383
384 /*!
385  *      Attempt to merge the rects in the current band with those in the
386  *      previous one. Used only by REGION_RegionOp.
387  *
388  * Results:
389  *      The new index for the previous band.
390  *
391  * \note Side Effects:
392  *      If coalescing takes place:
393  *          - rectangles in the previous band will have their bottom fields
394  *            altered.
395  *          - pReg->numRects will be decreased.
396  *
397  */
398 static INT FASTCALL REGION_Coalesce (
399              PROSRGNDATA pReg, /* Region to coalesce */
400              INT prevStart,  /* Index of start of previous band */
401              INT curStart    /* Index of start of current band */
402 ) {
403     RECT *pPrevRect;          /* Current rect in previous band */
404     RECT *pCurRect;           /* Current rect in current band */
405     RECT *pRegEnd;            /* End of region */
406     INT curNumRects;          /* Number of rectangles in current band */
407     INT prevNumRects;         /* Number of rectangles in previous band */
408     INT bandtop;               /* top coordinate for current band */
409
410     pRegEnd = (PRECT)pReg->Buffer + pReg->rdh.nCount;
411     pPrevRect = (PRECT)pReg->Buffer + prevStart;
412     prevNumRects = curStart - prevStart;
413
414     /*
415      * Figure out how many rectangles are in the current band. Have to do
416      * this because multiple bands could have been added in REGION_RegionOp
417      * at the end when one region has been exhausted.
418      */
419     pCurRect = (PRECT)pReg->Buffer + curStart;
420     bandtop = pCurRect->top;
421     for (curNumRects = 0;
422          (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
423          curNumRects++)
424     {
425                 pCurRect++;
426     }
427
428     if (pCurRect != pRegEnd)
429     {
430                 /*
431                  * If more than one band was added, we have to find the start
432                  * of the last band added so the next coalescing job can start
433                  * at the right place... (given when multiple bands are added,
434                  * this may be pointless -- see above).
435                  */
436                 pRegEnd--;
437                 while ((pRegEnd-1)->top == pRegEnd->top)
438                 {
439                     pRegEnd--;
440                 }
441                 curStart = pRegEnd - (PRECT)pReg->Buffer;
442                 pRegEnd = (PRECT)pReg->Buffer + pReg->rdh.nCount;
443     }
444
445     if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
446                 pCurRect -= curNumRects;
447                 /*
448                  * The bands may only be coalesced if the bottom of the previous
449                  * matches the top scanline of the current.
450                  */
451                 if (pPrevRect->bottom == pCurRect->top)
452                 {
453                     /*
454                      * Make sure the bands have rects in the same places. This
455                      * assumes that rects have been added in such a way that they
456                      * cover the most area possible. I.e. two rects in a band must
457                      * have some horizontal space between them.
458                      */
459                     do
460                     {
461                                 if ((pPrevRect->left != pCurRect->left) ||
462                                     (pPrevRect->right != pCurRect->right))
463                                 {
464                                     /*
465                                      * The bands don't line up so they can't be coalesced.
466                                      */
467                                     return (curStart);
468                                 }
469                                 pPrevRect++;
470                                 pCurRect++;
471                                 prevNumRects -= 1;
472                     } while (prevNumRects != 0);
473
474                     pReg->rdh.nCount -= curNumRects;
475                     pCurRect -= curNumRects;
476                     pPrevRect -= curNumRects;
477
478                     /*
479                      * The bands may be merged, so set the bottom of each rect
480                      * in the previous band to that of the corresponding rect in
481                      * the current band.
482                      */
483                     do
484                     {
485                                 pPrevRect->bottom = pCurRect->bottom;
486                                 pPrevRect++;
487                                 pCurRect++;
488                                 curNumRects -= 1;
489                     } while (curNumRects != 0);
490
491                     /*
492                      * If only one band was added to the region, we have to backup
493                      * curStart to the start of the previous band.
494                      *
495                      * If more than one band was added to the region, copy the
496                      * other bands down. The assumption here is that the other bands
497                      * came from the same region as the current one and no further
498                      * coalescing can be done on them since it's all been done
499                      * already... curStart is already in the right place.
500                      */
501                     if (pCurRect == pRegEnd)
502                     {
503                                 curStart = prevStart;
504                     }
505                     else
506                     {
507                                 do
508                                 {
509                                     *pPrevRect++ = *pCurRect++;
510                                 } while (pCurRect != pRegEnd);
511                     }
512                 }
513     }
514     return (curStart);
515 }
516
517 /*!
518  *      Apply an operation to two regions. Called by REGION_Union,
519  *      REGION_Inverse, REGION_Subtract, REGION_Intersect...
520  *
521  * Results:
522  *      None.
523  *
524  * Side Effects:
525  *      The new region is overwritten.
526  *
527  *\note The idea behind this function is to view the two regions as sets.
528  *      Together they cover a rectangle of area that this function divides
529  *      into horizontal bands where points are covered only by one region
530  *      or by both. For the first case, the nonOverlapFunc is called with
531  *      each the band and the band's upper and lower extents. For the
532  *      second, the overlapFunc is called to process the entire band. It
533  *      is responsible for clipping the rectangles in the band, though
534  *      this function provides the boundaries.
535  *      At the end of each band, the new region is coalesced, if possible,
536  *      to reduce the number of rectangles in the region.
537  *
538  */
539 static void FASTCALL
540 REGION_RegionOp(
541         ROSRGNDATA *newReg, /* Place to store result */
542         ROSRGNDATA *reg1,   /* First region in operation */
543         ROSRGNDATA *reg2,   /* 2nd region in operation */
544         overlapProcp overlapFunc,     /* Function to call for over-lapping bands */
545         nonOverlapProcp nonOverlap1Func, /* Function to call for non-overlapping bands in region 1 */
546         nonOverlapProcp nonOverlap2Func  /* Function to call for non-overlapping bands in region 2 */
547         )
548 {
549     RECT *r1;                         /* Pointer into first region */
550     RECT *r2;                         /* Pointer into 2d region */
551     RECT *r1End;                      /* End of 1st region */
552     RECT *r2End;                      /* End of 2d region */
553     INT ybot;                         /* Bottom of intersection */
554     INT ytop;                         /* Top of intersection */
555     RECT *oldRects;                   /* Old rects for newReg */
556     ULONG prevBand;                   /* Index of start of
557                                                  * previous band in newReg */
558     ULONG curBand;                    /* Index of start of current band in newReg */
559     RECT *r1BandEnd;                  /* End of current band in r1 */
560     RECT *r2BandEnd;                  /* End of current band in r2 */
561     ULONG top;                        /* Top of non-overlapping band */
562     ULONG bot;                        /* Bottom of non-overlapping band */
563
564     /*
565      * Initialization:
566      *  set r1, r2, r1End and r2End appropriately, preserve the important
567      * parts of the destination region until the end in case it's one of
568      * the two source regions, then mark the "new" region empty, allocating
569      * another array of rectangles for it to use.
570      */
571     r1 = (PRECT)reg1->Buffer;
572     r2 = (PRECT)reg2->Buffer;
573     r1End = r1 + reg1->rdh.nCount;
574     r2End = r2 + reg2->rdh.nCount;
575
576
577     /*
578      * newReg may be one of the src regions so we can't empty it. We keep a
579      * note of its rects pointer (so that we can free them later), preserve its
580      * extents and simply set numRects to zero.
581      */
582
583     oldRects = (PRECT)newReg->Buffer;
584     newReg->rdh.nCount = 0;
585
586     /*
587      * Allocate a reasonable number of rectangles for the new region. The idea
588      * is to allocate enough so the individual functions don't need to
589      * reallocate and copy the array, which is time consuming, yet we don't
590      * have to worry about using too much memory. I hope to be able to
591      * nuke the Xrealloc() at the end of this function eventually.
592      */
593     newReg->rdh.nRgnSize = max(reg1->rdh.nCount,reg2->rdh.nCount) * 2 * sizeof(RECT);
594
595     if (! (newReg->Buffer = ExAllocatePool( PagedPool, newReg->rdh.nRgnSize )))
596     {
597                 newReg->rdh.nRgnSize = 0;
598                 return;
599     }
600
601     /*
602      * Initialize ybot and ytop.
603      * In the upcoming loop, ybot and ytop serve different functions depending
604      * on whether the band being handled is an overlapping or non-overlapping
605      * band.
606      *  In the case of a non-overlapping band (only one of the regions
607      * has points in the band), ybot is the bottom of the most recent
608      * intersection and thus clips the top of the rectangles in that band.
609      * ytop is the top of the next intersection between the two regions and
610      * serves to clip the bottom of the rectangles in the current band.
611      *  For an overlapping band (where the two regions intersect), ytop clips
612      * the top of the rectangles of both regions and ybot clips the bottoms.
613      */
614     if (reg1->rdh.rcBound.top < reg2->rdh.rcBound.top)
615                 ybot = reg1->rdh.rcBound.top;
616     else
617                 ybot = reg2->rdh.rcBound.top;
618
619     /*
620      * prevBand serves to mark the start of the previous band so rectangles
621      * can be coalesced into larger rectangles. qv. miCoalesce, above.
622      * In the beginning, there is no previous band, so prevBand == curBand
623      * (curBand is set later on, of course, but the first band will always
624      * start at index 0). prevBand and curBand must be indices because of
625      * the possible expansion, and resultant moving, of the new region's
626      * array of rectangles.
627      */
628     prevBand = 0;
629
630     do
631     {
632                 curBand = newReg->rdh.nCount;
633
634                 /*
635                  * This algorithm proceeds one source-band (as opposed to a
636                  * destination band, which is determined by where the two regions
637                  * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
638                  * rectangle after the last one in the current band for their
639                  * respective regions.
640                  */
641                 r1BandEnd = r1;
642                 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
643                 {
644                     r1BandEnd++;
645                 }
646
647                 r2BandEnd = r2;
648                 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
649                 {
650                     r2BandEnd++;
651                 }
652
653                 /*
654                  * First handle the band that doesn't intersect, if any.
655                  *
656                  * Note that attention is restricted to one band in the
657                  * non-intersecting region at once, so if a region has n
658                  * bands between the current position and the next place it overlaps
659                  * the other, this entire loop will be passed through n times.
660                  */
661                 if (r1->top < r2->top)
662                 {
663                     top = max(r1->top,ybot);
664                     bot = min(r1->bottom,r2->top);
665
666                     if ((top != bot) && (nonOverlap1Func != NULL))
667                     {
668                                 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
669                     }
670
671                     ytop = r2->top;
672                 }
673                 else if (r2->top < r1->top)
674                 {
675                     top = max(r2->top,ybot);
676                     bot = min(r2->bottom,r1->top);
677
678                     if ((top != bot) && (nonOverlap2Func != NULL))
679                     {
680                                 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
681                     }
682
683                     ytop = r1->top;
684                 }
685                 else
686                 {
687                     ytop = r1->top;
688                 }
689
690                 /*
691                  * If any rectangles got added to the region, try and coalesce them
692                  * with rectangles from the previous band. Note we could just do
693                  * this test in miCoalesce, but some machines incur a not
694                  * inconsiderable cost for function calls, so...
695                  */
696                 if (newReg->rdh.nCount != curBand)
697                 {
698                     prevBand = REGION_Coalesce (newReg, prevBand, curBand);
699                 }
700
701                 /*
702                  * Now see if we've hit an intersecting band. The two bands only
703                  * intersect if ybot > ytop
704                  */
705                 ybot = min(r1->bottom, r2->bottom);
706                 curBand = newReg->rdh.nCount;
707                 if (ybot > ytop)
708                 {
709                     (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
710                 }
711
712                 if (newReg->rdh.nCount != curBand)
713                 {
714                     prevBand = REGION_Coalesce (newReg, prevBand, curBand);
715                 }
716
717                 /*
718                  * If we've finished with a band (bottom == ybot) we skip forward
719                  * in the region to the next band.
720                  */
721                 if (r1->bottom == ybot)
722                 {
723                     r1 = r1BandEnd;
724                 }
725                 if (r2->bottom == ybot)
726                 {
727                     r2 = r2BandEnd;
728                 }
729     } while ((r1 != r1End) && (r2 != r2End));
730
731     /*
732      * Deal with whichever region still has rectangles left.
733      */
734     curBand = newReg->rdh.nCount;
735     if (r1 != r1End)
736     {
737                 if (nonOverlap1Func != NULL)
738                 {
739                     do
740                     {
741                                 r1BandEnd = r1;
742                                 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
743                                 {
744                                     r1BandEnd++;
745                                 }
746                                 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
747                                                      max(r1->top,ybot), r1->bottom);
748                                 r1 = r1BandEnd;
749                     } while (r1 != r1End);
750                 }
751     }
752     else if ((r2 != r2End) && (nonOverlap2Func != NULL))
753     {
754                 do
755                 {
756                     r2BandEnd = r2;
757                     while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
758                     {
759                          r2BandEnd++;
760                     }
761                     (* nonOverlap2Func) (newReg, r2, r2BandEnd,
762                                         max(r2->top,ybot), r2->bottom);
763                     r2 = r2BandEnd;
764                 } while (r2 != r2End);
765     }
766
767     if (newReg->rdh.nCount != curBand)
768     {
769                 (void) REGION_Coalesce (newReg, prevBand, curBand);
770     }
771
772     /*
773      * A bit of cleanup. To keep regions from growing without bound,
774      * we shrink the array of rectangles to match the new number of
775      * rectangles in the region. This never goes to 0, however...
776      *
777      * Only do this stuff if the number of rectangles allocated is more than
778      * twice the number of rectangles in the region (a simple optimization...).
779      */
780     if ((2 * newReg->rdh.nCount*sizeof(RECT) < newReg->rdh.nRgnSize && (newReg->rdh.nCount > 2)))
781     {
782                 if (REGION_NOT_EMPTY(newReg))
783                 {
784                     RECT *prev_rects = (PRECT)newReg->Buffer;
785                     newReg->Buffer = ExAllocatePool( PagedPool, newReg->rdh.nCount*sizeof(RECT) );
786
787                     if (! newReg->Buffer)
788                                 newReg->Buffer = (char*)prev_rects;
789                         else{
790                                 newReg->rdh.nRgnSize = newReg->rdh.nCount*sizeof(RECT);
791                                 RtlCopyMemory( newReg->Buffer, prev_rects, newReg->rdh.nRgnSize );
792                                 ExFreePool( prev_rects );
793                         }
794                 }
795                 else
796                 {
797                     /*
798                      * No point in doing the extra work involved in an Xrealloc if
799                      * the region is empty
800                      */
801                     newReg->rdh.nRgnSize = sizeof(RECT);
802                     ExFreePool( newReg->Buffer );
803                     newReg->Buffer = ExAllocatePool( PagedPool, sizeof(RECT) );
804                         ASSERT( newReg->Buffer );
805                 }
806     }
807
808         if( newReg->rdh.nCount == 0 )
809                 newReg->rdh.iType = NULLREGION;
810         else
811                 newReg->rdh.iType = (newReg->rdh.nCount > 1)? COMPLEXREGION : SIMPLEREGION;
812
813         ExFreePool( oldRects );
814     return;
815 }
816
817 /***********************************************************************
818  *          Region Intersection
819  ***********************************************************************/
820
821
822 /*!
823  * Handle an overlapping band for REGION_Intersect.
824  *
825  * Results:
826  *      None.
827  *
828  * \note Side Effects:
829  *      Rectangles may be added to the region.
830  *
831  */
832 static void FASTCALL
833 REGION_IntersectO (
834         PROSRGNDATA pReg,
835         PRECT       r1,
836         PRECT       r1End,
837         PRECT       r2,
838         PRECT       r2End,
839         INT         top,
840         INT         bottom
841         )
842 {
843     INT       left, right;
844     RECT      *pNextRect;
845
846     pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
847
848     while ((r1 != r1End) && (r2 != r2End))
849     {
850                 left = max(r1->left, r2->left);
851                 right = min(r1->right, r2->right);
852
853                 /*
854                  * If there's any overlap between the two rectangles, add that
855                  * overlap to the new region.
856                  * There's no need to check for subsumption because the only way
857                  * such a need could arise is if some region has two rectangles
858                  * right next to each other. Since that should never happen...
859                  */
860                 if (left < right)
861                 {
862                     MEMCHECK(pReg, pNextRect, pReg->Buffer);
863                     pNextRect->left = left;
864                     pNextRect->top = top;
865                     pNextRect->right = right;
866                     pNextRect->bottom = bottom;
867                     pReg->rdh.nCount += 1;
868                     pNextRect++;
869                 }
870
871                 /*
872                  * Need to advance the pointers. Shift the one that extends
873                  * to the right the least, since the other still has a chance to
874                  * overlap with that region's next rectangle, if you see what I mean.
875                  */
876                 if (r1->right < r2->right)
877                 {
878                     r1++;
879                 }
880                 else if (r2->right < r1->right)
881                 {
882                     r2++;
883                 }
884                 else
885                 {
886                     r1++;
887                     r2++;
888                 }
889     }
890     return;
891 }
892
893 /***********************************************************************
894  *           REGION_IntersectRegion
895  */
896 static void FASTCALL REGION_IntersectRegion(ROSRGNDATA *newReg, ROSRGNDATA *reg1,
897                                    ROSRGNDATA *reg2)
898 {
899   /* check for trivial reject */
900   if ( (!(reg1->rdh.nCount)) || (!(reg2->rdh.nCount))  ||
901     (!EXTENTCHECK(&reg1->rdh.rcBound, &reg2->rdh.rcBound)))
902     newReg->rdh.nCount = 0;
903   else
904     REGION_RegionOp (newReg, reg1, reg2,
905       REGION_IntersectO, NULL, NULL);
906
907     /*
908      * Can't alter newReg's extents before we call miRegionOp because
909      * it might be one of the source regions and miRegionOp depends
910      * on the extents of those regions being the same. Besides, this
911      * way there's no checking against rectangles that will be nuked
912      * due to coalescing, so we have to examine fewer rectangles.
913      */
914
915     REGION_SetExtents(newReg);
916 }
917
918 /***********************************************************************
919  *           Region Union
920  ***********************************************************************/
921
922 /*!
923  *      Handle a non-overlapping band for the union operation. Just
924  *      Adds the rectangles into the region. Doesn't have to check for
925  *      subsumption or anything.
926  *
927  * Results:
928  *      None.
929  *
930  * \note Side Effects:
931  *      pReg->numRects is incremented and the final rectangles overwritten
932  *      with the rectangles we're passed.
933  *
934  */
935 static void FASTCALL
936 REGION_UnionNonO (
937         PROSRGNDATA pReg,
938         PRECT       r,
939         PRECT       rEnd,
940         INT         top,
941         INT         bottom
942         )
943 {
944     RECT *pNextRect;
945
946     pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
947
948     while (r != rEnd)
949     {
950                 MEMCHECK(pReg, pNextRect, pReg->Buffer);
951                 pNextRect->left = r->left;
952                 pNextRect->top = top;
953                 pNextRect->right = r->right;
954                 pNextRect->bottom = bottom;
955                 pReg->rdh.nCount += 1;
956                 pNextRect++;
957                 r++;
958     }
959     return;
960 }
961
962 /*!
963  *      Handle an overlapping band for the union operation. Picks the
964  *      left-most rectangle each time and merges it into the region.
965  *
966  * Results:
967  *      None.
968  *
969  * \note Side Effects:
970  *      Rectangles are overwritten in pReg->rects and pReg->numRects will
971  *      be changed.
972  *
973  */
974 static void FASTCALL
975 REGION_UnionO (
976         PROSRGNDATA pReg,
977         PRECT       r1,
978         PRECT       r1End,
979         PRECT       r2,
980         PRECT       r2End,
981         INT         top,
982         INT         bottom
983         )
984 {
985     RECT *pNextRect;
986
987     pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
988
989 #define MERGERECT(r) \
990     if ((pReg->rdh.nCount != 0) &&  \
991                 ((pNextRect-1)->top == top) &&  \
992                 ((pNextRect-1)->bottom == bottom) &&  \
993                 ((pNextRect-1)->right >= r->left))  \
994     {  \
995                 if ((pNextRect-1)->right < r->right)  \
996                 {  \
997                     (pNextRect-1)->right = r->right;  \
998                 }  \
999     }  \
1000     else  \
1001     {  \
1002                 MEMCHECK(pReg, pNextRect, pReg->Buffer);  \
1003                 pNextRect->top = top;  \
1004                 pNextRect->bottom = bottom;  \
1005                 pNextRect->left = r->left;  \
1006                 pNextRect->right = r->right;  \
1007                 pReg->rdh.nCount += 1;  \
1008                 pNextRect += 1;  \
1009     }  \
1010     r++;
1011
1012     while ((r1 != r1End) && (r2 != r2End))
1013     {
1014                 if (r1->left < r2->left)
1015                 {
1016                     MERGERECT(r1);
1017                 }
1018                 else
1019                 {
1020                     MERGERECT(r2);
1021                 }
1022     }
1023
1024     if (r1 != r1End)
1025     {
1026                 do
1027                 {
1028                     MERGERECT(r1);
1029                 } while (r1 != r1End);
1030     }
1031     else while (r2 != r2End)
1032     {
1033                 MERGERECT(r2);
1034     }
1035     return;
1036 }
1037
1038 /***********************************************************************
1039  *           REGION_UnionRegion
1040  */
1041 static void FASTCALL REGION_UnionRegion(ROSRGNDATA *newReg, ROSRGNDATA *reg1,
1042                                ROSRGNDATA *reg2)
1043 {
1044   /*  checks all the simple cases */
1045
1046   /*
1047    * Region 1 and 2 are the same or region 1 is empty
1048    */
1049   if (reg1 == reg2 || 0 == reg1->rdh.nCount ||
1050       reg1->rdh.rcBound.right <= reg1->rdh.rcBound.left ||
1051       reg1->rdh.rcBound.bottom <= reg1->rdh.rcBound.top)
1052     {
1053       if (newReg != reg2)
1054         {
1055           REGION_CopyRegion(newReg, reg2);
1056         }
1057       return;
1058     }
1059
1060     /*
1061      * if nothing to union (region 2 empty)
1062      */
1063   if (0 == reg2->rdh.nCount ||
1064       reg2->rdh.rcBound.right <= reg2->rdh.rcBound.left ||
1065       reg2->rdh.rcBound.bottom <= reg2->rdh.rcBound.top)
1066     {
1067       if (newReg != reg1)
1068         {
1069           REGION_CopyRegion(newReg, reg1);
1070         }
1071       return;
1072     }
1073
1074   /*
1075    * Region 1 completely subsumes region 2
1076    */
1077   if (1 == reg1->rdh.nCount &&
1078       reg1->rdh.rcBound.left <= reg2->rdh.rcBound.left &&
1079       reg1->rdh.rcBound.top <= reg2->rdh.rcBound.top &&
1080       reg2->rdh.rcBound.right <= reg1->rdh.rcBound.right &&
1081       reg2->rdh.rcBound.bottom <= reg1->rdh.rcBound.bottom)
1082     {
1083       if (newReg != reg1)
1084         {
1085           REGION_CopyRegion(newReg, reg1);
1086         }
1087       return;
1088     }
1089
1090   /*
1091    * Region 2 completely subsumes region 1
1092    */
1093   if (1 == reg2->rdh.nCount &&
1094       reg2->rdh.rcBound.left <= reg1->rdh.rcBound.left &&
1095       reg2->rdh.rcBound.top <= reg1->rdh.rcBound.top &&
1096       reg1->rdh.rcBound.right <= reg2->rdh.rcBound.right &&
1097       reg1->rdh.rcBound.bottom <= reg2->rdh.rcBound.bottom)
1098     {
1099       if (newReg != reg2)
1100         {
1101           REGION_CopyRegion(newReg, reg2);
1102         }
1103       return;
1104     }
1105
1106   REGION_RegionOp ( newReg, reg1, reg2, REGION_UnionO,
1107                   REGION_UnionNonO, REGION_UnionNonO );
1108   newReg->rdh.rcBound.left = min(reg1->rdh.rcBound.left, reg2->rdh.rcBound.left);
1109   newReg->rdh.rcBound.top = min(reg1->rdh.rcBound.top, reg2->rdh.rcBound.top);
1110   newReg->rdh.rcBound.right = max(reg1->rdh.rcBound.right, reg2->rdh.rcBound.right);
1111   newReg->rdh.rcBound.bottom = max(reg1->rdh.rcBound.bottom, reg2->rdh.rcBound.bottom);
1112 }
1113
1114 /***********************************************************************
1115  *           Region Subtraction
1116  ***********************************************************************/
1117
1118 /*!
1119  *      Deal with non-overlapping band for subtraction. Any parts from
1120  *      region 2 we discard. Anything from region 1 we add to the region.
1121  *
1122  * Results:
1123  *      None.
1124  *
1125  * \note Side Effects:
1126  *      pReg may be affected.
1127  *
1128  */
1129 static void FASTCALL
1130 REGION_SubtractNonO1 (
1131         PROSRGNDATA pReg,
1132         PRECT       r,
1133         PRECT       rEnd,
1134         INT         top,
1135         INT         bottom
1136         )
1137 {
1138     RECT *pNextRect;
1139
1140     pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
1141
1142     while (r != rEnd)
1143     {
1144                 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1145                 pNextRect->left = r->left;
1146                 pNextRect->top = top;
1147                 pNextRect->right = r->right;
1148                 pNextRect->bottom = bottom;
1149                 pReg->rdh.nCount += 1;
1150                 pNextRect++;
1151                 r++;
1152     }
1153     return;
1154 }
1155
1156
1157 /*!
1158  *      Overlapping band subtraction. x1 is the left-most point not yet
1159  *      checked.
1160  *
1161  * Results:
1162  *      None.
1163  *
1164  * \note Side Effects:
1165  *      pReg may have rectangles added to it.
1166  *
1167  */
1168 static void FASTCALL
1169 REGION_SubtractO (
1170         PROSRGNDATA pReg,
1171         PRECT       r1,
1172         PRECT       r1End,
1173         PRECT       r2,
1174         PRECT       r2End,
1175         INT         top,
1176         INT         bottom
1177         )
1178 {
1179     RECT *pNextRect;
1180     INT left;
1181
1182     left = r1->left;
1183     pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
1184
1185     while ((r1 != r1End) && (r2 != r2End))
1186     {
1187                 if (r2->right <= left)
1188                 {
1189                     /*
1190                      * Subtrahend missed the boat: go to next subtrahend.
1191                      */
1192                     r2++;
1193                 }
1194                 else if (r2->left <= left)
1195                 {
1196                     /*
1197                      * Subtrahend preceeds minuend: nuke left edge of minuend.
1198                      */
1199                     left = r2->right;
1200                     if (left >= r1->right)
1201                     {
1202                         /*
1203                          * Minuend completely covered: advance to next minuend and
1204                          * reset left fence to edge of new minuend.
1205                          */
1206                         r1++;
1207                         if (r1 != r1End)
1208                             left = r1->left;
1209                     }
1210                     else
1211                     {
1212                         /*
1213                          * Subtrahend now used up since it doesn't extend beyond
1214                          * minuend
1215                          */
1216                         r2++;
1217                     }
1218                 }
1219                 else if (r2->left < r1->right)
1220                 {
1221                     /*
1222                      * Left part of subtrahend covers part of minuend: add uncovered
1223                      * part of minuend to region and skip to next subtrahend.
1224                      */
1225                     MEMCHECK(pReg, pNextRect, pReg->Buffer);
1226                     pNextRect->left = left;
1227                     pNextRect->top = top;
1228                     pNextRect->right = r2->left;
1229                     pNextRect->bottom = bottom;
1230                     pReg->rdh.nCount += 1;
1231                     pNextRect++;
1232                     left = r2->right;
1233                     if (left >= r1->right)
1234                     {
1235                         /*
1236                          * Minuend used up: advance to new...
1237                          */
1238                         r1++;
1239                         if (r1 != r1End)
1240                             left = r1->left;
1241                     }
1242                     else
1243                     {
1244                         /*
1245                          * Subtrahend used up
1246                          */
1247                         r2++;
1248                     }
1249                 }
1250                 else
1251                 {
1252                     /*
1253                      * Minuend used up: add any remaining piece before advancing.
1254                      */
1255                     if (r1->right > left)
1256                     {
1257                                 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1258                                 pNextRect->left = left;
1259                                 pNextRect->top = top;
1260                                 pNextRect->right = r1->right;
1261                                 pNextRect->bottom = bottom;
1262                                 pReg->rdh.nCount += 1;
1263                                 pNextRect++;
1264                     }
1265                     r1++;
1266                     left = r1->left;
1267                 }
1268     }
1269
1270     /*
1271      * Add remaining minuend rectangles to region.
1272      */
1273     while (r1 != r1End)
1274     {
1275                 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1276                 pNextRect->left = left;
1277                 pNextRect->top = top;
1278                 pNextRect->right = r1->right;
1279                 pNextRect->bottom = bottom;
1280                 pReg->rdh.nCount += 1;
1281                 pNextRect++;
1282                 r1++;
1283                 if (r1 != r1End)
1284                 {
1285                     left = r1->left;
1286                 }
1287     }
1288     return;
1289 }
1290
1291 /*!
1292  *      Subtract regS from regM and leave the result in regD.
1293  *      S stands for subtrahend, M for minuend and D for difference.
1294  *
1295  * Results:
1296  *      TRUE.
1297  *
1298  * \note Side Effects:
1299  *      regD is overwritten.
1300  *
1301  */
1302 static void FASTCALL REGION_SubtractRegion(ROSRGNDATA *regD, ROSRGNDATA *regM,
1303                                                        ROSRGNDATA *regS )
1304 {
1305    /* check for trivial reject */
1306     if ( (!(regM->rdh.nCount)) || (!(regS->rdh.nCount))  ||
1307                 (!EXTENTCHECK(&regM->rdh.rcBound, &regS->rdh.rcBound)) )
1308     {
1309                 REGION_CopyRegion(regD, regM);
1310                 return;
1311     }
1312
1313     REGION_RegionOp (regD, regM, regS, REGION_SubtractO,
1314                 REGION_SubtractNonO1, NULL);
1315
1316     /*
1317      * Can't alter newReg's extents before we call miRegionOp because
1318      * it might be one of the source regions and miRegionOp depends
1319      * on the extents of those regions being the unaltered. Besides, this
1320      * way there's no checking against rectangles that will be nuked
1321      * due to coalescing, so we have to examine fewer rectangles.
1322      */
1323     REGION_SetExtents (regD);
1324 }
1325
1326 /***********************************************************************
1327  *           REGION_XorRegion
1328  */
1329 static void FASTCALL REGION_XorRegion(ROSRGNDATA *dr, ROSRGNDATA *sra,
1330                                                         ROSRGNDATA *srb)
1331 {
1332         HRGN htra, htrb;
1333     ROSRGNDATA *tra, *trb;
1334
1335     if ((! (htra = RGNDATA_AllocRgn(sra->rdh.nCount + 1))) ||
1336                 (! (htrb = RGNDATA_AllocRgn(srb->rdh.nCount + 1))))
1337         return;
1338         tra = RGNDATA_LockRgn( htra );
1339         if( !tra ){
1340                 NtGdiDeleteObject( htra );
1341                 NtGdiDeleteObject( htrb );
1342                 return;
1343         }
1344
1345         trb = RGNDATA_LockRgn( htrb );
1346         if( !trb ){
1347                 RGNDATA_UnlockRgn( htra );
1348                 NtGdiDeleteObject( htra );
1349                 NtGdiDeleteObject( htrb );
1350                 return;
1351         }
1352
1353     REGION_SubtractRegion(tra,sra,srb);
1354     REGION_SubtractRegion(trb,srb,sra);
1355     REGION_UnionRegion(dr,tra,trb);
1356         RGNDATA_UnlockRgn( htra );
1357         RGNDATA_UnlockRgn( htrb );
1358
1359     NtGdiDeleteObject( htra );
1360     NtGdiDeleteObject( htrb );
1361     return;
1362 }
1363
1364
1365 /*!
1366  * Adds a rectangle to a REGION
1367  */
1368 static void FASTCALL REGION_UnionRectWithRegion(const RECT *rect, ROSRGNDATA *rgn)
1369 {
1370     ROSRGNDATA region;
1371
1372     region.Buffer = (char*)(&(region.rdh.rcBound));
1373     region.rdh.nCount = 1;
1374     region.rdh.nRgnSize = sizeof( RECT );
1375     region.rdh.rcBound = *rect;
1376     REGION_UnionRegion(rgn, rgn, &region);
1377 }
1378
1379
1380 BOOL STDCALL REGION_LPTODP(HDC hdc, HRGN hDest, HRGN hSrc)
1381 {
1382   RECT *pCurRect, *pEndRect;
1383   PROSRGNDATA srcObj = NULL;
1384   PROSRGNDATA destObj = NULL;
1385
1386   DC * dc = DC_LockDc(hdc);
1387   RECT tmpRect;
1388   BOOL ret = FALSE;
1389
1390   if(!dc)
1391     return ret;
1392
1393   if(dc->w.MapMode == MM_TEXT) // Requires only a translation
1394   {
1395     if(NtGdiCombineRgn(hDest, hSrc, 0, RGN_COPY) == ERROR)
1396       goto done;
1397
1398     NtGdiOffsetRgn(hDest, dc->vportOrgX - dc->wndOrgX, dc->vportOrgY - dc->wndOrgY);
1399     ret = TRUE;
1400     goto done;
1401   }
1402
1403   if(!( srcObj = (PROSRGNDATA) RGNDATA_LockRgn( hSrc ) ))
1404         goto done;
1405   if(!( destObj = (PROSRGNDATA) RGNDATA_LockRgn( hDest ) ))
1406   {
1407     RGNDATA_UnlockRgn( hSrc );
1408     goto done;
1409   }
1410   EMPTY_REGION(destObj);
1411
1412   pEndRect = (PRECT)srcObj->Buffer + srcObj->rdh.nCount;
1413   for(pCurRect = (PRECT)srcObj->Buffer; pCurRect < pEndRect; pCurRect++)
1414   {
1415     tmpRect = *pCurRect;
1416     tmpRect.left = XLPTODP(dc, tmpRect.left);
1417     tmpRect.top = YLPTODP(dc, tmpRect.top);
1418     tmpRect.right = XLPTODP(dc, tmpRect.right);
1419     tmpRect.bottom = YLPTODP(dc, tmpRect.bottom);
1420
1421     if(tmpRect.left > tmpRect.right)
1422       { INT tmp = tmpRect.left; tmpRect.left = tmpRect.right; tmpRect.right = tmp; }
1423     if(tmpRect.top > tmpRect.bottom)
1424       { INT tmp = tmpRect.top; tmpRect.top = tmpRect.bottom; tmpRect.bottom = tmp; }
1425
1426     REGION_UnionRectWithRegion(&tmpRect, destObj);
1427   }
1428   ret = TRUE;
1429
1430   RGNDATA_UnlockRgn( hSrc );
1431   RGNDATA_UnlockRgn( hDest );
1432
1433 done:
1434   DC_UnlockDc( hdc );
1435   return ret;
1436 }
1437
1438 HRGN FASTCALL RGNDATA_AllocRgn(INT n)
1439 {
1440   HRGN hReg;
1441   PROSRGNDATA pReg;
1442   BOOL bRet;
1443
1444   if((hReg = (HRGN)GDIOBJ_AllocObj(sizeof(ROSRGNDATA), GDI_OBJECT_TYPE_REGION,
1445                                    (GDICLEANUPPROC) RGNDATA_InternalDelete))){
1446         if( (pReg = RGNDATA_LockRgn(hReg)) ){
1447
1448       if ((pReg->Buffer = ExAllocatePool(PagedPool, n * sizeof(RECT)))){
1449         EMPTY_REGION(pReg);
1450         pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
1451         pReg->rdh.nCount = n;
1452         pReg->rdh.nRgnSize = n*sizeof(RECT);
1453
1454         bRet = RGNDATA_UnlockRgn(hReg);
1455         ASSERT(bRet);
1456
1457         return hReg;
1458           }
1459
1460         }
1461         else
1462                 RGNDATA_FreeRgn(hReg);
1463   }
1464   return NULL;
1465 }
1466
1467 BOOL FASTCALL RGNDATA_InternalDelete( PROSRGNDATA pRgn )
1468 {
1469   ASSERT(pRgn);
1470   if(pRgn->Buffer)
1471     ExFreePool(pRgn->Buffer);
1472   return TRUE;
1473 }
1474
1475 // NtGdi Exported Functions
1476 INT
1477 STDCALL
1478 NtGdiCombineRgn(HRGN  hDest,
1479                     HRGN  hSrc1,
1480                     HRGN  hSrc2,
1481                     INT  CombineMode)
1482 {
1483   INT result = ERROR;
1484   GDIMULTILOCK Lock[3] = {{hDest, 0, GDI_OBJECT_TYPE_REGION}, {hSrc1, 0, GDI_OBJECT_TYPE_REGION}, {hSrc2, 0, GDI_OBJECT_TYPE_REGION}};
1485   PROSRGNDATA destRgn, src1Rgn, src2Rgn;
1486
1487   if ( !GDIOBJ_LockMultipleObj(Lock, sizeof(Lock)/sizeof(Lock[0])) )
1488     {
1489       DPRINT1("GDIOBJ_LockMultipleObj() failed\n" );
1490       return ERROR;
1491     }
1492
1493   destRgn = (PROSRGNDATA) Lock[0].pObj;
1494   src1Rgn = (PROSRGNDATA) Lock[1].pObj;
1495   src2Rgn = (PROSRGNDATA) Lock[2].pObj;
1496
1497   if( destRgn )
1498     {
1499       if( src1Rgn )
1500           {
1501             if (CombineMode == RGN_COPY)
1502               {
1503                 if( !REGION_CopyRegion(destRgn, src1Rgn) )
1504                   return ERROR;
1505                 result = destRgn->rdh.iType;
1506               }
1507             else
1508             {
1509               if( src2Rgn )
1510                 {
1511                   switch (CombineMode)
1512                     {
1513                       case RGN_AND:
1514                         REGION_IntersectRegion(destRgn, src1Rgn, src2Rgn);
1515                         break;
1516                       case RGN_OR:
1517                         REGION_UnionRegion(destRgn, src1Rgn, src2Rgn);
1518                         break;
1519                       case RGN_XOR:
1520                         REGION_XorRegion(destRgn, src1Rgn, src2Rgn);
1521                         break;
1522                       case RGN_DIFF:
1523                         REGION_SubtractRegion(destRgn, src1Rgn, src2Rgn);
1524                         break;
1525                     }
1526                   result = destRgn->rdh.iType;
1527                 }
1528             }
1529           }
1530     }
1531   else
1532     {
1533       DPRINT("NtGdiCombineRgn: hDest unavailable\n");
1534       result = ERROR;
1535     }
1536   GDIOBJ_UnlockMultipleObj(Lock, sizeof(Lock)/sizeof(Lock[0]));
1537   return result;
1538 }
1539
1540 HRGN
1541 STDCALL
1542 NtGdiCreateEllipticRgn(INT  LeftRect,
1543                             INT  TopRect,
1544                             INT  RightRect,
1545                             INT  BottomRect)
1546 {
1547   UNIMPLEMENTED;
1548 }
1549
1550 HRGN
1551 STDCALL
1552 NtGdiCreateEllipticRgnIndirect(CONST PRECT  rc)
1553 {
1554   UNIMPLEMENTED;
1555 }
1556
1557 HRGN
1558 STDCALL
1559 NtGdiCreatePolygonRgn(CONST PPOINT  pt,
1560                            INT  Count,
1561                            INT  PolyFillMode)
1562 {
1563   UNIMPLEMENTED;
1564 }
1565
1566 HRGN
1567 STDCALL
1568 NtGdiCreatePolyPolygonRgn(CONST PPOINT  pt,
1569                                CONST PINT  PolyCounts,
1570                                INT  Count,
1571                                INT  PolyFillMode)
1572 {
1573   UNIMPLEMENTED;
1574 }
1575
1576 HRGN
1577 STDCALL
1578 NtGdiCreateRectRgn(INT LeftRect,
1579                    INT TopRect,
1580                    INT RightRect,
1581                    INT BottomRect)
1582 {
1583   HRGN hRgn;
1584   PROSRGNDATA pRgnData;
1585   PRECT pRect;
1586
1587   if (RightRect < LeftRect || BottomRect < TopRect)
1588     {
1589       DPRINT1("Invalid parameters (%d, %d) - (%d, %d)\n", LeftRect, TopRect, RightRect, BottomRect);
1590       SetLastWin32Error(ERROR_INVALID_PARAMETER);
1591       return NULL;
1592     }
1593
1594   /* Allocate region data structure with space for 1 RECT */
1595   if ((hRgn = RGNDATA_AllocRgn(1)))
1596     {
1597       if ((pRgnData = RGNDATA_LockRgn(hRgn)))
1598         {
1599           pRect = (PRECT)pRgnData->Buffer;
1600           ASSERT(pRect);
1601
1602           /* Fill in the region data header */
1603           pRgnData->rdh.iType = (LeftRect == RightRect || TopRect == BottomRect) ? NULLREGION : SIMPLEREGION;
1604           NtGdiSetRect(&(pRgnData->rdh.rcBound), LeftRect, TopRect, RightRect, BottomRect);
1605
1606           /* use NtGdiCopyRect when implemented */
1607           NtGdiSetRect(pRect, LeftRect, TopRect, RightRect, BottomRect);
1608           RGNDATA_UnlockRgn(hRgn);
1609
1610           return hRgn;
1611         }
1612       NtGdiDeleteObject( hRgn );
1613     }
1614
1615   DPRINT1("NtGdiCreateRectRgn: can't allocate region\n");
1616   return NULL;
1617 }
1618
1619 HRGN STDCALL
1620 NtGdiCreateRectRgnIndirect(CONST PRECT rc)
1621 {
1622   RECT SafeRc;
1623   NTSTATUS Status;
1624
1625   Status = MmCopyFromCaller(&SafeRc, rc, sizeof(RECT));
1626   if (!NT_SUCCESS(Status))
1627     {
1628       return(NULL);
1629     }
1630   return(UnsafeIntCreateRectRgnIndirect(&SafeRc));
1631 }
1632
1633 HRGN STDCALL
1634 UnsafeIntCreateRectRgnIndirect(CONST PRECT  rc)
1635 {
1636   return(NtGdiCreateRectRgn(rc->left, rc->top, rc->right, rc->bottom));
1637 }
1638
1639 HRGN
1640 STDCALL
1641 NtGdiCreateRoundRectRgn(INT  LeftRect,
1642                              INT  TopRect,
1643                              INT  RightRect,
1644                              INT  BottomRect,
1645                              INT  WidthEllipse,
1646                              INT  HeightEllipse)
1647 {
1648   UNIMPLEMENTED;
1649 }
1650
1651 BOOL
1652 STDCALL
1653 NtGdiEqualRgn(HRGN  hSrcRgn1,
1654                    HRGN  hSrcRgn2)
1655 {
1656   PROSRGNDATA rgn1, rgn2;
1657   PRECT tRect1, tRect2;
1658   ULONG i;
1659   BOOL bRet = FALSE;
1660
1661   if( !(rgn1 = RGNDATA_LockRgn(hSrcRgn1)))
1662         return ERROR;
1663
1664   if( !(rgn2 = RGNDATA_LockRgn(hSrcRgn2))){
1665         RGNDATA_UnlockRgn( hSrcRgn1 );
1666         return ERROR;
1667   }
1668
1669   if(rgn1->rdh.nCount != rgn2->rdh.nCount ||
1670         rgn1->rdh.nCount == 0 ||
1671         rgn1->rdh.rcBound.left   != rgn2->rdh.rcBound.left ||
1672         rgn1->rdh.rcBound.right  != rgn2->rdh.rcBound.right ||
1673         rgn1->rdh.rcBound.top    != rgn2->rdh.rcBound.top ||
1674         rgn1->rdh.rcBound.bottom != rgn2->rdh.rcBound.bottom)
1675         goto exit;
1676
1677   tRect1 = (PRECT)rgn1->Buffer;
1678   tRect2 = (PRECT)rgn2->Buffer;
1679
1680   if( !tRect1 || !tRect2 )
1681         goto exit;
1682
1683   for(i=0; i < rgn1->rdh.nCount; i++)
1684   {
1685     if(tRect1[i].left   != tRect2[i].left ||
1686         tRect1[i].right  != tRect2[i].right ||
1687         tRect1[i].top    != tRect2[i].top ||
1688         tRect1[i].bottom != tRect2[i].bottom)
1689            goto exit;;
1690   }
1691   bRet = TRUE;
1692
1693 exit:
1694   RGNDATA_UnlockRgn( hSrcRgn1 );
1695   RGNDATA_UnlockRgn( hSrcRgn2 );
1696   return bRet;
1697 }
1698
1699 HRGN
1700 STDCALL
1701 NtGdiExtCreateRegion(CONST PXFORM  Xform,
1702                           DWORD  Count,
1703                           CONST PROSRGNDATA  RgnData)
1704 {
1705   HRGN hRgn;
1706
1707   // FIXME: Apply Xform transformation to the regional data
1708   if(Xform != NULL) {
1709
1710   }
1711
1712   return hRgn;
1713 }
1714
1715 BOOL
1716 STDCALL
1717 NtGdiFillRgn(HDC hDC, HRGN hRgn, HBRUSH hBrush)
1718 {
1719   HBRUSH oldhBrush;
1720   PROSRGNDATA rgn;
1721   PRECTL r;
1722
1723   if (NULL == (rgn = RGNDATA_LockRgn(hRgn)))
1724     {
1725       return FALSE;
1726     }
1727
1728   if (NULL == (oldhBrush = NtGdiSelectObject(hDC, hBrush)))
1729     {
1730       RGNDATA_UnlockRgn(hRgn);
1731       return FALSE;
1732     }
1733
1734   for (r = (PRECT) rgn->Buffer; r < ((PRECT) rgn->Buffer) + rgn->rdh.nCount; r++)
1735     {
1736       NtGdiPatBlt(hDC, r->left, r->top, r->right - r->left, r->bottom - r->top, PATCOPY);
1737     }
1738
1739   NtGdiSelectObject(hDC, oldhBrush);
1740   RGNDATA_UnlockRgn( hRgn );
1741
1742   return TRUE;
1743 }
1744
1745 BOOL
1746 STDCALL
1747 NtGdiFrameRgn(HDC  hDC,
1748                    HRGN  hRgn,
1749                    HBRUSH  hBrush,
1750                    INT  Width,
1751                    INT  Height)
1752 {
1753   UNIMPLEMENTED;
1754 }
1755
1756 INT STDCALL
1757 UnsafeIntGetRgnBox(HRGN  hRgn,
1758                     LPRECT  pRect)
1759 {
1760   PROSRGNDATA rgn = RGNDATA_LockRgn(hRgn);
1761   DWORD ret;
1762
1763   if (rgn)
1764     {
1765       *pRect = rgn->rdh.rcBound;
1766       ret = rgn->rdh.iType;
1767       RGNDATA_UnlockRgn( hRgn );
1768
1769       return ret;
1770     }
1771   return 0; //if invalid region return zero
1772 }
1773
1774
1775 INT STDCALL
1776 NtGdiGetRgnBox(HRGN  hRgn,
1777               LPRECT  pRect)
1778 {
1779   RECT SafeRect;
1780   DWORD ret;
1781
1782   ret = UnsafeIntGetRgnBox(hRgn, &SafeRect);
1783   if (ERROR == ret)
1784     {
1785       return ret;
1786     }
1787
1788   if (!NT_SUCCESS(MmCopyToCaller(pRect, &SafeRect, sizeof(RECT))))
1789     {
1790       return ERROR;
1791     }
1792
1793   return ret;
1794 }
1795
1796 BOOL
1797 STDCALL
1798 NtGdiInvertRgn(HDC  hDC,
1799                     HRGN  hRgn)
1800 {
1801   UNIMPLEMENTED;
1802 }
1803
1804 INT
1805 STDCALL
1806 NtGdiOffsetRgn(HRGN  hRgn,
1807                    INT  XOffset,
1808                    INT  YOffset)
1809 {
1810   PROSRGNDATA rgn = RGNDATA_LockRgn(hRgn);
1811   INT ret;
1812
1813   DPRINT("NtGdiOffsetRgn: hRgn %d Xoffs %d Yoffs %d rgn %x\n", hRgn, XOffset, YOffset, rgn );
1814
1815   if( !rgn ){
1816           DPRINT("NtGdiOffsetRgn: hRgn error\n");
1817           return ERROR;
1818   }
1819
1820   if(XOffset || YOffset) {
1821     int nbox = rgn->rdh.nCount;
1822         PRECT pbox = (PRECT)rgn->Buffer;
1823
1824     if(nbox && pbox) {
1825       while(nbox--) {
1826         pbox->left += XOffset;
1827         pbox->right += XOffset;
1828         pbox->top += YOffset;
1829         pbox->bottom += YOffset;
1830         pbox++;
1831       }
1832       rgn->rdh.rcBound.left += XOffset;
1833       rgn->rdh.rcBound.right += XOffset;
1834       rgn->rdh.rcBound.top += YOffset;
1835       rgn->rdh.rcBound.bottom += YOffset;
1836     }
1837   }
1838   ret = rgn->rdh.iType;
1839   RGNDATA_UnlockRgn( hRgn );
1840   return ret;
1841 }
1842
1843 BOOL
1844 STDCALL
1845 NtGdiPaintRgn(HDC  hDC,
1846                    HRGN  hRgn)
1847 {
1848   //RECT box;
1849   HRGN tmpVisRgn; //, prevVisRgn;
1850   DC *dc = DC_LockDc(hDC);
1851   PROSRGNDATA visrgn;
1852   CLIPOBJ* ClipRegion;
1853   BOOL bRet = FALSE;
1854   PBRUSHOBJ pBrush;
1855   POINTL BrushOrigin;
1856   SURFOBJ       *SurfObj;
1857
1858   if( !dc )
1859         return FALSE;
1860
1861   if(!(tmpVisRgn = NtGdiCreateRectRgn(0, 0, 0, 0))){
1862         DC_UnlockDc( hDC );
1863         return FALSE;
1864   }
1865
1866 /* ei enable later
1867   // Transform region into device co-ords
1868   if(!REGION_LPTODP(hDC, tmpVisRgn, hRgn) || NtGdiOffsetRgn(tmpVisRgn, dc->w.DCOrgX, dc->w.DCOrgY) == ERROR) {
1869     NtGdiDeleteObject( tmpVisRgn );
1870         DC_UnlockDc( hDC );
1871     return FALSE;
1872   }
1873 */
1874   /* enable when clipping is implemented
1875   NtGdiCombineRgn(tmpVisRgn, tmpVisRgn, dc->w.hGCClipRgn, RGN_AND);
1876   */
1877
1878   //visrgn = RGNDATA_LockRgn(tmpVisRgn);
1879   visrgn = RGNDATA_LockRgn(hRgn);
1880
1881   ClipRegion = IntEngCreateClipRegion (
1882           visrgn->rdh.nCount, (PRECTL)visrgn->Buffer, visrgn->rdh.rcBound );
1883   ASSERT( ClipRegion );
1884   pBrush = BRUSHOBJ_LockBrush(dc->w.hBrush);
1885   ASSERT(pBrush);
1886   BrushOrigin.x = dc->w.brushOrgX;
1887   BrushOrigin.y = dc->w.brushOrgY;
1888   SurfObj = (SURFOBJ*)AccessUserObject((ULONG)dc->Surface);
1889
1890   bRet = IntEngPaint(SurfObj,
1891          ClipRegion,
1892          pBrush,
1893          &BrushOrigin,
1894          0xFFFF);//FIXME:don't know what to put here
1895
1896   RGNDATA_UnlockRgn( tmpVisRgn );
1897
1898   // Fill the region
1899   DC_UnlockDc( hDC );
1900   return TRUE;
1901 }
1902
1903 BOOL
1904 STDCALL
1905 NtGdiPtInRegion(HRGN  hRgn,
1906                      INT  X,
1907                      INT  Y)
1908 {
1909   PROSRGNDATA rgn;
1910   ULONG i;
1911
1912   if( (rgn = RGNDATA_LockRgn(hRgn) ) )
1913           return FALSE;
1914
1915   if(rgn->rdh.nCount > 0 && INRECT(rgn->rdh.rcBound, X, Y)){
1916     for(i = 0; i < rgn->rdh.nCount; i++) {
1917       if(INRECT(*(PRECT)&rgn->Buffer[i], X, Y)){
1918                 RGNDATA_UnlockRgn(hRgn);
1919                 return TRUE;
1920           }
1921     }
1922   }
1923   RGNDATA_UnlockRgn(hRgn);
1924   return FALSE;
1925 }
1926
1927 BOOL
1928 FASTCALL
1929 UnsafeIntRectInRegion(HRGN  hRgn,
1930                       CONST LPRECT rc)
1931 {
1932   PROSRGNDATA rgn;
1933   PRECT pCurRect, pRectEnd;
1934   BOOL bRet = FALSE;
1935
1936   if( !( rgn  = RGNDATA_LockRgn(hRgn) ) )
1937         return ERROR;
1938
1939   // this is (just) a useful optimization
1940   if((rgn->rdh.nCount > 0) && EXTENTCHECK(&rgn->rdh.rcBound, rc))
1941   {
1942     for (pCurRect = (PRECT)rgn->Buffer, pRectEnd = pCurRect + rgn->rdh.nCount; pCurRect < pRectEnd; pCurRect++)
1943     {
1944       if (pCurRect->bottom <= rc->top) continue; // not far enough down yet
1945       if (pCurRect->top >= rc->bottom) break;    // too far down
1946       if (pCurRect->right <= rc->left) continue; // not far enough over yet
1947       if (pCurRect->left >= rc->right) continue;
1948       bRet = TRUE;
1949       break;
1950     }
1951   }
1952   RGNDATA_UnlockRgn(hRgn);
1953   return bRet;
1954 }
1955
1956 BOOL
1957 STDCALL
1958 NtGdiRectInRegion(HRGN  hRgn,
1959                        CONST LPRECT  unsaferc)
1960 {
1961   RECT rc;
1962
1963   if (!NT_SUCCESS(MmCopyFromCaller(&rc, unsaferc, sizeof(RECT))))
1964     {
1965       DPRINT1("NtGdiRectInRegion: bogus rc\n");
1966       return ERROR;
1967     }
1968
1969   return UnsafeIntRectInRegion(hRgn, &rc);
1970 }
1971
1972 BOOL
1973 STDCALL
1974 NtGdiSetRectRgn(HRGN  hRgn,
1975                      INT  LeftRect,
1976                      INT  TopRect,
1977                      INT  RightRect,
1978                      INT  BottomRect)
1979 {
1980   PROSRGNDATA rgn;
1981   PRECT firstRect;
1982
1983
1984
1985   if( !( rgn = RGNDATA_LockRgn(hRgn) ) )
1986           return 0; //per documentation
1987
1988   if (LeftRect > RightRect) { INT tmp = LeftRect; LeftRect = RightRect; RightRect = tmp; }
1989   if (TopRect > BottomRect) { INT tmp = TopRect; TopRect = BottomRect; BottomRect = tmp; }
1990
1991   if((LeftRect != RightRect) && (TopRect != BottomRect))
1992   {
1993     firstRect = (PRECT)rgn->Buffer;
1994         ASSERT( firstRect );
1995         firstRect->left = rgn->rdh.rcBound.left = LeftRect;
1996     firstRect->top = rgn->rdh.rcBound.top = TopRect;
1997     firstRect->right = rgn->rdh.rcBound.right = RightRect;
1998     firstRect->bottom = rgn->rdh.rcBound.bottom = BottomRect;
1999     rgn->rdh.nCount = 1;
2000     rgn->rdh.iType = SIMPLEREGION;
2001   } else
2002     EMPTY_REGION(rgn);
2003
2004   RGNDATA_UnlockRgn( hRgn );
2005   return TRUE;
2006 }
2007
2008 HRGN FASTCALL
2009 UnsafeIntUnionRectWithRgn(HRGN hDest, CONST PRECT Rect)
2010 {
2011   PROSRGNDATA pRgn;
2012
2013   pRgn = RGNDATA_LockRgn(hDest);
2014   if (NULL == pRgn)
2015     {
2016       SetLastWin32Error(ERROR_INVALID_HANDLE);
2017       return NULL;
2018     }
2019
2020   REGION_UnionRectWithRegion(Rect, pRgn);
2021   RGNDATA_UnlockRgn(hDest);
2022
2023   return hDest;
2024 }
2025
2026 HRGN STDCALL
2027 NtGdiUnionRectWithRgn(HRGN hDest, CONST PRECT UnsafeRect)
2028 {
2029   RECT SafeRect;
2030
2031   if (! NT_SUCCESS(MmCopyFromCaller(&SafeRect, UnsafeRect, sizeof(RECT))))
2032     {
2033       SetLastWin32Error(ERROR_INVALID_PARAMETER);
2034       return NULL;
2035     }
2036
2037   return UnsafeIntUnionRectWithRgn(hDest, &SafeRect);
2038 }
2039
2040 /*!
2041  * MSDN: GetRegionData, Return Values:
2042  *
2043  * "If the function succeeds and dwCount specifies an adequate number of bytes,
2044  * the return value is always dwCount. If dwCount is too small or the function
2045  * fails, the return value is 0. If lpRgnData is NULL, the return value is the
2046  * required number of bytes.
2047  *
2048  * If the function fails, the return value is zero."
2049  */
2050 DWORD STDCALL NtGdiGetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata)
2051 {
2052     DWORD size;
2053     PROSRGNDATA obj = RGNDATA_LockRgn( hrgn );
2054
2055     if(!obj)
2056                 return 0;
2057
2058     size = obj->rdh.nCount * sizeof(RECT);
2059     if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
2060     {
2061         RGNDATA_UnlockRgn( hrgn );
2062                 if (rgndata) /* buffer is too small, signal it by return 0 */
2063                     return 0;
2064                 else            /* user requested buffer size with rgndata NULL */
2065                     return size + sizeof(RGNDATAHEADER);
2066     }
2067
2068         //first we copy the header then we copy buffer
2069         if( !NT_SUCCESS( MmCopyToCaller( rgndata, obj, sizeof( RGNDATAHEADER )))){
2070                 RGNDATA_UnlockRgn( hrgn );
2071                 return 0;
2072         }
2073         if( !NT_SUCCESS( MmCopyToCaller( (char*)rgndata+sizeof( RGNDATAHEADER ), obj->Buffer, size ))){
2074                 RGNDATA_UnlockRgn( hrgn );
2075                 return 0;
2076         }
2077
2078     RGNDATA_UnlockRgn( hrgn );
2079     return size + sizeof(RGNDATAHEADER);
2080 }
2081 /* EOF */