winealsa: Unify the checks for wBitsPerSample.
[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         return FALSE;
275     }
276
277     if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
278
279     /* if not using a region, paint the interior first so the outline can overlap it */
280     if (interior && !outline)
281     {
282         ret = brush_region( pdev, interior );
283         DeleteObject( interior );
284         interior = 0;
285     }
286
287     reset_dash_origin( pdev );
288     pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
289
290     if (interior)
291     {
292         CombineRgn( interior, interior, outline, RGN_DIFF );
293         ret = brush_region( pdev, interior );
294         DeleteObject( interior );
295     }
296     if (outline)
297     {
298         if (ret) ret = pen_region( pdev, outline );
299         DeleteObject( outline );
300     }
301     HeapFree( GetProcessHeap(), 0, points );
302     return ret;
303 }
304
305 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
306    black bkgnd.  [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
307 static const BYTE ramp[17] =
308 {
309     0,    0x4d, 0x68, 0x7c,
310     0x8c, 0x9a, 0xa7, 0xb2,
311     0xbd, 0xc7, 0xd0, 0xd9,
312     0xe1, 0xe9, 0xf0, 0xf8,
313     0xff
314 };
315
316 /* For a give text-color component and a glyph level, calculate the
317    range of dst intensities, the min/max corresponding to 0/0xff bkgnd
318    components respectively.
319
320    The minimum is a linear interpolation between 0 and the value in
321    the ramp table.
322
323    The maximum is a linear interpolation between the value from the
324    ramp table read in reverse and 0xff.
325
326    To find the resulting pixel intensity, we note that if the text and
327    bkgnd intensities are the same then the result must be that
328    intensity.  Otherwise we linearly interpolate between either the
329    min or the max value and this intermediate value depending on which
330    side of the inequality we lie.
331 */
332
333 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
334 {
335     *min_comp = (ramp[aa] * text_comp) / 0xff;
336     *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
337 }
338
339 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
340 {
341     int i;
342
343     for (i = 0; i < 17; i++)
344     {
345         get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
346         get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
347         get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
348     }
349 }
350
351 /**********************************************************************
352  *                 get_text_bkgnd_masks
353  *
354  * See the comment above get_pen_bkgnd_masks
355  */
356 static inline void get_text_bkgnd_masks( dibdrv_physdev *pdev, rop_mask *mask )
357 {
358     COLORREF bg = GetBkColor( pdev->dev.hdc );
359
360     mask->and = 0;
361
362     if (pdev->dib.bit_count != 1)
363         mask->xor = get_pixel_color( pdev, bg, FALSE );
364     else
365     {
366         COLORREF fg = GetTextColor( pdev->dev.hdc );
367         mask->xor = get_pixel_color( pdev, fg, TRUE );
368         if (fg != bg) mask->xor = ~mask->xor;
369     }
370 }
371
372 static void draw_glyph( dibdrv_physdev *pdev, const POINT *origin, const GLYPHMETRICS *metrics,
373                         const struct gdi_image_bits *image, DWORD text_color,
374                         const struct intensity_range *ranges, const struct clipped_rects *clipped_rects )
375 {
376     int i;
377     RECT rect, clipped_rect;
378     POINT src_origin;
379     dib_info glyph_dib;
380
381     glyph_dib.bit_count = 8;
382     glyph_dib.width     = metrics->gmBlackBoxX;
383     glyph_dib.height    = metrics->gmBlackBoxY;
384     glyph_dib.stride    = get_dib_stride( metrics->gmBlackBoxX, 8 );
385     glyph_dib.bits      = *image;
386
387     rect.left   = origin->x  + metrics->gmptGlyphOrigin.x;
388     rect.top    = origin->y  - metrics->gmptGlyphOrigin.y;
389     rect.right  = rect.left  + metrics->gmBlackBoxX;
390     rect.bottom = rect.top   + metrics->gmBlackBoxY;
391
392     for (i = 0; i < clipped_rects->count; i++)
393     {
394         if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
395         {
396             src_origin.x = clipped_rect.left - rect.left;
397             src_origin.y = clipped_rect.top  - rect.top;
398
399             pdev->dib.funcs->draw_glyph( &pdev->dib, &clipped_rect, &glyph_dib, &src_origin,
400                                          text_color, ranges );
401         }
402     }
403 }
404
405 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
406 static const int padding[4] = {0, 3, 2, 1};
407
408 /***********************************************************************
409  *         get_glyph_bitmap
410  *
411  * Retrieve a 17-level bitmap for the appropiate glyph.
412  *
413  * For non-antialiased bitmaps convert them to the 17-level format
414  * using only values 0 or 16.
415  */
416 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
417                                struct gdi_image_bits *image )
418 {
419     UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
420     static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
421     UINT indices[3] = {0, 0, 0x20};
422     int i, x, y;
423     DWORD ret, size;
424     BYTE *buf, *dst, *src;
425     int pad, stride;
426
427     image->ptr = NULL;
428     image->is_copy = FALSE;
429     image->free = free_heap_bits;
430     image->param = NULL;
431
432     indices[0] = index;
433
434     for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
435     {
436         index = indices[i];
437         ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
438         if (ret != GDI_ERROR) break;
439     }
440
441     if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
442     if (!ret) return ERROR_SUCCESS; /* empty glyph */
443
444     /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
445     pad = padding[ metrics->gmBlackBoxX % 4 ];
446     stride = get_dib_stride( metrics->gmBlackBoxX, 8 );
447     size = metrics->gmBlackBoxY * stride;
448
449     buf = HeapAlloc( GetProcessHeap(), 0, size );
450     if (!buf) return ERROR_OUTOFMEMORY;
451
452     ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
453     if (ret == GDI_ERROR)
454     {
455         HeapFree( GetProcessHeap(), 0, buf );
456         return ERROR_NOT_FOUND;
457     }
458
459     if (aa_flags == GGO_BITMAP)
460     {
461         for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
462         {
463             src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
464             dst = buf + y * stride;
465
466             if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
467
468             for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
469                 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
470         }
471     }
472     else if (pad)
473     {
474         for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
475             memset( dst + metrics->gmBlackBoxX, 0, pad );
476     }
477
478     image->ptr = buf;
479     return ERROR_SUCCESS;
480 }
481
482 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
483                                 struct bitblt_coords *src, INT x, INT y, UINT flags,
484                                 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
485 {
486     dib_info dib;
487     UINT i;
488     DWORD err;
489     BOOL got_pixel;
490     COLORREF fg, bg;
491     DWORD fg_pixel, bg_pixel;
492     struct intensity_range glyph_intensities[17];
493
494     assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
495
496     init_dib_info_from_bitmapinfo( &dib, info, bits->ptr, 0 );
497
498     fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
499     if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
500
501     get_aa_ranges( fg, glyph_intensities );
502
503     if (flags & ETO_OPAQUE)
504     {
505         rop_mask bkgnd_color;
506
507         bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
508         if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
509
510         bkgnd_color.and = 0;
511         bkgnd_color.xor = bg_pixel;
512         dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
513     }
514
515     for (i = 0; i < count; i++)
516     {
517         GLYPHMETRICS metrics;
518         struct gdi_image_bits image;
519
520         err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &image );
521         if (err) continue;
522
523         if (image.ptr)
524         {
525             RECT rect, clipped_rect;
526             POINT src_origin;
527             dib_info glyph_dib;
528
529             glyph_dib.bit_count = 8;
530             glyph_dib.width     = metrics.gmBlackBoxX;
531             glyph_dib.height    = metrics.gmBlackBoxY;
532             glyph_dib.stride    = get_dib_stride( metrics.gmBlackBoxX, 8 );
533             glyph_dib.bits      = image;
534
535             rect.left   = x + metrics.gmptGlyphOrigin.x;
536             rect.top    = y - metrics.gmptGlyphOrigin.y;
537             rect.right  = rect.left + metrics.gmBlackBoxX;
538             rect.bottom = rect.top  + metrics.gmBlackBoxY;
539
540             if (intersect_rect( &clipped_rect, &rect, &src->visrect ))
541             {
542                 src_origin.x = clipped_rect.left - rect.left;
543                 src_origin.y = clipped_rect.top  - rect.top;
544
545                 dib.funcs->draw_glyph( &dib, &clipped_rect, &glyph_dib, &src_origin,
546                                        fg_pixel, glyph_intensities );
547             }
548         }
549         if (image.free) image.free( &image );
550
551         if (dx)
552         {
553             if (flags & ETO_PDY)
554             {
555                 x += dx[ i * 2 ];
556                 y += dx[ i * 2 + 1];
557             }
558             else
559                 x += dx[ i ];
560         }
561         else
562         {
563             x += metrics.gmCellIncX;
564             y += metrics.gmCellIncY;
565         }
566     }
567     return TRUE;
568 }
569
570 /***********************************************************************
571  *           dibdrv_ExtTextOut
572  */
573 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
574                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
575 {
576     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
577     struct clipped_rects clipped_rects;
578     UINT aa_flags, i;
579     POINT origin;
580     DWORD text_color, err;
581     struct intensity_range ranges[17];
582
583     init_clipped_rects( &clipped_rects );
584
585     if (flags & ETO_OPAQUE)
586     {
587         rop_mask bkgnd_color;
588         get_text_bkgnd_masks( pdev, &bkgnd_color );
589         get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
590         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
591                                       bkgnd_color.and, bkgnd_color.xor );
592     }
593
594     if (count == 0) goto done;
595
596     if (flags & ETO_CLIPPED)
597     {
598         if (!(flags & ETO_OPAQUE))  /* otherwise we have done it already */
599             get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
600     }
601     else
602     {
603         free_clipped_rects( &clipped_rects );
604         get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
605     }
606     if (!clipped_rects.count) return TRUE;
607
608     text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
609     get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
610
611     aa_flags = get_font_aa_flags( dev->hdc );
612     origin.x = x;
613     origin.y = y;
614     for (i = 0; i < count; i++)
615     {
616         GLYPHMETRICS metrics;
617         struct gdi_image_bits image;
618
619         err = get_glyph_bitmap( dev->hdc, (UINT)str[i], aa_flags, &metrics, &image );
620         if (err) continue;
621
622         if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image, text_color, ranges, &clipped_rects );
623         if (image.free) image.free( &image );
624
625         if (dx)
626         {
627             if (flags & ETO_PDY)
628             {
629                 origin.x += dx[ i * 2 ];
630                 origin.y += dx[ i * 2 + 1];
631             }
632             else
633                 origin.x += dx[ i ];
634         }
635         else
636         {
637             origin.x += metrics.gmCellIncX;
638             origin.y += metrics.gmCellIncY;
639         }
640     }
641
642 done:
643     free_clipped_rects( &clipped_rects );
644     return TRUE;
645 }
646
647 /***********************************************************************
648  *           dibdrv_Arc
649  */
650 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
651                  INT start_x, INT start_y, INT end_x, INT end_y )
652 {
653     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
654 }
655
656 /***********************************************************************
657  *           dibdrv_ArcTo
658  */
659 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
660                    INT start_x, INT start_y, INT end_x, INT end_y )
661 {
662     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
663 }
664
665 /***********************************************************************
666  *           dibdrv_Chord
667  */
668 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
669                    INT start_x, INT start_y, INT end_x, INT end_y )
670 {
671     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
672 }
673
674 /***********************************************************************
675  *           dibdrv_Ellipse
676  */
677 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
678 {
679     return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
680 }
681
682 /***********************************************************************
683  *           dibdrv_ExtFloodFill
684  */
685 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
686 {
687     FIXME( "not implemented yet\n" );
688     return TRUE;
689 }
690
691 /***********************************************************************
692  *           dibdrv_GetNearestColor
693  */
694 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
695 {
696     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
697     DWORD pixel;
698
699     TRACE( "(%p, %08x)\n", dev, color );
700
701     pixel = get_pixel_color( pdev, color, FALSE );
702     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
703 }
704
705 /***********************************************************************
706  *           dibdrv_GetPixel
707  */
708 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
709 {
710     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
711     POINT pt;
712     DWORD pixel;
713
714     TRACE( "(%p, %d, %d)\n", dev, x, y );
715
716     pt.x = x;
717     pt.y = y;
718     LPtoDP( dev->hdc, &pt, 1 );
719
720     if (pt.x < 0 || pt.x >= pdev->dib.width ||
721         pt.y < 0 || pt.y >= pdev->dib.height)
722         return CLR_INVALID;
723
724     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
725     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
726 }
727
728 /***********************************************************************
729  *           dibdrv_LineTo
730  */
731 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
732 {
733     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
734     POINT pts[2];
735     HRGN region = 0;
736     BOOL ret;
737
738     GetCurrentPositionEx(dev->hdc, pts);
739     pts[1].x = x;
740     pts[1].y = y;
741
742     LPtoDP(dev->hdc, pts, 2);
743
744     if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
745
746     reset_dash_origin(pdev);
747
748     ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
749
750     if (region)
751     {
752         if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
753         ret = pen_region( pdev, region );
754         DeleteObject( region );
755     }
756     return ret;
757 }
758
759 /***********************************************************************
760  *           get_rop2_from_rop
761  *
762  * Returns the binary rop that is equivalent to the provided ternary rop
763  * if the src bits are ignored.
764  */
765 static inline INT get_rop2_from_rop(INT rop)
766 {
767     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
768 }
769
770 /***********************************************************************
771  *           dibdrv_PatBlt
772  */
773 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
774 {
775     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
776
777     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
778
779     return brush_rect( pdev, &pdev->brush, &dst->visrect, pdev->clip, get_rop2_from_rop(rop) );
780 }
781
782 /***********************************************************************
783  *           dibdrv_PaintRgn
784  */
785 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
786 {
787     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
788     const WINEREGION *region;
789     int i;
790     RECT rect;
791
792     TRACE("%p, %p\n", dev, rgn);
793
794     region = get_wine_region( rgn );
795     if(!region) return FALSE;
796
797     for(i = 0; i < region->numRects; i++)
798     {
799         rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
800                                 region->rects[i].right, region->rects[i].bottom, FALSE );
801         brush_rect( pdev, &pdev->brush, &rect, pdev->clip, GetROP2( dev->hdc ) );
802     }
803
804     release_wine_region( rgn );
805     return TRUE;
806 }
807
808 /***********************************************************************
809  *           dibdrv_PolyPolygon
810  */
811 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
812 {
813     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
814     DWORD total, i, pos;
815     BOOL ret = TRUE;
816     POINT *points;
817     HRGN outline = 0, interior = 0;
818
819     for (i = total = 0; i < polygons; i++)
820     {
821         if (counts[i] < 2) return FALSE;
822         total += counts[i];
823     }
824
825     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
826     if (!points) return FALSE;
827     memcpy( points, pt, total * sizeof(*pt) );
828     LPtoDP( dev->hdc, points, total );
829
830     if (pdev->brush.style != BS_NULL &&
831         !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
832     {
833         HeapFree( GetProcessHeap(), 0, points );
834         return FALSE;
835     }
836
837     if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
838
839     /* if not using a region, paint the interior first so the outline can overlap it */
840     if (interior && !outline)
841     {
842         ret = brush_region( pdev, interior );
843         DeleteObject( interior );
844         interior = 0;
845     }
846
847     for (i = pos = 0; i < polygons; i++)
848     {
849         reset_dash_origin( pdev );
850         pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
851         pos += counts[i];
852     }
853
854     if (interior)
855     {
856         CombineRgn( interior, interior, outline, RGN_DIFF );
857         ret = brush_region( pdev, interior );
858         DeleteObject( interior );
859     }
860     if (outline)
861     {
862         if (ret) ret = pen_region( pdev, outline );
863         DeleteObject( outline );
864     }
865     HeapFree( GetProcessHeap(), 0, points );
866     return ret;
867 }
868
869 /***********************************************************************
870  *           dibdrv_PolyPolyline
871  */
872 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
873 {
874     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
875     DWORD max_points = 0, i;
876     POINT *points;
877     BOOL ret = TRUE;
878     HRGN outline = 0;
879
880     for (i = 0; i < polylines; i++)
881     {
882         if (counts[i] < 2) return FALSE;
883         max_points = max( counts[i], max_points );
884     }
885
886     points = HeapAlloc( GetProcessHeap(), 0, max_points * sizeof(*pt) );
887     if (!points) return FALSE;
888
889     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
890     {
891         HeapFree( GetProcessHeap(), 0, points );
892         return FALSE;
893     }
894
895     for (i = 0; i < polylines; i++)
896     {
897         memcpy( points, pt, counts[i] * sizeof(*pt) );
898         pt += counts[i];
899         LPtoDP( dev->hdc, points, counts[i] );
900
901         reset_dash_origin( pdev );
902         pdev->pen_lines( pdev, counts[i], points, FALSE, outline );
903     }
904
905     if (outline)
906     {
907         if (pdev->clip) CombineRgn( outline, outline, pdev->clip, RGN_AND );
908         ret = pen_region( pdev, outline );
909         DeleteObject( outline );
910     }
911
912     HeapFree( GetProcessHeap(), 0, points );
913     return ret;
914 }
915
916 /***********************************************************************
917  *           dibdrv_Polygon
918  */
919 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
920 {
921     INT counts[1] = { count };
922
923     return dibdrv_PolyPolygon( dev, pt, counts, 1 );
924 }
925
926 /***********************************************************************
927  *           dibdrv_Polyline
928  */
929 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
930 {
931     DWORD counts[1] = { count };
932
933     if (count < 0) return FALSE;
934     return dibdrv_PolyPolyline( dev, pt, counts, 1 );
935 }
936
937 /***********************************************************************
938  *           dibdrv_Rectangle
939  */
940 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
941 {
942     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
943     RECT rect;
944     POINT pts[4];
945     BOOL ret;
946     HRGN outline = 0;
947
948     TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
949
950     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
951
952     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
953
954     rect.right--;
955     rect.bottom--;
956     reset_dash_origin(pdev);
957
958     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
959     {
960         /* 4 pts going clockwise starting from bottom-right */
961         pts[0].x = pts[3].x = rect.right;
962         pts[0].y = pts[1].y = rect.bottom;
963         pts[1].x = pts[2].x = rect.left;
964         pts[2].y = pts[3].y = rect.top;
965     }
966     else
967     {
968         /* 4 pts going anti-clockwise starting from top-right */
969         pts[0].x = pts[3].x = rect.right;
970         pts[0].y = pts[1].y = rect.top;
971         pts[1].x = pts[2].x = rect.left;
972         pts[2].y = pts[3].y = rect.bottom;
973     }
974
975     pdev->pen_lines(pdev, 4, pts, TRUE, outline);
976
977     if (outline)
978     {
979         if (pdev->brush.style != BS_NULL)
980         {
981             HRGN interior = CreateRectRgnIndirect( &rect );
982
983             CombineRgn( interior, interior, outline, RGN_DIFF );
984             brush_region( pdev, interior );
985             DeleteObject( interior );
986         }
987         ret = pen_region( pdev, outline );
988         DeleteObject( outline );
989     }
990     else
991     {
992         rect.left   += (pdev->pen_width + 1) / 2;
993         rect.top    += (pdev->pen_width + 1) / 2;
994         rect.right  -= pdev->pen_width / 2;
995         rect.bottom -= pdev->pen_width / 2;
996         ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip, GetROP2(dev->hdc) );
997     }
998     return ret;
999 }
1000
1001 /***********************************************************************
1002  *           dibdrv_RoundRect
1003  */
1004 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1005                        INT ellipse_width, INT ellipse_height )
1006 {
1007     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1008     RECT rect;
1009     POINT pt[2], *points;
1010     int i, end, count;
1011     BOOL ret = TRUE;
1012     HRGN outline = 0, interior = 0;
1013
1014     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1015
1016     pt[0].x = pt[0].y = 0;
1017     pt[1].x = ellipse_width;
1018     pt[1].y = ellipse_height;
1019     LPtoDP( dev->hdc, pt, 2 );
1020     ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1021     ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1022     if (ellipse_width <= 2|| ellipse_height <= 2)
1023         return dibdrv_Rectangle( dev, left, top, right, bottom );
1024
1025     points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1026     if (!points) return FALSE;
1027
1028     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1029     {
1030         HeapFree( GetProcessHeap(), 0, points );
1031         return FALSE;
1032     }
1033
1034     if (pdev->brush.style != BS_NULL &&
1035         !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1036                                          ellipse_width, ellipse_height )))
1037     {
1038         HeapFree( GetProcessHeap(), 0, points );
1039         return FALSE;
1040     }
1041
1042     if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1043
1044     /* if not using a region, paint the interior first so the outline can overlap it */
1045     if (interior && !outline)
1046     {
1047         ret = brush_region( pdev, interior );
1048         DeleteObject( interior );
1049         interior = 0;
1050     }
1051
1052     count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1053
1054     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1055     {
1056         for (i = 0; i < count; i++)
1057         {
1058             points[i].x = rect.right - ellipse_width + points[i].x;
1059             points[i].y = rect.bottom - ellipse_height + points[i].y;
1060         }
1061     }
1062     else
1063     {
1064         for (i = 0; i < count; i++)
1065         {
1066             points[i].x = rect.right - ellipse_width + points[i].x;
1067             points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1068         }
1069     }
1070
1071     /* horizontal symmetry */
1072
1073     end = 2 * count - 1;
1074     /* avoid duplicating the midpoint */
1075     if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1076     for (i = 0; i < count; i++)
1077     {
1078         points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1079         points[end - i].y = points[i].y;
1080     }
1081     count = end + 1;
1082
1083     /* vertical symmetry */
1084
1085     end = 2 * count - 1;
1086     /* avoid duplicating the midpoint */
1087     if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1088     for (i = 0; i < count; i++)
1089     {
1090         points[end - i].x = points[i].x;
1091         points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1092     }
1093     count = end + 1;
1094
1095     reset_dash_origin( pdev );
1096     pdev->pen_lines( pdev, count, points, TRUE, outline );
1097
1098     if (interior)
1099     {
1100         CombineRgn( interior, interior, outline, RGN_DIFF );
1101         ret = brush_region( pdev, interior );
1102         DeleteObject( interior );
1103     }
1104     if (outline)
1105     {
1106         if (ret) ret = pen_region( pdev, outline );
1107         DeleteObject( outline );
1108     }
1109     HeapFree( GetProcessHeap(), 0, points );
1110     return ret;
1111 }
1112
1113 /***********************************************************************
1114  *           dibdrv_Pie
1115  */
1116 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1117                  INT start_x, INT start_y, INT end_x, INT end_y )
1118 {
1119     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1120 }
1121
1122 /***********************************************************************
1123  *           dibdrv_SetPixel
1124  */
1125 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1126 {
1127     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1128     struct clipped_rects clipped_rects;
1129     RECT rect;
1130     POINT pt;
1131     DWORD pixel;
1132
1133     TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1134
1135     pt.x = x;
1136     pt.y = y;
1137     LPtoDP( dev->hdc, &pt, 1 );
1138     rect.left = pt.x;
1139     rect.top =  pt.y;
1140     rect.right = rect.left + 1;
1141     rect.bottom = rect.top + 1;
1142
1143     /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1144     pixel = get_pixel_color( pdev, color, FALSE );
1145     color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1146
1147     if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1148     pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1149     free_clipped_rects( &clipped_rects );
1150     return color;
1151 }