d3drm: Fix wrong condition.
[wine] / dlls / gdi32 / dibdrv / graphics.c
1 /*
2  * DIB driver graphics operations.
3  *
4  * Copyright 2011 Huw Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <assert.h>
22 #include "gdi_private.h"
23 #include "dibdrv.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(dib);
28
29 static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
30 {
31     struct clipped_rects clipped_rects;
32     BOOL ret;
33
34     if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
35     ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
36                         GetROP2( pdev->dev.hdc ));
37     free_clipped_rects( &clipped_rects );
38     return ret;
39 }
40
41 /* paint a region with the brush (note: the region can be modified) */
42 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
43 {
44     if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
45     return brush_rect( pdev, &pdev->brush, NULL, region );
46 }
47
48 /* paint a region with the pen (note: the region can be modified) */
49 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
50 {
51     if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
52     return brush_rect( pdev, &pdev->pen_brush, NULL, region );
53 }
54
55 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
56 {
57     RECT rect;
58
59     rect.left   = left;
60     rect.top    = top;
61     rect.right  = right;
62     rect.bottom = bottom;
63     if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
64     {
65         /* shift the rectangle so that the right border is included after mirroring */
66         /* it would be more correct to do this after LPtoDP but that's not what Windows does */
67         rect.left--;
68         rect.right--;
69     }
70     LPtoDP( hdc, (POINT *)&rect, 2 );
71     if (rect.left > rect.right)
72     {
73         int tmp = rect.left;
74         rect.left = rect.right;
75         rect.right = tmp;
76     }
77     if (rect.top > rect.bottom)
78     {
79         int tmp = rect.top;
80         rect.top = rect.bottom;
81         rect.bottom = tmp;
82     }
83     return rect;
84 }
85
86 static BOOL get_pen_device_rect( dibdrv_physdev *dev, RECT *rect, int left, int top, int right, int bottom )
87 {
88     *rect = get_device_rect( dev->dev.hdc, left, top, right, bottom, TRUE );
89     if (rect->left == rect->right || rect->top == rect->bottom) return FALSE;
90
91     if (dev->pen_style == PS_INSIDEFRAME)
92     {
93         rect->left   += dev->pen_width / 2;
94         rect->top    += dev->pen_width / 2;
95         rect->right  -= (dev->pen_width - 1) / 2;
96         rect->bottom -= (dev->pen_width - 1) / 2;
97     }
98     return TRUE;
99 }
100
101 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
102 {
103     const WINEREGION *region;
104     RECT bounds, rect;
105     int width = 0;
106
107     if (!dev->bounds) return;
108     reset_bounds( &bounds );
109
110     if (dev->pen_uses_region)
111     {
112         /* Windows uses some heuristics to estimate the distance from the point that will be painted */
113         width = dev->pen_width + 2;
114         if (dev->pen_join == PS_JOIN_MITER)
115         {
116             width *= 5;
117             if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
118         }
119         else
120         {
121             if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4;
122             else width = (width + 1) / 2;
123         }
124
125         /* in case the heuristics are wrong, add the actual region too */
126         if ((region = get_wine_region( rgn )))
127         {
128             add_bounds_rect( &bounds, &region->extents );
129             release_wine_region( rgn );
130         }
131     }
132
133     while (count-- > 0)
134     {
135         rect.left   = points->x - width;
136         rect.top    = points->y - width;
137         rect.right  = points->x + width + 1;
138         rect.bottom = points->y + width + 1;
139         add_bounds_rect( &bounds, &rect );
140         points++;
141     }
142
143     add_clipped_bounds( dev, &bounds, dev->clip );
144 }
145
146 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
147 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
148 static int ellipse_first_quadrant( int width, int height, POINT *data )
149 {
150     const int a = width - 1;
151     const int b = height - 1;
152     const INT64 asq = (INT64)8 * a * a;
153     const INT64 bsq = (INT64)8 * b * b;
154     INT64 dx  = (INT64)4 * b * b * (1 - a);
155     INT64 dy  = (INT64)4 * a * a * (1 + (b % 2));
156     INT64 err = dx + dy + a * a * (b % 2);
157     int pos = 0;
158     POINT pt;
159
160     pt.x = a;
161     pt.y = height / 2;
162
163     /* based on an algorithm by Alois Zingl */
164
165     while (pt.x >= width / 2)
166     {
167         INT64 e2 = 2 * err;
168         data[pos++] = pt;
169         if (e2 >= dx)
170         {
171             pt.x--;
172             err += dx += bsq;
173         }
174         if (e2 <= dy)
175         {
176             pt.y++;
177             err += dy += asq;
178         }
179     }
180     return pos;
181 }
182
183 static int find_intersection( const POINT *points, int x, int y, int count )
184 {
185     int i;
186
187     if (y >= 0)
188     {
189         if (x >= 0)  /* first quadrant */
190         {
191             for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
192             return i;
193         }
194         /* second quadrant */
195         for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break;
196         return 2 * count - i;
197     }
198     if (x >= 0)  /* fourth quadrant */
199     {
200         for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break;
201         return 4 * count - i;
202     }
203     /* third quadrant */
204     for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break;
205     return 2 * count + i;
206 }
207
208 static int get_arc_points( PHYSDEV dev, const RECT *rect, POINT start, POINT end, POINT *points )
209 {
210     int i, pos, count, start_pos, end_pos;
211     int width = rect->right - rect->left;
212     int height = rect->bottom - rect->top;
213
214     count = ellipse_first_quadrant( width, height, points );
215     for (i = 0; i < count; i++)
216     {
217         points[i].x -= width / 2;
218         points[i].y -= height / 2;
219     }
220     if (GetArcDirection( dev->hdc ) != AD_CLOCKWISE)
221     {
222         start.y = -start.y;
223         end.y = -end.y;
224     }
225     start_pos = find_intersection( points, start.x, start.y, count );
226     end_pos = find_intersection( points, end.x, end.y, count );
227     if (end_pos <= start_pos) end_pos += 4 * count;
228
229     pos = count;
230     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
231     {
232         for (i = start_pos; i < end_pos; i++, pos++)
233         {
234             switch ((i / count) % 4)
235             {
236             case 0:
237                 points[pos].x = rect->left + width/2 + points[i % count].x;
238                 points[pos].y = rect->top + height/2 + points[i % count].y;
239                 break;
240             case 1:
241                 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
242                 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
243                 break;
244             case 2:
245                 points[pos].x = rect->left + width/2 - points[i % count].x;
246                 points[pos].y = rect->top + height/2 - points[i % count].y;
247                 break;
248             case 3:
249                 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
250                 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
251                 break;
252             }
253         }
254     }
255     else
256     {
257         for (i = start_pos; i < end_pos; i++, pos++)
258         {
259             switch ((i / count) % 4)
260             {
261             case 0:
262                 points[pos].x = rect->left + width/2 + points[i % count].x;
263                 points[pos].y = rect->top + height/2 - points[i % count].y;
264                 break;
265             case 1:
266                 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
267                 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
268                 break;
269             case 2:
270                 points[pos].x = rect->left + width/2 - points[i % count].x;
271                 points[pos].y = rect->top + height/2 + points[i % count].y;
272                 break;
273             case 3:
274                 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
275                 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
276                 break;
277             }
278         }
279     }
280
281     memmove( points, points + count, (pos - count) * sizeof(POINT) );
282     return pos - count;
283 }
284
285 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
286 static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
287                       INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines )
288 {
289     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
290     RECT rect;
291     POINT pt[2], *points;
292     int width, height, count;
293     BOOL ret = TRUE;
294     HRGN outline = 0, interior = 0;
295
296     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
297
298     width = rect.right - rect.left;
299     height = rect.bottom - rect.top;
300
301     pt[0].x = start_x;
302     pt[0].y = start_y;
303     pt[1].x = end_x;
304     pt[1].y = end_y;
305     LPtoDP( dev->hdc, pt, 2 );
306     /* make them relative to the ellipse center */
307     pt[0].x -= left + width / 2;
308     pt[0].y -= top + height / 2;
309     pt[1].x -= left + width / 2;
310     pt[1].y -= top + height / 2;
311
312     points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) );
313     if (!points) return FALSE;
314
315     if (extra_lines == -1)
316     {
317         GetCurrentPositionEx( dev->hdc, points );
318         LPtoDP( dev->hdc, points, 1 );
319         count = 1 + get_arc_points( dev, &rect, pt[0], pt[1], points + 1 );
320     }
321     else count = get_arc_points( dev, &rect, pt[0], pt[1], points );
322
323     if (extra_lines == 2)
324     {
325         points[count].x = rect.left + width / 2;
326         points[count].y = rect.top + height / 2;
327         count++;
328     }
329     if (count < 2)
330     {
331         HeapFree( GetProcessHeap(), 0, points );
332         return TRUE;
333     }
334
335     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
336     {
337         HeapFree( GetProcessHeap(), 0, points );
338         return FALSE;
339     }
340
341     if (pdev->brush.style != BS_NULL && extra_lines > 0 &&
342         !(interior = CreatePolygonRgn( points, count, WINDING )))
343     {
344         HeapFree( GetProcessHeap(), 0, points );
345         if (outline) DeleteObject( outline );
346         return FALSE;
347     }
348
349     /* if not using a region, paint the interior first so the outline can overlap it */
350     if (interior && !outline)
351     {
352         ret = brush_region( pdev, interior );
353         DeleteObject( interior );
354         interior = 0;
355     }
356
357     reset_dash_origin( pdev );
358     pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
359     add_pen_lines_bounds( pdev, count, points, outline );
360
361     if (interior)
362     {
363         CombineRgn( interior, interior, outline, RGN_DIFF );
364         ret = brush_region( pdev, interior );
365         DeleteObject( interior );
366     }
367     if (outline)
368     {
369         if (ret) ret = pen_region( pdev, outline );
370         DeleteObject( outline );
371     }
372     HeapFree( GetProcessHeap(), 0, points );
373     return ret;
374 }
375
376 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
377    black bkgnd.  [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
378 static const BYTE ramp[17] =
379 {
380     0,    0x4d, 0x68, 0x7c,
381     0x8c, 0x9a, 0xa7, 0xb2,
382     0xbd, 0xc7, 0xd0, 0xd9,
383     0xe1, 0xe9, 0xf0, 0xf8,
384     0xff
385 };
386
387 /* For a give text-color component and a glyph level, calculate the
388    range of dst intensities, the min/max corresponding to 0/0xff bkgnd
389    components respectively.
390
391    The minimum is a linear interpolation between 0 and the value in
392    the ramp table.
393
394    The maximum is a linear interpolation between the value from the
395    ramp table read in reverse and 0xff.
396
397    To find the resulting pixel intensity, we note that if the text and
398    bkgnd intensities are the same then the result must be that
399    intensity.  Otherwise we linearly interpolate between either the
400    min or the max value and this intermediate value depending on which
401    side of the inequality we lie.
402 */
403
404 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
405 {
406     *min_comp = (ramp[aa] * text_comp) / 0xff;
407     *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
408 }
409
410 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
411 {
412     int i;
413
414     for (i = 0; i < 17; i++)
415     {
416         get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
417         get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
418         get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
419     }
420 }
421
422 /**********************************************************************
423  *                 get_text_bkgnd_masks
424  *
425  * See the comment above get_pen_bkgnd_masks
426  */
427 static inline void get_text_bkgnd_masks( dibdrv_physdev *pdev, rop_mask *mask )
428 {
429     COLORREF bg = GetBkColor( pdev->dev.hdc );
430
431     mask->and = 0;
432
433     if (pdev->dib.bit_count != 1)
434         mask->xor = get_pixel_color( pdev, bg, FALSE );
435     else
436     {
437         COLORREF fg = GetTextColor( pdev->dev.hdc );
438         mask->xor = get_pixel_color( pdev, fg, TRUE );
439         if (fg != bg) mask->xor = ~mask->xor;
440     }
441 }
442
443 static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics,
444                         const dib_info *glyph_dib, DWORD text_color,
445                         const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
446                         RECT *bounds )
447 {
448     int i;
449     RECT rect, clipped_rect;
450     POINT src_origin;
451
452     rect.left   = x          + metrics->gmptGlyphOrigin.x;
453     rect.top    = y          - metrics->gmptGlyphOrigin.y;
454     rect.right  = rect.left  + metrics->gmBlackBoxX;
455     rect.bottom = rect.top   + metrics->gmBlackBoxY;
456     if (bounds) add_bounds_rect( bounds, &rect );
457
458     for (i = 0; i < clipped_rects->count; i++)
459     {
460         if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
461         {
462             src_origin.x = clipped_rect.left - rect.left;
463             src_origin.y = clipped_rect.top  - rect.top;
464
465             dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
466                                     text_color, ranges );
467         }
468     }
469 }
470
471 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
472 static const int padding[4] = {0, 3, 2, 1};
473
474 /***********************************************************************
475  *         get_glyph_bitmap
476  *
477  * Retrieve a 17-level bitmap for the appropriate glyph.
478  *
479  * For non-antialiased bitmaps convert them to the 17-level format
480  * using only values 0 or 16.
481  */
482 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
483                                dib_info *glyph_dib )
484 {
485     UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
486     static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
487     UINT indices[3] = {0, 0, 0x20};
488     int i, x, y;
489     DWORD ret, size;
490     BYTE *buf, *dst, *src;
491     int pad;
492
493     glyph_dib->bits.ptr = NULL;
494     glyph_dib->bits.is_copy = FALSE;
495     glyph_dib->bits.free = free_heap_bits;
496     glyph_dib->bits.param = NULL;
497
498     indices[0] = index;
499
500     for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
501     {
502         index = indices[i];
503         ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
504         if (ret != GDI_ERROR) break;
505     }
506
507     if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
508     if (!ret) return ERROR_SUCCESS; /* empty glyph */
509
510     /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
511     glyph_dib->bit_count   = 8;
512     glyph_dib->width       = metrics->gmBlackBoxX;
513     glyph_dib->height      = metrics->gmBlackBoxY;
514     glyph_dib->rect.left   = 0;
515     glyph_dib->rect.top    = 0;
516     glyph_dib->rect.right  = metrics->gmBlackBoxX;
517     glyph_dib->rect.bottom = metrics->gmBlackBoxY;
518     glyph_dib->stride      = get_dib_stride( metrics->gmBlackBoxX, glyph_dib->bit_count );
519
520     pad = padding[ metrics->gmBlackBoxX % 4 ];
521     size = metrics->gmBlackBoxY * glyph_dib->stride;
522
523     buf = HeapAlloc( GetProcessHeap(), 0, size );
524     if (!buf) return ERROR_OUTOFMEMORY;
525
526     ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
527     if (ret == GDI_ERROR)
528     {
529         HeapFree( GetProcessHeap(), 0, buf );
530         return ERROR_NOT_FOUND;
531     }
532
533     if (aa_flags == GGO_BITMAP)
534     {
535         for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
536         {
537             src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
538             dst = buf + y * glyph_dib->stride;
539
540             if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
541
542             for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
543                 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
544         }
545     }
546     else if (pad)
547     {
548         for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += glyph_dib->stride)
549             memset( dst + metrics->gmBlackBoxX, 0, pad );
550     }
551
552     glyph_dib->bits.ptr = buf;
553     return ERROR_SUCCESS;
554 }
555
556 static void render_string( HDC hdc, dib_info *dib, INT x, INT y, UINT flags, UINT aa_flags,
557                            const WCHAR *str, UINT count, const INT *dx, DWORD text_color,
558                            const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
559                            RECT *bounds )
560 {
561     UINT i;
562     DWORD err;
563     GLYPHMETRICS metrics;
564     dib_info glyph_dib;
565
566     for (i = 0; i < count; i++)
567     {
568         err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &glyph_dib );
569         if (err) continue;
570
571         if (glyph_dib.bits.ptr)
572             draw_glyph( dib, x, y, &metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
573
574         free_dib_info( &glyph_dib );
575
576         if (dx)
577         {
578             if (flags & ETO_PDY)
579             {
580                 x += dx[ i * 2 ];
581                 y += dx[ i * 2 + 1];
582             }
583             else
584                 x += dx[ i ];
585         }
586         else
587         {
588             x += metrics.gmCellIncX;
589             y += metrics.gmCellIncY;
590         }
591     }
592 }
593
594 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
595                                 struct bitblt_coords *src, INT x, INT y, UINT flags,
596                                 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
597 {
598     dib_info dib;
599     BOOL got_pixel;
600     COLORREF fg, bg;
601     DWORD fg_pixel, bg_pixel;
602     struct intensity_range glyph_intensities[17];
603     struct clipped_rects visrect;
604
605     assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
606
607     init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
608
609     visrect.count = 1;
610     visrect.rects = &src->visrect;
611
612     fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
613     if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
614
615     get_aa_ranges( fg, glyph_intensities );
616
617     if (flags & ETO_OPAQUE)
618     {
619         rop_mask bkgnd_color;
620
621         bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
622         if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
623
624         bkgnd_color.and = 0;
625         bkgnd_color.xor = bg_pixel;
626         dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
627     }
628
629     render_string( hdc, &dib, x, y, flags, aa_flags, str, count, dx,
630                    fg_pixel, glyph_intensities, &visrect, NULL );
631     return TRUE;
632 }
633
634 /***********************************************************************
635  *           dibdrv_ExtTextOut
636  */
637 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
638                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
639 {
640     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
641     struct clipped_rects clipped_rects;
642     UINT aa_flags;
643     RECT bounds;
644     DWORD text_color;
645     struct intensity_range ranges[17];
646
647     init_clipped_rects( &clipped_rects );
648     reset_bounds( &bounds );
649
650     if (flags & ETO_OPAQUE)
651     {
652         rop_mask bkgnd_color;
653         get_text_bkgnd_masks( pdev, &bkgnd_color );
654         add_bounds_rect( &bounds, rect );
655         get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
656         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
657                                       bkgnd_color.and, bkgnd_color.xor );
658     }
659
660     if (count == 0) goto done;
661
662     if (flags & ETO_CLIPPED)
663     {
664         if (!(flags & ETO_OPAQUE))  /* otherwise we have done it already */
665             get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
666     }
667     else
668     {
669         free_clipped_rects( &clipped_rects );
670         get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
671     }
672     if (!clipped_rects.count) goto done;
673
674     text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
675     get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
676
677     aa_flags = get_font_aa_flags( dev->hdc );
678
679     render_string( dev->hdc, &pdev->dib, x, y, flags, aa_flags, str, count, dx,
680                    text_color, ranges, &clipped_rects, &bounds );
681
682 done:
683     add_clipped_bounds( pdev, &bounds, pdev->clip );
684     free_clipped_rects( &clipped_rects );
685     return TRUE;
686 }
687
688 /***********************************************************************
689  *           dibdrv_Arc
690  */
691 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
692                  INT start_x, INT start_y, INT end_x, INT end_y )
693 {
694     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
695 }
696
697 /***********************************************************************
698  *           dibdrv_ArcTo
699  */
700 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
701                    INT start_x, INT start_y, INT end_x, INT end_y )
702 {
703     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
704 }
705
706 /***********************************************************************
707  *           dibdrv_Chord
708  */
709 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
710                    INT start_x, INT start_y, INT end_x, INT end_y )
711 {
712     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
713 }
714
715 /***********************************************************************
716  *           dibdrv_Ellipse
717  */
718 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
719 {
720     return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
721 }
722
723 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
724 {
725     /* the clip rgn stops the flooding */
726     if (clip && !PtInRegion( clip, x, y )) return FALSE;
727
728     if (type == FLOODFILLBORDER)
729         return dib->funcs->get_pixel( dib, x, y ) != pixel;
730     else
731         return dib->funcs->get_pixel( dib, x, y ) == pixel;
732 }
733
734 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
735
736 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
737 {
738     RECT next;
739
740     next.top = row->top + offset;
741     next.bottom = next.top + 1;
742     next.left = next.right = row->left;
743     while (next.right < row->right)
744     {
745         if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
746         else
747         {
748             if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
749                 fill_row( dib, clip, &next, pixel, type, rgn );
750             next.left = ++next.right;
751         }
752     }
753     if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
754         fill_row( dib, clip, &next, pixel, type, rgn );
755 }
756
757 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
758 {
759     while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
760     while (row->right < dib->rect.right - dib->rect.left &&
761            is_interior( dib, clip, row->right, row->top, pixel, type))
762         row->right++;
763
764     add_rect_to_region( rgn, row );
765
766     if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
767     if (row->top < dib->rect.bottom - dib->rect.top - 1)
768         do_next_row( dib, clip, row, 1, pixel, type, rgn );
769 }
770
771 /***********************************************************************
772  *           dibdrv_ExtFloodFill
773  */
774 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
775 {
776     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
777     DWORD pixel = get_pixel_color( pdev, color, FALSE );
778     RECT row;
779     HRGN rgn;
780
781     TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
782
783     if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
784
785     if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
786     row.left = x;
787     row.right = x + 1;
788     row.top = y;
789     row.bottom = y + 1;
790
791     fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
792
793     add_clipped_bounds( pdev, NULL, rgn );
794     brush_region( pdev, rgn );
795
796     DeleteObject( rgn );
797     return TRUE;
798 }
799
800 /***********************************************************************
801  *           dibdrv_GetNearestColor
802  */
803 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
804 {
805     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
806     DWORD pixel;
807
808     TRACE( "(%p, %08x)\n", dev, color );
809
810     pixel = get_pixel_color( pdev, color, FALSE );
811     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
812 }
813
814 /***********************************************************************
815  *           dibdrv_GetPixel
816  */
817 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
818 {
819     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
820     POINT pt;
821     DWORD pixel;
822
823     TRACE( "(%p, %d, %d)\n", dev, x, y );
824
825     pt.x = x;
826     pt.y = y;
827     LPtoDP( dev->hdc, &pt, 1 );
828
829     if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
830         pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
831         return CLR_INVALID;
832
833     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
834     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
835 }
836
837 /***********************************************************************
838  *           dibdrv_LineTo
839  */
840 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
841 {
842     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
843     POINT pts[2];
844     HRGN region = 0;
845     BOOL ret;
846
847     GetCurrentPositionEx(dev->hdc, pts);
848     pts[1].x = x;
849     pts[1].y = y;
850
851     LPtoDP(dev->hdc, pts, 2);
852
853     if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
854
855     reset_dash_origin(pdev);
856
857     ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
858     add_pen_lines_bounds( pdev, 2, pts, region );
859
860     if (region)
861     {
862         ret = pen_region( pdev, region );
863         DeleteObject( region );
864     }
865     return ret;
866 }
867
868 /***********************************************************************
869  *           get_rop2_from_rop
870  *
871  * Returns the binary rop that is equivalent to the provided ternary rop
872  * if the src bits are ignored.
873  */
874 static inline INT get_rop2_from_rop(INT rop)
875 {
876     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
877 }
878
879 /***********************************************************************
880  *           dibdrv_PatBlt
881  */
882 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
883 {
884     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
885     dib_brush *brush = &pdev->brush;
886     int rop2 = get_rop2_from_rop( rop );
887     struct clipped_rects clipped_rects;
888     DWORD and = 0, xor = 0;
889     BOOL ret = TRUE;
890
891     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
892
893     add_clipped_bounds( pdev, &dst->visrect, 0 );
894     if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
895
896     switch (rop2)  /* shortcuts for rops that don't involve the brush */
897     {
898     case R2_NOT:   and = ~0u;
899         /* fall through */
900     case R2_WHITE: xor = ~0u;
901         /* fall through */
902     case R2_BLACK:
903         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
904         /* fall through */
905     case R2_NOP:
906         break;
907     default:
908         ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
909         break;
910     }
911     free_clipped_rects( &clipped_rects );
912     return ret;
913 }
914
915 /***********************************************************************
916  *           dibdrv_PaintRgn
917  */
918 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
919 {
920     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
921     const WINEREGION *region;
922     int i;
923     RECT rect, bounds;
924
925     TRACE("%p, %p\n", dev, rgn);
926
927     reset_bounds( &bounds );
928
929     region = get_wine_region( rgn );
930     if(!region) return FALSE;
931
932     for(i = 0; i < region->numRects; i++)
933     {
934         rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
935                                 region->rects[i].right, region->rects[i].bottom, FALSE );
936         add_bounds_rect( &bounds, &rect );
937         brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
938     }
939
940     release_wine_region( rgn );
941     add_clipped_bounds( pdev, &bounds, pdev->clip );
942     return TRUE;
943 }
944
945 /***********************************************************************
946  *           dibdrv_PolyPolygon
947  */
948 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
949 {
950     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
951     DWORD total, i, pos;
952     BOOL ret = TRUE;
953     POINT *points;
954     HRGN outline = 0, interior = 0;
955
956     for (i = total = 0; i < polygons; i++)
957     {
958         if (counts[i] < 2) return FALSE;
959         total += counts[i];
960     }
961
962     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
963     if (!points) return FALSE;
964     memcpy( points, pt, total * sizeof(*pt) );
965     LPtoDP( dev->hdc, points, total );
966
967     if (pdev->brush.style != BS_NULL &&
968         !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
969     {
970         HeapFree( GetProcessHeap(), 0, points );
971         return FALSE;
972     }
973
974     if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
975
976     /* if not using a region, paint the interior first so the outline can overlap it */
977     if (interior && !outline)
978     {
979         ret = brush_region( pdev, interior );
980         DeleteObject( interior );
981         interior = 0;
982     }
983
984     for (i = pos = 0; i < polygons; i++)
985     {
986         reset_dash_origin( pdev );
987         pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
988         pos += counts[i];
989     }
990     add_pen_lines_bounds( pdev, total, points, outline );
991
992     if (interior)
993     {
994         CombineRgn( interior, interior, outline, RGN_DIFF );
995         ret = brush_region( pdev, interior );
996         DeleteObject( interior );
997     }
998     if (outline)
999     {
1000         if (ret) ret = pen_region( pdev, outline );
1001         DeleteObject( outline );
1002     }
1003     HeapFree( GetProcessHeap(), 0, points );
1004     return ret;
1005 }
1006
1007 /***********************************************************************
1008  *           dibdrv_PolyPolyline
1009  */
1010 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1011 {
1012     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1013     DWORD total, pos, i;
1014     POINT *points;
1015     BOOL ret = TRUE;
1016     HRGN outline = 0;
1017
1018     for (i = total = 0; i < polylines; i++)
1019     {
1020         if (counts[i] < 2) return FALSE;
1021         total += counts[i];
1022     }
1023
1024     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1025     if (!points) return FALSE;
1026     memcpy( points, pt, total * sizeof(*pt) );
1027     LPtoDP( dev->hdc, points, total );
1028
1029     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1030     {
1031         HeapFree( GetProcessHeap(), 0, points );
1032         return FALSE;
1033     }
1034
1035     for (i = pos = 0; i < polylines; i++)
1036     {
1037         reset_dash_origin( pdev );
1038         pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1039         pos += counts[i];
1040     }
1041     add_pen_lines_bounds( pdev, total, points, outline );
1042
1043     if (outline)
1044     {
1045         ret = pen_region( pdev, outline );
1046         DeleteObject( outline );
1047     }
1048
1049     HeapFree( GetProcessHeap(), 0, points );
1050     return ret;
1051 }
1052
1053 /***********************************************************************
1054  *           dibdrv_Polygon
1055  */
1056 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1057 {
1058     INT counts[1] = { count };
1059
1060     return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1061 }
1062
1063 /***********************************************************************
1064  *           dibdrv_Polyline
1065  */
1066 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1067 {
1068     DWORD counts[1] = { count };
1069
1070     if (count < 0) return FALSE;
1071     return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1072 }
1073
1074 /***********************************************************************
1075  *           dibdrv_Rectangle
1076  */
1077 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1078 {
1079     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1080     RECT rect;
1081     POINT pts[4];
1082     BOOL ret;
1083     HRGN outline = 0;
1084
1085     TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1086
1087     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1088
1089     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1090
1091     rect.right--;
1092     rect.bottom--;
1093     reset_dash_origin(pdev);
1094
1095     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1096     {
1097         /* 4 pts going clockwise starting from bottom-right */
1098         pts[0].x = pts[3].x = rect.right;
1099         pts[0].y = pts[1].y = rect.bottom;
1100         pts[1].x = pts[2].x = rect.left;
1101         pts[2].y = pts[3].y = rect.top;
1102     }
1103     else
1104     {
1105         /* 4 pts going anti-clockwise starting from top-right */
1106         pts[0].x = pts[3].x = rect.right;
1107         pts[0].y = pts[1].y = rect.top;
1108         pts[1].x = pts[2].x = rect.left;
1109         pts[2].y = pts[3].y = rect.bottom;
1110     }
1111
1112     pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1113     add_pen_lines_bounds( pdev, 4, pts, outline );
1114
1115     if (outline)
1116     {
1117         if (pdev->brush.style != BS_NULL)
1118         {
1119             HRGN interior = CreateRectRgnIndirect( &rect );
1120
1121             CombineRgn( interior, interior, outline, RGN_DIFF );
1122             brush_region( pdev, interior );
1123             DeleteObject( interior );
1124         }
1125         ret = pen_region( pdev, outline );
1126         DeleteObject( outline );
1127     }
1128     else
1129     {
1130         rect.left   += (pdev->pen_width + 1) / 2;
1131         rect.top    += (pdev->pen_width + 1) / 2;
1132         rect.right  -= pdev->pen_width / 2;
1133         rect.bottom -= pdev->pen_width / 2;
1134         ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1135     }
1136     return ret;
1137 }
1138
1139 /***********************************************************************
1140  *           dibdrv_RoundRect
1141  */
1142 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1143                        INT ellipse_width, INT ellipse_height )
1144 {
1145     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1146     RECT rect;
1147     POINT pt[2], *points;
1148     int i, end, count;
1149     BOOL ret = TRUE;
1150     HRGN outline = 0, interior = 0;
1151
1152     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1153
1154     pt[0].x = pt[0].y = 0;
1155     pt[1].x = ellipse_width;
1156     pt[1].y = ellipse_height;
1157     LPtoDP( dev->hdc, pt, 2 );
1158     ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1159     ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1160     if (ellipse_width <= 2|| ellipse_height <= 2)
1161         return dibdrv_Rectangle( dev, left, top, right, bottom );
1162
1163     points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1164     if (!points) return FALSE;
1165
1166     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1167     {
1168         HeapFree( GetProcessHeap(), 0, points );
1169         return FALSE;
1170     }
1171
1172     if (pdev->brush.style != BS_NULL &&
1173         !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1174                                          ellipse_width, ellipse_height )))
1175     {
1176         HeapFree( GetProcessHeap(), 0, points );
1177         if (outline) DeleteObject( outline );
1178         return FALSE;
1179     }
1180
1181     /* if not using a region, paint the interior first so the outline can overlap it */
1182     if (interior && !outline)
1183     {
1184         ret = brush_region( pdev, interior );
1185         DeleteObject( interior );
1186         interior = 0;
1187     }
1188
1189     count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1190
1191     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1192     {
1193         for (i = 0; i < count; i++)
1194         {
1195             points[i].x = rect.right - ellipse_width + points[i].x;
1196             points[i].y = rect.bottom - ellipse_height + points[i].y;
1197         }
1198     }
1199     else
1200     {
1201         for (i = 0; i < count; i++)
1202         {
1203             points[i].x = rect.right - ellipse_width + points[i].x;
1204             points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1205         }
1206     }
1207
1208     /* horizontal symmetry */
1209
1210     end = 2 * count - 1;
1211     /* avoid duplicating the midpoint */
1212     if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1213     for (i = 0; i < count; i++)
1214     {
1215         points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1216         points[end - i].y = points[i].y;
1217     }
1218     count = end + 1;
1219
1220     /* vertical symmetry */
1221
1222     end = 2 * count - 1;
1223     /* avoid duplicating the midpoint */
1224     if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1225     for (i = 0; i < count; i++)
1226     {
1227         points[end - i].x = points[i].x;
1228         points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1229     }
1230     count = end + 1;
1231
1232     reset_dash_origin( pdev );
1233     pdev->pen_lines( pdev, count, points, TRUE, outline );
1234     add_pen_lines_bounds( pdev, count, points, outline );
1235
1236     if (interior)
1237     {
1238         CombineRgn( interior, interior, outline, RGN_DIFF );
1239         ret = brush_region( pdev, interior );
1240         DeleteObject( interior );
1241     }
1242     if (outline)
1243     {
1244         if (ret) ret = pen_region( pdev, outline );
1245         DeleteObject( outline );
1246     }
1247     HeapFree( GetProcessHeap(), 0, points );
1248     return ret;
1249 }
1250
1251 /***********************************************************************
1252  *           dibdrv_Pie
1253  */
1254 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1255                  INT start_x, INT start_y, INT end_x, INT end_y )
1256 {
1257     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1258 }
1259
1260 /***********************************************************************
1261  *           dibdrv_SetPixel
1262  */
1263 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1264 {
1265     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1266     struct clipped_rects clipped_rects;
1267     RECT rect;
1268     POINT pt;
1269     DWORD pixel;
1270
1271     TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1272
1273     pt.x = x;
1274     pt.y = y;
1275     LPtoDP( dev->hdc, &pt, 1 );
1276     rect.left = pt.x;
1277     rect.top =  pt.y;
1278     rect.right = rect.left + 1;
1279     rect.bottom = rect.top + 1;
1280     add_clipped_bounds( pdev, &rect, pdev->clip );
1281
1282     /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1283     pixel = get_pixel_color( pdev, color, FALSE );
1284     color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1285
1286     if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1287     pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1288     free_clipped_rects( &clipped_rects );
1289     return color;
1290 }