gdi32: Add support for updating a region from the pen backend functions instead of...
[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 RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
30 {
31     RECT rect;
32
33     rect.left   = left;
34     rect.top    = top;
35     rect.right  = right;
36     rect.bottom = bottom;
37     if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
38     {
39         /* shift the rectangle so that the right border is included after mirroring */
40         /* it would be more correct to do this after LPtoDP but that's not what Windows does */
41         rect.left--;
42         rect.right--;
43     }
44     LPtoDP( hdc, (POINT *)&rect, 2 );
45     if (rect.left > rect.right)
46     {
47         int tmp = rect.left;
48         rect.left = rect.right;
49         rect.right = tmp;
50     }
51     if (rect.top > rect.bottom)
52     {
53         int tmp = rect.top;
54         rect.top = rect.bottom;
55         rect.bottom = tmp;
56     }
57     return rect;
58 }
59
60 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
61    black bkgnd.  [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
62 static const BYTE ramp[17] =
63 {
64     0,    0x4d, 0x68, 0x7c,
65     0x8c, 0x9a, 0xa7, 0xb2,
66     0xbd, 0xc7, 0xd0, 0xd9,
67     0xe1, 0xe9, 0xf0, 0xf8,
68     0xff
69 };
70
71 /* For a give text-color component and a glyph level, calculate the
72    range of dst intensities, the min/max corresponding to 0/0xff bkgnd
73    components respectively.
74
75    The minimum is a linear interpolation between 0 and the value in
76    the ramp table.
77
78    The maximum is a linear interpolation between the value from the
79    ramp table read in reverse and 0xff.
80
81    To find the resulting pixel intensity, we note that if the text and
82    bkgnd intensities are the same then the result must be that
83    intensity.  Otherwise we linearly interpolate between either the
84    min or the max value and this intermediate value depending on which
85    side of the inequality we lie.
86 */
87
88 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
89 {
90     *min_comp = (ramp[aa] * text_comp) / 0xff;
91     *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
92 }
93
94 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
95 {
96     int i;
97
98     for (i = 0; i < 17; i++)
99     {
100         get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
101         get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
102         get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
103     }
104 }
105
106 /**********************************************************************
107  *                 get_text_bkgnd_masks
108  *
109  * See the comment above get_pen_bkgnd_masks
110  */
111 static inline void get_text_bkgnd_masks( dibdrv_physdev *pdev, rop_mask *mask )
112 {
113     COLORREF bg = GetBkColor( pdev->dev.hdc );
114
115     mask->and = 0;
116
117     if (pdev->dib.bit_count != 1)
118         mask->xor = get_pixel_color( pdev, bg, FALSE );
119     else
120     {
121         COLORREF fg = GetTextColor( pdev->dev.hdc );
122         mask->xor = get_pixel_color( pdev, fg, TRUE );
123         if (fg != bg) mask->xor = ~mask->xor;
124     }
125 }
126
127 static void draw_glyph( dibdrv_physdev *pdev, const POINT *origin, const GLYPHMETRICS *metrics,
128                         const struct gdi_image_bits *image, DWORD text_color,
129                         const struct intensity_range *ranges, const struct clipped_rects *clipped_rects )
130 {
131     int i;
132     RECT rect, clipped_rect;
133     POINT src_origin;
134     dib_info glyph_dib;
135
136     glyph_dib.bit_count = 8;
137     glyph_dib.width     = metrics->gmBlackBoxX;
138     glyph_dib.height    = metrics->gmBlackBoxY;
139     glyph_dib.stride    = get_dib_stride( metrics->gmBlackBoxX, 8 );
140     glyph_dib.bits      = *image;
141
142     rect.left   = origin->x  + metrics->gmptGlyphOrigin.x;
143     rect.top    = origin->y  - metrics->gmptGlyphOrigin.y;
144     rect.right  = rect.left  + metrics->gmBlackBoxX;
145     rect.bottom = rect.top   + metrics->gmBlackBoxY;
146
147     for (i = 0; i < clipped_rects->count; i++)
148     {
149         if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
150         {
151             src_origin.x = clipped_rect.left - rect.left;
152             src_origin.y = clipped_rect.top  - rect.top;
153
154             pdev->dib.funcs->draw_glyph( &pdev->dib, &clipped_rect, &glyph_dib, &src_origin,
155                                          text_color, ranges );
156         }
157     }
158 }
159
160 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
161 static const int padding[4] = {0, 3, 2, 1};
162
163 /***********************************************************************
164  *         get_glyph_bitmap
165  *
166  * Retrieve a 17-level bitmap for the appropiate glyph.
167  *
168  * For non-antialiased bitmaps convert them to the 17-level format
169  * using only values 0 or 16.
170  */
171 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
172                                struct gdi_image_bits *image )
173 {
174     UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
175     static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
176     UINT indices[3] = {0, 0, 0x20};
177     int i, x, y;
178     DWORD ret, size;
179     BYTE *buf, *dst, *src;
180     int pad, stride;
181
182     image->ptr = NULL;
183     image->is_copy = FALSE;
184     image->free = free_heap_bits;
185     image->param = NULL;
186
187     indices[0] = index;
188
189     for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
190     {
191         index = indices[i];
192         ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
193         if (ret != GDI_ERROR) break;
194     }
195
196     if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
197     if (!ret) return ERROR_SUCCESS; /* empty glyph */
198
199     /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
200     pad = padding[ metrics->gmBlackBoxX % 4 ];
201     stride = get_dib_stride( metrics->gmBlackBoxX, 8 );
202     size = metrics->gmBlackBoxY * stride;
203
204     buf = HeapAlloc( GetProcessHeap(), 0, size );
205     if (!buf) return ERROR_OUTOFMEMORY;
206
207     ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
208     if (ret == GDI_ERROR)
209     {
210         HeapFree( GetProcessHeap(), 0, buf );
211         return ERROR_NOT_FOUND;
212     }
213
214     if (aa_flags == GGO_BITMAP)
215     {
216         for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
217         {
218             src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
219             dst = buf + y * stride;
220
221             if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
222
223             for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
224                 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
225         }
226     }
227     else if (pad)
228     {
229         for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
230             memset( dst + metrics->gmBlackBoxX, 0, pad );
231     }
232
233     image->ptr = buf;
234     return ERROR_SUCCESS;
235 }
236
237 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
238                                 struct bitblt_coords *src, INT x, INT y, UINT flags,
239                                 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
240 {
241     dib_info dib;
242     UINT i;
243     DWORD err;
244     BOOL got_pixel;
245     COLORREF fg, bg;
246     DWORD fg_pixel, bg_pixel;
247     struct intensity_range glyph_intensities[17];
248
249     assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
250
251     init_dib_info_from_bitmapinfo( &dib, info, bits->ptr, 0 );
252
253     fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
254     if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
255
256     get_aa_ranges( fg, glyph_intensities );
257
258     if (flags & ETO_OPAQUE)
259     {
260         rop_mask bkgnd_color;
261
262         bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
263         if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
264
265         bkgnd_color.and = 0;
266         bkgnd_color.xor = bg_pixel;
267         dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
268     }
269
270     for (i = 0; i < count; i++)
271     {
272         GLYPHMETRICS metrics;
273         struct gdi_image_bits image;
274
275         err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &image );
276         if (err) continue;
277
278         if (image.ptr)
279         {
280             RECT rect, clipped_rect;
281             POINT src_origin;
282             dib_info glyph_dib;
283
284             glyph_dib.bit_count = 8;
285             glyph_dib.width     = metrics.gmBlackBoxX;
286             glyph_dib.height    = metrics.gmBlackBoxY;
287             glyph_dib.stride    = get_dib_stride( metrics.gmBlackBoxX, 8 );
288             glyph_dib.bits      = image;
289
290             rect.left   = x + metrics.gmptGlyphOrigin.x;
291             rect.top    = y - metrics.gmptGlyphOrigin.y;
292             rect.right  = rect.left + metrics.gmBlackBoxX;
293             rect.bottom = rect.top  + metrics.gmBlackBoxY;
294
295             if (intersect_rect( &clipped_rect, &rect, &src->visrect ))
296             {
297                 src_origin.x = clipped_rect.left - rect.left;
298                 src_origin.y = clipped_rect.top  - rect.top;
299
300                 dib.funcs->draw_glyph( &dib, &clipped_rect, &glyph_dib, &src_origin,
301                                        fg_pixel, glyph_intensities );
302             }
303         }
304         if (image.free) image.free( &image );
305
306         if (dx)
307         {
308             if (flags & ETO_PDY)
309             {
310                 x += dx[ i * 2 ];
311                 y += dx[ i * 2 + 1];
312             }
313             else
314                 x += dx[ i ];
315         }
316         else
317         {
318             x += metrics.gmCellIncX;
319             y += metrics.gmCellIncY;
320         }
321     }
322     return TRUE;
323 }
324
325 /***********************************************************************
326  *           dibdrv_ExtTextOut
327  */
328 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
329                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
330 {
331     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
332     struct clipped_rects clipped_rects;
333     UINT aa_flags, i;
334     POINT origin;
335     DWORD text_color, err;
336     struct intensity_range ranges[17];
337
338     init_clipped_rects( &clipped_rects );
339
340     if (flags & ETO_OPAQUE)
341     {
342         rop_mask bkgnd_color;
343         get_text_bkgnd_masks( pdev, &bkgnd_color );
344         get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
345         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
346                                       bkgnd_color.and, bkgnd_color.xor );
347     }
348
349     if (count == 0) goto done;
350
351     if (flags & ETO_CLIPPED)
352     {
353         if (!(flags & ETO_OPAQUE))  /* otherwise we have done it already */
354             get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
355     }
356     else
357     {
358         free_clipped_rects( &clipped_rects );
359         get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
360     }
361     if (!clipped_rects.count) return TRUE;
362
363     text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
364     get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
365
366     aa_flags = get_font_aa_flags( dev->hdc );
367     origin.x = x;
368     origin.y = y;
369     for (i = 0; i < count; i++)
370     {
371         GLYPHMETRICS metrics;
372         struct gdi_image_bits image;
373
374         err = get_glyph_bitmap( dev->hdc, (UINT)str[i], aa_flags, &metrics, &image );
375         if (err) continue;
376
377         if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image, text_color, ranges, &clipped_rects );
378         if (image.free) image.free( &image );
379
380         if (dx)
381         {
382             if (flags & ETO_PDY)
383             {
384                 origin.x += dx[ i * 2 ];
385                 origin.y += dx[ i * 2 + 1];
386             }
387             else
388                 origin.x += dx[ i ];
389         }
390         else
391         {
392             origin.x += metrics.gmCellIncX;
393             origin.y += metrics.gmCellIncY;
394         }
395     }
396
397 done:
398     free_clipped_rects( &clipped_rects );
399     return TRUE;
400 }
401
402 /***********************************************************************
403  *           dibdrv_GetNearestColor
404  */
405 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
406 {
407     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
408     DWORD pixel;
409
410     TRACE( "(%p, %08x)\n", dev, color );
411
412     pixel = get_pixel_color( pdev, color, FALSE );
413     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
414 }
415
416 /***********************************************************************
417  *           dibdrv_GetPixel
418  */
419 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
420 {
421     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
422     POINT pt;
423     DWORD pixel;
424
425     TRACE( "(%p, %d, %d)\n", dev, x, y );
426
427     pt.x = x;
428     pt.y = y;
429     LPtoDP( dev->hdc, &pt, 1 );
430
431     if (pt.x < 0 || pt.x >= pdev->dib.width ||
432         pt.y < 0 || pt.y >= pdev->dib.height)
433         return CLR_INVALID;
434
435     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
436     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
437 }
438
439 /***********************************************************************
440  *           dibdrv_LineTo
441  */
442 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
443 {
444     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pLineTo );
445     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
446     POINT pts[2];
447
448     GetCurrentPositionEx(dev->hdc, pts);
449     pts[1].x = x;
450     pts[1].y = y;
451
452     LPtoDP(dev->hdc, pts, 2);
453
454     reset_dash_origin(pdev);
455
456     if(defer_pen(pdev) || !pdev->pen_lines(pdev, 2, pts, FALSE, 0))
457         return next->funcs->pLineTo( next, x, y );
458
459     return TRUE;
460 }
461
462 /***********************************************************************
463  *           get_rop2_from_rop
464  *
465  * Returns the binary rop that is equivalent to the provided ternary rop
466  * if the src bits are ignored.
467  */
468 static inline INT get_rop2_from_rop(INT rop)
469 {
470     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
471 }
472
473 /***********************************************************************
474  *           dibdrv_PatBlt
475  */
476 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
477 {
478     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
479
480     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
481
482     return brush_rect( pdev, &dst->visrect, pdev->clip, get_rop2_from_rop(rop) );
483 }
484
485 /***********************************************************************
486  *           dibdrv_PaintRgn
487  */
488 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
489 {
490     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
491     const WINEREGION *region;
492     int i;
493     RECT rect;
494
495     TRACE("%p, %p\n", dev, rgn);
496
497     region = get_wine_region( rgn );
498     if(!region) return FALSE;
499
500     for(i = 0; i < region->numRects; i++)
501     {
502         rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
503                                 region->rects[i].right, region->rects[i].bottom, FALSE );
504         brush_rect( pdev, &rect, pdev->clip, GetROP2( dev->hdc ) );
505     }
506
507     release_wine_region( rgn );
508     return TRUE;
509 }
510
511 /***********************************************************************
512  *           dibdrv_PolyPolyline
513  */
514 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
515 {
516     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
517     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPolyPolyline );
518     DWORD max_points = 0, i;
519     POINT *points;
520
521     if (defer_pen( pdev )) return next->funcs->pPolyPolyline( next, pt, counts, polylines );
522
523     for (i = 0; i < polylines; i++) max_points = max( counts[i], max_points );
524
525     points = HeapAlloc( GetProcessHeap(), 0, max_points * sizeof(*pt) );
526     if (!points) return FALSE;
527
528     for (i = 0; i < polylines; i++)
529     {
530         memcpy( points, pt, counts[i] * sizeof(*pt) );
531         pt += counts[i];
532         LPtoDP( dev->hdc, points, counts[i] );
533
534         reset_dash_origin( pdev );
535         pdev->pen_lines( pdev, counts[i], points, FALSE, 0 );
536     }
537
538     HeapFree( GetProcessHeap(), 0, points );
539     return TRUE;
540 }
541
542 /***********************************************************************
543  *           dibdrv_Polyline
544  */
545 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
546 {
547     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
548     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPolyline );
549     POINT *points;
550
551     if (defer_pen( pdev )) return next->funcs->pPolyline( next, pt, count );
552
553     points = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pt) );
554     if (!points) return FALSE;
555
556     memcpy( points, pt, count * sizeof(*pt) );
557     LPtoDP( dev->hdc, points, count );
558
559     reset_dash_origin( pdev );
560     pdev->pen_lines( pdev, count, points, FALSE, 0 );
561
562     HeapFree( GetProcessHeap(), 0, points );
563     return TRUE;
564 }
565
566 /***********************************************************************
567  *           dibdrv_Rectangle
568  */
569 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
570 {
571     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pRectangle );
572     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
573     RECT rect = get_device_rect( dev->hdc, left, top, right, bottom, TRUE );
574     POINT pts[4];
575
576     TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
577
578     if(rect.left == rect.right || rect.top == rect.bottom) return TRUE;
579
580     if(defer_pen(pdev))
581         return next->funcs->pRectangle( next, left, top, right, bottom );
582
583     if (pdev->pen_style == PS_INSIDEFRAME)
584     {
585         rect.left   += pdev->pen_width / 2;
586         rect.top    += pdev->pen_width / 2;
587         rect.right  -= (pdev->pen_width - 1) / 2;
588         rect.bottom -= (pdev->pen_width - 1) / 2;
589     }
590
591     reset_dash_origin(pdev);
592
593     /* 4 pts going anti-clockwise starting from top-right */
594     pts[0].x = pts[3].x = rect.right - 1;
595     pts[0].y = pts[1].y = rect.top;
596     pts[1].x = pts[2].x = rect.left;
597     pts[2].y = pts[3].y = rect.bottom - 1;
598
599     pdev->pen_lines(pdev, 4, pts, TRUE, 0);
600
601     rect.left   += (pdev->pen_width + 1) / 2;
602     rect.top    += (pdev->pen_width + 1) / 2;
603     rect.right  -= (pdev->pen_width + 2) / 2;
604     rect.bottom -= (pdev->pen_width + 2) / 2;
605
606     return brush_rect( pdev, &rect, pdev->clip, GetROP2(dev->hdc) );
607 }
608
609 /***********************************************************************
610  *           dibdrv_SetPixel
611  */
612 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
613 {
614     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
615     struct clipped_rects clipped_rects;
616     RECT rect;
617     POINT pt;
618     DWORD pixel;
619
620     TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
621
622     pt.x = x;
623     pt.y = y;
624     LPtoDP( dev->hdc, &pt, 1 );
625     rect.left = pt.x;
626     rect.top =  pt.y;
627     rect.right = rect.left + 1;
628     rect.bottom = rect.top + 1;
629
630     /* SetPixel doesn't do the 1bpp massaging like other fg colors */
631     pixel = get_pixel_color( pdev, color, FALSE );
632     color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
633
634     if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
635     pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
636     free_clipped_rects( &clipped_rects );
637     return color;
638 }