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