winealsa: Fix capture overrun logging.
[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             if (glyph_dib->bit_count == 32)
466                 dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
467                                                  text_color );
468             else
469                 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
470                                         text_color, ranges );
471         }
472     }
473 }
474
475 static int get_glyph_depth( UINT aa_flags )
476 {
477     switch (aa_flags)
478     {
479     case GGO_BITMAP: return 1;
480
481     case GGO_GRAY2_BITMAP:
482     case GGO_GRAY4_BITMAP:
483     case GGO_GRAY8_BITMAP:
484     case WINE_GGO_GRAY16_BITMAP: return 8;
485
486     case WINE_GGO_HRGB_BITMAP:
487     case WINE_GGO_HBGR_BITMAP:
488     case WINE_GGO_VRGB_BITMAP:
489     case WINE_GGO_VBGR_BITMAP: return 32;
490
491     default:
492         ERR("Unexpected flags %08x\n", aa_flags);
493         return 0;
494     }
495 }
496
497 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
498 static const int padding[4] = {0, 3, 2, 1};
499
500 /***********************************************************************
501  *         get_glyph_bitmap
502  *
503  * Retrieve a 17-level bitmap for the appropriate glyph.
504  *
505  * For non-antialiased bitmaps convert them to the 17-level format
506  * using only values 0 or 16.
507  */
508 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
509                                dib_info *glyph_dib )
510 {
511     UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
512     static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
513     UINT indices[3] = {0, 0, 0x20};
514     int i, x, y;
515     DWORD ret, size;
516     BYTE *buf, *dst, *src;
517     int pad = 0, depth = get_glyph_depth( aa_flags );
518
519     glyph_dib->bits.ptr = NULL;
520     glyph_dib->bits.is_copy = FALSE;
521     glyph_dib->bits.free = free_heap_bits;
522     glyph_dib->bits.param = NULL;
523
524     indices[0] = index;
525
526     for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
527     {
528         index = indices[i];
529         ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
530         if (ret != GDI_ERROR) break;
531     }
532
533     if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
534     if (!ret) return ERROR_SUCCESS; /* empty glyph */
535
536     /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
537     glyph_dib->bit_count   = depth == 1 ? 8 : depth;
538     glyph_dib->width       = metrics->gmBlackBoxX;
539     glyph_dib->height      = metrics->gmBlackBoxY;
540     glyph_dib->rect.left   = 0;
541     glyph_dib->rect.top    = 0;
542     glyph_dib->rect.right  = metrics->gmBlackBoxX;
543     glyph_dib->rect.bottom = metrics->gmBlackBoxY;
544     glyph_dib->stride      = get_dib_stride( metrics->gmBlackBoxX, glyph_dib->bit_count );
545
546     if (glyph_dib->bit_count == 8) pad = padding[ metrics->gmBlackBoxX % 4 ];
547     size = metrics->gmBlackBoxY * glyph_dib->stride;
548
549     buf = HeapAlloc( GetProcessHeap(), 0, size );
550     if (!buf) return ERROR_OUTOFMEMORY;
551
552     ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
553     if (ret == GDI_ERROR)
554     {
555         HeapFree( GetProcessHeap(), 0, buf );
556         return ERROR_NOT_FOUND;
557     }
558
559     if (aa_flags == GGO_BITMAP)
560     {
561         for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
562         {
563             src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
564             dst = buf + y * glyph_dib->stride;
565
566             if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
567
568             for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
569                 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
570         }
571     }
572     else if (pad)
573     {
574         for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += glyph_dib->stride)
575             memset( dst + metrics->gmBlackBoxX, 0, pad );
576     }
577
578     glyph_dib->bits.ptr = buf;
579     return ERROR_SUCCESS;
580 }
581
582 static void render_string( HDC hdc, dib_info *dib, INT x, INT y, UINT flags, UINT aa_flags,
583                            const WCHAR *str, UINT count, const INT *dx, DWORD text_color,
584                            const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
585                            RECT *bounds )
586 {
587     UINT i;
588     DWORD err;
589     GLYPHMETRICS metrics;
590     dib_info glyph_dib;
591
592     for (i = 0; i < count; i++)
593     {
594         err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &glyph_dib );
595         if (err) continue;
596
597         if (glyph_dib.bits.ptr)
598             draw_glyph( dib, x, y, &metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
599
600         free_dib_info( &glyph_dib );
601
602         if (dx)
603         {
604             if (flags & ETO_PDY)
605             {
606                 x += dx[ i * 2 ];
607                 y += dx[ i * 2 + 1];
608             }
609             else
610                 x += dx[ i ];
611         }
612         else
613         {
614             x += metrics.gmCellIncX;
615             y += metrics.gmCellIncY;
616         }
617     }
618 }
619
620 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
621                                 struct bitblt_coords *src, INT x, INT y, UINT flags,
622                                 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
623 {
624     dib_info dib;
625     BOOL got_pixel;
626     COLORREF fg, bg;
627     DWORD fg_pixel, bg_pixel;
628     struct intensity_range glyph_intensities[17];
629     struct clipped_rects visrect;
630
631     assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
632
633     init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
634
635     visrect.count = 1;
636     visrect.rects = &src->visrect;
637
638     fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
639     if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
640
641     get_aa_ranges( fg, glyph_intensities );
642
643     if (flags & ETO_OPAQUE)
644     {
645         rop_mask bkgnd_color;
646
647         bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
648         if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
649
650         bkgnd_color.and = 0;
651         bkgnd_color.xor = bg_pixel;
652         dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
653     }
654
655     render_string( hdc, &dib, x, y, flags, aa_flags, str, count, dx,
656                    fg_pixel, glyph_intensities, &visrect, NULL );
657     return TRUE;
658 }
659
660 /***********************************************************************
661  *           dibdrv_ExtTextOut
662  */
663 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
664                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
665 {
666     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
667     struct clipped_rects clipped_rects;
668     UINT aa_flags;
669     RECT bounds;
670     DWORD text_color;
671     struct intensity_range ranges[17];
672
673     init_clipped_rects( &clipped_rects );
674     reset_bounds( &bounds );
675
676     if (flags & ETO_OPAQUE)
677     {
678         rop_mask bkgnd_color;
679         get_text_bkgnd_masks( pdev, &bkgnd_color );
680         add_bounds_rect( &bounds, rect );
681         get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
682         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
683                                       bkgnd_color.and, bkgnd_color.xor );
684     }
685
686     if (count == 0) goto done;
687
688     if (flags & ETO_CLIPPED)
689     {
690         if (!(flags & ETO_OPAQUE))  /* otherwise we have done it already */
691             get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
692     }
693     else
694     {
695         free_clipped_rects( &clipped_rects );
696         get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
697     }
698     if (!clipped_rects.count) goto done;
699
700     text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
701     get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
702
703     aa_flags = get_font_aa_flags( dev->hdc );
704
705     render_string( dev->hdc, &pdev->dib, x, y, flags, aa_flags, str, count, dx,
706                    text_color, ranges, &clipped_rects, &bounds );
707
708 done:
709     add_clipped_bounds( pdev, &bounds, pdev->clip );
710     free_clipped_rects( &clipped_rects );
711     return TRUE;
712 }
713
714 /***********************************************************************
715  *           dibdrv_Arc
716  */
717 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
718                  INT start_x, INT start_y, INT end_x, INT end_y )
719 {
720     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
721 }
722
723 /***********************************************************************
724  *           dibdrv_ArcTo
725  */
726 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
727                    INT start_x, INT start_y, INT end_x, INT end_y )
728 {
729     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
730 }
731
732 /***********************************************************************
733  *           dibdrv_Chord
734  */
735 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
736                    INT start_x, INT start_y, INT end_x, INT end_y )
737 {
738     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
739 }
740
741 /***********************************************************************
742  *           dibdrv_Ellipse
743  */
744 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
745 {
746     return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
747 }
748
749 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
750 {
751     /* the clip rgn stops the flooding */
752     if (clip && !PtInRegion( clip, x, y )) return FALSE;
753
754     if (type == FLOODFILLBORDER)
755         return dib->funcs->get_pixel( dib, x, y ) != pixel;
756     else
757         return dib->funcs->get_pixel( dib, x, y ) == pixel;
758 }
759
760 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
761
762 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
763 {
764     RECT next;
765
766     next.top = row->top + offset;
767     next.bottom = next.top + 1;
768     next.left = next.right = row->left;
769     while (next.right < row->right)
770     {
771         if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
772         else
773         {
774             if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
775                 fill_row( dib, clip, &next, pixel, type, rgn );
776             next.left = ++next.right;
777         }
778     }
779     if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
780         fill_row( dib, clip, &next, pixel, type, rgn );
781 }
782
783 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
784 {
785     while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
786     while (row->right < dib->rect.right - dib->rect.left &&
787            is_interior( dib, clip, row->right, row->top, pixel, type))
788         row->right++;
789
790     add_rect_to_region( rgn, row );
791
792     if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
793     if (row->top < dib->rect.bottom - dib->rect.top - 1)
794         do_next_row( dib, clip, row, 1, pixel, type, rgn );
795 }
796
797 /***********************************************************************
798  *           dibdrv_ExtFloodFill
799  */
800 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
801 {
802     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
803     DWORD pixel = get_pixel_color( pdev, color, FALSE );
804     RECT row;
805     HRGN rgn;
806
807     TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
808
809     if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
810
811     if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
812     row.left = x;
813     row.right = x + 1;
814     row.top = y;
815     row.bottom = y + 1;
816
817     fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
818
819     add_clipped_bounds( pdev, NULL, rgn );
820     brush_region( pdev, rgn );
821
822     DeleteObject( rgn );
823     return TRUE;
824 }
825
826 /***********************************************************************
827  *           dibdrv_GetNearestColor
828  */
829 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
830 {
831     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
832     DWORD pixel;
833
834     TRACE( "(%p, %08x)\n", dev, color );
835
836     pixel = get_pixel_color( pdev, color, FALSE );
837     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
838 }
839
840 /***********************************************************************
841  *           dibdrv_GetPixel
842  */
843 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
844 {
845     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
846     POINT pt;
847     DWORD pixel;
848
849     TRACE( "(%p, %d, %d)\n", dev, x, y );
850
851     pt.x = x;
852     pt.y = y;
853     LPtoDP( dev->hdc, &pt, 1 );
854
855     if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
856         pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
857         return CLR_INVALID;
858
859     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
860     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
861 }
862
863 /***********************************************************************
864  *           dibdrv_LineTo
865  */
866 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
867 {
868     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
869     POINT pts[2];
870     HRGN region = 0;
871     BOOL ret;
872
873     GetCurrentPositionEx(dev->hdc, pts);
874     pts[1].x = x;
875     pts[1].y = y;
876
877     LPtoDP(dev->hdc, pts, 2);
878
879     if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
880
881     reset_dash_origin(pdev);
882
883     ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
884     add_pen_lines_bounds( pdev, 2, pts, region );
885
886     if (region)
887     {
888         ret = pen_region( pdev, region );
889         DeleteObject( region );
890     }
891     return ret;
892 }
893
894 /***********************************************************************
895  *           get_rop2_from_rop
896  *
897  * Returns the binary rop that is equivalent to the provided ternary rop
898  * if the src bits are ignored.
899  */
900 static inline INT get_rop2_from_rop(INT rop)
901 {
902     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
903 }
904
905 /***********************************************************************
906  *           dibdrv_PatBlt
907  */
908 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
909 {
910     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
911     dib_brush *brush = &pdev->brush;
912     int rop2 = get_rop2_from_rop( rop );
913     struct clipped_rects clipped_rects;
914     DWORD and = 0, xor = 0;
915     BOOL ret = TRUE;
916
917     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
918
919     add_clipped_bounds( pdev, &dst->visrect, 0 );
920     if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
921
922     switch (rop2)  /* shortcuts for rops that don't involve the brush */
923     {
924     case R2_NOT:   and = ~0u;
925         /* fall through */
926     case R2_WHITE: xor = ~0u;
927         /* fall through */
928     case R2_BLACK:
929         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
930         /* fall through */
931     case R2_NOP:
932         break;
933     default:
934         ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
935         break;
936     }
937     free_clipped_rects( &clipped_rects );
938     return ret;
939 }
940
941 /***********************************************************************
942  *           dibdrv_PaintRgn
943  */
944 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
945 {
946     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
947     const WINEREGION *region;
948     int i;
949     RECT rect, bounds;
950
951     TRACE("%p, %p\n", dev, rgn);
952
953     reset_bounds( &bounds );
954
955     region = get_wine_region( rgn );
956     if(!region) return FALSE;
957
958     for(i = 0; i < region->numRects; i++)
959     {
960         rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
961                                 region->rects[i].right, region->rects[i].bottom, FALSE );
962         add_bounds_rect( &bounds, &rect );
963         brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
964     }
965
966     release_wine_region( rgn );
967     add_clipped_bounds( pdev, &bounds, pdev->clip );
968     return TRUE;
969 }
970
971 /***********************************************************************
972  *           dibdrv_PolyPolygon
973  */
974 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
975 {
976     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
977     DWORD total, i, pos;
978     BOOL ret = TRUE;
979     POINT *points;
980     HRGN outline = 0, interior = 0;
981
982     for (i = total = 0; i < polygons; i++)
983     {
984         if (counts[i] < 2) return FALSE;
985         total += counts[i];
986     }
987
988     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
989     if (!points) return FALSE;
990     memcpy( points, pt, total * sizeof(*pt) );
991     LPtoDP( dev->hdc, points, total );
992
993     if (pdev->brush.style != BS_NULL &&
994         !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
995     {
996         HeapFree( GetProcessHeap(), 0, points );
997         return FALSE;
998     }
999
1000     if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1001
1002     /* if not using a region, paint the interior first so the outline can overlap it */
1003     if (interior && !outline)
1004     {
1005         ret = brush_region( pdev, interior );
1006         DeleteObject( interior );
1007         interior = 0;
1008     }
1009
1010     for (i = pos = 0; i < polygons; i++)
1011     {
1012         reset_dash_origin( pdev );
1013         pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1014         pos += counts[i];
1015     }
1016     add_pen_lines_bounds( pdev, total, points, outline );
1017
1018     if (interior)
1019     {
1020         CombineRgn( interior, interior, outline, RGN_DIFF );
1021         ret = brush_region( pdev, interior );
1022         DeleteObject( interior );
1023     }
1024     if (outline)
1025     {
1026         if (ret) ret = pen_region( pdev, outline );
1027         DeleteObject( outline );
1028     }
1029     HeapFree( GetProcessHeap(), 0, points );
1030     return ret;
1031 }
1032
1033 /***********************************************************************
1034  *           dibdrv_PolyPolyline
1035  */
1036 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1037 {
1038     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1039     DWORD total, pos, i;
1040     POINT *points;
1041     BOOL ret = TRUE;
1042     HRGN outline = 0;
1043
1044     for (i = total = 0; i < polylines; i++)
1045     {
1046         if (counts[i] < 2) return FALSE;
1047         total += counts[i];
1048     }
1049
1050     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1051     if (!points) return FALSE;
1052     memcpy( points, pt, total * sizeof(*pt) );
1053     LPtoDP( dev->hdc, points, total );
1054
1055     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1056     {
1057         HeapFree( GetProcessHeap(), 0, points );
1058         return FALSE;
1059     }
1060
1061     for (i = pos = 0; i < polylines; i++)
1062     {
1063         reset_dash_origin( pdev );
1064         pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1065         pos += counts[i];
1066     }
1067     add_pen_lines_bounds( pdev, total, points, outline );
1068
1069     if (outline)
1070     {
1071         ret = pen_region( pdev, outline );
1072         DeleteObject( outline );
1073     }
1074
1075     HeapFree( GetProcessHeap(), 0, points );
1076     return ret;
1077 }
1078
1079 /***********************************************************************
1080  *           dibdrv_Polygon
1081  */
1082 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1083 {
1084     INT counts[1] = { count };
1085
1086     return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1087 }
1088
1089 /***********************************************************************
1090  *           dibdrv_Polyline
1091  */
1092 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1093 {
1094     DWORD counts[1] = { count };
1095
1096     if (count < 0) return FALSE;
1097     return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1098 }
1099
1100 /***********************************************************************
1101  *           dibdrv_Rectangle
1102  */
1103 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1104 {
1105     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1106     RECT rect;
1107     POINT pts[4];
1108     BOOL ret;
1109     HRGN outline = 0;
1110
1111     TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1112
1113     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1114
1115     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1116
1117     rect.right--;
1118     rect.bottom--;
1119     reset_dash_origin(pdev);
1120
1121     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1122     {
1123         /* 4 pts going clockwise starting from bottom-right */
1124         pts[0].x = pts[3].x = rect.right;
1125         pts[0].y = pts[1].y = rect.bottom;
1126         pts[1].x = pts[2].x = rect.left;
1127         pts[2].y = pts[3].y = rect.top;
1128     }
1129     else
1130     {
1131         /* 4 pts going anti-clockwise starting from top-right */
1132         pts[0].x = pts[3].x = rect.right;
1133         pts[0].y = pts[1].y = rect.top;
1134         pts[1].x = pts[2].x = rect.left;
1135         pts[2].y = pts[3].y = rect.bottom;
1136     }
1137
1138     pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1139     add_pen_lines_bounds( pdev, 4, pts, outline );
1140
1141     if (outline)
1142     {
1143         if (pdev->brush.style != BS_NULL)
1144         {
1145             HRGN interior = CreateRectRgnIndirect( &rect );
1146
1147             CombineRgn( interior, interior, outline, RGN_DIFF );
1148             brush_region( pdev, interior );
1149             DeleteObject( interior );
1150         }
1151         ret = pen_region( pdev, outline );
1152         DeleteObject( outline );
1153     }
1154     else
1155     {
1156         rect.left   += (pdev->pen_width + 1) / 2;
1157         rect.top    += (pdev->pen_width + 1) / 2;
1158         rect.right  -= pdev->pen_width / 2;
1159         rect.bottom -= pdev->pen_width / 2;
1160         ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1161     }
1162     return ret;
1163 }
1164
1165 /***********************************************************************
1166  *           dibdrv_RoundRect
1167  */
1168 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1169                        INT ellipse_width, INT ellipse_height )
1170 {
1171     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1172     RECT rect;
1173     POINT pt[2], *points;
1174     int i, end, count;
1175     BOOL ret = TRUE;
1176     HRGN outline = 0, interior = 0;
1177
1178     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1179
1180     pt[0].x = pt[0].y = 0;
1181     pt[1].x = ellipse_width;
1182     pt[1].y = ellipse_height;
1183     LPtoDP( dev->hdc, pt, 2 );
1184     ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1185     ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1186     if (ellipse_width <= 2|| ellipse_height <= 2)
1187         return dibdrv_Rectangle( dev, left, top, right, bottom );
1188
1189     points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1190     if (!points) return FALSE;
1191
1192     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1193     {
1194         HeapFree( GetProcessHeap(), 0, points );
1195         return FALSE;
1196     }
1197
1198     if (pdev->brush.style != BS_NULL &&
1199         !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1200                                          ellipse_width, ellipse_height )))
1201     {
1202         HeapFree( GetProcessHeap(), 0, points );
1203         if (outline) DeleteObject( outline );
1204         return FALSE;
1205     }
1206
1207     /* if not using a region, paint the interior first so the outline can overlap it */
1208     if (interior && !outline)
1209     {
1210         ret = brush_region( pdev, interior );
1211         DeleteObject( interior );
1212         interior = 0;
1213     }
1214
1215     count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1216
1217     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1218     {
1219         for (i = 0; i < count; i++)
1220         {
1221             points[i].x = rect.right - ellipse_width + points[i].x;
1222             points[i].y = rect.bottom - ellipse_height + points[i].y;
1223         }
1224     }
1225     else
1226     {
1227         for (i = 0; i < count; i++)
1228         {
1229             points[i].x = rect.right - ellipse_width + points[i].x;
1230             points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1231         }
1232     }
1233
1234     /* horizontal symmetry */
1235
1236     end = 2 * count - 1;
1237     /* avoid duplicating the midpoint */
1238     if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1239     for (i = 0; i < count; i++)
1240     {
1241         points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1242         points[end - i].y = points[i].y;
1243     }
1244     count = end + 1;
1245
1246     /* vertical symmetry */
1247
1248     end = 2 * count - 1;
1249     /* avoid duplicating the midpoint */
1250     if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1251     for (i = 0; i < count; i++)
1252     {
1253         points[end - i].x = points[i].x;
1254         points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1255     }
1256     count = end + 1;
1257
1258     reset_dash_origin( pdev );
1259     pdev->pen_lines( pdev, count, points, TRUE, outline );
1260     add_pen_lines_bounds( pdev, count, points, outline );
1261
1262     if (interior)
1263     {
1264         CombineRgn( interior, interior, outline, RGN_DIFF );
1265         ret = brush_region( pdev, interior );
1266         DeleteObject( interior );
1267     }
1268     if (outline)
1269     {
1270         if (ret) ret = pen_region( pdev, outline );
1271         DeleteObject( outline );
1272     }
1273     HeapFree( GetProcessHeap(), 0, points );
1274     return ret;
1275 }
1276
1277 /***********************************************************************
1278  *           dibdrv_Pie
1279  */
1280 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1281                  INT start_x, INT start_y, INT end_x, INT end_y )
1282 {
1283     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1284 }
1285
1286 /***********************************************************************
1287  *           dibdrv_SetPixel
1288  */
1289 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1290 {
1291     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1292     struct clipped_rects clipped_rects;
1293     RECT rect;
1294     POINT pt;
1295     DWORD pixel;
1296
1297     TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1298
1299     pt.x = x;
1300     pt.y = y;
1301     LPtoDP( dev->hdc, &pt, 1 );
1302     rect.left = pt.x;
1303     rect.top =  pt.y;
1304     rect.right = rect.left + 1;
1305     rect.bottom = rect.top + 1;
1306     add_clipped_bounds( pdev, &rect, pdev->clip );
1307
1308     /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1309     pixel = get_pixel_color( pdev, color, FALSE );
1310     color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1311
1312     if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1313     pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1314     free_clipped_rects( &clipped_rects );
1315     return color;
1316 }