gdi32: Pass the brush origin explicitly to the brush backend functions.
[wine.git] / dlls / gdi32 / dibdrv / graphics.c
bloba39f1e2fa2ba4a87406edf1c9afedc251addbcf0
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 #include <assert.h>
22 #include "gdi_private.h"
23 #include "dibdrv.h"
25 #include "wine/unicode.h"
26 #include "wine/debug.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(dib);
30 struct cached_glyph
32 GLYPHMETRICS metrics;
33 BYTE bits[1];
36 enum glyph_type
38 GLYPH_INDEX,
39 GLYPH_WCHAR,
40 GLYPH_NBTYPES
43 #define GLYPH_CACHE_PAGE_SIZE 0x100
44 #define GLYPH_CACHE_PAGES (0x10000 / GLYPH_CACHE_PAGE_SIZE)
46 struct cached_font
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];
57 static struct list font_cache = LIST_INIT( font_cache );
59 static CRITICAL_SECTION font_cache_cs;
60 static CRITICAL_SECTION_DEBUG critsect_debug =
62 0, 0, &font_cache_cs,
63 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
64 0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_cs") }
66 static CRITICAL_SECTION font_cache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
69 static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
71 DC *dc = get_physdev_dc( &pdev->dev );
72 struct clipped_rects clipped_rects;
73 BOOL ret;
75 if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
76 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
77 &dc->brush_org, dc->ROPmode );
78 free_clipped_rects( &clipped_rects );
79 return ret;
82 /* paint a region with the brush (note: the region can be modified) */
83 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
85 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
86 return brush_rect( pdev, &pdev->brush, NULL, region );
89 /* paint a region with the pen (note: the region can be modified) */
90 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
92 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
93 return brush_rect( pdev, &pdev->pen_brush, NULL, region );
96 static RECT get_device_rect( DC *dc, int left, int top, int right, int bottom, BOOL rtl_correction )
98 RECT rect;
100 rect.left = left;
101 rect.top = top;
102 rect.right = right;
103 rect.bottom = bottom;
104 if (rtl_correction && dc->layout & LAYOUT_RTL)
106 /* shift the rectangle so that the right border is included after mirroring */
107 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
108 rect.left--;
109 rect.right--;
111 lp_to_dp( dc, (POINT *)&rect, 2 );
112 order_rect( &rect );
113 return rect;
116 static BOOL get_pen_device_rect( DC *dc, dibdrv_physdev *dev, RECT *rect,
117 int left, int top, int right, int bottom )
119 *rect = get_device_rect( dc, left, top, right, bottom, TRUE );
120 if (rect->left == rect->right || rect->top == rect->bottom) return FALSE;
122 if (dev->pen_style == PS_INSIDEFRAME)
124 rect->left += dev->pen_width / 2;
125 rect->top += dev->pen_width / 2;
126 rect->right -= (dev->pen_width - 1) / 2;
127 rect->bottom -= (dev->pen_width - 1) / 2;
129 return TRUE;
132 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
134 const WINEREGION *region;
135 RECT bounds, rect;
136 int width = 0;
138 if (!dev->bounds) return;
139 reset_bounds( &bounds );
141 if (dev->pen_uses_region)
143 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
144 width = dev->pen_width + 2;
145 if (dev->pen_join == PS_JOIN_MITER)
147 width *= 5;
148 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
150 else
152 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4;
153 else width = (width + 1) / 2;
156 /* in case the heuristics are wrong, add the actual region too */
157 if ((region = get_wine_region( rgn )))
159 add_bounds_rect( &bounds, &region->extents );
160 release_wine_region( rgn );
164 while (count-- > 0)
166 rect.left = points->x - width;
167 rect.top = points->y - width;
168 rect.right = points->x + width + 1;
169 rect.bottom = points->y + width + 1;
170 add_bounds_rect( &bounds, &rect );
171 points++;
174 add_clipped_bounds( dev, &bounds, dev->clip );
177 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
178 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
179 static int ellipse_first_quadrant( int width, int height, POINT *data )
181 const int a = width - 1;
182 const int b = height - 1;
183 const INT64 asq = (INT64)8 * a * a;
184 const INT64 bsq = (INT64)8 * b * b;
185 INT64 dx = (INT64)4 * b * b * (1 - a);
186 INT64 dy = (INT64)4 * a * a * (1 + (b % 2));
187 INT64 err = dx + dy + a * a * (b % 2);
188 int pos = 0;
189 POINT pt;
191 pt.x = a;
192 pt.y = height / 2;
194 /* based on an algorithm by Alois Zingl */
196 while (pt.x >= width / 2)
198 INT64 e2 = 2 * err;
199 data[pos++] = pt;
200 if (e2 >= dx)
202 pt.x--;
203 err += dx += bsq;
205 if (e2 <= dy)
207 pt.y++;
208 err += dy += asq;
211 return pos;
214 static int find_intersection( const POINT *points, int x, int y, int count )
216 int i;
218 if (y >= 0)
220 if (x >= 0) /* first quadrant */
222 for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
223 return i;
225 /* second quadrant */
226 for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break;
227 return 2 * count - i;
229 if (x >= 0) /* fourth quadrant */
231 for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break;
232 return 4 * count - i;
234 /* third quadrant */
235 for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break;
236 return 2 * count + i;
239 static int get_arc_points( int arc_dir, const RECT *rect, POINT start, POINT end, POINT *points )
241 int i, pos, count, start_pos, end_pos;
242 int width = rect->right - rect->left;
243 int height = rect->bottom - rect->top;
245 count = ellipse_first_quadrant( width, height, points );
246 for (i = 0; i < count; i++)
248 points[i].x -= width / 2;
249 points[i].y -= height / 2;
251 if (arc_dir != AD_CLOCKWISE)
253 start.y = -start.y;
254 end.y = -end.y;
256 start_pos = find_intersection( points, start.x, start.y, count );
257 end_pos = find_intersection( points, end.x, end.y, count );
258 if (end_pos <= start_pos) end_pos += 4 * count;
260 pos = count;
261 if (arc_dir == AD_CLOCKWISE)
263 for (i = start_pos; i < end_pos; i++, pos++)
265 switch ((i / count) % 4)
267 case 0:
268 points[pos].x = rect->left + width/2 + points[i % count].x;
269 points[pos].y = rect->top + height/2 + points[i % count].y;
270 break;
271 case 1:
272 points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x;
273 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
274 break;
275 case 2:
276 points[pos].x = rect->right-1 - width/2 - points[i % count].x;
277 points[pos].y = rect->bottom-1 - height/2 - points[i % count].y;
278 break;
279 case 3:
280 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
281 points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y;
282 break;
286 else
288 for (i = start_pos; i < end_pos; i++, pos++)
290 switch ((i / count) % 4)
292 case 0:
293 points[pos].x = rect->left + width/2 + points[i % count].x;
294 points[pos].y = rect->bottom-1 - height/2 - points[i % count].y;
295 break;
296 case 1:
297 points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x;
298 points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y;
299 break;
300 case 2:
301 points[pos].x = rect->right-1 - width/2 - points[i % count].x;
302 points[pos].y = rect->top + height/2 + points[i % count].y;
303 break;
304 case 3:
305 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
306 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
307 break;
312 memmove( points, points + count, (pos - count) * sizeof(POINT) );
313 return pos - count;
316 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
317 static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
318 INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines )
320 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
321 DC *dc = get_physdev_dc( dev );
322 RECT rect;
323 POINT pt[2], *points;
324 int width, height, count;
325 BOOL ret = TRUE;
326 HRGN outline = 0, interior = 0;
328 if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE;
330 width = rect.right - rect.left;
331 height = rect.bottom - rect.top;
333 pt[0].x = start_x;
334 pt[0].y = start_y;
335 pt[1].x = end_x;
336 pt[1].y = end_y;
337 lp_to_dp( dc, pt, 2 );
338 /* make them relative to the ellipse center */
339 pt[0].x -= rect.left + width / 2;
340 pt[0].y -= rect.top + height / 2;
341 pt[1].x -= rect.left + width / 2;
342 pt[1].y -= rect.top + height / 2;
344 points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) );
345 if (!points) return FALSE;
347 if (extra_lines == -1)
349 points[0] = dc->cur_pos;
350 lp_to_dp( dc, points, 1 );
351 count = 1 + get_arc_points( dc->ArcDirection, &rect, pt[0], pt[1], points + 1 );
353 else count = get_arc_points( dc->ArcDirection, &rect, pt[0], pt[1], points );
355 if (extra_lines == 2)
357 points[count].x = rect.left + width / 2;
358 points[count].y = rect.top + height / 2;
359 count++;
361 if (count < 2)
363 HeapFree( GetProcessHeap(), 0, points );
364 return TRUE;
367 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
369 HeapFree( GetProcessHeap(), 0, points );
370 return FALSE;
373 if (pdev->brush.style != BS_NULL && extra_lines > 0 &&
374 !(interior = CreatePolygonRgn( points, count, WINDING )))
376 HeapFree( GetProcessHeap(), 0, points );
377 if (outline) DeleteObject( outline );
378 return FALSE;
381 /* if not using a region, paint the interior first so the outline can overlap it */
382 if (interior && !outline)
384 ret = brush_region( pdev, interior );
385 DeleteObject( interior );
386 interior = 0;
389 reset_dash_origin( pdev );
390 pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
391 add_pen_lines_bounds( pdev, count, points, outline );
393 if (interior)
395 CombineRgn( interior, interior, outline, RGN_DIFF );
396 ret = brush_region( pdev, interior );
397 DeleteObject( interior );
399 if (outline)
401 if (ret) ret = pen_region( pdev, outline );
402 DeleteObject( outline );
404 HeapFree( GetProcessHeap(), 0, points );
405 return ret;
408 /* helper for path stroking and filling functions */
409 static BOOL stroke_and_fill_path( dibdrv_physdev *dev, BOOL stroke, BOOL fill )
411 DC *dc = get_physdev_dc( &dev->dev );
412 struct gdi_path *path;
413 POINT *points;
414 BYTE *types;
415 BOOL ret = TRUE;
416 HRGN outline = 0, interior = 0;
417 int i, pos, total;
419 if (dev->brush.style == BS_NULL) fill = FALSE;
421 if (!(path = get_gdi_flat_path( dc, fill ? &interior : NULL ))) return FALSE;
422 if (!(total = get_gdi_path_data( path, &points, &types ))) goto done;
424 if (stroke && dev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
426 /* if not using a region, paint the interior first so the outline can overlap it */
427 if (interior && !outline)
429 ret = brush_region( dev, interior );
430 DeleteObject( interior );
431 interior = 0;
434 if (stroke)
436 pos = 0;
437 for (i = 1; i < total; i++)
439 if (types[i] != PT_MOVETO) continue;
440 if (i > pos + 1)
442 reset_dash_origin( dev );
443 dev->pen_lines( dev, i - pos, points + pos,
444 fill || types[i - 1] & PT_CLOSEFIGURE, outline );
446 pos = i;
448 if (i > pos + 1)
450 reset_dash_origin( dev );
451 dev->pen_lines( dev, i - pos, points + pos,
452 fill || types[i - 1] & PT_CLOSEFIGURE, outline );
456 add_pen_lines_bounds( dev, total, points, outline );
458 if (interior)
460 CombineRgn( interior, interior, outline, RGN_DIFF );
461 ret = brush_region( dev, interior );
462 DeleteObject( interior );
464 if (outline)
466 if (ret) ret = pen_region( dev, outline );
467 DeleteObject( outline );
470 done:
471 free_gdi_path( path );
472 return ret;
475 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
476 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
477 static const BYTE ramp[17] =
479 0, 0x4d, 0x68, 0x7c,
480 0x8c, 0x9a, 0xa7, 0xb2,
481 0xbd, 0xc7, 0xd0, 0xd9,
482 0xe1, 0xe9, 0xf0, 0xf8,
483 0xff
486 /* For a give text-color component and a glyph level, calculate the
487 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
488 components respectively.
490 The minimum is a linear interpolation between 0 and the value in
491 the ramp table.
493 The maximum is a linear interpolation between the value from the
494 ramp table read in reverse and 0xff.
496 To find the resulting pixel intensity, we note that if the text and
497 bkgnd intensities are the same then the result must be that
498 intensity. Otherwise we linearly interpolate between either the
499 min or the max value and this intermediate value depending on which
500 side of the inequality we lie.
503 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
505 *min_comp = (ramp[aa] * text_comp) / 0xff;
506 *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
509 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
511 int i;
513 for (i = 0; i < 17; i++)
515 get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
516 get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
517 get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
521 static DWORD font_cache_hash( struct cached_font *font )
523 DWORD hash = 0, *ptr, two_chars;
524 WORD *pwc;
525 int i;
527 hash ^= font->aa_flags;
528 for(i = 0, ptr = (DWORD*)&font->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
529 hash ^= *ptr;
530 for(i = 0, ptr = (DWORD*)&font->lf; i < 7; i++, ptr++)
531 hash ^= *ptr;
532 for(i = 0, ptr = (DWORD*)font->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
533 two_chars = *ptr;
534 pwc = (WCHAR *)&two_chars;
535 if (!*pwc) break;
536 *pwc = toupperW(*pwc);
537 pwc++;
538 *pwc = toupperW(*pwc);
539 hash ^= two_chars;
540 if (!*pwc) break;
542 return hash;
545 static int font_cache_cmp( const struct cached_font *p1, const struct cached_font *p2 )
547 int ret = p1->hash - p2->hash;
548 if (!ret) ret = p1->aa_flags - p2->aa_flags;
549 if (!ret) ret = memcmp( &p1->xform, &p2->xform, sizeof(p1->xform) );
550 if (!ret) ret = memcmp( &p1->lf, &p2->lf, FIELD_OFFSET( LOGFONTW, lfFaceName ));
551 if (!ret) ret = strcmpiW( p1->lf.lfFaceName, p2->lf.lfFaceName );
552 return ret;
555 static struct cached_font *add_cached_font( DC *dc, HFONT hfont, UINT aa_flags )
557 struct cached_font font, *ptr, *last_unused = NULL;
558 UINT i = 0, j, k;
560 GetObjectW( hfont, sizeof(font.lf), &font.lf );
561 font.xform = dc->xformWorld2Vport;
562 font.xform.eDx = font.xform.eDy = 0; /* unused, would break hashing */
563 if (dc->GraphicsMode == GM_COMPATIBLE)
565 font.lf.lfOrientation = font.lf.lfEscapement;
566 if (font.xform.eM11 * font.xform.eM22 < 0)
567 font.lf.lfOrientation = -font.lf.lfOrientation;
569 font.lf.lfWidth = abs( font.lf.lfWidth );
570 font.aa_flags = aa_flags;
571 font.hash = font_cache_hash( &font );
573 EnterCriticalSection( &font_cache_cs );
574 LIST_FOR_EACH_ENTRY( ptr, &font_cache, struct cached_font, entry )
576 if (!font_cache_cmp( &font, ptr ))
578 InterlockedIncrement( &ptr->ref );
579 list_remove( &ptr->entry );
580 goto done;
582 if (!ptr->ref)
584 i++;
585 last_unused = ptr;
589 if (i > 5) /* keep at least 5 of the most-recently used fonts around */
591 ptr = last_unused;
592 for (i = 0; i < GLYPH_NBTYPES; i++)
594 for (j = 0; j < GLYPH_CACHE_PAGES; j++)
596 if (!ptr->glyphs[i][j]) continue;
597 for (k = 0; k < GLYPH_CACHE_PAGE_SIZE; k++)
598 HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j][k] );
599 HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j] );
602 list_remove( &ptr->entry );
604 else if (!(ptr = HeapAlloc( GetProcessHeap(), 0, sizeof(*ptr) )))
606 LeaveCriticalSection( &font_cache_cs );
607 return NULL;
610 *ptr = font;
611 ptr->ref = 1;
612 memset( ptr->glyphs, 0, sizeof(ptr->glyphs) );
613 done:
614 list_add_head( &font_cache, &ptr->entry );
615 LeaveCriticalSection( &font_cache_cs );
616 TRACE( "%d %s -> %p\n", ptr->lf.lfHeight, debugstr_w(ptr->lf.lfFaceName), ptr );
617 return ptr;
620 void release_cached_font( struct cached_font *font )
622 if (font) InterlockedDecrement( &font->ref );
625 static struct cached_glyph *add_cached_glyph( struct cached_font *font, UINT index, UINT flags,
626 struct cached_glyph *glyph )
628 struct cached_glyph *ret;
629 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
630 UINT page = index / GLYPH_CACHE_PAGE_SIZE;
631 UINT entry = index % GLYPH_CACHE_PAGE_SIZE;
633 if (!font->glyphs[type][page])
635 struct cached_glyph **ptr;
637 ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, GLYPH_CACHE_PAGE_SIZE * sizeof(*ptr) );
638 if (!ptr)
640 HeapFree( GetProcessHeap(), 0, glyph );
641 return NULL;
643 if (InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page], ptr, NULL ))
644 HeapFree( GetProcessHeap(), 0, ptr );
646 ret = InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page][entry], glyph, NULL );
647 if (!ret) ret = glyph;
648 else HeapFree( GetProcessHeap(), 0, glyph );
649 return ret;
652 static struct cached_glyph *get_cached_glyph( struct cached_font *font, UINT index, UINT flags )
654 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
655 UINT page = index / GLYPH_CACHE_PAGE_SIZE;
657 if (!font->glyphs[type][page]) return NULL;
658 return font->glyphs[type][page][index % GLYPH_CACHE_PAGE_SIZE];
661 /**********************************************************************
662 * get_text_bkgnd_masks
664 * See the comment above get_pen_bkgnd_masks
666 static inline void get_text_bkgnd_masks( DC *dc, const dib_info *dib, rop_mask *mask )
668 COLORREF bg = dc->backgroundColor;
670 mask->and = 0;
672 if (dib->bit_count != 1)
673 mask->xor = get_pixel_color( dc, dib, bg, FALSE );
674 else
676 COLORREF fg = dc->textColor;
677 mask->xor = get_pixel_color( dc, dib, fg, TRUE );
678 if (fg != bg) mask->xor = ~mask->xor;
682 static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics,
683 const dib_info *glyph_dib, DWORD text_color,
684 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
685 RECT *bounds )
687 int i;
688 RECT rect, clipped_rect;
689 POINT src_origin;
691 rect.left = x + metrics->gmptGlyphOrigin.x;
692 rect.top = y - metrics->gmptGlyphOrigin.y;
693 rect.right = rect.left + metrics->gmBlackBoxX;
694 rect.bottom = rect.top + metrics->gmBlackBoxY;
695 if (bounds) add_bounds_rect( bounds, &rect );
697 for (i = 0; i < clipped_rects->count; i++)
699 if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
701 src_origin.x = clipped_rect.left - rect.left;
702 src_origin.y = clipped_rect.top - rect.top;
704 if (glyph_dib->bit_count == 32)
705 dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
706 text_color );
707 else
708 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
709 text_color, ranges );
714 static int get_glyph_depth( UINT aa_flags )
716 switch (aa_flags)
718 case GGO_BITMAP: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */
719 case GGO_GRAY2_BITMAP:
720 case GGO_GRAY4_BITMAP:
721 case GGO_GRAY8_BITMAP:
722 case WINE_GGO_GRAY16_BITMAP: return 8;
724 case WINE_GGO_HRGB_BITMAP:
725 case WINE_GGO_HBGR_BITMAP:
726 case WINE_GGO_VRGB_BITMAP:
727 case WINE_GGO_VBGR_BITMAP: return 32;
729 default:
730 ERR("Unexpected flags %08x\n", aa_flags);
731 return 0;
735 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
736 static const int padding[4] = {0, 3, 2, 1};
738 /***********************************************************************
739 * cache_glyph_bitmap
741 * Retrieve a 17-level bitmap for the appropriate glyph.
743 * For non-antialiased bitmaps convert them to the 17-level format
744 * using only values 0 or 16.
746 static struct cached_glyph *cache_glyph_bitmap( DC *dc, struct cached_font *font, UINT index, UINT flags )
748 UINT ggo_flags = font->aa_flags;
749 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
750 UINT indices[3] = {0, 0, 0x20};
751 int i, x, y;
752 DWORD ret, size;
753 BYTE *dst, *src;
754 int pad = 0, stride, bit_count;
755 GLYPHMETRICS metrics;
756 struct cached_glyph *glyph;
758 if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
759 indices[0] = index;
760 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
762 index = indices[i];
763 ret = GetGlyphOutlineW( dc->hSelf, index, ggo_flags, &metrics, 0, NULL, &identity );
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 = HeapAlloc( GetProcessHeap(), 0, 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 = GetGlyphOutlineW( dc->hSelf, index, ggo_flags, &metrics, size, glyph->bits, &identity );
779 if (ret == GDI_ERROR)
781 HeapFree( GetProcessHeap(), 0, glyph );
782 return NULL;
784 assert( ret <= size );
785 if (font->aa_flags == GGO_BITMAP)
787 for (y = metrics.gmBlackBoxY - 1; y >= 0; y--)
789 src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 );
790 dst = glyph->bits + y * stride;
792 if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad );
794 for (x = metrics.gmBlackBoxX - 1; x >= 0; x--)
795 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
798 else if (pad)
800 for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride)
801 memset( dst + metrics.gmBlackBoxX, 0, pad );
804 done:
805 glyph->metrics = metrics;
806 return add_cached_glyph( font, index, flags, glyph );
809 static void render_string( DC *dc, dib_info *dib, struct cached_font *font, INT x, INT y,
810 UINT flags, const WCHAR *str, UINT count, const INT *dx,
811 const struct clipped_rects *clipped_rects, RECT *bounds )
813 UINT i;
814 struct cached_glyph *glyph;
815 dib_info glyph_dib;
816 DWORD text_color;
817 struct intensity_range ranges[17];
819 glyph_dib.bit_count = get_glyph_depth( font->aa_flags );
820 glyph_dib.rect.left = 0;
821 glyph_dib.rect.top = 0;
822 glyph_dib.bits.is_copy = FALSE;
823 glyph_dib.bits.free = NULL;
825 text_color = get_pixel_color( dc, dib, dc->textColor, TRUE );
827 if (glyph_dib.bit_count == 8)
828 get_aa_ranges( dib->funcs->pixel_to_colorref( dib, text_color ), ranges );
830 for (i = 0; i < count; i++)
832 if (!(glyph = get_cached_glyph( font, str[i], flags )) &&
833 !(glyph = cache_glyph_bitmap( dc, font, str[i], flags ))) continue;
835 glyph_dib.width = glyph->metrics.gmBlackBoxX;
836 glyph_dib.height = glyph->metrics.gmBlackBoxY;
837 glyph_dib.rect.right = glyph->metrics.gmBlackBoxX;
838 glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY;
839 glyph_dib.stride = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count );
840 glyph_dib.bits.ptr = glyph->bits;
842 draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
844 if (dx)
846 if (flags & ETO_PDY)
848 x += dx[ i * 2 ];
849 y += dx[ i * 2 + 1];
851 else
852 x += dx[ i ];
854 else
856 x += glyph->metrics.gmCellIncX;
857 y += glyph->metrics.gmCellIncY;
862 BOOL render_aa_text_bitmapinfo( DC *dc, BITMAPINFO *info, struct gdi_image_bits *bits,
863 struct bitblt_coords *src, INT x, INT y, UINT flags,
864 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
866 dib_info dib;
867 struct clipped_rects visrect;
868 struct cached_font *font;
870 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
872 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
874 visrect.count = 1;
875 visrect.rects = &src->visrect;
877 if (flags & ETO_OPAQUE)
879 rop_mask bkgnd_color;
880 get_text_bkgnd_masks( dc, &dib, &bkgnd_color );
881 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
884 if (!(font = add_cached_font( dc, dc->hFont, aa_flags ))) return FALSE;
886 render_string( dc, &dib, font, x, y, flags, str, count, dx, &visrect, NULL );
887 release_cached_font( font );
888 return TRUE;
891 /***********************************************************************
892 * dibdrv_ExtTextOut
894 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
895 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
897 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
898 DC *dc = get_physdev_dc( dev );
899 struct clipped_rects clipped_rects;
900 RECT bounds;
902 if (!pdev->font) return FALSE;
904 init_clipped_rects( &clipped_rects );
905 reset_bounds( &bounds );
907 if (flags & ETO_OPAQUE)
909 rop_mask bkgnd_color;
910 get_text_bkgnd_masks( dc, &pdev->dib, &bkgnd_color );
911 add_bounds_rect( &bounds, rect );
912 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
913 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
914 bkgnd_color.and, bkgnd_color.xor );
917 if (count == 0) goto done;
919 if (flags & ETO_CLIPPED)
921 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
922 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
924 else
926 free_clipped_rects( &clipped_rects );
927 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
929 if (!clipped_rects.count) goto done;
931 render_string( dc, &pdev->dib, pdev->font, x, y, flags, str, count, dx,
932 &clipped_rects, &bounds );
934 done:
935 add_clipped_bounds( pdev, &bounds, pdev->clip );
936 free_clipped_rects( &clipped_rects );
937 return TRUE;
940 /***********************************************************************
941 * dibdrv_SelectFont
943 HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
945 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
946 DC *dc = get_physdev_dc( dev );
947 HFONT ret;
949 if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */
951 dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
952 ret = dev->funcs->pSelectFont( dev, font, aa_flags );
953 if (ret)
955 struct cached_font *prev = pdev->font;
956 pdev->font = add_cached_font( dc, font, *aa_flags ? *aa_flags : GGO_BITMAP );
957 release_cached_font( prev );
959 return ret;
962 /***********************************************************************
963 * dibdrv_Arc
965 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
966 INT start_x, INT start_y, INT end_x, INT end_y )
968 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
971 /***********************************************************************
972 * dibdrv_ArcTo
974 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
975 INT start_x, INT start_y, INT end_x, INT end_y )
977 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
980 /***********************************************************************
981 * dibdrv_Chord
983 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
984 INT start_x, INT start_y, INT end_x, INT end_y )
986 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
989 /***********************************************************************
990 * dibdrv_Ellipse
992 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
994 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
997 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
999 /* the clip rgn stops the flooding */
1000 if (clip && !PtInRegion( clip, x, y )) return FALSE;
1002 if (type == FLOODFILLBORDER)
1003 return dib->funcs->get_pixel( dib, x, y ) != pixel;
1004 else
1005 return dib->funcs->get_pixel( dib, x, y ) == pixel;
1008 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
1010 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
1012 RECT next;
1014 next.top = row->top + offset;
1015 next.bottom = next.top + 1;
1016 next.left = next.right = row->left;
1017 while (next.right < row->right)
1019 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
1020 else
1022 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
1023 fill_row( dib, clip, &next, pixel, type, rgn );
1024 next.left = ++next.right;
1027 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
1028 fill_row( dib, clip, &next, pixel, type, rgn );
1031 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
1033 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
1034 while (row->right < dib->rect.right - dib->rect.left &&
1035 is_interior( dib, clip, row->right, row->top, pixel, type))
1036 row->right++;
1038 add_rect_to_region( rgn, row );
1040 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
1041 if (row->top < dib->rect.bottom - dib->rect.top - 1)
1042 do_next_row( dib, clip, row, 1, pixel, type, rgn );
1045 /***********************************************************************
1046 * dibdrv_ExtFloodFill
1048 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
1050 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1051 DC *dc = get_physdev_dc( dev );
1052 DWORD pixel = get_pixel_color( dc, &pdev->dib, color, FALSE );
1053 RECT row;
1054 HRGN rgn;
1056 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
1058 if (x < 0 || x >= pdev->dib.rect.right - pdev->dib.rect.left ||
1059 y < 0 || y >= pdev->dib.rect.bottom - pdev->dib.rect.top) return FALSE;
1061 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
1063 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1064 row.left = x;
1065 row.right = x + 1;
1066 row.top = y;
1067 row.bottom = y + 1;
1069 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
1071 add_clipped_bounds( pdev, NULL, rgn );
1072 brush_region( pdev, rgn );
1074 DeleteObject( rgn );
1075 return TRUE;
1078 /***********************************************************************
1079 * dibdrv_FillPath
1081 BOOL dibdrv_FillPath( PHYSDEV dev )
1083 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1085 return stroke_and_fill_path( pdev, FALSE, TRUE );
1088 /***********************************************************************
1089 * dibdrv_GetNearestColor
1091 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
1093 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1094 DC *dc = get_physdev_dc( dev );
1095 DWORD pixel;
1097 TRACE( "(%p, %08x)\n", dev, color );
1099 pixel = get_pixel_color( dc, &pdev->dib, color, FALSE );
1100 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1103 /***********************************************************************
1104 * dibdrv_GetPixel
1106 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
1108 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1109 DC *dc = get_physdev_dc( dev );
1110 POINT pt;
1111 DWORD pixel;
1113 TRACE( "(%p, %d, %d)\n", dev, x, y );
1115 pt.x = x;
1116 pt.y = y;
1117 lp_to_dp( dc, &pt, 1 );
1119 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
1120 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
1121 return CLR_INVALID;
1123 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
1124 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1127 /***********************************************************************
1128 * dibdrv_LineTo
1130 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
1132 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1133 DC *dc = get_physdev_dc( dev );
1134 POINT pts[2];
1135 HRGN region = 0;
1136 BOOL ret;
1138 pts[0] = dc->cur_pos;
1139 pts[1].x = x;
1140 pts[1].y = y;
1142 lp_to_dp(dc, pts, 2);
1144 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1146 reset_dash_origin(pdev);
1148 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
1149 add_pen_lines_bounds( pdev, 2, pts, region );
1151 if (region)
1153 ret = pen_region( pdev, region );
1154 DeleteObject( region );
1156 return ret;
1159 /***********************************************************************
1160 * get_rop2_from_rop
1162 * Returns the binary rop that is equivalent to the provided ternary rop
1163 * if the src bits are ignored.
1165 static inline INT get_rop2_from_rop(INT rop)
1167 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
1170 /***********************************************************************
1171 * dibdrv_PatBlt
1173 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
1175 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1176 dib_brush *brush = &pdev->brush;
1177 DC *dc = get_physdev_dc( dev );
1178 int rop2 = get_rop2_from_rop( rop );
1179 struct clipped_rects clipped_rects;
1180 DWORD and = 0, xor = 0;
1181 BOOL ret = TRUE;
1183 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
1185 add_clipped_bounds( pdev, &dst->visrect, 0 );
1186 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
1188 switch (rop2) /* shortcuts for rops that don't involve the brush */
1190 case R2_NOT: and = ~0u;
1191 /* fall through */
1192 case R2_WHITE: xor = ~0u;
1193 /* fall through */
1194 case R2_BLACK:
1195 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
1196 /* fall through */
1197 case R2_NOP:
1198 break;
1199 default:
1200 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
1201 &dc->brush_org, rop2 );
1202 break;
1204 free_clipped_rects( &clipped_rects );
1205 return ret;
1208 /***********************************************************************
1209 * dibdrv_PaintRgn
1211 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
1213 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1214 const WINEREGION *region;
1215 int i;
1216 RECT rect, bounds;
1217 DC *dc = get_physdev_dc( dev );
1219 TRACE("%p, %p\n", dev, rgn);
1221 reset_bounds( &bounds );
1223 region = get_wine_region( rgn );
1224 if(!region) return FALSE;
1226 for(i = 0; i < region->numRects; i++)
1228 rect = get_device_rect( dc, region->rects[i].left, region->rects[i].top,
1229 region->rects[i].right, region->rects[i].bottom, FALSE );
1230 add_bounds_rect( &bounds, &rect );
1231 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1234 release_wine_region( rgn );
1235 add_clipped_bounds( pdev, &bounds, pdev->clip );
1236 return TRUE;
1239 /***********************************************************************
1240 * dibdrv_PolyPolygon
1242 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
1244 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1245 DC *dc = get_physdev_dc( dev );
1246 DWORD total, i, pos;
1247 BOOL ret = TRUE;
1248 POINT pt_buf[32];
1249 POINT *points = pt_buf;
1250 HRGN outline = 0, interior = 0;
1252 for (i = total = 0; i < polygons; i++)
1254 if (counts[i] < 2) return FALSE;
1255 total += counts[i];
1258 if (total > sizeof(pt_buf) / sizeof(pt_buf[0]))
1260 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1261 if (!points) return FALSE;
1263 memcpy( points, pt, total * sizeof(*pt) );
1264 lp_to_dp( dc, points, total );
1266 if (pdev->brush.style != BS_NULL &&
1267 !(interior = CreatePolyPolygonRgn( points, counts, polygons, dc->polyFillMode )))
1269 ret = FALSE;
1270 goto done;
1273 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1275 /* if not using a region, paint the interior first so the outline can overlap it */
1276 if (interior && !outline)
1278 ret = brush_region( pdev, interior );
1279 DeleteObject( interior );
1280 interior = 0;
1283 for (i = pos = 0; i < polygons; i++)
1285 reset_dash_origin( pdev );
1286 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1287 pos += counts[i];
1289 add_pen_lines_bounds( pdev, total, points, outline );
1291 if (interior)
1293 CombineRgn( interior, interior, outline, RGN_DIFF );
1294 ret = brush_region( pdev, interior );
1295 DeleteObject( interior );
1297 if (outline)
1299 if (ret) ret = pen_region( pdev, outline );
1300 DeleteObject( outline );
1303 done:
1304 if (points != pt_buf) HeapFree( GetProcessHeap(), 0, points );
1305 return ret;
1308 /***********************************************************************
1309 * dibdrv_PolyPolyline
1311 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1313 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1314 DC *dc = get_physdev_dc( dev );
1315 DWORD total, pos, i;
1316 POINT pt_buf[32];
1317 POINT *points = pt_buf;
1318 BOOL ret = TRUE;
1319 HRGN outline = 0;
1321 for (i = total = 0; i < polylines; i++)
1323 if (counts[i] < 2) return FALSE;
1324 total += counts[i];
1327 if (total > sizeof(pt_buf) / sizeof(pt_buf[0]))
1329 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1330 if (!points) return FALSE;
1332 memcpy( points, pt, total * sizeof(*pt) );
1333 lp_to_dp( dc, points, total );
1335 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1337 ret = FALSE;
1338 goto done;
1341 for (i = pos = 0; i < polylines; i++)
1343 reset_dash_origin( pdev );
1344 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1345 pos += counts[i];
1347 add_pen_lines_bounds( pdev, total, points, outline );
1349 if (outline)
1351 ret = pen_region( pdev, outline );
1352 DeleteObject( outline );
1355 done:
1356 if (points != pt_buf) HeapFree( GetProcessHeap(), 0, points );
1357 return ret;
1360 /***********************************************************************
1361 * dibdrv_Polygon
1363 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1365 INT counts[1] = { count };
1367 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1370 /***********************************************************************
1371 * dibdrv_Polyline
1373 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1375 DWORD counts[1] = { count };
1377 if (count < 0) return FALSE;
1378 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1381 /***********************************************************************
1382 * dibdrv_Rectangle
1384 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1386 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1387 DC *dc = get_physdev_dc( dev );
1388 RECT rect;
1389 POINT pts[4];
1390 BOOL ret;
1391 HRGN outline = 0;
1393 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1395 if (dc->GraphicsMode == GM_ADVANCED)
1397 pts[0].x = pts[3].x = left;
1398 pts[0].y = pts[1].y = top;
1399 pts[1].x = pts[2].x = right;
1400 pts[2].y = pts[3].y = bottom;
1401 return dibdrv_Polygon( dev, pts, 4 );
1404 if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE;
1406 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1408 rect.right--;
1409 rect.bottom--;
1410 reset_dash_origin(pdev);
1412 if (dc->ArcDirection == AD_CLOCKWISE)
1414 /* 4 pts going clockwise starting from bottom-right */
1415 pts[0].x = pts[3].x = rect.right;
1416 pts[0].y = pts[1].y = rect.bottom;
1417 pts[1].x = pts[2].x = rect.left;
1418 pts[2].y = pts[3].y = rect.top;
1420 else
1422 /* 4 pts going anti-clockwise starting from top-right */
1423 pts[0].x = pts[3].x = rect.right;
1424 pts[0].y = pts[1].y = rect.top;
1425 pts[1].x = pts[2].x = rect.left;
1426 pts[2].y = pts[3].y = rect.bottom;
1429 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1430 add_pen_lines_bounds( pdev, 4, pts, outline );
1432 if (outline)
1434 if (pdev->brush.style != BS_NULL)
1436 HRGN interior = CreateRectRgnIndirect( &rect );
1438 CombineRgn( interior, interior, outline, RGN_DIFF );
1439 brush_region( pdev, interior );
1440 DeleteObject( interior );
1442 ret = pen_region( pdev, outline );
1443 DeleteObject( outline );
1445 else
1447 rect.left += (pdev->pen_width + 1) / 2;
1448 rect.top += (pdev->pen_width + 1) / 2;
1449 rect.right -= pdev->pen_width / 2;
1450 rect.bottom -= pdev->pen_width / 2;
1451 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1453 return ret;
1456 /***********************************************************************
1457 * dibdrv_RoundRect
1459 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1460 INT ellipse_width, INT ellipse_height )
1462 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1463 DC *dc = get_physdev_dc( dev );
1464 RECT rect;
1465 POINT pt[2], *points;
1466 int i, end, count;
1467 BOOL ret = TRUE;
1468 HRGN outline = 0, interior = 0;
1470 if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE;
1472 pt[0].x = pt[0].y = 0;
1473 pt[1].x = ellipse_width;
1474 pt[1].y = ellipse_height;
1475 lp_to_dp( dc, pt, 2 );
1476 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1477 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1478 if (ellipse_width <= 2|| ellipse_height <= 2)
1479 return dibdrv_Rectangle( dev, left, top, right, bottom );
1481 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1482 if (!points) return FALSE;
1484 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1486 HeapFree( GetProcessHeap(), 0, points );
1487 return FALSE;
1490 if (pdev->brush.style != BS_NULL &&
1491 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1492 ellipse_width, ellipse_height )))
1494 HeapFree( GetProcessHeap(), 0, points );
1495 if (outline) DeleteObject( outline );
1496 return FALSE;
1499 /* if not using a region, paint the interior first so the outline can overlap it */
1500 if (interior && !outline)
1502 ret = brush_region( pdev, interior );
1503 DeleteObject( interior );
1504 interior = 0;
1507 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1509 if (dc->ArcDirection == AD_CLOCKWISE)
1511 for (i = 0; i < count; i++)
1513 points[i].x = rect.right - ellipse_width + points[i].x;
1514 points[i].y = rect.bottom - ellipse_height + points[i].y;
1517 else
1519 for (i = 0; i < count; i++)
1521 points[i].x = rect.right - ellipse_width + points[i].x;
1522 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1526 /* horizontal symmetry */
1528 end = 2 * count - 1;
1529 /* avoid duplicating the midpoint */
1530 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1531 for (i = 0; i < count; i++)
1533 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1534 points[end - i].y = points[i].y;
1536 count = end + 1;
1538 /* vertical symmetry */
1540 end = 2 * count - 1;
1541 /* avoid duplicating the midpoint */
1542 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1543 for (i = 0; i < count; i++)
1545 points[end - i].x = points[i].x;
1546 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1548 count = end + 1;
1550 reset_dash_origin( pdev );
1551 pdev->pen_lines( pdev, count, points, TRUE, outline );
1552 add_pen_lines_bounds( pdev, count, points, outline );
1554 if (interior)
1556 CombineRgn( interior, interior, outline, RGN_DIFF );
1557 ret = brush_region( pdev, interior );
1558 DeleteObject( interior );
1560 if (outline)
1562 if (ret) ret = pen_region( pdev, outline );
1563 DeleteObject( outline );
1565 HeapFree( GetProcessHeap(), 0, points );
1566 return ret;
1569 /***********************************************************************
1570 * dibdrv_Pie
1572 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1573 INT start_x, INT start_y, INT end_x, INT end_y )
1575 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1578 /***********************************************************************
1579 * dibdrv_SetPixel
1581 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1583 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1584 DC *dc = get_physdev_dc( dev );
1585 struct clipped_rects clipped_rects;
1586 RECT rect;
1587 POINT pt;
1588 DWORD pixel;
1590 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1592 pt.x = x;
1593 pt.y = y;
1594 lp_to_dp( dc, &pt, 1 );
1595 rect.left = pt.x;
1596 rect.top = pt.y;
1597 rect.right = rect.left + 1;
1598 rect.bottom = rect.top + 1;
1599 add_clipped_bounds( pdev, &rect, pdev->clip );
1601 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1602 pixel = get_pixel_color( dc, &pdev->dib, color, FALSE );
1603 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1605 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1606 fill_with_pixel( dc, &pdev->dib, pixel, clipped_rects.count, clipped_rects.rects, dc->ROPmode );
1607 free_clipped_rects( &clipped_rects );
1608 return color;
1611 /***********************************************************************
1612 * dibdrv_StrokeAndFillPath
1614 BOOL dibdrv_StrokeAndFillPath( PHYSDEV dev )
1616 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1618 return stroke_and_fill_path( pdev, TRUE, TRUE );
1621 /***********************************************************************
1622 * dibdrv_StrokePath
1624 BOOL dibdrv_StrokePath( PHYSDEV dev )
1626 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1628 return stroke_and_fill_path( pdev, TRUE, FALSE );