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
22 #include "gdi_private.h"
25 #include "wine/unicode.h"
26 #include "wine/debug.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(dib);
45 struct cached_glyph **glyphs;
48 static struct list font_cache = LIST_INIT( font_cache );
50 static CRITICAL_SECTION font_cache_cs;
51 static CRITICAL_SECTION_DEBUG critsect_debug =
54 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
55 0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_cs") }
57 static CRITICAL_SECTION font_cache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
60 static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
62 struct clipped_rects clipped_rects;
65 if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
66 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
67 GetROP2( pdev->dev.hdc ));
68 free_clipped_rects( &clipped_rects );
72 /* paint a region with the brush (note: the region can be modified) */
73 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
75 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
76 return brush_rect( pdev, &pdev->brush, NULL, region );
79 /* paint a region with the pen (note: the region can be modified) */
80 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
82 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
83 return brush_rect( pdev, &pdev->pen_brush, NULL, region );
86 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
94 if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
96 /* shift the rectangle so that the right border is included after mirroring */
97 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
101 LPtoDP( hdc, (POINT *)&rect, 2 );
102 if (rect.left > rect.right)
105 rect.left = rect.right;
108 if (rect.top > rect.bottom)
111 rect.top = rect.bottom;
117 static BOOL get_pen_device_rect( dibdrv_physdev *dev, RECT *rect, int left, int top, int right, int bottom )
119 *rect = get_device_rect( dev->dev.hdc, 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;
132 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
134 const WINEREGION *region;
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)
148 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
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, ®ion->extents );
160 release_wine_region( rgn );
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 );
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);
194 /* based on an algorithm by Alois Zingl */
196 while (pt.x >= width / 2)
214 static int find_intersection( const POINT *points, int x, int y, int count )
220 if (x >= 0) /* first quadrant */
222 for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
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;
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( PHYSDEV dev, 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 (GetArcDirection( dev->hdc ) != AD_CLOCKWISE)
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;
261 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
263 for (i = start_pos; i < end_pos; i++, pos++)
265 switch ((i / count) % 4)
268 points[pos].x = rect->left + width/2 + points[i % count].x;
269 points[pos].y = rect->top + height/2 + points[i % count].y;
272 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
273 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
276 points[pos].x = rect->left + width/2 - points[i % count].x;
277 points[pos].y = rect->top + height/2 - points[i % count].y;
280 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
281 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
288 for (i = start_pos; i < end_pos; i++, pos++)
290 switch ((i / count) % 4)
293 points[pos].x = rect->left + width/2 + points[i % count].x;
294 points[pos].y = rect->top + height/2 - points[i % count].y;
297 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
298 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
301 points[pos].x = rect->left + width/2 - points[i % count].x;
302 points[pos].y = rect->top + height/2 + points[i % count].y;
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;
312 memmove( points, points + count, (pos - count) * sizeof(POINT) );
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 );
322 POINT pt[2], *points;
323 int width, height, count;
325 HRGN outline = 0, interior = 0;
327 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
329 width = rect.right - rect.left;
330 height = rect.bottom - rect.top;
336 LPtoDP( dev->hdc, pt, 2 );
337 /* make them relative to the ellipse center */
338 pt[0].x -= rect.left + width / 2;
339 pt[0].y -= rect.top + height / 2;
340 pt[1].x -= rect.left + width / 2;
341 pt[1].y -= rect.top + height / 2;
343 points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) );
344 if (!points) return FALSE;
346 if (extra_lines == -1)
348 GetCurrentPositionEx( dev->hdc, points );
349 LPtoDP( dev->hdc, points, 1 );
350 count = 1 + get_arc_points( dev, &rect, pt[0], pt[1], points + 1 );
352 else count = get_arc_points( dev, &rect, pt[0], pt[1], points );
354 if (extra_lines == 2)
356 points[count].x = rect.left + width / 2;
357 points[count].y = rect.top + height / 2;
362 HeapFree( GetProcessHeap(), 0, points );
366 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
368 HeapFree( GetProcessHeap(), 0, points );
372 if (pdev->brush.style != BS_NULL && extra_lines > 0 &&
373 !(interior = CreatePolygonRgn( points, count, WINDING )))
375 HeapFree( GetProcessHeap(), 0, points );
376 if (outline) DeleteObject( outline );
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 DeleteObject( interior );
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 );
394 CombineRgn( interior, interior, outline, RGN_DIFF );
395 ret = brush_region( pdev, interior );
396 DeleteObject( interior );
400 if (ret) ret = pen_region( pdev, outline );
401 DeleteObject( outline );
403 HeapFree( GetProcessHeap(), 0, points );
407 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
408 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
409 static const BYTE ramp[17] =
412 0x8c, 0x9a, 0xa7, 0xb2,
413 0xbd, 0xc7, 0xd0, 0xd9,
414 0xe1, 0xe9, 0xf0, 0xf8,
418 /* For a give text-color component and a glyph level, calculate the
419 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
420 components respectively.
422 The minimum is a linear interpolation between 0 and the value in
425 The maximum is a linear interpolation between the value from the
426 ramp table read in reverse and 0xff.
428 To find the resulting pixel intensity, we note that if the text and
429 bkgnd intensities are the same then the result must be that
430 intensity. Otherwise we linearly interpolate between either the
431 min or the max value and this intermediate value depending on which
432 side of the inequality we lie.
435 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
437 *min_comp = (ramp[aa] * text_comp) / 0xff;
438 *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
441 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
445 for (i = 0; i < 17; i++)
447 get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
448 get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
449 get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
453 static DWORD font_cache_hash( struct cached_font *font )
455 DWORD hash = 0, *ptr, two_chars;
459 hash ^= font->aa_flags;
460 for(i = 0, ptr = (DWORD*)&font->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
462 for(i = 0, ptr = (DWORD*)&font->lf; i < 7; i++, ptr++)
464 for(i = 0, ptr = (DWORD*)font->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
466 pwc = (WCHAR *)&two_chars;
468 *pwc = toupperW(*pwc);
470 *pwc = toupperW(*pwc);
477 static int font_cache_cmp( const struct cached_font *p1, const struct cached_font *p2 )
479 int ret = p1->hash - p2->hash;
480 if (!ret) ret = p1->aa_flags - p2->aa_flags;
481 if (!ret) ret = memcmp( &p1->xform, &p2->xform, sizeof(p1->xform) );
482 if (!ret) ret = memcmp( &p1->lf, &p2->lf, FIELD_OFFSET( LOGFONTW, lfFaceName ));
483 if (!ret) ret = strcmpiW( p1->lf.lfFaceName, p2->lf.lfFaceName );
487 static struct cached_font *add_cached_font( HDC hdc, HFONT hfont, UINT aa_flags )
489 struct cached_font font, *ptr, *last_unused = NULL;
492 GetObjectW( hfont, sizeof(font.lf), &font.lf );
493 GetTransform( hdc, 0x204, &font.xform );
494 font.xform.eDx = font.xform.eDy = 0; /* unused, would break hashing */
495 if (GetGraphicsMode( hdc ) == GM_COMPATIBLE && font.xform.eM11 * font.xform.eM22 < 0)
496 font.lf.lfOrientation = -font.lf.lfOrientation;
497 font.lf.lfWidth = abs( font.lf.lfWidth );
498 font.aa_flags = aa_flags;
499 font.hash = font_cache_hash( &font );
501 EnterCriticalSection( &font_cache_cs );
502 LIST_FOR_EACH_ENTRY( ptr, &font_cache, struct cached_font, entry )
504 if (!font_cache_cmp( &font, ptr ))
506 InterlockedIncrement( &ptr->ref );
507 list_remove( &ptr->entry );
517 if (i > 5) /* keep at least 5 of the most-recently used fonts around */
520 for (i = 0; i < ptr->nb_glyphs; i++) HeapFree( GetProcessHeap(), 0, ptr->glyphs[i] );
521 HeapFree( GetProcessHeap(), 0, ptr->glyphs );
522 list_remove( &ptr->entry );
524 else if (!(ptr = HeapAlloc( GetProcessHeap(), 0, sizeof(*ptr) )))
526 LeaveCriticalSection( &font_cache_cs );
536 list_add_head( &font_cache, &ptr->entry );
537 LeaveCriticalSection( &font_cache_cs );
538 TRACE( "%d %s -> %p\n", ptr->lf.lfHeight, debugstr_w(ptr->lf.lfFaceName), ptr );
542 void release_cached_font( struct cached_font *font )
544 if (font) InterlockedDecrement( &font->ref );
547 static void add_cached_glyph( struct cached_font *font, UINT index, struct cached_glyph *glyph )
549 if (index >= font->nb_glyphs)
551 UINT new_count = (index + 128) & ~127;
552 struct cached_glyph **new;
555 new = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
556 font->glyphs, new_count * sizeof(*new) );
558 new = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*new) );
561 font->nb_glyphs = new_count;
563 font->glyphs[index] = glyph;
566 static struct cached_glyph *get_cached_glyph( struct cached_font *font, UINT index )
568 if (index < font->nb_glyphs) return font->glyphs[index];
572 /**********************************************************************
573 * get_text_bkgnd_masks
575 * See the comment above get_pen_bkgnd_masks
577 static inline void get_text_bkgnd_masks( dibdrv_physdev *pdev, rop_mask *mask )
579 COLORREF bg = GetBkColor( pdev->dev.hdc );
583 if (pdev->dib.bit_count != 1)
584 mask->xor = get_pixel_color( pdev, bg, FALSE );
587 COLORREF fg = GetTextColor( pdev->dev.hdc );
588 mask->xor = get_pixel_color( pdev, fg, TRUE );
589 if (fg != bg) mask->xor = ~mask->xor;
593 static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics,
594 const dib_info *glyph_dib, DWORD text_color,
595 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
599 RECT rect, clipped_rect;
602 rect.left = x + metrics->gmptGlyphOrigin.x;
603 rect.top = y - metrics->gmptGlyphOrigin.y;
604 rect.right = rect.left + metrics->gmBlackBoxX;
605 rect.bottom = rect.top + metrics->gmBlackBoxY;
606 if (bounds) add_bounds_rect( bounds, &rect );
608 for (i = 0; i < clipped_rects->count; i++)
610 if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
612 src_origin.x = clipped_rect.left - rect.left;
613 src_origin.y = clipped_rect.top - rect.top;
615 if (glyph_dib->bit_count == 32)
616 dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
619 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
620 text_color, ranges );
625 static int get_glyph_depth( UINT aa_flags )
629 case GGO_BITMAP: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */
630 case GGO_GRAY2_BITMAP:
631 case GGO_GRAY4_BITMAP:
632 case GGO_GRAY8_BITMAP:
633 case WINE_GGO_GRAY16_BITMAP: return 8;
635 case WINE_GGO_HRGB_BITMAP:
636 case WINE_GGO_HBGR_BITMAP:
637 case WINE_GGO_VRGB_BITMAP:
638 case WINE_GGO_VBGR_BITMAP: return 32;
641 ERR("Unexpected flags %08x\n", aa_flags);
646 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
647 static const int padding[4] = {0, 3, 2, 1};
649 /***********************************************************************
652 * Retrieve a 17-level bitmap for the appropriate glyph.
654 * For non-antialiased bitmaps convert them to the 17-level format
655 * using only values 0 or 16.
657 static struct cached_glyph *cache_glyph_bitmap( HDC hdc, struct cached_font *font, UINT index )
659 UINT ggo_flags = font->aa_flags | GGO_GLYPH_INDEX;
660 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
661 UINT indices[3] = {0, 0, 0x20};
665 int pad = 0, stride, bit_count;
666 GLYPHMETRICS metrics;
667 struct cached_glyph *glyph;
670 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
673 ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, 0, NULL, &identity );
674 if (ret != GDI_ERROR) break;
676 if (ret == GDI_ERROR) return NULL;
678 bit_count = get_glyph_depth( font->aa_flags );
679 stride = get_dib_stride( metrics.gmBlackBoxX, bit_count );
680 size = metrics.gmBlackBoxY * stride;
681 glyph = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( struct cached_glyph, bits[size] ));
682 if (!glyph) return NULL;
683 if (!ret) goto done; /* zero-size glyph */
685 if (bit_count == 8) pad = padding[ metrics.gmBlackBoxX % 4 ];
687 ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, size, glyph->bits, &identity );
688 if (ret == GDI_ERROR)
690 HeapFree( GetProcessHeap(), 0, glyph );
693 assert( ret <= size );
694 if (font->aa_flags == GGO_BITMAP)
696 for (y = metrics.gmBlackBoxY - 1; y >= 0; y--)
698 src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 );
699 dst = glyph->bits + y * stride;
701 if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad );
703 for (x = metrics.gmBlackBoxX - 1; x >= 0; x--)
704 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
709 for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride)
710 memset( dst + metrics.gmBlackBoxX, 0, pad );
714 glyph->metrics = metrics;
715 add_cached_glyph( font, index, glyph );
719 static void render_string( HDC hdc, dib_info *dib, struct cached_font *font, INT x, INT y,
720 UINT flags, const WCHAR *str, UINT count, const INT *dx, DWORD text_color,
721 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
725 struct cached_glyph *glyph;
728 glyph_dib.bit_count = get_glyph_depth( font->aa_flags );
729 glyph_dib.rect.left = 0;
730 glyph_dib.rect.top = 0;
731 glyph_dib.bits.is_copy = FALSE;
732 glyph_dib.bits.free = NULL;
734 EnterCriticalSection( &font_cache_cs );
735 for (i = 0; i < count; i++)
737 if (!(glyph = get_cached_glyph( font, str[i] )) &&
738 !(glyph = cache_glyph_bitmap( hdc, font, str[i] ))) continue;
740 glyph_dib.width = glyph->metrics.gmBlackBoxX;
741 glyph_dib.height = glyph->metrics.gmBlackBoxY;
742 glyph_dib.rect.right = glyph->metrics.gmBlackBoxX;
743 glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY;
744 glyph_dib.stride = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count );
745 glyph_dib.bits.ptr = glyph->bits;
747 draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
761 x += glyph->metrics.gmCellIncX;
762 y += glyph->metrics.gmCellIncY;
765 LeaveCriticalSection( &font_cache_cs );
768 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
769 struct bitblt_coords *src, INT x, INT y, UINT flags,
770 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
775 DWORD fg_pixel, bg_pixel;
776 struct intensity_range glyph_intensities[17];
777 struct clipped_rects visrect;
778 struct cached_font *font;
780 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
782 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
785 visrect.rects = &src->visrect;
787 fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
788 if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
790 get_aa_ranges( fg, glyph_intensities );
792 if (flags & ETO_OPAQUE)
794 rop_mask bkgnd_color;
796 bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
797 if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
800 bkgnd_color.xor = bg_pixel;
801 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
804 if (!(font = add_cached_font( hdc, GetCurrentObject( hdc, OBJ_FONT ), aa_flags ))) return FALSE;
806 render_string( hdc, &dib, font, x, y, flags, str, count, dx,
807 fg_pixel, glyph_intensities, &visrect, NULL );
808 release_cached_font( font );
812 /***********************************************************************
815 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
816 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
818 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
819 struct clipped_rects clipped_rects;
823 struct intensity_range ranges[17];
825 if (!pdev->font) return FALSE;
827 init_clipped_rects( &clipped_rects );
828 reset_bounds( &bounds );
830 if (flags & ETO_OPAQUE)
832 rop_mask bkgnd_color;
833 get_text_bkgnd_masks( pdev, &bkgnd_color );
834 add_bounds_rect( &bounds, rect );
835 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
836 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
837 bkgnd_color.and, bkgnd_color.xor );
840 if (count == 0) goto done;
842 if (flags & ETO_CLIPPED)
844 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
845 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
849 free_clipped_rects( &clipped_rects );
850 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
852 if (!clipped_rects.count) goto done;
854 text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
855 get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
857 dc = get_dc_ptr( dev->hdc );
858 render_string( dev->hdc, &pdev->dib, pdev->font, x, y, flags, str, count, dx,
859 text_color, ranges, &clipped_rects, &bounds );
860 release_dc_ptr( dc );
863 add_clipped_bounds( pdev, &bounds, pdev->clip );
864 free_clipped_rects( &clipped_rects );
868 /***********************************************************************
871 HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
873 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
876 if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */
878 dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
879 ret = dev->funcs->pSelectFont( dev, font, aa_flags );
882 struct cached_font *prev = pdev->font;
883 pdev->font = add_cached_font( dev->hdc, font, *aa_flags ? *aa_flags : GGO_BITMAP );
884 release_cached_font( prev );
889 /***********************************************************************
892 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
893 INT start_x, INT start_y, INT end_x, INT end_y )
895 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
898 /***********************************************************************
901 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
902 INT start_x, INT start_y, INT end_x, INT end_y )
904 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
907 /***********************************************************************
910 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
911 INT start_x, INT start_y, INT end_x, INT end_y )
913 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
916 /***********************************************************************
919 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
921 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
924 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
926 /* the clip rgn stops the flooding */
927 if (clip && !PtInRegion( clip, x, y )) return FALSE;
929 if (type == FLOODFILLBORDER)
930 return dib->funcs->get_pixel( dib, x, y ) != pixel;
932 return dib->funcs->get_pixel( dib, x, y ) == pixel;
935 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
937 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
941 next.top = row->top + offset;
942 next.bottom = next.top + 1;
943 next.left = next.right = row->left;
944 while (next.right < row->right)
946 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
949 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
950 fill_row( dib, clip, &next, pixel, type, rgn );
951 next.left = ++next.right;
954 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
955 fill_row( dib, clip, &next, pixel, type, rgn );
958 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
960 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
961 while (row->right < dib->rect.right - dib->rect.left &&
962 is_interior( dib, clip, row->right, row->top, pixel, type))
965 add_rect_to_region( rgn, row );
967 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
968 if (row->top < dib->rect.bottom - dib->rect.top - 1)
969 do_next_row( dib, clip, row, 1, pixel, type, rgn );
972 /***********************************************************************
973 * dibdrv_ExtFloodFill
975 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
977 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
978 DWORD pixel = get_pixel_color( pdev, color, FALSE );
982 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
984 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
986 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
992 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
994 add_clipped_bounds( pdev, NULL, rgn );
995 brush_region( pdev, rgn );
1001 /***********************************************************************
1002 * dibdrv_GetNearestColor
1004 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
1006 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1009 TRACE( "(%p, %08x)\n", dev, color );
1011 pixel = get_pixel_color( pdev, color, FALSE );
1012 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1015 /***********************************************************************
1018 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
1020 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1024 TRACE( "(%p, %d, %d)\n", dev, x, y );
1028 LPtoDP( dev->hdc, &pt, 1 );
1030 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
1031 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
1034 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
1035 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1038 /***********************************************************************
1041 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
1043 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1048 GetCurrentPositionEx(dev->hdc, pts);
1052 LPtoDP(dev->hdc, pts, 2);
1054 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1056 reset_dash_origin(pdev);
1058 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
1059 add_pen_lines_bounds( pdev, 2, pts, region );
1063 ret = pen_region( pdev, region );
1064 DeleteObject( region );
1069 /***********************************************************************
1072 * Returns the binary rop that is equivalent to the provided ternary rop
1073 * if the src bits are ignored.
1075 static inline INT get_rop2_from_rop(INT rop)
1077 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
1080 /***********************************************************************
1083 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
1085 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1086 dib_brush *brush = &pdev->brush;
1087 int rop2 = get_rop2_from_rop( rop );
1088 struct clipped_rects clipped_rects;
1089 DWORD and = 0, xor = 0;
1092 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
1094 add_clipped_bounds( pdev, &dst->visrect, 0 );
1095 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
1097 switch (rop2) /* shortcuts for rops that don't involve the brush */
1099 case R2_NOT: and = ~0u;
1101 case R2_WHITE: xor = ~0u;
1104 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
1109 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
1112 free_clipped_rects( &clipped_rects );
1116 /***********************************************************************
1119 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
1121 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1122 const WINEREGION *region;
1126 TRACE("%p, %p\n", dev, rgn);
1128 reset_bounds( &bounds );
1130 region = get_wine_region( rgn );
1131 if(!region) return FALSE;
1133 for(i = 0; i < region->numRects; i++)
1135 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
1136 region->rects[i].right, region->rects[i].bottom, FALSE );
1137 add_bounds_rect( &bounds, &rect );
1138 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1141 release_wine_region( rgn );
1142 add_clipped_bounds( pdev, &bounds, pdev->clip );
1146 /***********************************************************************
1147 * dibdrv_PolyPolygon
1149 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
1151 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1152 DWORD total, i, pos;
1155 HRGN outline = 0, interior = 0;
1157 for (i = total = 0; i < polygons; i++)
1159 if (counts[i] < 2) return FALSE;
1163 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1164 if (!points) return FALSE;
1165 memcpy( points, pt, total * sizeof(*pt) );
1166 LPtoDP( dev->hdc, points, total );
1168 if (pdev->brush.style != BS_NULL &&
1169 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
1171 HeapFree( GetProcessHeap(), 0, points );
1175 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1177 /* if not using a region, paint the interior first so the outline can overlap it */
1178 if (interior && !outline)
1180 ret = brush_region( pdev, interior );
1181 DeleteObject( interior );
1185 for (i = pos = 0; i < polygons; i++)
1187 reset_dash_origin( pdev );
1188 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1191 add_pen_lines_bounds( pdev, total, points, outline );
1195 CombineRgn( interior, interior, outline, RGN_DIFF );
1196 ret = brush_region( pdev, interior );
1197 DeleteObject( interior );
1201 if (ret) ret = pen_region( pdev, outline );
1202 DeleteObject( outline );
1204 HeapFree( GetProcessHeap(), 0, points );
1208 /***********************************************************************
1209 * dibdrv_PolyPolyline
1211 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1213 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1214 DWORD total, pos, i;
1219 for (i = total = 0; i < polylines; i++)
1221 if (counts[i] < 2) return FALSE;
1225 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1226 if (!points) return FALSE;
1227 memcpy( points, pt, total * sizeof(*pt) );
1228 LPtoDP( dev->hdc, points, total );
1230 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1232 HeapFree( GetProcessHeap(), 0, points );
1236 for (i = pos = 0; i < polylines; i++)
1238 reset_dash_origin( pdev );
1239 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1242 add_pen_lines_bounds( pdev, total, points, outline );
1246 ret = pen_region( pdev, outline );
1247 DeleteObject( outline );
1250 HeapFree( GetProcessHeap(), 0, points );
1254 /***********************************************************************
1257 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1259 INT counts[1] = { count };
1261 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1264 /***********************************************************************
1267 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1269 DWORD counts[1] = { count };
1271 if (count < 0) return FALSE;
1272 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1275 /***********************************************************************
1278 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1280 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1286 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1288 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1290 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1294 reset_dash_origin(pdev);
1296 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1298 /* 4 pts going clockwise starting from bottom-right */
1299 pts[0].x = pts[3].x = rect.right;
1300 pts[0].y = pts[1].y = rect.bottom;
1301 pts[1].x = pts[2].x = rect.left;
1302 pts[2].y = pts[3].y = rect.top;
1306 /* 4 pts going anti-clockwise starting from top-right */
1307 pts[0].x = pts[3].x = rect.right;
1308 pts[0].y = pts[1].y = rect.top;
1309 pts[1].x = pts[2].x = rect.left;
1310 pts[2].y = pts[3].y = rect.bottom;
1313 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1314 add_pen_lines_bounds( pdev, 4, pts, outline );
1318 if (pdev->brush.style != BS_NULL)
1320 HRGN interior = CreateRectRgnIndirect( &rect );
1322 CombineRgn( interior, interior, outline, RGN_DIFF );
1323 brush_region( pdev, interior );
1324 DeleteObject( interior );
1326 ret = pen_region( pdev, outline );
1327 DeleteObject( outline );
1331 rect.left += (pdev->pen_width + 1) / 2;
1332 rect.top += (pdev->pen_width + 1) / 2;
1333 rect.right -= pdev->pen_width / 2;
1334 rect.bottom -= pdev->pen_width / 2;
1335 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1340 /***********************************************************************
1343 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1344 INT ellipse_width, INT ellipse_height )
1346 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1348 POINT pt[2], *points;
1351 HRGN outline = 0, interior = 0;
1353 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1355 pt[0].x = pt[0].y = 0;
1356 pt[1].x = ellipse_width;
1357 pt[1].y = ellipse_height;
1358 LPtoDP( dev->hdc, pt, 2 );
1359 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1360 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1361 if (ellipse_width <= 2|| ellipse_height <= 2)
1362 return dibdrv_Rectangle( dev, left, top, right, bottom );
1364 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1365 if (!points) return FALSE;
1367 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1369 HeapFree( GetProcessHeap(), 0, points );
1373 if (pdev->brush.style != BS_NULL &&
1374 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1375 ellipse_width, ellipse_height )))
1377 HeapFree( GetProcessHeap(), 0, points );
1378 if (outline) DeleteObject( outline );
1382 /* if not using a region, paint the interior first so the outline can overlap it */
1383 if (interior && !outline)
1385 ret = brush_region( pdev, interior );
1386 DeleteObject( interior );
1390 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1392 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1394 for (i = 0; i < count; i++)
1396 points[i].x = rect.right - ellipse_width + points[i].x;
1397 points[i].y = rect.bottom - ellipse_height + points[i].y;
1402 for (i = 0; i < count; i++)
1404 points[i].x = rect.right - ellipse_width + points[i].x;
1405 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1409 /* horizontal symmetry */
1411 end = 2 * count - 1;
1412 /* avoid duplicating the midpoint */
1413 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1414 for (i = 0; i < count; i++)
1416 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1417 points[end - i].y = points[i].y;
1421 /* vertical symmetry */
1423 end = 2 * count - 1;
1424 /* avoid duplicating the midpoint */
1425 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1426 for (i = 0; i < count; i++)
1428 points[end - i].x = points[i].x;
1429 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1433 reset_dash_origin( pdev );
1434 pdev->pen_lines( pdev, count, points, TRUE, outline );
1435 add_pen_lines_bounds( pdev, count, points, outline );
1439 CombineRgn( interior, interior, outline, RGN_DIFF );
1440 ret = brush_region( pdev, interior );
1441 DeleteObject( interior );
1445 if (ret) ret = pen_region( pdev, outline );
1446 DeleteObject( outline );
1448 HeapFree( GetProcessHeap(), 0, points );
1452 /***********************************************************************
1455 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1456 INT start_x, INT start_y, INT end_x, INT end_y )
1458 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1461 /***********************************************************************
1464 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1466 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1467 struct clipped_rects clipped_rects;
1472 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1476 LPtoDP( dev->hdc, &pt, 1 );
1479 rect.right = rect.left + 1;
1480 rect.bottom = rect.top + 1;
1481 add_clipped_bounds( pdev, &rect, pdev->clip );
1483 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1484 pixel = get_pixel_color( pdev, color, FALSE );
1485 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1487 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1488 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1489 free_clipped_rects( &clipped_rects );