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