xref: /reactos/win32ss/gdi/eng/gradient.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS kernel
4  * PURPOSE:           GDI Driver Gradient Functions
5  * FILE:              win32ss/gdi/eng/gradient.c
6  * PROGRAMER:         Thomas Weidenmueller
7  */
8 
9 #include <win32k.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 /* MACROS *********************************************************************/
15 
16 const LONG LINC[2] = {-1, 1};
17 
18 #define VERTEX(n) (pVertex + gt->n)
19 #define COMPAREVERTEX(a, b) ((a)->x == (b)->x && (a)->y == (b)->y)
20 
21 #define VCMPCLR(a,b,c,color) (a->color != b->color || a->color != c->color)
22 #define VCMPCLRS(a,b,c) \
23   !(!VCMPCLR(a,b,c,Red) || !VCMPCLR(a,b,c,Green) || !VCMPCLR(a,b,c,Blue))
24 
25 /* Horizontal/Vertical gradients */
26 #define HVINITCOL(Col, id) \
27   c[id] = v1->Col >> 8; \
28   dc[id] = abs((v2->Col >> 8) - c[id]); \
29   ec[id] = -(dy >> 1); \
30   ic[id] = LINC[(v2->Col >> 8) > c[id]]
31 #define HVSTEPCOL(id) \
32   ec[id] += dc[id]; \
33   while(ec[id] > 0) \
34   { \
35     c[id] += ic[id]; \
36     ec[id] -= dy; \
37   }
38 
39 /* FUNCTIONS ******************************************************************/
40 
41 BOOL
42 FASTCALL
IntEngGradientFillRect(IN SURFOBJ * psoDest,IN CLIPOBJ * pco,IN XLATEOBJ * pxlo,IN TRIVERTEX * pVertex,IN ULONG nVertex,IN PGRADIENT_RECT gRect,IN RECTL * prclExtents,IN POINTL * pptlDitherOrg,IN BOOL Horizontal)43 IntEngGradientFillRect(
44     IN SURFOBJ  *psoDest,
45     IN CLIPOBJ  *pco,
46     IN XLATEOBJ  *pxlo,
47     IN TRIVERTEX  *pVertex,
48     IN ULONG  nVertex,
49     IN PGRADIENT_RECT gRect,
50     IN RECTL  *prclExtents,
51     IN POINTL  *pptlDitherOrg,
52     IN BOOL Horizontal)
53 {
54     SURFOBJ *psoOutput;
55     TRIVERTEX *v1, *v2;
56     RECTL rcGradient, rcSG;
57     RECT_ENUM RectEnum;
58     BOOL EnumMore;
59     ULONG i;
60     POINTL Translate;
61     INTENG_ENTER_LEAVE EnterLeave;
62     LONG y, dy, c[3], dc[3], ec[3], ic[3];
63 
64     v1 = (pVertex + gRect->UpperLeft);
65     v2 = (pVertex + gRect->LowerRight);
66 
67     rcGradient.left = min(v1->x, v2->x);
68     rcGradient.right = max(v1->x, v2->x);
69     rcGradient.top = min(v1->y, v2->y);
70     rcGradient.bottom = max(v1->y, v2->y);
71     rcSG = rcGradient;
72     RECTL_vOffsetRect(&rcSG, pptlDitherOrg->x, pptlDitherOrg->y);
73 
74     if(Horizontal)
75     {
76         dy = abs(rcGradient.right - rcGradient.left);
77     }
78     else
79     {
80         dy = abs(rcGradient.bottom - rcGradient.top);
81     }
82 
83     if(!IntEngEnter(&EnterLeave, psoDest, &rcSG, FALSE, &Translate, &psoOutput))
84     {
85         return FALSE;
86     }
87 
88     if((v1->Red != v2->Red || v1->Green != v2->Green || v1->Blue != v2->Blue) && dy > 1)
89     {
90         CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
91         do
92         {
93             RECTL FillRect;
94             ULONG Color;
95 
96             if (Horizontal)
97             {
98                 EnumMore = CLIPOBJ_bEnum(pco, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
99                 for (i = 0; i < RectEnum.c && RectEnum.arcl[i].top <= rcSG.bottom; i++)
100                 {
101                     if (RECTL_bIntersectRect(&FillRect, &RectEnum.arcl[i], &rcSG))
102                     {
103                         HVINITCOL(Red, 0);
104                         HVINITCOL(Green, 1);
105                         HVINITCOL(Blue, 2);
106 
107                         for (y = rcSG.left; y < FillRect.right; y++)
108                         {
109                             if (y >= FillRect.left)
110                             {
111                                 Color = XLATEOBJ_iXlate(pxlo, RGB(c[0], c[1], c[2]));
112                                 DibFunctionsForBitmapFormat[psoOutput->iBitmapFormat].DIB_VLine(
113                                     psoOutput, y + Translate.x, FillRect.top + Translate.y, FillRect.bottom + Translate.y, Color);
114                             }
115                             HVSTEPCOL(0);
116                             HVSTEPCOL(1);
117                             HVSTEPCOL(2);
118                         }
119                     }
120                 }
121 
122                 continue;
123             }
124 
125             /* vertical */
126             EnumMore = CLIPOBJ_bEnum(pco, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
127             for (i = 0; i < RectEnum.c && RectEnum.arcl[i].top <= rcSG.bottom; i++)
128             {
129                 if (RECTL_bIntersectRect(&FillRect, &RectEnum.arcl[i], &rcSG))
130                 {
131                     HVINITCOL(Red, 0);
132                     HVINITCOL(Green, 1);
133                     HVINITCOL(Blue, 2);
134 
135                     for (y = rcSG.top; y < FillRect.bottom; y++)
136                     {
137                         if (y >= FillRect.top)
138                         {
139                             Color = XLATEOBJ_iXlate(pxlo, RGB(c[0], c[1], c[2]));
140                             DibFunctionsForBitmapFormat[psoOutput->iBitmapFormat].DIB_HLine(psoOutput,
141                                                                                             FillRect.left + Translate.x,
142                                                                                             FillRect.right + Translate.x,
143                                                                                             y + Translate.y,
144                                                                                             Color);
145                         }
146                         HVSTEPCOL(0);
147                         HVSTEPCOL(1);
148                         HVSTEPCOL(2);
149                     }
150                 }
151             }
152 
153         }
154         while (EnumMore);
155 
156         return IntEngLeave(&EnterLeave);
157     }
158 
159     /* rectangle has only one color, no calculation required */
160     CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
161     do
162     {
163         RECTL FillRect;
164         ULONG Color = XLATEOBJ_iXlate(pxlo, RGB(v1->Red >> 8, v1->Green >> 8, v1->Blue >> 8));
165 
166         EnumMore = CLIPOBJ_bEnum(pco, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
167         for (i = 0; i < RectEnum.c && RectEnum.arcl[i].top <= rcSG.bottom; i++)
168         {
169             if (RECTL_bIntersectRect(&FillRect, &RectEnum.arcl[i], &rcSG))
170             {
171                 for (; FillRect.top < FillRect.bottom; FillRect.top++)
172                 {
173                     DibFunctionsForBitmapFormat[psoOutput->iBitmapFormat].DIB_HLine(psoOutput,
174                                                                                     FillRect.left + Translate.x,
175                                                                                     FillRect.right + Translate.x,
176                                                                                     FillRect.top + Translate.y,
177                                                                                     Color);
178                 }
179             }
180         }
181     }
182     while (EnumMore);
183 
184     return IntEngLeave(&EnterLeave);
185 }
186 
187 /* Fill triangle with solid color */
188 #define S_FILLLINE(linefrom,lineto) \
189   if(sx[lineto] < sx[linefrom]) \
190     DibFunctionsForBitmapFormat[psoOutput->iBitmapFormat].DIB_HLine(psoOutput, max(sx[lineto], FillRect.left), min(sx[linefrom], FillRect.right), sy, Color); \
191   else \
192     DibFunctionsForBitmapFormat[psoOutput->iBitmapFormat].DIB_HLine(psoOutput, max(sx[linefrom], FillRect.left), min(sx[lineto], FillRect.right), sy, Color);
193 
194 #define S_DOLINE(a,b,line) \
195   ex[line] += dx[line]; \
196   while(ex[line] > 0 && x[line] != destx[line]) \
197   { \
198     x[line] += incx[line]; \
199     sx[line] += incx[line]; \
200     ex[line] -= dy[line]; \
201   }
202 
203 #define S_GOLINE(a,b,line) \
204   if(y >= a->y && y <= b->y) \
205   {
206 
207 #define S_ENDLINE(a,b,line) \
208   }
209 
210 #define S_INITLINE(a,b,line) \
211   x[line] = a->x; \
212   sx[line] =  a->x + pptlDitherOrg->x; \
213   dx[line] = abs(b->x - a->x); \
214   dy[line] = abs(b->y - a->y); \
215   incx[line] = LINC[b->x > a->x]; \
216   ex[line] = -(dy[line]>>1); \
217   destx[line] = b->x
218 
219 /* Fill triangle with gradient */
220 #define INITCOL(a,b,line,col,id) \
221   c[line][id] = a->col >> 8; \
222   dc[line][id] = abs((b->col >> 8) - c[line][id]); \
223   ec[line][id] = -(dy[line]>>1); \
224   ic[line][id] = LINC[(b->col >> 8) > c[line][id]]
225 
226 #define STEPCOL(a,b,line,col,id) \
227   ec[line][id] += dc[line][id]; \
228   while(ec[line][id] > 0) \
229   { \
230     c[line][id] += ic[line][id]; \
231     ec[line][id] -= dy[line]; \
232   }
233 
234 #define FINITCOL(linefrom,lineto,colid) \
235   gc[colid] = c[linefrom][colid]; \
236   gd[colid] = abs(c[lineto][colid] - gc[colid]); \
237   ge[colid] = -(gx >> 1); \
238   gi[colid] = LINC[c[lineto][colid] > gc[colid]]
239 
240 #define FDOCOL(linefrom,lineto,colid) \
241   ge[colid] += gd[colid]; \
242   while(ge[colid] > 0) \
243   { \
244     gc[colid] += gi[colid]; \
245     ge[colid] -= gx; \
246   }
247 
248 #define FILLLINE(linefrom,lineto) \
249   gx = abs(sx[lineto] - sx[linefrom]); \
250   gxi = LINC[sx[linefrom] < sx[lineto]]; \
251   FINITCOL(linefrom, lineto, 0); \
252   FINITCOL(linefrom, lineto, 1); \
253   FINITCOL(linefrom, lineto, 2); \
254   for(g = sx[linefrom]; g != sx[lineto]; g += gxi) \
255   { \
256     if(InY && g >= FillRect.left && g < FillRect.right) \
257     { \
258       Color = XLATEOBJ_iXlate(pxlo, RGB(gc[0], gc[1], gc[2])); \
259       DibFunctionsForBitmapFormat[psoOutput->iBitmapFormat].DIB_PutPixel(psoOutput, g, sy, Color); \
260     } \
261     FDOCOL(linefrom, lineto, 0); \
262     FDOCOL(linefrom, lineto, 1); \
263     FDOCOL(linefrom, lineto, 2); \
264   }
265 
266 #define DOLINE(a,b,line) \
267   STEPCOL(a, b, line, Red, 0); \
268   STEPCOL(a, b, line, Green, 1); \
269   STEPCOL(a, b, line, Blue, 2); \
270   ex[line] += dx[line]; \
271   while(ex[line] > 0 && x[line] != destx[line]) \
272   { \
273     x[line] += incx[line]; \
274     sx[line] += incx[line]; \
275     ex[line] -= dy[line]; \
276   }
277 
278 #define GOLINE(a,b,line) \
279   if(y >= a->y && y <= b->y) \
280   {
281 
282 #define ENDLINE(a,b,line) \
283   }
284 
285 #define INITLINE(a,b,line) \
286   x[line] = a->x; \
287   sx[line] =  a->x + pptlDitherOrg->x; \
288   dx[line] = abs(b->x - a->x); \
289   dy[line] = max(abs(b->y - a->y),1); \
290   incx[line] = LINC[b->x > a->x]; \
291   ex[line] = -(dy[line]>>1); \
292   destx[line] = b->x
293 
294 #define DOINIT(a, b, line) \
295   INITLINE(a, b, line); \
296   INITCOL(a, b, line, Red, 0); \
297   INITCOL(a, b, line, Green, 1); \
298   INITCOL(a, b, line, Blue, 2);
299 
300 #define SMALLER(a,b)     (a->y < b->y) || (a->y == b->y && a->x < b->x)
301 
302 #define SWAP(a,b,c)  c = a;\
303                      a = b;\
304                      b = c
305 
306 #define NLINES 3
307 
308 BOOL
309 FASTCALL
IntEngGradientFillTriangle(IN SURFOBJ * psoDest,IN CLIPOBJ * pco,IN XLATEOBJ * pxlo,IN TRIVERTEX * pVertex,IN ULONG nVertex,IN PGRADIENT_TRIANGLE gTriangle,IN RECTL * prclExtents,IN POINTL * pptlDitherOrg)310 IntEngGradientFillTriangle(
311     IN SURFOBJ  *psoDest,
312     IN CLIPOBJ  *pco,
313     IN XLATEOBJ  *pxlo,
314     IN TRIVERTEX  *pVertex,
315     IN ULONG  nVertex,
316     IN PGRADIENT_TRIANGLE gTriangle,
317     IN RECTL  *prclExtents,
318     IN POINTL  *pptlDitherOrg)
319 {
320     SURFOBJ *psoOutput;
321     PTRIVERTEX v1, v2, v3;
322     RECT_ENUM RectEnum;
323     BOOL EnumMore;
324     ULONG i;
325     POINTL Translate;
326     INTENG_ENTER_LEAVE EnterLeave;
327     RECTL FillRect = { 0, 0, 0, 0 };
328     ULONG Color;
329 
330     BOOL sx[NLINES];
331     LONG x[NLINES], dx[NLINES], dy[NLINES], incx[NLINES], ex[NLINES], destx[NLINES];
332     LONG c[NLINES][3], dc[NLINES][3], ec[NLINES][3], ic[NLINES][3]; /* colors on lines */
333     LONG g, gx, gxi, gc[3], gd[3], ge[3], gi[3]; /* colors in triangle */
334     LONG sy, y, bt;
335 
336     v1 = (pVertex + gTriangle->Vertex1);
337     v2 = (pVertex + gTriangle->Vertex2);
338     v3 = (pVertex + gTriangle->Vertex3);
339 
340     /* bubble sort */
341     if (SMALLER(v2, v1))
342     {
343         TRIVERTEX *t;
344         SWAP(v1, v2, t);
345     }
346 
347     if (SMALLER(v3, v2))
348     {
349         TRIVERTEX *t;
350         SWAP(v2, v3, t);
351         if (SMALLER(v2, v1))
352         {
353             SWAP(v1, v2, t);
354         }
355     }
356 
357     DPRINT("Triangle: (%i,%i) (%i,%i) (%i,%i)\n", v1->x, v1->y, v2->x, v2->y, v3->x, v3->y);
358     /* FIXME: commented out because of an endless loop - fix triangles first */
359     DPRINT("FIXME: IntEngGradientFillTriangle is broken\n");
360 
361     if (!IntEngEnter(&EnterLeave, psoDest, &FillRect, FALSE, &Translate, &psoOutput))
362     {
363         return FALSE;
364     }
365 
366     if (VCMPCLRS(v1, v2, v3))
367     {
368       CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
369       do
370       {
371         EnumMore = CLIPOBJ_bEnum(pco, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
372         for (i = 0; i < RectEnum.c && RectEnum.arcl[i].top <= prclExtents->bottom; i++)
373         {
374           if (RECTL_bIntersectRect(&FillRect, &RectEnum.arcl[i], prclExtents))
375           {
376             BOOL InY;
377 
378             DOINIT(v1, v3, 0);
379             DOINIT(v1, v2, 1);
380             DOINIT(v2, v3, 2);
381 
382             y = v1->y;
383             sy = v1->y + pptlDitherOrg->y;
384             bt = min(v3->y + pptlDitherOrg->y, FillRect.bottom);
385 
386             while (sy < bt)
387             {
388               InY = !(sy < FillRect.top || sy >= FillRect.bottom);
389               GOLINE(v1, v3, 0);
390               DOLINE(v1, v3, 0);
391               ENDLINE(v1, v3, 0);
392 
393               GOLINE(v1, v2, 1);
394               DOLINE(v1, v2, 1);
395               FILLLINE(0, 1);
396               ENDLINE(v1, v2, 1);
397 
398               GOLINE(v2, v3, 2);
399               DOLINE(v2, v3, 2);
400               FILLLINE(0, 2);
401               ENDLINE(23, v3, 2);
402 
403               y++;
404               sy++;
405             }
406           }
407         }
408       } while (EnumMore);
409 
410       return IntEngLeave(&EnterLeave);
411     }
412 
413     /* fill triangle with one solid color */
414 
415     Color = XLATEOBJ_iXlate(pxlo, RGB(v1->Red >> 8, v1->Green >> 8, v1->Blue >> 8));
416     CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
417     do
418     {
419       EnumMore = CLIPOBJ_bEnum(pco, (ULONG) sizeof(RectEnum), (PVOID) &RectEnum);
420       for (i = 0; i < RectEnum.c && RectEnum.arcl[i].top <= prclExtents->bottom; i++)
421       {
422         if (RECTL_bIntersectRect(&FillRect, &RectEnum.arcl[i], prclExtents))
423         {
424           S_INITLINE(v1, v3, 0);
425           S_INITLINE(v1, v2, 1);
426           S_INITLINE(v2, v3, 2);
427 
428           y = v1->y;
429           sy = v1->y + pptlDitherOrg->y;
430           bt = min(v3->y + pptlDitherOrg->y, FillRect.bottom);
431 
432           while (sy < bt)
433           {
434             S_GOLINE(v1, v3, 0);
435             S_DOLINE(v1, v3, 0);
436             S_ENDLINE(v1, v3, 0);
437 
438             S_GOLINE(v1, v2, 1);
439             S_DOLINE(v1, v2, 1);
440             S_FILLLINE(0, 1);
441             S_ENDLINE(v1, v2, 1);
442 
443             S_GOLINE(v2, v3, 2);
444             S_DOLINE(v2, v3, 2);
445             S_FILLLINE(0, 2);
446             S_ENDLINE(23, v3, 2);
447 
448             y++;
449             sy++;
450           }
451         }
452       }
453     } while (EnumMore);
454 
455     return IntEngLeave(&EnterLeave);
456 }
457 
458 
459 static
460 BOOL
IntEngIsNULLTriangle(TRIVERTEX * pVertex,GRADIENT_TRIANGLE * gt)461 IntEngIsNULLTriangle(TRIVERTEX  *pVertex, GRADIENT_TRIANGLE *gt)
462 {
463     if(COMPAREVERTEX(VERTEX(Vertex1), VERTEX(Vertex2)))
464         return TRUE;
465     if(COMPAREVERTEX(VERTEX(Vertex1), VERTEX(Vertex3)))
466         return TRUE;
467     if(COMPAREVERTEX(VERTEX(Vertex2), VERTEX(Vertex3)))
468         return TRUE;
469     return FALSE;
470 }
471 
472 
473 BOOL
474 APIENTRY
EngGradientFill(_Inout_ SURFOBJ * psoDest,_In_ CLIPOBJ * pco,_In_opt_ XLATEOBJ * pxlo,_In_ TRIVERTEX * pVertex,_In_ ULONG nVertex,_In_ PVOID pMesh,_In_ ULONG nMesh,_In_ RECTL * prclExtents,_In_ POINTL * pptlDitherOrg,_In_ ULONG ulMode)475 EngGradientFill(
476     _Inout_ SURFOBJ *psoDest,
477     _In_ CLIPOBJ *pco,
478     _In_opt_ XLATEOBJ *pxlo,
479     _In_ TRIVERTEX *pVertex,
480     _In_ ULONG nVertex,
481     _In_ PVOID pMesh,
482     _In_ ULONG nMesh,
483     _In_ RECTL *prclExtents,
484     _In_ POINTL *pptlDitherOrg,
485     _In_ ULONG ulMode)
486 {
487     ULONG i;
488     BOOL ret = FALSE;
489 
490     /* Check for NULL clip object */
491     if (pco == NULL)
492     {
493         /* Use the trivial one instead */
494         pco = (CLIPOBJ *)&gxcoTrivial;//.coClient;
495     }
496 
497     switch(ulMode)
498     {
499         case GRADIENT_FILL_RECT_H:
500         case GRADIENT_FILL_RECT_V:
501         {
502             PGRADIENT_RECT gr = (PGRADIENT_RECT)pMesh;
503             for (i = 0; i < nMesh; i++, gr++)
504             {
505                 if (!IntEngGradientFillRect(psoDest,
506                                             pco,
507                                             pxlo,
508                                             pVertex,
509                                             nVertex,
510                                             gr,
511                                             prclExtents,
512                                             pptlDitherOrg,
513                                             (ulMode == GRADIENT_FILL_RECT_H)))
514                 {
515                     break;
516                 }
517             }
518             ret = TRUE;
519             break;
520         }
521         case GRADIENT_FILL_TRIANGLE:
522         {
523             PGRADIENT_TRIANGLE gt = (PGRADIENT_TRIANGLE)pMesh;
524             for (i = 0; i < nMesh; i++, gt++)
525             {
526                 if (IntEngIsNULLTriangle(pVertex, gt))
527                 {
528                     /* skip empty triangles */
529                     continue;
530                 }
531                 if (!IntEngGradientFillTriangle(psoDest,
532                                                 pco,
533                                                 pxlo,
534                                                 pVertex,
535                                                 nVertex,
536                                                 gt,
537                                                 prclExtents,
538                                                 pptlDitherOrg))
539                 {
540                     break;
541                 }
542             }
543             ret = TRUE;
544             break;
545         }
546     }
547 
548     return ret;
549 }
550 
551 BOOL
552 APIENTRY
IntEngGradientFill(IN SURFOBJ * psoDest,IN CLIPOBJ * pco,IN XLATEOBJ * pxlo,IN TRIVERTEX * pVertex,IN ULONG nVertex,IN PVOID pMesh,IN ULONG nMesh,IN RECTL * prclExtents,IN POINTL * pptlDitherOrg,IN ULONG ulMode)553 IntEngGradientFill(
554     IN SURFOBJ *psoDest,
555     IN CLIPOBJ *pco,
556     IN XLATEOBJ *pxlo,
557     IN TRIVERTEX *pVertex,
558     IN ULONG nVertex,
559     IN PVOID pMesh,
560     IN ULONG nMesh,
561     IN RECTL *prclExtents,
562     IN POINTL *pptlDitherOrg,
563     IN ULONG ulMode)
564 {
565     BOOL Ret;
566     SURFACE *psurf;
567     ASSERT(psoDest);
568 
569     psurf = CONTAINING_RECORD(psoDest, SURFACE, SurfObj);
570     ASSERT(psurf);
571 
572     if (psurf->flags & HOOK_GRADIENTFILL)
573     {
574         Ret = GDIDEVFUNCS(psoDest).GradientFill(psoDest,
575                                                 pco,
576                                                 pxlo,
577                                                 pVertex,
578                                                 nVertex,
579                                                 pMesh,
580                                                 nMesh,
581                                                 prclExtents,
582                                                 pptlDitherOrg,
583                                                 ulMode);
584     }
585     else
586     {
587         Ret = EngGradientFill(psoDest,
588                               pco,
589                               pxlo,
590                               pVertex,
591                               nVertex,
592                               pMesh,
593                               nMesh,
594                               prclExtents,
595                               pptlDitherOrg,
596                               ulMode);
597     }
598 
599     return Ret;
600 }
601