include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / win32u / dibdrv / graphics.c
blob009143bc2d22a202437b3e6a6cf3731f123fc00f
1 /*
2 * DIB driver graphics operations.
4 * Copyright 2011 Huw Davies
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.
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.
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include <assert.h>
26 #include <pthread.h>
27 #include "ntgdi_private.h"
28 #include "dibdrv.h"
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(dib);
34 struct cached_glyph
36 GLYPHMETRICS metrics;
37 BYTE bits[1];
40 enum glyph_type
42 GLYPH_INDEX,
43 GLYPH_WCHAR,
44 GLYPH_NBTYPES
47 #define GLYPH_CACHE_PAGE_SIZE 0x100
48 #define GLYPH_CACHE_PAGES (0x10000 / GLYPH_CACHE_PAGE_SIZE)
50 struct cached_font
52 struct list entry;
53 LONG ref;
54 DWORD hash;
55 LOGFONTW lf;
56 XFORM xform;
57 UINT aa_flags;
58 struct cached_glyph **glyphs[GLYPH_NBTYPES][GLYPH_CACHE_PAGES];
61 static struct list font_cache = LIST_INIT( font_cache );
63 static pthread_mutex_t font_cache_lock = PTHREAD_MUTEX_INITIALIZER;
66 static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
68 DC *dc = get_physdev_dc( &pdev->dev );
69 struct clipped_rects clipped_rects;
70 BOOL ret;
72 if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
73 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
74 &dc->attr->brush_org, dc->attr->rop_mode );
75 free_clipped_rects( &clipped_rects );
76 return ret;
79 /* paint a region with the brush (note: the region can be modified) */
80 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
82 if (pdev->clip) NtGdiCombineRgn( region, region, pdev->clip, RGN_AND );
83 return brush_rect( pdev, &pdev->brush, NULL, region );
86 /* paint a region with the pen (note: the region can be modified) */
87 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
89 if (pdev->clip) NtGdiCombineRgn( region, region, pdev->clip, RGN_AND );
90 return brush_rect( pdev, &pdev->pen_brush, NULL, region );
93 static RECT get_device_rect( DC *dc, int left, int top, int right, int bottom, BOOL rtl_correction )
95 RECT rect;
97 rect.left = left;
98 rect.top = top;
99 rect.right = right;
100 rect.bottom = bottom;
101 if (rtl_correction && dc->attr->layout & LAYOUT_RTL)
103 /* shift the rectangle so that the right border is included after mirroring */
104 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
105 rect.left--;
106 rect.right--;
108 lp_to_dp( dc, (POINT *)&rect, 2 );
109 order_rect( &rect );
110 return rect;
113 static BOOL get_pen_device_rect( DC *dc, dibdrv_physdev *dev, RECT *rect,
114 int left, int top, int right, int bottom )
116 *rect = get_device_rect( dc, left, top, right, bottom, TRUE );
117 if (rect->left == rect->right || rect->top == rect->bottom) return FALSE;
119 if (dev->pen_style == PS_INSIDEFRAME)
121 rect->left += dev->pen_width / 2;
122 rect->top += dev->pen_width / 2;
123 rect->right -= (dev->pen_width - 1) / 2;
124 rect->bottom -= (dev->pen_width - 1) / 2;
126 return TRUE;
129 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
131 const WINEREGION *region;
132 RECT bounds, rect;
133 int width = 0;
135 if (!dev->bounds) return;
136 reset_bounds( &bounds );
138 if (dev->pen_uses_region)
140 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
141 width = dev->pen_width + 2;
142 if (dev->pen_join == PS_JOIN_MITER)
144 width *= 5;
145 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
147 else
149 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4;
150 else width = (width + 1) / 2;
153 /* in case the heuristics are wrong, add the actual region too */
154 if ((region = get_wine_region( rgn )))
156 add_bounds_rect( &bounds, &region->extents );
157 release_wine_region( rgn );
161 while (count-- > 0)
163 rect.left = points->x - width;
164 rect.top = points->y - width;
165 rect.right = points->x + width + 1;
166 rect.bottom = points->y + width + 1;
167 add_bounds_rect( &bounds, &rect );
168 points++;
171 add_clipped_bounds( dev, &bounds, dev->clip );
174 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
175 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
176 static int ellipse_first_quadrant( int width, int height, POINT *data )
178 const int a = width - 1;
179 const int b = height - 1;
180 const INT64 asq = (INT64)8 * a * a;
181 const INT64 bsq = (INT64)8 * b * b;
182 INT64 dx = (INT64)4 * b * b * (1 - a);
183 INT64 dy = (INT64)4 * a * a * (1 + (b % 2));
184 INT64 err = dx + dy + a * a * (b % 2);
185 int pos = 0;
186 POINT pt;
188 pt.x = a;
189 pt.y = height / 2;
191 /* based on an algorithm by Alois Zingl */
193 while (pt.x >= width / 2)
195 INT64 e2 = 2 * err;
196 data[pos++] = pt;
197 if (e2 >= dx)
199 pt.x--;
200 err += dx += bsq;
202 if (e2 <= dy)
204 pt.y++;
205 err += dy += asq;
208 return pos;
211 static int find_intersection( const POINT *points, int x, int y, int count )
213 int i;
215 if (y >= 0)
217 if (x >= 0) /* first quadrant */
219 for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
220 return i;
222 /* second quadrant */
223 for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break;
224 return 2 * count - i;
226 if (x >= 0) /* fourth quadrant */
228 for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break;
229 return 4 * count - i;
231 /* third quadrant */
232 for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break;
233 return 2 * count + i;
236 static int get_arc_points( int arc_dir, const RECT *rect, POINT start, POINT end, POINT *points )
238 int i, pos, count, start_pos, end_pos;
239 int width = rect->right - rect->left;
240 int height = rect->bottom - rect->top;
242 count = ellipse_first_quadrant( width, height, points );
243 for (i = 0; i < count; i++)
245 points[i].x -= width / 2;
246 points[i].y -= height / 2;
248 if (arc_dir != AD_CLOCKWISE)
250 start.y = -start.y;
251 end.y = -end.y;
253 start_pos = find_intersection( points, start.x, start.y, count );
254 end_pos = find_intersection( points, end.x, end.y, count );
255 if (end_pos <= start_pos) end_pos += 4 * count;
257 pos = count;
258 if (arc_dir == AD_CLOCKWISE)
260 for (i = start_pos; i < end_pos; i++, pos++)
262 switch ((i / count) % 4)
264 case 0:
265 points[pos].x = rect->left + width/2 + points[i % count].x;
266 points[pos].y = rect->top + height/2 + points[i % count].y;
267 break;
268 case 1:
269 points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x;
270 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
271 break;
272 case 2:
273 points[pos].x = rect->right-1 - width/2 - points[i % count].x;
274 points[pos].y = rect->bottom-1 - height/2 - points[i % count].y;
275 break;
276 case 3:
277 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
278 points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y;
279 break;
283 else
285 for (i = start_pos; i < end_pos; i++, pos++)
287 switch ((i / count) % 4)
289 case 0:
290 points[pos].x = rect->left + width/2 + points[i % count].x;
291 points[pos].y = rect->bottom-1 - height/2 - points[i % count].y;
292 break;
293 case 1:
294 points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x;
295 points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y;
296 break;
297 case 2:
298 points[pos].x = rect->right-1 - width/2 - points[i % count].x;
299 points[pos].y = rect->top + height/2 + points[i % count].y;
300 break;
301 case 3:
302 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
303 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
304 break;
309 memmove( points, points + count, (pos - count) * sizeof(POINT) );
310 return pos - count;
313 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
314 static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
315 INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines )
317 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
318 DC *dc = get_physdev_dc( dev );
319 RECT rect, rc;
320 POINT pt[2], *points;
321 int width, height, count;
322 BOOL ret = TRUE;
323 HRGN outline = 0, interior = 0;
325 if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE;
327 width = rect.right - rect.left;
328 height = rect.bottom - rect.top;
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 lp_to_dp( dc, 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;
341 points = malloc( (width + height) * 3 * sizeof(*points) );
342 if (!points) return FALSE;
344 if (extra_lines == -1)
346 points[0] = dc->attr->cur_pos;
347 lp_to_dp( dc, points, 1 );
348 count = 1 + get_arc_points( dc->attr->arc_direction, &rect, pt[0], pt[1], points + 1 );
350 else count = get_arc_points( dc->attr->arc_direction, &rect, pt[0], pt[1], points );
352 if (extra_lines == 2)
354 points[count].x = rect.left + width / 2;
355 points[count].y = rect.top + height / 2;
356 count++;
358 if (count < 2)
360 free( points );
361 return TRUE;
364 if (pdev->pen_uses_region && !(outline = NtGdiCreateRectRgn( 0, 0, 0, 0 )))
366 free( points );
367 return FALSE;
370 if (pdev->brush.style != BS_NULL &&
371 extra_lines > 0 &&
372 get_dib_rect( &pdev->dib, &rc ) &&
373 !(interior = create_polypolygon_region( points, &count, 1, WINDING, &rc )))
375 free( points );
376 if (outline) NtGdiDeleteObjectApp( outline );
377 return FALSE;
380 /* if not using a region, paint the interior first so the outline can overlap it */
381 if (interior && !outline)
383 ret = brush_region( pdev, interior );
384 NtGdiDeleteObjectApp( interior );
385 interior = 0;
388 reset_dash_origin( pdev );
389 pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
390 add_pen_lines_bounds( pdev, count, points, outline );
392 if (interior)
394 NtGdiCombineRgn( interior, interior, outline, RGN_DIFF );
395 ret = brush_region( pdev, interior );
396 NtGdiDeleteObjectApp( interior );
398 if (outline)
400 if (ret) ret = pen_region( pdev, outline );
401 NtGdiDeleteObjectApp( outline );
403 free( points );
404 return ret;
407 /* helper for path stroking and filling functions */
408 static BOOL stroke_and_fill_path( dibdrv_physdev *dev, BOOL stroke, BOOL fill )
410 DC *dc = get_physdev_dc( &dev->dev );
411 struct gdi_path *path;
412 POINT *points;
413 BYTE *types;
414 BOOL ret = TRUE;
415 HRGN outline = 0, interior = 0;
416 int i, pos, total;
418 if (dev->brush.style == BS_NULL) fill = FALSE;
420 if (!(path = get_gdi_flat_path( dc, fill ? &interior : NULL ))) return FALSE;
421 if (!(total = get_gdi_path_data( path, &points, &types ))) goto done;
423 if (stroke && dev->pen_uses_region) outline = NtGdiCreateRectRgn( 0, 0, 0, 0 );
425 /* if not using a region, paint the interior first so the outline can overlap it */
426 if (interior && !outline)
428 ret = brush_region( dev, interior );
429 NtGdiDeleteObjectApp( interior );
430 interior = 0;
433 if (stroke)
435 pos = 0;
436 for (i = 1; i < total; i++)
438 if (types[i] != PT_MOVETO) continue;
439 if (i > pos + 1)
441 reset_dash_origin( dev );
442 dev->pen_lines( dev, i - pos, points + pos,
443 fill || types[i - 1] & PT_CLOSEFIGURE, outline );
445 pos = i;
447 if (i > pos + 1)
449 reset_dash_origin( dev );
450 dev->pen_lines( dev, i - pos, points + pos,
451 fill || types[i - 1] & PT_CLOSEFIGURE, outline );
455 add_pen_lines_bounds( dev, total, points, outline );
457 if (interior)
459 NtGdiCombineRgn( interior, interior, outline, RGN_DIFF );
460 ret = brush_region( dev, interior );
461 NtGdiDeleteObjectApp( interior );
463 if (outline)
465 if (ret) ret = pen_region( dev, outline );
466 NtGdiDeleteObjectApp( outline );
469 done:
470 free_gdi_path( path );
471 return ret;
474 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
475 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
476 static const BYTE ramp[17] =
478 0, 0x4d, 0x68, 0x7c,
479 0x8c, 0x9a, 0xa7, 0xb2,
480 0xbd, 0xc7, 0xd0, 0xd9,
481 0xe1, 0xe9, 0xf0, 0xf8,
482 0xff
485 /* For a give text-color component and a glyph level, calculate the
486 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
487 components respectively.
489 The minimum is a linear interpolation between 0 and the value in
490 the ramp table.
492 The maximum is a linear interpolation between the value from the
493 ramp table read in reverse and 0xff.
495 To find the resulting pixel intensity, we note that if the text and
496 bkgnd intensities are the same then the result must be that
497 intensity. Otherwise we linearly interpolate between either the
498 min or the max value and this intermediate value depending on which
499 side of the inequality we lie.
502 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
504 *min_comp = (ramp[aa] * text_comp) / 0xff;
505 *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
508 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
510 int i;
512 for (i = 0; i < 17; i++)
514 get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
515 get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
516 get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
520 static DWORD font_cache_hash( struct cached_font *font )
522 DWORD hash = 0, *ptr, two_chars;
523 WORD *pwc;
524 int i;
526 hash ^= font->aa_flags;
527 for(i = 0, ptr = (DWORD*)&font->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
528 hash ^= *ptr;
529 for(i = 0, ptr = (DWORD*)&font->lf; i < 7; i++, ptr++)
530 hash ^= *ptr;
531 for(i = 0, ptr = (DWORD*)font->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
532 two_chars = *ptr;
533 pwc = (WCHAR *)&two_chars;
534 if (!*pwc) break;
535 *pwc = towupper(*pwc);
536 pwc++;
537 *pwc = towupper(*pwc);
538 hash ^= two_chars;
539 if (!*pwc) break;
541 return hash;
544 static int font_cache_cmp( const struct cached_font *p1, const struct cached_font *p2 )
546 int ret = p1->hash - p2->hash;
547 if (!ret) ret = p1->aa_flags - p2->aa_flags;
548 if (!ret) ret = memcmp( &p1->xform, &p2->xform, sizeof(p1->xform) );
549 if (!ret) ret = memcmp( &p1->lf, &p2->lf, FIELD_OFFSET( LOGFONTW, lfFaceName ));
550 if (!ret) ret = wcsicmp( p1->lf.lfFaceName, p2->lf.lfFaceName );
551 return ret;
554 static struct cached_font *add_cached_font( DC *dc, HFONT hfont, UINT aa_flags )
556 struct cached_font font, *ptr, *last_unused = NULL;
557 UINT i = 0, j, k;
559 NtGdiExtGetObjectW( hfont, sizeof(font.lf), &font.lf );
560 font.xform = dc->xformWorld2Vport;
561 font.xform.eDx = font.xform.eDy = 0; /* unused, would break hashing */
562 if (dc->attr->graphics_mode == GM_COMPATIBLE)
564 font.lf.lfOrientation = font.lf.lfEscapement;
565 if (font.xform.eM11 * font.xform.eM22 < 0)
566 font.lf.lfOrientation = -font.lf.lfOrientation;
568 font.lf.lfWidth = abs( font.lf.lfWidth );
569 font.aa_flags = aa_flags;
570 font.hash = font_cache_hash( &font );
572 pthread_mutex_lock( &font_cache_lock );
573 LIST_FOR_EACH_ENTRY( ptr, &font_cache, struct cached_font, entry )
575 if (!font_cache_cmp( &font, ptr ))
577 InterlockedIncrement( &ptr->ref );
578 list_remove( &ptr->entry );
579 goto done;
581 if (!ptr->ref)
583 i++;
584 last_unused = ptr;
588 if (i > 5) /* keep at least 5 of the most-recently used fonts around */
590 ptr = last_unused;
591 for (i = 0; i < GLYPH_NBTYPES; i++)
593 for (j = 0; j < GLYPH_CACHE_PAGES; j++)
595 if (!ptr->glyphs[i][j]) continue;
596 for (k = 0; k < GLYPH_CACHE_PAGE_SIZE; k++)
597 free( ptr->glyphs[i][j][k] );
598 free( ptr->glyphs[i][j] );
601 list_remove( &ptr->entry );
603 else if (!(ptr = malloc( sizeof(*ptr) )))
605 pthread_mutex_unlock( &font_cache_lock );
606 return NULL;
609 *ptr = font;
610 ptr->ref = 1;
611 memset( ptr->glyphs, 0, sizeof(ptr->glyphs) );
612 done:
613 list_add_head( &font_cache, &ptr->entry );
614 pthread_mutex_unlock( &font_cache_lock );
615 TRACE( "%d %s -> %p\n", (int)ptr->lf.lfHeight, debugstr_w(ptr->lf.lfFaceName), ptr );
616 return ptr;
619 void release_cached_font( struct cached_font *font )
621 if (font) InterlockedDecrement( &font->ref );
624 static struct cached_glyph *add_cached_glyph( struct cached_font *font, UINT index, UINT flags,
625 struct cached_glyph *glyph )
627 struct cached_glyph *ret;
628 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
629 UINT page = index / GLYPH_CACHE_PAGE_SIZE;
630 UINT entry = index % GLYPH_CACHE_PAGE_SIZE;
632 if (!font->glyphs[type][page])
634 struct cached_glyph **ptr;
636 ptr = calloc( 1, GLYPH_CACHE_PAGE_SIZE * sizeof(*ptr) );
637 if (!ptr)
639 free( glyph );
640 return NULL;
642 if (InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page], ptr, NULL ))
643 free( ptr );
645 ret = InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page][entry], glyph, NULL );
646 if (!ret) ret = glyph;
647 else free( glyph );
648 return ret;
651 static struct cached_glyph *get_cached_glyph( struct cached_font *font, UINT index, UINT flags )
653 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
654 UINT page = index / GLYPH_CACHE_PAGE_SIZE;
656 if (!font->glyphs[type][page]) return NULL;
657 return font->glyphs[type][page][index % GLYPH_CACHE_PAGE_SIZE];
660 /**********************************************************************
661 * get_text_bkgnd_masks
663 * See the comment above get_pen_bkgnd_masks
665 static inline void get_text_bkgnd_masks( DC *dc, const dib_info *dib, rop_mask *mask )
667 COLORREF bg = dc->attr->background_color;
669 mask->and = 0;
671 if (dib->bit_count != 1)
672 mask->xor = get_pixel_color( dc, dib, bg, FALSE );
673 else
675 COLORREF fg = dc->attr->text_color;
676 mask->xor = get_pixel_color( dc, dib, fg, TRUE );
677 if (fg != bg) mask->xor = ~mask->xor;
681 static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics,
682 const dib_info *glyph_dib, DWORD text_color,
683 const struct font_intensities *intensity,
684 const struct clipped_rects *clipped_rects, RECT *bounds )
686 int i;
687 RECT rect, clipped_rect;
688 POINT src_origin;
690 rect.left = x + metrics->gmptGlyphOrigin.x;
691 rect.top = y - metrics->gmptGlyphOrigin.y;
692 rect.right = rect.left + metrics->gmBlackBoxX;
693 rect.bottom = rect.top + metrics->gmBlackBoxY;
694 if (bounds) add_bounds_rect( bounds, &rect );
696 for (i = 0; i < clipped_rects->count; i++)
698 if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
700 src_origin.x = clipped_rect.left - rect.left;
701 src_origin.y = clipped_rect.top - rect.top;
703 if (glyph_dib->bit_count == 32)
704 dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
705 text_color, intensity->gamma_ramp );
706 else
707 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
708 text_color, intensity->ranges );
713 static int get_glyph_depth( UINT aa_flags )
715 switch (aa_flags)
717 case GGO_BITMAP: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */
718 case GGO_GRAY2_BITMAP:
719 case GGO_GRAY4_BITMAP:
720 case GGO_GRAY8_BITMAP:
721 case WINE_GGO_GRAY16_BITMAP: return 8;
723 case WINE_GGO_HRGB_BITMAP:
724 case WINE_GGO_HBGR_BITMAP:
725 case WINE_GGO_VRGB_BITMAP:
726 case WINE_GGO_VBGR_BITMAP: return 32;
728 default:
729 ERR("Unexpected flags %08x\n", aa_flags);
730 return 0;
734 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
735 static const int padding[4] = {0, 3, 2, 1};
737 /***********************************************************************
738 * cache_glyph_bitmap
740 * Retrieve a 17-level bitmap for the appropriate glyph.
742 * For non-antialiased bitmaps convert them to the 17-level format
743 * using only values 0 or 16.
745 static struct cached_glyph *cache_glyph_bitmap( DC *dc, struct cached_font *font, UINT index, UINT flags )
747 UINT ggo_flags = font->aa_flags;
748 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
749 UINT indices[3] = {0, 0, 0x20};
750 int i, x, y;
751 DWORD ret, size;
752 BYTE *dst, *src;
753 int pad = 0, stride, bit_count;
754 GLYPHMETRICS metrics;
755 struct cached_glyph *glyph;
757 if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
758 indices[0] = index;
759 for (i = 0; i < ARRAY_SIZE( indices ); i++)
761 index = indices[i];
762 ret = NtGdiGetGlyphOutline( dc->hSelf, index, ggo_flags, &metrics, 0, NULL,
763 &identity, FALSE );
764 if (ret != GDI_ERROR) break;
766 if (ret == GDI_ERROR) return NULL;
767 if (!ret) metrics.gmBlackBoxX = metrics.gmBlackBoxY = 0; /* empty glyph */
769 bit_count = get_glyph_depth( font->aa_flags );
770 stride = get_dib_stride( metrics.gmBlackBoxX, bit_count );
771 size = metrics.gmBlackBoxY * stride;
772 glyph = malloc( FIELD_OFFSET( struct cached_glyph, bits[size] ));
773 if (!glyph) return NULL;
774 if (!size) goto done; /* empty glyph */
776 if (bit_count == 8) pad = padding[ metrics.gmBlackBoxX % 4 ];
778 ret = NtGdiGetGlyphOutline( dc->hSelf, index, ggo_flags, &metrics, size, glyph->bits,
779 &identity, FALSE );
780 if (ret == GDI_ERROR)
782 free( glyph );
783 return NULL;
785 assert( ret <= size );
786 if (font->aa_flags == GGO_BITMAP)
788 for (y = metrics.gmBlackBoxY - 1; y >= 0; y--)
790 src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 );
791 dst = glyph->bits + y * stride;
793 if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad );
795 for (x = metrics.gmBlackBoxX - 1; x >= 0; x--)
796 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
799 else if (pad)
801 for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride)
802 memset( dst + metrics.gmBlackBoxX, 0, pad );
805 done:
806 glyph->metrics = metrics;
807 return add_cached_glyph( font, index, flags, glyph );
810 static void render_string( DC *dc, dib_info *dib, struct cached_font *font, INT x, INT y,
811 UINT flags, const WCHAR *str, UINT count, const INT *dx,
812 const struct clipped_rects *clipped_rects, RECT *bounds )
814 UINT i;
815 struct cached_glyph *glyph;
816 dib_info glyph_dib;
817 DWORD text_color;
818 struct font_intensities intensity;
820 glyph_dib.bit_count = get_glyph_depth( font->aa_flags );
821 glyph_dib.rect.left = 0;
822 glyph_dib.rect.top = 0;
823 glyph_dib.bits.is_copy = FALSE;
824 glyph_dib.bits.free = NULL;
826 text_color = get_pixel_color( dc, dib, dc->attr->text_color, TRUE );
828 if (glyph_dib.bit_count == 32)
829 intensity.gamma_ramp = dc->font_gamma_ramp;
830 else
831 get_aa_ranges( dib->funcs->pixel_to_colorref( dib, text_color ), intensity.ranges );
833 for (i = 0; i < count; i++)
835 if (!(glyph = get_cached_glyph( font, str[i], flags )) &&
836 !(glyph = cache_glyph_bitmap( dc, font, str[i], flags ))) continue;
838 glyph_dib.width = glyph->metrics.gmBlackBoxX;
839 glyph_dib.height = glyph->metrics.gmBlackBoxY;
840 glyph_dib.rect.right = glyph->metrics.gmBlackBoxX;
841 glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY;
842 glyph_dib.stride = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count );
843 glyph_dib.bits.ptr = glyph->bits;
845 draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, &intensity, clipped_rects, bounds );
847 if (dx)
849 if (flags & ETO_PDY)
851 x += dx[ i * 2 ];
852 y += dx[ i * 2 + 1];
854 else
855 x += dx[ i ];
857 else
859 x += glyph->metrics.gmCellIncX;
860 y += glyph->metrics.gmCellIncY;
865 BOOL render_aa_text_bitmapinfo( DC *dc, BITMAPINFO *info, struct gdi_image_bits *bits,
866 struct bitblt_coords *src, INT x, INT y, UINT flags,
867 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
869 dib_info dib;
870 struct clipped_rects visrect;
871 struct cached_font *font;
873 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
875 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
877 visrect.count = 1;
878 visrect.rects = &src->visrect;
880 if (flags & ETO_OPAQUE)
882 rop_mask bkgnd_color;
883 get_text_bkgnd_masks( dc, &dib, &bkgnd_color );
884 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
887 if (!(font = add_cached_font( dc, dc->hFont, aa_flags ))) return FALSE;
889 render_string( dc, &dib, font, x, y, flags, str, count, dx, &visrect, NULL );
890 release_cached_font( font );
891 return TRUE;
894 /***********************************************************************
895 * dibdrv_ExtTextOut
897 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
898 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
900 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
901 DC *dc = get_physdev_dc( dev );
902 struct clipped_rects clipped_rects;
903 RECT bounds;
905 if (!pdev->font) return FALSE;
907 init_clipped_rects( &clipped_rects );
908 reset_bounds( &bounds );
910 if (flags & ETO_OPAQUE)
912 rop_mask bkgnd_color;
913 get_text_bkgnd_masks( dc, &pdev->dib, &bkgnd_color );
914 add_bounds_rect( &bounds, rect );
915 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
916 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
917 bkgnd_color.and, bkgnd_color.xor );
920 if (count == 0) goto done;
922 if (flags & ETO_CLIPPED)
924 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
925 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
927 else
929 free_clipped_rects( &clipped_rects );
930 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
932 if (!clipped_rects.count) goto done;
934 render_string( dc, &pdev->dib, pdev->font, x, y, flags, str, count, dx,
935 &clipped_rects, &bounds );
937 done:
938 add_clipped_bounds( pdev, &bounds, pdev->clip );
939 free_clipped_rects( &clipped_rects );
940 return TRUE;
943 /***********************************************************************
944 * dibdrv_SelectFont
946 HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
948 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
949 DC *dc = get_physdev_dc( dev );
950 HFONT ret;
952 if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */
954 dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
955 ret = dev->funcs->pSelectFont( dev, font, aa_flags );
956 if (ret)
958 struct cached_font *prev = pdev->font;
959 pdev->font = add_cached_font( dc, font, *aa_flags ? *aa_flags : GGO_BITMAP );
960 release_cached_font( prev );
962 return ret;
965 /***********************************************************************
966 * dibdrv_Arc
968 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
969 INT start_x, INT start_y, INT end_x, INT end_y )
971 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
974 /***********************************************************************
975 * dibdrv_ArcTo
977 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
978 INT start_x, INT start_y, INT end_x, INT end_y )
980 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
983 /***********************************************************************
984 * dibdrv_Chord
986 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
987 INT start_x, INT start_y, INT end_x, INT end_y )
989 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
992 /***********************************************************************
993 * dibdrv_Ellipse
995 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
997 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
1000 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
1002 /* the clip rgn stops the flooding */
1003 if (clip && !NtGdiPtInRegion( clip, x, y )) return FALSE;
1005 if (type == FLOODFILLBORDER)
1006 return dib->funcs->get_pixel( dib, x, y ) != pixel;
1007 else
1008 return dib->funcs->get_pixel( dib, x, y ) == pixel;
1011 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
1013 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
1015 RECT next;
1017 next.top = row->top + offset;
1018 next.bottom = next.top + 1;
1019 next.left = next.right = row->left;
1020 while (next.right < row->right)
1022 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
1023 else
1025 if (next.left != next.right && !NtGdiPtInRegion( rgn, next.left, next.top ))
1026 fill_row( dib, clip, &next, pixel, type, rgn );
1027 next.left = ++next.right;
1030 if (next.left != next.right && !NtGdiPtInRegion( rgn, next.left, next.top ))
1031 fill_row( dib, clip, &next, pixel, type, rgn );
1034 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
1036 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
1037 while (row->right < dib->rect.right - dib->rect.left &&
1038 is_interior( dib, clip, row->right, row->top, pixel, type))
1039 row->right++;
1041 add_rect_to_region( rgn, row );
1043 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
1044 if (row->top < dib->rect.bottom - dib->rect.top - 1)
1045 do_next_row( dib, clip, row, 1, pixel, type, rgn );
1048 /***********************************************************************
1049 * dibdrv_ExtFloodFill
1051 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
1053 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1054 DC *dc = get_physdev_dc( dev );
1055 DWORD pixel = get_pixel_color( dc, &pdev->dib, color, FALSE );
1056 RECT row;
1057 HRGN rgn;
1059 TRACE( "(%p, %d, %d, %s, %d)\n", pdev, x, y, debugstr_color(color), type );
1061 if (x < 0 || x >= pdev->dib.rect.right - pdev->dib.rect.left ||
1062 y < 0 || y >= pdev->dib.rect.bottom - pdev->dib.rect.top) return FALSE;
1064 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
1066 if (!(rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1067 row.left = x;
1068 row.right = x + 1;
1069 row.top = y;
1070 row.bottom = y + 1;
1072 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
1074 add_clipped_bounds( pdev, NULL, rgn );
1075 brush_region( pdev, rgn );
1077 NtGdiDeleteObjectApp( rgn );
1078 return TRUE;
1081 /***********************************************************************
1082 * dibdrv_FillPath
1084 BOOL dibdrv_FillPath( PHYSDEV dev )
1086 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1088 return stroke_and_fill_path( pdev, FALSE, TRUE );
1091 /***********************************************************************
1092 * dibdrv_GetNearestColor
1094 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
1096 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1097 DC *dc = get_physdev_dc( dev );
1098 DWORD pixel;
1100 TRACE( "(%p, %s)\n", dev, debugstr_color(color) );
1102 pixel = get_pixel_color( dc, &pdev->dib, color, FALSE );
1103 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1106 /***********************************************************************
1107 * dibdrv_GetPixel
1109 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
1111 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1112 DC *dc = get_physdev_dc( dev );
1113 POINT pt;
1114 RECT rect;
1115 DWORD pixel;
1117 TRACE( "(%p, %d, %d)\n", dev, x, y );
1119 pt.x = x;
1120 pt.y = y;
1121 lp_to_dp( dc, &pt, 1 );
1122 rect.left = pt.x;
1123 rect.top = pt.y;
1124 rect.right = rect.left + 1;
1125 rect.bottom = rect.top + 1;
1126 if (!clip_rect_to_dib( &pdev->dib, &rect )) return CLR_INVALID;
1128 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
1129 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1132 /***********************************************************************
1133 * dibdrv_LineTo
1135 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
1137 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1138 DC *dc = get_physdev_dc( dev );
1139 POINT pts[2];
1140 HRGN region = 0;
1141 BOOL ret;
1143 pts[0] = dc->attr->cur_pos;
1144 pts[1].x = x;
1145 pts[1].y = y;
1147 lp_to_dp(dc, pts, 2);
1149 if (pdev->pen_uses_region && !(region = NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1151 reset_dash_origin(pdev);
1153 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
1154 add_pen_lines_bounds( pdev, 2, pts, region );
1156 if (region)
1158 ret = pen_region( pdev, region );
1159 NtGdiDeleteObjectApp( region );
1161 return ret;
1164 /***********************************************************************
1165 * get_rop2_from_rop
1167 * Returns the binary rop that is equivalent to the provided ternary rop
1168 * if the src bits are ignored.
1170 static inline INT get_rop2_from_rop(INT rop)
1172 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
1175 /***********************************************************************
1176 * dibdrv_PatBlt
1178 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
1180 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1181 dib_brush *brush = &pdev->brush;
1182 DC *dc = get_physdev_dc( dev );
1183 int rop2 = get_rop2_from_rop( rop );
1184 struct clipped_rects clipped_rects;
1185 DWORD and = 0, xor = 0;
1186 BOOL ret = TRUE;
1188 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, (int)rop);
1190 add_clipped_bounds( pdev, &dst->visrect, 0 );
1191 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
1193 switch (rop2) /* shortcuts for rops that don't involve the brush */
1195 case R2_NOT: and = ~0u;
1196 /* fall through */
1197 case R2_WHITE: xor = ~0u;
1198 /* fall through */
1199 case R2_BLACK:
1200 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
1201 /* fall through */
1202 case R2_NOP:
1203 break;
1204 default:
1205 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
1206 &dc->attr->brush_org, rop2 );
1207 break;
1209 free_clipped_rects( &clipped_rects );
1210 return ret;
1213 /***********************************************************************
1214 * dibdrv_PaintRgn
1216 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
1218 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1219 const WINEREGION *region;
1220 int i;
1221 RECT rect, bounds;
1222 DC *dc = get_physdev_dc( dev );
1224 TRACE("%p, %p\n", dev, rgn);
1226 reset_bounds( &bounds );
1228 region = get_wine_region( rgn );
1229 if(!region) return FALSE;
1231 for(i = 0; i < region->numRects; i++)
1233 rect = get_device_rect( dc, region->rects[i].left, region->rects[i].top,
1234 region->rects[i].right, region->rects[i].bottom, FALSE );
1235 add_bounds_rect( &bounds, &rect );
1236 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1239 release_wine_region( rgn );
1240 add_clipped_bounds( pdev, &bounds, pdev->clip );
1241 return TRUE;
1244 /***********************************************************************
1245 * dibdrv_PolyPolygon
1247 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, UINT polygons )
1249 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1250 DC *dc = get_physdev_dc( dev );
1251 DWORD total, i, pos;
1252 RECT rc;
1253 BOOL ret = TRUE;
1254 POINT pt_buf[32];
1255 POINT *points = pt_buf;
1256 HRGN outline = 0, interior = 0;
1258 for (i = total = 0; i < polygons; i++)
1260 if (counts[i] < 2) return FALSE;
1261 total += counts[i];
1264 if (total > ARRAY_SIZE( pt_buf ))
1266 points = malloc( total * sizeof(*pt) );
1267 if (!points) return FALSE;
1269 memcpy( points, pt, total * sizeof(*pt) );
1270 lp_to_dp( dc, points, total );
1272 if (pdev->brush.style != BS_NULL &&
1273 get_dib_rect( &pdev->dib, &rc ) &&
1274 !(interior = create_polypolygon_region( points, counts, polygons,
1275 dc->attr->poly_fill_mode, &rc )))
1277 ret = FALSE;
1278 goto done;
1281 if (pdev->pen_uses_region) outline = NtGdiCreateRectRgn( 0, 0, 0, 0 );
1283 /* if not using a region, paint the interior first so the outline can overlap it */
1284 if (interior && !outline)
1286 ret = brush_region( pdev, interior );
1287 NtGdiDeleteObjectApp( interior );
1288 interior = 0;
1291 for (i = pos = 0; i < polygons; i++)
1293 reset_dash_origin( pdev );
1294 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1295 pos += counts[i];
1297 add_pen_lines_bounds( pdev, total, points, outline );
1299 if (interior)
1301 NtGdiCombineRgn( interior, interior, outline, RGN_DIFF );
1302 ret = brush_region( pdev, interior );
1303 NtGdiDeleteObjectApp( interior );
1305 if (outline)
1307 if (ret) ret = pen_region( pdev, outline );
1308 NtGdiDeleteObjectApp( outline );
1311 done:
1312 if (points != pt_buf) free( points );
1313 return ret;
1316 /***********************************************************************
1317 * dibdrv_PolyPolyline
1319 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1321 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1322 DC *dc = get_physdev_dc( dev );
1323 DWORD total, pos, i;
1324 POINT pt_buf[32];
1325 POINT *points = pt_buf;
1326 BOOL ret = TRUE;
1327 HRGN outline = 0;
1329 for (i = total = 0; i < polylines; i++)
1331 if (counts[i] < 2) return FALSE;
1332 total += counts[i];
1335 if (total > ARRAY_SIZE( pt_buf ))
1337 points = malloc( total * sizeof(*pt) );
1338 if (!points) return FALSE;
1340 memcpy( points, pt, total * sizeof(*pt) );
1341 lp_to_dp( dc, points, total );
1343 if (pdev->pen_uses_region && !(outline = NtGdiCreateRectRgn( 0, 0, 0, 0 )))
1345 ret = FALSE;
1346 goto done;
1349 for (i = pos = 0; i < polylines; i++)
1351 reset_dash_origin( pdev );
1352 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1353 pos += counts[i];
1355 add_pen_lines_bounds( pdev, total, points, outline );
1357 if (outline)
1359 ret = pen_region( pdev, outline );
1360 NtGdiDeleteObjectApp( outline );
1363 done:
1364 if (points != pt_buf) free( points );
1365 return ret;
1368 /***********************************************************************
1369 * dibdrv_Rectangle
1371 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1373 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1374 DC *dc = get_physdev_dc( dev );
1375 RECT rect;
1376 POINT pts[4];
1377 BOOL ret;
1378 HRGN outline = 0;
1380 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1382 if (dc->attr->graphics_mode == GM_ADVANCED)
1384 const INT count = 4;
1385 pts[0].x = pts[3].x = left;
1386 pts[0].y = pts[1].y = top;
1387 pts[1].x = pts[2].x = right;
1388 pts[2].y = pts[3].y = bottom;
1389 return dibdrv_PolyPolygon( dev, pts, &count, 1 );
1392 if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE;
1394 if (pdev->pen_uses_region && !(outline = NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1396 rect.right--;
1397 rect.bottom--;
1398 reset_dash_origin(pdev);
1400 if (dc->attr->arc_direction == AD_CLOCKWISE)
1402 /* 4 pts going clockwise starting from bottom-right */
1403 pts[0].x = pts[3].x = rect.right;
1404 pts[0].y = pts[1].y = rect.bottom;
1405 pts[1].x = pts[2].x = rect.left;
1406 pts[2].y = pts[3].y = rect.top;
1408 else
1410 /* 4 pts going anti-clockwise starting from top-right */
1411 pts[0].x = pts[3].x = rect.right;
1412 pts[0].y = pts[1].y = rect.top;
1413 pts[1].x = pts[2].x = rect.left;
1414 pts[2].y = pts[3].y = rect.bottom;
1417 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1418 add_pen_lines_bounds( pdev, 4, pts, outline );
1420 if (outline)
1422 if (pdev->brush.style != BS_NULL)
1424 HRGN interior = NtGdiCreateRectRgn( rect.left, rect.top, rect.right, rect.bottom );
1426 NtGdiCombineRgn( interior, interior, outline, RGN_DIFF );
1427 brush_region( pdev, interior );
1428 NtGdiDeleteObjectApp( interior );
1430 ret = pen_region( pdev, outline );
1431 NtGdiDeleteObjectApp( outline );
1433 else
1435 rect.left += (pdev->pen_width + 1) / 2;
1436 rect.top += (pdev->pen_width + 1) / 2;
1437 rect.right -= pdev->pen_width / 2;
1438 rect.bottom -= pdev->pen_width / 2;
1439 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1441 return ret;
1444 /***********************************************************************
1445 * dibdrv_RoundRect
1447 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1448 INT ellipse_width, INT ellipse_height )
1450 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1451 DC *dc = get_physdev_dc( dev );
1452 RECT rect;
1453 POINT pt[2], *points;
1454 int i, end, count;
1455 BOOL ret = TRUE;
1456 HRGN outline = 0, interior = 0;
1458 if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE;
1460 pt[0].x = pt[0].y = 0;
1461 pt[1].x = ellipse_width;
1462 pt[1].y = ellipse_height;
1463 lp_to_dp( dc, pt, 2 );
1464 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1465 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1466 if (ellipse_width <= 2|| ellipse_height <= 2)
1467 return dibdrv_Rectangle( dev, left, top, right, bottom );
1469 points = malloc( (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1470 if (!points) return FALSE;
1472 if (pdev->pen_uses_region && !(outline = NtGdiCreateRectRgn( 0, 0, 0, 0 )))
1474 free( points );
1475 return FALSE;
1478 if (pdev->brush.style != BS_NULL &&
1479 !(interior = NtGdiCreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1480 ellipse_width, ellipse_height )))
1482 free( points );
1483 if (outline) NtGdiDeleteObjectApp( outline );
1484 return FALSE;
1487 /* if not using a region, paint the interior first so the outline can overlap it */
1488 if (interior && !outline)
1490 ret = brush_region( pdev, interior );
1491 NtGdiDeleteObjectApp( interior );
1492 interior = 0;
1495 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1497 if (dc->attr->arc_direction == AD_CLOCKWISE)
1499 for (i = 0; i < count; i++)
1501 points[i].x = rect.right - ellipse_width + points[i].x;
1502 points[i].y = rect.bottom - ellipse_height + points[i].y;
1505 else
1507 for (i = 0; i < count; i++)
1509 points[i].x = rect.right - ellipse_width + points[i].x;
1510 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1514 /* horizontal symmetry */
1516 end = 2 * count - 1;
1517 /* avoid duplicating the midpoint */
1518 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1519 for (i = 0; i < count; i++)
1521 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1522 points[end - i].y = points[i].y;
1524 count = end + 1;
1526 /* vertical symmetry */
1528 end = 2 * count - 1;
1529 /* avoid duplicating the midpoint */
1530 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1531 for (i = 0; i < count; i++)
1533 points[end - i].x = points[i].x;
1534 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1536 count = end + 1;
1538 reset_dash_origin( pdev );
1539 pdev->pen_lines( pdev, count, points, TRUE, outline );
1540 add_pen_lines_bounds( pdev, count, points, outline );
1542 if (interior)
1544 NtGdiCombineRgn( interior, interior, outline, RGN_DIFF );
1545 ret = brush_region( pdev, interior );
1546 NtGdiDeleteObjectApp( interior );
1548 if (outline)
1550 if (ret) ret = pen_region( pdev, outline );
1551 NtGdiDeleteObjectApp( outline );
1553 free( points );
1554 return ret;
1557 /***********************************************************************
1558 * dibdrv_Pie
1560 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1561 INT start_x, INT start_y, INT end_x, INT end_y )
1563 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1566 /***********************************************************************
1567 * dibdrv_SetPixel
1569 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1571 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1572 DC *dc = get_physdev_dc( dev );
1573 struct clipped_rects clipped_rects;
1574 RECT rect;
1575 POINT pt;
1576 DWORD pixel;
1578 TRACE( "(%p, %d, %d, %s)\n", dev, x, y, debugstr_color(color) );
1580 pt.x = x;
1581 pt.y = y;
1582 lp_to_dp( dc, &pt, 1 );
1583 rect.left = pt.x;
1584 rect.top = pt.y;
1585 rect.right = rect.left + 1;
1586 rect.bottom = rect.top + 1;
1587 add_clipped_bounds( pdev, &rect, pdev->clip );
1589 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1590 pixel = get_pixel_color( dc, &pdev->dib, color, FALSE );
1591 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1593 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1594 fill_with_pixel( dc, &pdev->dib, pixel, clipped_rects.count, clipped_rects.rects, dc->attr->rop_mode );
1595 free_clipped_rects( &clipped_rects );
1596 return color;
1599 /***********************************************************************
1600 * dibdrv_StrokeAndFillPath
1602 BOOL dibdrv_StrokeAndFillPath( PHYSDEV dev )
1604 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1606 return stroke_and_fill_path( pdev, TRUE, TRUE );
1609 /***********************************************************************
1610 * dibdrv_StrokePath
1612 BOOL dibdrv_StrokePath( PHYSDEV dev )
1614 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1616 return stroke_and_fill_path( pdev, TRUE, FALSE );