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