Release 1.5.29.
[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/unicode.h"
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(dib);
29
30 struct cached_glyph
31 {
32     GLYPHMETRICS metrics;
33     BYTE         bits[1];
34 };
35
36 enum glyph_type
37 {
38     GLYPH_INDEX,
39     GLYPH_WCHAR,
40     GLYPH_NBTYPES
41 };
42
43 #define GLYPH_CACHE_PAGE_SIZE  0x100
44 #define GLYPH_CACHE_PAGES      (0x10000 / GLYPH_CACHE_PAGE_SIZE)
45
46 struct cached_font
47 {
48     struct list           entry;
49     LONG                  ref;
50     DWORD                 hash;
51     LOGFONTW              lf;
52     XFORM                 xform;
53     UINT                  aa_flags;
54     struct cached_glyph **glyphs[GLYPH_NBTYPES][GLYPH_CACHE_PAGES];
55 };
56
57 static struct list font_cache = LIST_INIT( font_cache );
58
59 static CRITICAL_SECTION font_cache_cs;
60 static CRITICAL_SECTION_DEBUG critsect_debug =
61 {
62     0, 0, &font_cache_cs,
63     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
64       0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_cs") }
65 };
66 static CRITICAL_SECTION font_cache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
67
68
69 static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
70 {
71     struct clipped_rects clipped_rects;
72     BOOL ret;
73
74     if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
75     ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
76                         GetROP2( pdev->dev.hdc ));
77     free_clipped_rects( &clipped_rects );
78     return ret;
79 }
80
81 /* paint a region with the brush (note: the region can be modified) */
82 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
83 {
84     if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
85     return brush_rect( pdev, &pdev->brush, NULL, region );
86 }
87
88 /* paint a region with the pen (note: the region can be modified) */
89 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
90 {
91     if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
92     return brush_rect( pdev, &pdev->pen_brush, NULL, region );
93 }
94
95 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
96 {
97     RECT rect;
98
99     rect.left   = left;
100     rect.top    = top;
101     rect.right  = right;
102     rect.bottom = bottom;
103     if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
104     {
105         /* shift the rectangle so that the right border is included after mirroring */
106         /* it would be more correct to do this after LPtoDP but that's not what Windows does */
107         rect.left--;
108         rect.right--;
109     }
110     LPtoDP( hdc, (POINT *)&rect, 2 );
111     order_rect( &rect );
112     return rect;
113 }
114
115 static BOOL get_pen_device_rect( dibdrv_physdev *dev, RECT *rect, int left, int top, int right, int bottom )
116 {
117     *rect = get_device_rect( dev->dev.hdc, left, top, right, bottom, TRUE );
118     if (rect->left == rect->right || rect->top == rect->bottom) return FALSE;
119
120     if (dev->pen_style == PS_INSIDEFRAME)
121     {
122         rect->left   += dev->pen_width / 2;
123         rect->top    += dev->pen_width / 2;
124         rect->right  -= (dev->pen_width - 1) / 2;
125         rect->bottom -= (dev->pen_width - 1) / 2;
126     }
127     return TRUE;
128 }
129
130 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
131 {
132     const WINEREGION *region;
133     RECT bounds, rect;
134     int width = 0;
135
136     if (!dev->bounds) return;
137     reset_bounds( &bounds );
138
139     if (dev->pen_uses_region)
140     {
141         /* Windows uses some heuristics to estimate the distance from the point that will be painted */
142         width = dev->pen_width + 2;
143         if (dev->pen_join == PS_JOIN_MITER)
144         {
145             width *= 5;
146             if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
147         }
148         else
149         {
150             if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4;
151             else width = (width + 1) / 2;
152         }
153
154         /* in case the heuristics are wrong, add the actual region too */
155         if ((region = get_wine_region( rgn )))
156         {
157             add_bounds_rect( &bounds, &region->extents );
158             release_wine_region( rgn );
159         }
160     }
161
162     while (count-- > 0)
163     {
164         rect.left   = points->x - width;
165         rect.top    = points->y - width;
166         rect.right  = points->x + width + 1;
167         rect.bottom = points->y + width + 1;
168         add_bounds_rect( &bounds, &rect );
169         points++;
170     }
171
172     add_clipped_bounds( dev, &bounds, dev->clip );
173 }
174
175 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
176 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
177 static int ellipse_first_quadrant( int width, int height, POINT *data )
178 {
179     const int a = width - 1;
180     const int b = height - 1;
181     const INT64 asq = (INT64)8 * a * a;
182     const INT64 bsq = (INT64)8 * b * b;
183     INT64 dx  = (INT64)4 * b * b * (1 - a);
184     INT64 dy  = (INT64)4 * a * a * (1 + (b % 2));
185     INT64 err = dx + dy + a * a * (b % 2);
186     int pos = 0;
187     POINT pt;
188
189     pt.x = a;
190     pt.y = height / 2;
191
192     /* based on an algorithm by Alois Zingl */
193
194     while (pt.x >= width / 2)
195     {
196         INT64 e2 = 2 * err;
197         data[pos++] = pt;
198         if (e2 >= dx)
199         {
200             pt.x--;
201             err += dx += bsq;
202         }
203         if (e2 <= dy)
204         {
205             pt.y++;
206             err += dy += asq;
207         }
208     }
209     return pos;
210 }
211
212 static int find_intersection( const POINT *points, int x, int y, int count )
213 {
214     int i;
215
216     if (y >= 0)
217     {
218         if (x >= 0)  /* first quadrant */
219         {
220             for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
221             return i;
222         }
223         /* second quadrant */
224         for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break;
225         return 2 * count - i;
226     }
227     if (x >= 0)  /* fourth quadrant */
228     {
229         for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break;
230         return 4 * count - i;
231     }
232     /* third quadrant */
233     for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break;
234     return 2 * count + i;
235 }
236
237 static int get_arc_points( PHYSDEV dev, const RECT *rect, POINT start, POINT end, POINT *points )
238 {
239     int i, pos, count, start_pos, end_pos;
240     int width = rect->right - rect->left;
241     int height = rect->bottom - rect->top;
242
243     count = ellipse_first_quadrant( width, height, points );
244     for (i = 0; i < count; i++)
245     {
246         points[i].x -= width / 2;
247         points[i].y -= height / 2;
248     }
249     if (GetArcDirection( dev->hdc ) != AD_CLOCKWISE)
250     {
251         start.y = -start.y;
252         end.y = -end.y;
253     }
254     start_pos = find_intersection( points, start.x, start.y, count );
255     end_pos = find_intersection( points, end.x, end.y, count );
256     if (end_pos <= start_pos) end_pos += 4 * count;
257
258     pos = count;
259     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
260     {
261         for (i = start_pos; i < end_pos; i++, pos++)
262         {
263             switch ((i / count) % 4)
264             {
265             case 0:
266                 points[pos].x = rect->left + width/2 + points[i % count].x;
267                 points[pos].y = rect->top + height/2 + points[i % count].y;
268                 break;
269             case 1:
270                 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
271                 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
272                 break;
273             case 2:
274                 points[pos].x = rect->left + width/2 - points[i % count].x;
275                 points[pos].y = rect->top + height/2 - points[i % count].y;
276                 break;
277             case 3:
278                 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
279                 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
280                 break;
281             }
282         }
283     }
284     else
285     {
286         for (i = start_pos; i < end_pos; i++, pos++)
287         {
288             switch ((i / count) % 4)
289             {
290             case 0:
291                 points[pos].x = rect->left + width/2 + points[i % count].x;
292                 points[pos].y = rect->top + height/2 - points[i % count].y;
293                 break;
294             case 1:
295                 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
296                 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
297                 break;
298             case 2:
299                 points[pos].x = rect->left + width/2 - points[i % count].x;
300                 points[pos].y = rect->top + height/2 + points[i % count].y;
301                 break;
302             case 3:
303                 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
304                 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
305                 break;
306             }
307         }
308     }
309
310     memmove( points, points + count, (pos - count) * sizeof(POINT) );
311     return pos - count;
312 }
313
314 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
315 static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
316                       INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines )
317 {
318     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
319     RECT rect;
320     POINT pt[2], *points;
321     int width, height, count;
322     BOOL ret = TRUE;
323     HRGN outline = 0, interior = 0;
324
325     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
326
327     width = rect.right - rect.left;
328     height = rect.bottom - rect.top;
329
330     pt[0].x = start_x;
331     pt[0].y = start_y;
332     pt[1].x = end_x;
333     pt[1].y = end_y;
334     LPtoDP( dev->hdc, pt, 2 );
335     /* make them relative to the ellipse center */
336     pt[0].x -= rect.left + width / 2;
337     pt[0].y -= rect.top + height / 2;
338     pt[1].x -= rect.left + width / 2;
339     pt[1].y -= rect.top + height / 2;
340
341     points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) );
342     if (!points) return FALSE;
343
344     if (extra_lines == -1)
345     {
346         GetCurrentPositionEx( dev->hdc, points );
347         LPtoDP( dev->hdc, points, 1 );
348         count = 1 + get_arc_points( dev, &rect, pt[0], pt[1], points + 1 );
349     }
350     else count = get_arc_points( dev, &rect, pt[0], pt[1], points );
351
352     if (extra_lines == 2)
353     {
354         points[count].x = rect.left + width / 2;
355         points[count].y = rect.top + height / 2;
356         count++;
357     }
358     if (count < 2)
359     {
360         HeapFree( GetProcessHeap(), 0, points );
361         return TRUE;
362     }
363
364     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
365     {
366         HeapFree( GetProcessHeap(), 0, points );
367         return FALSE;
368     }
369
370     if (pdev->brush.style != BS_NULL && extra_lines > 0 &&
371         !(interior = CreatePolygonRgn( points, count, WINDING )))
372     {
373         HeapFree( GetProcessHeap(), 0, points );
374         if (outline) DeleteObject( outline );
375         return FALSE;
376     }
377
378     /* if not using a region, paint the interior first so the outline can overlap it */
379     if (interior && !outline)
380     {
381         ret = brush_region( pdev, interior );
382         DeleteObject( interior );
383         interior = 0;
384     }
385
386     reset_dash_origin( pdev );
387     pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
388     add_pen_lines_bounds( pdev, count, points, outline );
389
390     if (interior)
391     {
392         CombineRgn( interior, interior, outline, RGN_DIFF );
393         ret = brush_region( pdev, interior );
394         DeleteObject( interior );
395     }
396     if (outline)
397     {
398         if (ret) ret = pen_region( pdev, outline );
399         DeleteObject( outline );
400     }
401     HeapFree( GetProcessHeap(), 0, points );
402     return ret;
403 }
404
405 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
406    black bkgnd.  [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
407 static const BYTE ramp[17] =
408 {
409     0,    0x4d, 0x68, 0x7c,
410     0x8c, 0x9a, 0xa7, 0xb2,
411     0xbd, 0xc7, 0xd0, 0xd9,
412     0xe1, 0xe9, 0xf0, 0xf8,
413     0xff
414 };
415
416 /* For a give text-color component and a glyph level, calculate the
417    range of dst intensities, the min/max corresponding to 0/0xff bkgnd
418    components respectively.
419
420    The minimum is a linear interpolation between 0 and the value in
421    the ramp table.
422
423    The maximum is a linear interpolation between the value from the
424    ramp table read in reverse and 0xff.
425
426    To find the resulting pixel intensity, we note that if the text and
427    bkgnd intensities are the same then the result must be that
428    intensity.  Otherwise we linearly interpolate between either the
429    min or the max value and this intermediate value depending on which
430    side of the inequality we lie.
431 */
432
433 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
434 {
435     *min_comp = (ramp[aa] * text_comp) / 0xff;
436     *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
437 }
438
439 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
440 {
441     int i;
442
443     for (i = 0; i < 17; i++)
444     {
445         get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
446         get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
447         get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
448     }
449 }
450
451 static DWORD font_cache_hash( struct cached_font *font )
452 {
453     DWORD hash = 0, *ptr, two_chars;
454     WORD *pwc;
455     int i;
456
457     hash ^= font->aa_flags;
458     for(i = 0, ptr = (DWORD*)&font->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
459         hash ^= *ptr;
460     for(i = 0, ptr = (DWORD*)&font->lf; i < 7; i++, ptr++)
461         hash ^= *ptr;
462     for(i = 0, ptr = (DWORD*)font->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
463         two_chars = *ptr;
464         pwc = (WCHAR *)&two_chars;
465         if (!*pwc) break;
466         *pwc = toupperW(*pwc);
467         pwc++;
468         *pwc = toupperW(*pwc);
469         hash ^= two_chars;
470         if (!*pwc) break;
471     }
472     return hash;
473 }
474
475 static int font_cache_cmp( const struct cached_font *p1, const struct cached_font *p2 )
476 {
477     int ret = p1->hash - p2->hash;
478     if (!ret) ret = p1->aa_flags - p2->aa_flags;
479     if (!ret) ret = memcmp( &p1->xform, &p2->xform, sizeof(p1->xform) );
480     if (!ret) ret = memcmp( &p1->lf, &p2->lf, FIELD_OFFSET( LOGFONTW, lfFaceName ));
481     if (!ret) ret = strcmpiW( p1->lf.lfFaceName, p2->lf.lfFaceName );
482     return ret;
483 }
484
485 static struct cached_font *add_cached_font( HDC hdc, HFONT hfont, UINT aa_flags )
486 {
487     struct cached_font font, *ptr, *last_unused = NULL;
488     UINT i = 0, j, k;
489
490     GetObjectW( hfont, sizeof(font.lf), &font.lf );
491     GetTransform( hdc, 0x204, &font.xform );
492     font.xform.eDx = font.xform.eDy = 0;  /* unused, would break hashing */
493     if (GetGraphicsMode( hdc ) == GM_COMPATIBLE)
494     {
495         font.lf.lfOrientation = font.lf.lfEscapement;
496         if (font.xform.eM11 * font.xform.eM22 < 0)
497             font.lf.lfOrientation = -font.lf.lfOrientation;
498     }
499     font.lf.lfWidth = abs( font.lf.lfWidth );
500     font.aa_flags = aa_flags;
501     font.hash = font_cache_hash( &font );
502
503     EnterCriticalSection( &font_cache_cs );
504     LIST_FOR_EACH_ENTRY( ptr, &font_cache, struct cached_font, entry )
505     {
506         if (!font_cache_cmp( &font, ptr ))
507         {
508             InterlockedIncrement( &ptr->ref );
509             list_remove( &ptr->entry );
510             goto done;
511         }
512         if (!ptr->ref)
513         {
514             i++;
515             last_unused = ptr;
516         }
517     }
518
519     if (i > 5)  /* keep at least 5 of the most-recently used fonts around */
520     {
521         ptr = last_unused;
522         for (i = 0; i < GLYPH_NBTYPES; i++)
523         {
524             for (j = 0; j < GLYPH_CACHE_PAGES; j++)
525             {
526                 if (!ptr->glyphs[i][j]) continue;
527                 for (k = 0; k < GLYPH_CACHE_PAGE_SIZE; k++)
528                     HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j][k] );
529                 HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j] );
530             }
531         }
532         list_remove( &ptr->entry );
533     }
534     else if (!(ptr = HeapAlloc( GetProcessHeap(), 0, sizeof(*ptr) )))
535     {
536         LeaveCriticalSection( &font_cache_cs );
537         return NULL;
538     }
539
540     *ptr = font;
541     ptr->ref = 1;
542     memset( ptr->glyphs, 0, sizeof(ptr->glyphs) );
543 done:
544     list_add_head( &font_cache, &ptr->entry );
545     LeaveCriticalSection( &font_cache_cs );
546     TRACE( "%d %s -> %p\n", ptr->lf.lfHeight, debugstr_w(ptr->lf.lfFaceName), ptr );
547     return ptr;
548 }
549
550 void release_cached_font( struct cached_font *font )
551 {
552     if (font) InterlockedDecrement( &font->ref );
553 }
554
555 static struct cached_glyph *add_cached_glyph( struct cached_font *font, UINT index, UINT flags,
556                                               struct cached_glyph *glyph )
557 {
558     struct cached_glyph *ret;
559     enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
560     UINT page = index / GLYPH_CACHE_PAGE_SIZE;
561     UINT entry = index % GLYPH_CACHE_PAGE_SIZE;
562
563     if (!font->glyphs[type][page])
564     {
565         struct cached_glyph **ptr;
566
567         ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, GLYPH_CACHE_PAGE_SIZE * sizeof(*ptr) );
568         if (!ptr)
569         {
570             HeapFree( GetProcessHeap(), 0, glyph );
571             return NULL;
572         }
573         if (InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page], ptr, NULL ))
574             HeapFree( GetProcessHeap(), 0, ptr );
575     }
576     ret = InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page][entry], glyph, NULL );
577     if (!ret) ret = glyph;
578     else HeapFree( GetProcessHeap(), 0, glyph );
579     return ret;
580 }
581
582 static struct cached_glyph *get_cached_glyph( struct cached_font *font, UINT index, UINT flags )
583 {
584     enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
585     UINT page = index / GLYPH_CACHE_PAGE_SIZE;
586
587     if (!font->glyphs[type][page]) return NULL;
588     return font->glyphs[type][page][index % GLYPH_CACHE_PAGE_SIZE];
589 }
590
591 /**********************************************************************
592  *                 get_text_bkgnd_masks
593  *
594  * See the comment above get_pen_bkgnd_masks
595  */
596 static inline void get_text_bkgnd_masks( HDC hdc, const dib_info *dib, rop_mask *mask )
597 {
598     COLORREF bg = GetBkColor( hdc );
599
600     mask->and = 0;
601
602     if (dib->bit_count != 1)
603         mask->xor = get_pixel_color( hdc, dib, bg, FALSE );
604     else
605     {
606         COLORREF fg = GetTextColor( hdc );
607         mask->xor = get_pixel_color( hdc, dib, fg, TRUE );
608         if (fg != bg) mask->xor = ~mask->xor;
609     }
610 }
611
612 static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics,
613                         const dib_info *glyph_dib, DWORD text_color,
614                         const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
615                         RECT *bounds )
616 {
617     int i;
618     RECT rect, clipped_rect;
619     POINT src_origin;
620
621     rect.left   = x          + metrics->gmptGlyphOrigin.x;
622     rect.top    = y          - metrics->gmptGlyphOrigin.y;
623     rect.right  = rect.left  + metrics->gmBlackBoxX;
624     rect.bottom = rect.top   + metrics->gmBlackBoxY;
625     if (bounds) add_bounds_rect( bounds, &rect );
626
627     for (i = 0; i < clipped_rects->count; i++)
628     {
629         if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
630         {
631             src_origin.x = clipped_rect.left - rect.left;
632             src_origin.y = clipped_rect.top  - rect.top;
633
634             if (glyph_dib->bit_count == 32)
635                 dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
636                                                  text_color );
637             else
638                 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
639                                         text_color, ranges );
640         }
641     }
642 }
643
644 static int get_glyph_depth( UINT aa_flags )
645 {
646     switch (aa_flags)
647     {
648     case GGO_BITMAP: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */
649     case GGO_GRAY2_BITMAP:
650     case GGO_GRAY4_BITMAP:
651     case GGO_GRAY8_BITMAP:
652     case WINE_GGO_GRAY16_BITMAP: return 8;
653
654     case WINE_GGO_HRGB_BITMAP:
655     case WINE_GGO_HBGR_BITMAP:
656     case WINE_GGO_VRGB_BITMAP:
657     case WINE_GGO_VBGR_BITMAP: return 32;
658
659     default:
660         ERR("Unexpected flags %08x\n", aa_flags);
661         return 0;
662     }
663 }
664
665 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
666 static const int padding[4] = {0, 3, 2, 1};
667
668 /***********************************************************************
669  *         cache_glyph_bitmap
670  *
671  * Retrieve a 17-level bitmap for the appropriate glyph.
672  *
673  * For non-antialiased bitmaps convert them to the 17-level format
674  * using only values 0 or 16.
675  */
676 static struct cached_glyph *cache_glyph_bitmap( HDC hdc, struct cached_font *font, UINT index, UINT flags )
677 {
678     UINT ggo_flags = font->aa_flags;
679     static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
680     UINT indices[3] = {0, 0, 0x20};
681     int i, x, y;
682     DWORD ret, size;
683     BYTE *dst, *src;
684     int pad = 0, stride, bit_count;
685     GLYPHMETRICS metrics;
686     struct cached_glyph *glyph;
687
688     if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
689     indices[0] = index;
690     for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
691     {
692         index = indices[i];
693         ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, 0, NULL, &identity );
694         if (ret != GDI_ERROR) break;
695     }
696     if (ret == GDI_ERROR) return NULL;
697
698     bit_count = get_glyph_depth( font->aa_flags );
699     stride = get_dib_stride( metrics.gmBlackBoxX, bit_count );
700     size = metrics.gmBlackBoxY * stride;
701     glyph = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( struct cached_glyph, bits[size] ));
702     if (!glyph) return NULL;
703     if (!ret) goto done;  /* zero-size glyph */
704
705     if (bit_count == 8) pad = padding[ metrics.gmBlackBoxX % 4 ];
706
707     ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, size, glyph->bits, &identity );
708     if (ret == GDI_ERROR)
709     {
710         HeapFree( GetProcessHeap(), 0, glyph );
711         return NULL;
712     }
713     assert( ret <= size );
714     if (font->aa_flags == GGO_BITMAP)
715     {
716         for (y = metrics.gmBlackBoxY - 1; y >= 0; y--)
717         {
718             src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 );
719             dst = glyph->bits + y * stride;
720
721             if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad );
722
723             for (x = metrics.gmBlackBoxX - 1; x >= 0; x--)
724                 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
725         }
726     }
727     else if (pad)
728     {
729         for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride)
730             memset( dst + metrics.gmBlackBoxX, 0, pad );
731     }
732
733 done:
734     glyph->metrics = metrics;
735     return add_cached_glyph( font, index, flags, glyph );
736 }
737
738 static void render_string( HDC hdc, dib_info *dib, struct cached_font *font, INT x, INT y,
739                            UINT flags, const WCHAR *str, UINT count, const INT *dx,
740                            const struct clipped_rects *clipped_rects, RECT *bounds )
741 {
742     UINT i;
743     struct cached_glyph *glyph;
744     dib_info glyph_dib;
745     DWORD text_color;
746     struct intensity_range ranges[17];
747
748     glyph_dib.bit_count    = get_glyph_depth( font->aa_flags );
749     glyph_dib.rect.left    = 0;
750     glyph_dib.rect.top     = 0;
751     glyph_dib.bits.is_copy = FALSE;
752     glyph_dib.bits.free    = NULL;
753
754     text_color = get_pixel_color( hdc, dib, GetTextColor( hdc ), TRUE );
755
756     if (glyph_dib.bit_count == 8)
757         get_aa_ranges( dib->funcs->pixel_to_colorref( dib, text_color ), ranges );
758
759     for (i = 0; i < count; i++)
760     {
761         if (!(glyph = get_cached_glyph( font, str[i], flags )) &&
762             !(glyph = cache_glyph_bitmap( hdc, font, str[i], flags ))) continue;
763
764         glyph_dib.width       = glyph->metrics.gmBlackBoxX;
765         glyph_dib.height      = glyph->metrics.gmBlackBoxY;
766         glyph_dib.rect.right  = glyph->metrics.gmBlackBoxX;
767         glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY;
768         glyph_dib.stride      = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count );
769         glyph_dib.bits.ptr    = glyph->bits;
770
771         draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
772
773         if (dx)
774         {
775             if (flags & ETO_PDY)
776             {
777                 x += dx[ i * 2 ];
778                 y += dx[ i * 2 + 1];
779             }
780             else
781                 x += dx[ i ];
782         }
783         else
784         {
785             x += glyph->metrics.gmCellIncX;
786             y += glyph->metrics.gmCellIncY;
787         }
788     }
789 }
790
791 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
792                                 struct bitblt_coords *src, INT x, INT y, UINT flags,
793                                 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
794 {
795     dib_info dib;
796     struct clipped_rects visrect;
797     struct cached_font *font;
798
799     assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
800
801     init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
802
803     visrect.count = 1;
804     visrect.rects = &src->visrect;
805
806     if (flags & ETO_OPAQUE)
807     {
808         rop_mask bkgnd_color;
809         get_text_bkgnd_masks( hdc, &dib, &bkgnd_color );
810         dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
811     }
812
813     if (!(font = add_cached_font( hdc, GetCurrentObject( hdc, OBJ_FONT ), aa_flags ))) return FALSE;
814
815     render_string( hdc, &dib, font, x, y, flags, str, count, dx, &visrect, NULL );
816     release_cached_font( font );
817     return TRUE;
818 }
819
820 /***********************************************************************
821  *           dibdrv_ExtTextOut
822  */
823 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
824                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
825 {
826     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
827     struct clipped_rects clipped_rects;
828     RECT bounds;
829
830     if (!pdev->font) return FALSE;
831
832     init_clipped_rects( &clipped_rects );
833     reset_bounds( &bounds );
834
835     if (flags & ETO_OPAQUE)
836     {
837         rop_mask bkgnd_color;
838         get_text_bkgnd_masks( dev->hdc, &pdev->dib, &bkgnd_color );
839         add_bounds_rect( &bounds, rect );
840         get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
841         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
842                                       bkgnd_color.and, bkgnd_color.xor );
843     }
844
845     if (count == 0) goto done;
846
847     if (flags & ETO_CLIPPED)
848     {
849         if (!(flags & ETO_OPAQUE))  /* otherwise we have done it already */
850             get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
851     }
852     else
853     {
854         free_clipped_rects( &clipped_rects );
855         get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
856     }
857     if (!clipped_rects.count) goto done;
858
859     render_string( dev->hdc, &pdev->dib, pdev->font, x, y, flags, str, count, dx,
860                    &clipped_rects, &bounds );
861
862 done:
863     add_clipped_bounds( pdev, &bounds, pdev->clip );
864     free_clipped_rects( &clipped_rects );
865     return TRUE;
866 }
867
868 /***********************************************************************
869  *           dibdrv_SelectFont
870  */
871 HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
872 {
873     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
874     HFONT ret;
875
876     if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP;  /* no anti-aliasing on <= 8bpp */
877
878     dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
879     ret = dev->funcs->pSelectFont( dev, font, aa_flags );
880     if (ret)
881     {
882         struct cached_font *prev = pdev->font;
883         pdev->font = add_cached_font( dev->hdc, font, *aa_flags ? *aa_flags : GGO_BITMAP );
884         release_cached_font( prev );
885     }
886     return ret;
887 }
888
889 /***********************************************************************
890  *           dibdrv_Arc
891  */
892 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
893                  INT start_x, INT start_y, INT end_x, INT end_y )
894 {
895     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
896 }
897
898 /***********************************************************************
899  *           dibdrv_ArcTo
900  */
901 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
902                    INT start_x, INT start_y, INT end_x, INT end_y )
903 {
904     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
905 }
906
907 /***********************************************************************
908  *           dibdrv_Chord
909  */
910 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
911                    INT start_x, INT start_y, INT end_x, INT end_y )
912 {
913     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
914 }
915
916 /***********************************************************************
917  *           dibdrv_Ellipse
918  */
919 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
920 {
921     return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
922 }
923
924 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
925 {
926     /* the clip rgn stops the flooding */
927     if (clip && !PtInRegion( clip, x, y )) return FALSE;
928
929     if (type == FLOODFILLBORDER)
930         return dib->funcs->get_pixel( dib, x, y ) != pixel;
931     else
932         return dib->funcs->get_pixel( dib, x, y ) == pixel;
933 }
934
935 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
936
937 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
938 {
939     RECT next;
940
941     next.top = row->top + offset;
942     next.bottom = next.top + 1;
943     next.left = next.right = row->left;
944     while (next.right < row->right)
945     {
946         if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
947         else
948         {
949             if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
950                 fill_row( dib, clip, &next, pixel, type, rgn );
951             next.left = ++next.right;
952         }
953     }
954     if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
955         fill_row( dib, clip, &next, pixel, type, rgn );
956 }
957
958 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
959 {
960     while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
961     while (row->right < dib->rect.right - dib->rect.left &&
962            is_interior( dib, clip, row->right, row->top, pixel, type))
963         row->right++;
964
965     add_rect_to_region( rgn, row );
966
967     if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
968     if (row->top < dib->rect.bottom - dib->rect.top - 1)
969         do_next_row( dib, clip, row, 1, pixel, type, rgn );
970 }
971
972 /***********************************************************************
973  *           dibdrv_ExtFloodFill
974  */
975 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
976 {
977     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
978     DWORD pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
979     RECT row;
980     HRGN rgn;
981
982     TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
983
984     if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
985
986     if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
987     row.left = x;
988     row.right = x + 1;
989     row.top = y;
990     row.bottom = y + 1;
991
992     fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
993
994     add_clipped_bounds( pdev, NULL, rgn );
995     brush_region( pdev, rgn );
996
997     DeleteObject( rgn );
998     return TRUE;
999 }
1000
1001 /***********************************************************************
1002  *           dibdrv_GetNearestColor
1003  */
1004 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
1005 {
1006     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1007     DWORD pixel;
1008
1009     TRACE( "(%p, %08x)\n", dev, color );
1010
1011     pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
1012     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1013 }
1014
1015 /***********************************************************************
1016  *           dibdrv_GetPixel
1017  */
1018 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
1019 {
1020     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1021     POINT pt;
1022     DWORD pixel;
1023
1024     TRACE( "(%p, %d, %d)\n", dev, x, y );
1025
1026     pt.x = x;
1027     pt.y = y;
1028     LPtoDP( dev->hdc, &pt, 1 );
1029
1030     if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
1031         pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
1032         return CLR_INVALID;
1033
1034     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
1035     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1036 }
1037
1038 /***********************************************************************
1039  *           dibdrv_LineTo
1040  */
1041 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
1042 {
1043     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1044     POINT pts[2];
1045     HRGN region = 0;
1046     BOOL ret;
1047
1048     GetCurrentPositionEx(dev->hdc, pts);
1049     pts[1].x = x;
1050     pts[1].y = y;
1051
1052     LPtoDP(dev->hdc, pts, 2);
1053
1054     if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1055
1056     reset_dash_origin(pdev);
1057
1058     ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
1059     add_pen_lines_bounds( pdev, 2, pts, region );
1060
1061     if (region)
1062     {
1063         ret = pen_region( pdev, region );
1064         DeleteObject( region );
1065     }
1066     return ret;
1067 }
1068
1069 /***********************************************************************
1070  *           get_rop2_from_rop
1071  *
1072  * Returns the binary rop that is equivalent to the provided ternary rop
1073  * if the src bits are ignored.
1074  */
1075 static inline INT get_rop2_from_rop(INT rop)
1076 {
1077     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
1078 }
1079
1080 /***********************************************************************
1081  *           dibdrv_PatBlt
1082  */
1083 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
1084 {
1085     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1086     dib_brush *brush = &pdev->brush;
1087     int rop2 = get_rop2_from_rop( rop );
1088     struct clipped_rects clipped_rects;
1089     DWORD and = 0, xor = 0;
1090     BOOL ret = TRUE;
1091
1092     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
1093
1094     add_clipped_bounds( pdev, &dst->visrect, 0 );
1095     if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
1096
1097     switch (rop2)  /* shortcuts for rops that don't involve the brush */
1098     {
1099     case R2_NOT:   and = ~0u;
1100         /* fall through */
1101     case R2_WHITE: xor = ~0u;
1102         /* fall through */
1103     case R2_BLACK:
1104         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
1105         /* fall through */
1106     case R2_NOP:
1107         break;
1108     default:
1109         ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
1110         break;
1111     }
1112     free_clipped_rects( &clipped_rects );
1113     return ret;
1114 }
1115
1116 /***********************************************************************
1117  *           dibdrv_PaintRgn
1118  */
1119 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
1120 {
1121     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1122     const WINEREGION *region;
1123     int i;
1124     RECT rect, bounds;
1125
1126     TRACE("%p, %p\n", dev, rgn);
1127
1128     reset_bounds( &bounds );
1129
1130     region = get_wine_region( rgn );
1131     if(!region) return FALSE;
1132
1133     for(i = 0; i < region->numRects; i++)
1134     {
1135         rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
1136                                 region->rects[i].right, region->rects[i].bottom, FALSE );
1137         add_bounds_rect( &bounds, &rect );
1138         brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1139     }
1140
1141     release_wine_region( rgn );
1142     add_clipped_bounds( pdev, &bounds, pdev->clip );
1143     return TRUE;
1144 }
1145
1146 /***********************************************************************
1147  *           dibdrv_PolyPolygon
1148  */
1149 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
1150 {
1151     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1152     DWORD total, i, pos;
1153     BOOL ret = TRUE;
1154     POINT *points;
1155     HRGN outline = 0, interior = 0;
1156
1157     for (i = total = 0; i < polygons; i++)
1158     {
1159         if (counts[i] < 2) return FALSE;
1160         total += counts[i];
1161     }
1162
1163     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1164     if (!points) return FALSE;
1165     memcpy( points, pt, total * sizeof(*pt) );
1166     LPtoDP( dev->hdc, points, total );
1167
1168     if (pdev->brush.style != BS_NULL &&
1169         !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
1170     {
1171         HeapFree( GetProcessHeap(), 0, points );
1172         return FALSE;
1173     }
1174
1175     if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1176
1177     /* if not using a region, paint the interior first so the outline can overlap it */
1178     if (interior && !outline)
1179     {
1180         ret = brush_region( pdev, interior );
1181         DeleteObject( interior );
1182         interior = 0;
1183     }
1184
1185     for (i = pos = 0; i < polygons; i++)
1186     {
1187         reset_dash_origin( pdev );
1188         pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1189         pos += counts[i];
1190     }
1191     add_pen_lines_bounds( pdev, total, points, outline );
1192
1193     if (interior)
1194     {
1195         CombineRgn( interior, interior, outline, RGN_DIFF );
1196         ret = brush_region( pdev, interior );
1197         DeleteObject( interior );
1198     }
1199     if (outline)
1200     {
1201         if (ret) ret = pen_region( pdev, outline );
1202         DeleteObject( outline );
1203     }
1204     HeapFree( GetProcessHeap(), 0, points );
1205     return ret;
1206 }
1207
1208 /***********************************************************************
1209  *           dibdrv_PolyPolyline
1210  */
1211 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1212 {
1213     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1214     DWORD total, pos, i;
1215     POINT *points;
1216     BOOL ret = TRUE;
1217     HRGN outline = 0;
1218
1219     for (i = total = 0; i < polylines; i++)
1220     {
1221         if (counts[i] < 2) return FALSE;
1222         total += counts[i];
1223     }
1224
1225     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1226     if (!points) return FALSE;
1227     memcpy( points, pt, total * sizeof(*pt) );
1228     LPtoDP( dev->hdc, points, total );
1229
1230     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1231     {
1232         HeapFree( GetProcessHeap(), 0, points );
1233         return FALSE;
1234     }
1235
1236     for (i = pos = 0; i < polylines; i++)
1237     {
1238         reset_dash_origin( pdev );
1239         pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1240         pos += counts[i];
1241     }
1242     add_pen_lines_bounds( pdev, total, points, outline );
1243
1244     if (outline)
1245     {
1246         ret = pen_region( pdev, outline );
1247         DeleteObject( outline );
1248     }
1249
1250     HeapFree( GetProcessHeap(), 0, points );
1251     return ret;
1252 }
1253
1254 /***********************************************************************
1255  *           dibdrv_Polygon
1256  */
1257 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1258 {
1259     INT counts[1] = { count };
1260
1261     return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1262 }
1263
1264 /***********************************************************************
1265  *           dibdrv_Polyline
1266  */
1267 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1268 {
1269     DWORD counts[1] = { count };
1270
1271     if (count < 0) return FALSE;
1272     return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1273 }
1274
1275 /***********************************************************************
1276  *           dibdrv_Rectangle
1277  */
1278 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1279 {
1280     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1281     RECT rect;
1282     POINT pts[4];
1283     BOOL ret;
1284     HRGN outline = 0;
1285
1286     TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1287
1288     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1289
1290     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1291
1292     rect.right--;
1293     rect.bottom--;
1294     reset_dash_origin(pdev);
1295
1296     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1297     {
1298         /* 4 pts going clockwise starting from bottom-right */
1299         pts[0].x = pts[3].x = rect.right;
1300         pts[0].y = pts[1].y = rect.bottom;
1301         pts[1].x = pts[2].x = rect.left;
1302         pts[2].y = pts[3].y = rect.top;
1303     }
1304     else
1305     {
1306         /* 4 pts going anti-clockwise starting from top-right */
1307         pts[0].x = pts[3].x = rect.right;
1308         pts[0].y = pts[1].y = rect.top;
1309         pts[1].x = pts[2].x = rect.left;
1310         pts[2].y = pts[3].y = rect.bottom;
1311     }
1312
1313     pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1314     add_pen_lines_bounds( pdev, 4, pts, outline );
1315
1316     if (outline)
1317     {
1318         if (pdev->brush.style != BS_NULL)
1319         {
1320             HRGN interior = CreateRectRgnIndirect( &rect );
1321
1322             CombineRgn( interior, interior, outline, RGN_DIFF );
1323             brush_region( pdev, interior );
1324             DeleteObject( interior );
1325         }
1326         ret = pen_region( pdev, outline );
1327         DeleteObject( outline );
1328     }
1329     else
1330     {
1331         rect.left   += (pdev->pen_width + 1) / 2;
1332         rect.top    += (pdev->pen_width + 1) / 2;
1333         rect.right  -= pdev->pen_width / 2;
1334         rect.bottom -= pdev->pen_width / 2;
1335         ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1336     }
1337     return ret;
1338 }
1339
1340 /***********************************************************************
1341  *           dibdrv_RoundRect
1342  */
1343 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1344                        INT ellipse_width, INT ellipse_height )
1345 {
1346     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1347     RECT rect;
1348     POINT pt[2], *points;
1349     int i, end, count;
1350     BOOL ret = TRUE;
1351     HRGN outline = 0, interior = 0;
1352
1353     if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1354
1355     pt[0].x = pt[0].y = 0;
1356     pt[1].x = ellipse_width;
1357     pt[1].y = ellipse_height;
1358     LPtoDP( dev->hdc, pt, 2 );
1359     ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1360     ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1361     if (ellipse_width <= 2|| ellipse_height <= 2)
1362         return dibdrv_Rectangle( dev, left, top, right, bottom );
1363
1364     points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1365     if (!points) return FALSE;
1366
1367     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1368     {
1369         HeapFree( GetProcessHeap(), 0, points );
1370         return FALSE;
1371     }
1372
1373     if (pdev->brush.style != BS_NULL &&
1374         !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1375                                          ellipse_width, ellipse_height )))
1376     {
1377         HeapFree( GetProcessHeap(), 0, points );
1378         if (outline) DeleteObject( outline );
1379         return FALSE;
1380     }
1381
1382     /* if not using a region, paint the interior first so the outline can overlap it */
1383     if (interior && !outline)
1384     {
1385         ret = brush_region( pdev, interior );
1386         DeleteObject( interior );
1387         interior = 0;
1388     }
1389
1390     count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1391
1392     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1393     {
1394         for (i = 0; i < count; i++)
1395         {
1396             points[i].x = rect.right - ellipse_width + points[i].x;
1397             points[i].y = rect.bottom - ellipse_height + points[i].y;
1398         }
1399     }
1400     else
1401     {
1402         for (i = 0; i < count; i++)
1403         {
1404             points[i].x = rect.right - ellipse_width + points[i].x;
1405             points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1406         }
1407     }
1408
1409     /* horizontal symmetry */
1410
1411     end = 2 * count - 1;
1412     /* avoid duplicating the midpoint */
1413     if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1414     for (i = 0; i < count; i++)
1415     {
1416         points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1417         points[end - i].y = points[i].y;
1418     }
1419     count = end + 1;
1420
1421     /* vertical symmetry */
1422
1423     end = 2 * count - 1;
1424     /* avoid duplicating the midpoint */
1425     if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1426     for (i = 0; i < count; i++)
1427     {
1428         points[end - i].x = points[i].x;
1429         points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1430     }
1431     count = end + 1;
1432
1433     reset_dash_origin( pdev );
1434     pdev->pen_lines( pdev, count, points, TRUE, outline );
1435     add_pen_lines_bounds( pdev, count, points, outline );
1436
1437     if (interior)
1438     {
1439         CombineRgn( interior, interior, outline, RGN_DIFF );
1440         ret = brush_region( pdev, interior );
1441         DeleteObject( interior );
1442     }
1443     if (outline)
1444     {
1445         if (ret) ret = pen_region( pdev, outline );
1446         DeleteObject( outline );
1447     }
1448     HeapFree( GetProcessHeap(), 0, points );
1449     return ret;
1450 }
1451
1452 /***********************************************************************
1453  *           dibdrv_Pie
1454  */
1455 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1456                  INT start_x, INT start_y, INT end_x, INT end_y )
1457 {
1458     return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1459 }
1460
1461 /***********************************************************************
1462  *           dibdrv_SetPixel
1463  */
1464 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1465 {
1466     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1467     struct clipped_rects clipped_rects;
1468     RECT rect;
1469     POINT pt;
1470     DWORD pixel;
1471
1472     TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1473
1474     pt.x = x;
1475     pt.y = y;
1476     LPtoDP( dev->hdc, &pt, 1 );
1477     rect.left = pt.x;
1478     rect.top =  pt.y;
1479     rect.right = rect.left + 1;
1480     rect.bottom = rect.top + 1;
1481     add_clipped_bounds( pdev, &rect, pdev->clip );
1482
1483     /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1484     pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
1485     color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1486
1487     if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1488     pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1489     free_clipped_rects( &clipped_rects );
1490     return color;
1491 }