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