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/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dib);
29 static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
31 struct clipped_rects clipped_rects;
34 if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
35 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
36 GetROP2( pdev->dev.hdc ));
37 free_clipped_rects( &clipped_rects );
41 /* paint a region with the brush (note: the region can be modified) */
42 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
44 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
45 return brush_rect( pdev, &pdev->brush, NULL, region );
48 /* paint a region with the pen (note: the region can be modified) */
49 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
51 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
52 return brush_rect( pdev, &pdev->pen_brush, NULL, region );
55 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
63 if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
65 /* shift the rectangle so that the right border is included after mirroring */
66 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
70 LPtoDP( hdc, (POINT *)&rect, 2 );
71 if (rect.left > rect.right)
74 rect.left = rect.right;
77 if (rect.top > rect.bottom)
80 rect.top = rect.bottom;
86 static BOOL get_pen_device_rect( dibdrv_physdev *dev, RECT *rect, int left, int top, int right, int bottom )
88 *rect = get_device_rect( dev->dev.hdc, left, top, right, bottom, TRUE );
89 if (rect->left == rect->right || rect->top == rect->bottom) return FALSE;
91 if (dev->pen_style == PS_INSIDEFRAME)
93 rect->left += dev->pen_width / 2;
94 rect->top += dev->pen_width / 2;
95 rect->right -= (dev->pen_width - 1) / 2;
96 rect->bottom -= (dev->pen_width - 1) / 2;
101 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
103 const WINEREGION *region;
107 if (!dev->bounds) return;
108 reset_bounds( &bounds );
110 if (dev->pen_uses_region)
112 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
113 width = dev->pen_width + 2;
114 if (dev->pen_join == PS_JOIN_MITER)
117 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
121 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4;
122 else width = (width + 1) / 2;
125 /* in case the heuristics are wrong, add the actual region too */
126 if ((region = get_wine_region( rgn )))
128 add_bounds_rect( &bounds, ®ion->extents );
129 release_wine_region( rgn );
135 rect.left = points->x - width;
136 rect.top = points->y - width;
137 rect.right = points->x + width + 1;
138 rect.bottom = points->y + width + 1;
139 add_bounds_rect( &bounds, &rect );
143 add_clipped_bounds( dev, &bounds, dev->clip );
146 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
147 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
148 static int ellipse_first_quadrant( int width, int height, POINT *data )
150 const int a = width - 1;
151 const int b = height - 1;
152 const INT64 asq = (INT64)8 * a * a;
153 const INT64 bsq = (INT64)8 * b * b;
154 INT64 dx = (INT64)4 * b * b * (1 - a);
155 INT64 dy = (INT64)4 * a * a * (1 + (b % 2));
156 INT64 err = dx + dy + a * a * (b % 2);
163 /* based on an algorithm by Alois Zingl */
165 while (pt.x >= width / 2)
183 static int find_intersection( const POINT *points, int x, int y, int count )
189 if (x >= 0) /* first quadrant */
191 for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
194 /* second quadrant */
195 for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break;
196 return 2 * count - i;
198 if (x >= 0) /* fourth quadrant */
200 for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break;
201 return 4 * count - i;
204 for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break;
205 return 2 * count + i;
208 static int get_arc_points( PHYSDEV dev, const RECT *rect, POINT start, POINT end, POINT *points )
210 int i, pos, count, start_pos, end_pos;
211 int width = rect->right - rect->left;
212 int height = rect->bottom - rect->top;
214 count = ellipse_first_quadrant( width, height, points );
215 for (i = 0; i < count; i++)
217 points[i].x -= width / 2;
218 points[i].y -= height / 2;
220 if (GetArcDirection( dev->hdc ) != AD_CLOCKWISE)
225 start_pos = find_intersection( points, start.x, start.y, count );
226 end_pos = find_intersection( points, end.x, end.y, count );
227 if (end_pos <= start_pos) end_pos += 4 * count;
230 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
232 for (i = start_pos; i < end_pos; i++, pos++)
234 switch ((i / count) % 4)
237 points[pos].x = rect->left + width/2 + points[i % count].x;
238 points[pos].y = rect->top + height/2 + points[i % count].y;
241 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
242 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
245 points[pos].x = rect->left + width/2 - points[i % count].x;
246 points[pos].y = rect->top + height/2 - points[i % count].y;
249 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
250 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
257 for (i = start_pos; i < end_pos; i++, pos++)
259 switch ((i / count) % 4)
262 points[pos].x = rect->left + width/2 + points[i % count].x;
263 points[pos].y = rect->top + height/2 - points[i % count].y;
266 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
267 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
270 points[pos].x = rect->left + width/2 - points[i % count].x;
271 points[pos].y = rect->top + height/2 + points[i % count].y;
274 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
275 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
281 memmove( points, points + count, (pos - count) * sizeof(POINT) );
285 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
286 static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
287 INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines )
289 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
291 POINT pt[2], *points;
292 int width, height, count;
294 HRGN outline = 0, interior = 0;
296 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
298 width = rect.right - rect.left;
299 height = rect.bottom - rect.top;
305 LPtoDP( dev->hdc, pt, 2 );
306 /* make them relative to the ellipse center */
307 pt[0].x -= left + width / 2;
308 pt[0].y -= top + height / 2;
309 pt[1].x -= left + width / 2;
310 pt[1].y -= top + height / 2;
312 points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) );
313 if (!points) return FALSE;
315 if (extra_lines == -1)
317 GetCurrentPositionEx( dev->hdc, points );
318 LPtoDP( dev->hdc, points, 1 );
319 count = 1 + get_arc_points( dev, &rect, pt[0], pt[1], points + 1 );
321 else count = get_arc_points( dev, &rect, pt[0], pt[1], points );
323 if (extra_lines == 2)
325 points[count].x = rect.left + width / 2;
326 points[count].y = rect.top + height / 2;
331 HeapFree( GetProcessHeap(), 0, points );
335 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
337 HeapFree( GetProcessHeap(), 0, points );
341 if (pdev->brush.style != BS_NULL && extra_lines > 0 &&
342 !(interior = CreatePolygonRgn( points, count, WINDING )))
344 HeapFree( GetProcessHeap(), 0, points );
345 if (outline) DeleteObject( outline );
349 /* if not using a region, paint the interior first so the outline can overlap it */
350 if (interior && !outline)
352 ret = brush_region( pdev, interior );
353 DeleteObject( interior );
357 reset_dash_origin( pdev );
358 pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
359 add_pen_lines_bounds( pdev, count, points, outline );
363 CombineRgn( interior, interior, outline, RGN_DIFF );
364 ret = brush_region( pdev, interior );
365 DeleteObject( interior );
369 if (ret) ret = pen_region( pdev, outline );
370 DeleteObject( outline );
372 HeapFree( GetProcessHeap(), 0, points );
376 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
377 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
378 static const BYTE ramp[17] =
381 0x8c, 0x9a, 0xa7, 0xb2,
382 0xbd, 0xc7, 0xd0, 0xd9,
383 0xe1, 0xe9, 0xf0, 0xf8,
387 /* For a give text-color component and a glyph level, calculate the
388 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
389 components respectively.
391 The minimum is a linear interpolation between 0 and the value in
394 The maximum is a linear interpolation between the value from the
395 ramp table read in reverse and 0xff.
397 To find the resulting pixel intensity, we note that if the text and
398 bkgnd intensities are the same then the result must be that
399 intensity. Otherwise we linearly interpolate between either the
400 min or the max value and this intermediate value depending on which
401 side of the inequality we lie.
404 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
406 *min_comp = (ramp[aa] * text_comp) / 0xff;
407 *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
410 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
414 for (i = 0; i < 17; i++)
416 get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
417 get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
418 get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
422 /**********************************************************************
423 * get_text_bkgnd_masks
425 * See the comment above get_pen_bkgnd_masks
427 static inline void get_text_bkgnd_masks( dibdrv_physdev *pdev, rop_mask *mask )
429 COLORREF bg = GetBkColor( pdev->dev.hdc );
433 if (pdev->dib.bit_count != 1)
434 mask->xor = get_pixel_color( pdev, bg, FALSE );
437 COLORREF fg = GetTextColor( pdev->dev.hdc );
438 mask->xor = get_pixel_color( pdev, fg, TRUE );
439 if (fg != bg) mask->xor = ~mask->xor;
443 static void draw_glyph( dibdrv_physdev *pdev, const POINT *origin, const GLYPHMETRICS *metrics,
444 const struct gdi_image_bits *image, DWORD text_color,
445 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
449 RECT rect, clipped_rect;
453 glyph_dib.bit_count = 8;
454 glyph_dib.width = metrics->gmBlackBoxX;
455 glyph_dib.height = metrics->gmBlackBoxY;
456 glyph_dib.rect.left = 0;
457 glyph_dib.rect.top = 0;
458 glyph_dib.rect.right = metrics->gmBlackBoxX;
459 glyph_dib.rect.bottom = metrics->gmBlackBoxY;
460 glyph_dib.stride = get_dib_stride( metrics->gmBlackBoxX, 8 );
461 glyph_dib.bits = *image;
463 rect.left = origin->x + metrics->gmptGlyphOrigin.x;
464 rect.top = origin->y - metrics->gmptGlyphOrigin.y;
465 rect.right = rect.left + metrics->gmBlackBoxX;
466 rect.bottom = rect.top + metrics->gmBlackBoxY;
467 add_bounds_rect( bounds, &rect );
469 for (i = 0; i < clipped_rects->count; i++)
471 if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
473 src_origin.x = clipped_rect.left - rect.left;
474 src_origin.y = clipped_rect.top - rect.top;
476 pdev->dib.funcs->draw_glyph( &pdev->dib, &clipped_rect, &glyph_dib, &src_origin,
477 text_color, ranges );
482 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
483 static const int padding[4] = {0, 3, 2, 1};
485 /***********************************************************************
488 * Retrieve a 17-level bitmap for the appropriate glyph.
490 * For non-antialiased bitmaps convert them to the 17-level format
491 * using only values 0 or 16.
493 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
494 struct gdi_image_bits *image )
496 UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
497 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
498 UINT indices[3] = {0, 0, 0x20};
501 BYTE *buf, *dst, *src;
505 image->is_copy = FALSE;
506 image->free = free_heap_bits;
511 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
514 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
515 if (ret != GDI_ERROR) break;
518 if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
519 if (!ret) return ERROR_SUCCESS; /* empty glyph */
521 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
522 pad = padding[ metrics->gmBlackBoxX % 4 ];
523 stride = get_dib_stride( metrics->gmBlackBoxX, 8 );
524 size = metrics->gmBlackBoxY * stride;
526 buf = HeapAlloc( GetProcessHeap(), 0, size );
527 if (!buf) return ERROR_OUTOFMEMORY;
529 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
530 if (ret == GDI_ERROR)
532 HeapFree( GetProcessHeap(), 0, buf );
533 return ERROR_NOT_FOUND;
536 if (aa_flags == GGO_BITMAP)
538 for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
540 src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
541 dst = buf + y * stride;
543 if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
545 for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
546 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
551 for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
552 memset( dst + metrics->gmBlackBoxX, 0, pad );
556 return ERROR_SUCCESS;
559 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
560 struct bitblt_coords *src, INT x, INT y, UINT flags,
561 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
568 DWORD fg_pixel, bg_pixel;
569 struct intensity_range glyph_intensities[17];
571 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
573 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
575 fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
576 if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
578 get_aa_ranges( fg, glyph_intensities );
580 if (flags & ETO_OPAQUE)
582 rop_mask bkgnd_color;
584 bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
585 if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
588 bkgnd_color.xor = bg_pixel;
589 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
592 for (i = 0; i < count; i++)
594 GLYPHMETRICS metrics;
595 struct gdi_image_bits image;
597 err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &image );
602 RECT rect, clipped_rect;
606 glyph_dib.bit_count = 8;
607 glyph_dib.width = metrics.gmBlackBoxX;
608 glyph_dib.height = metrics.gmBlackBoxY;
609 glyph_dib.rect.left = 0;
610 glyph_dib.rect.top = 0;
611 glyph_dib.rect.right = metrics.gmBlackBoxX;
612 glyph_dib.rect.bottom = metrics.gmBlackBoxY;
613 glyph_dib.stride = get_dib_stride( metrics.gmBlackBoxX, 8 );
614 glyph_dib.bits = image;
616 rect.left = x + metrics.gmptGlyphOrigin.x;
617 rect.top = y - metrics.gmptGlyphOrigin.y;
618 rect.right = rect.left + metrics.gmBlackBoxX;
619 rect.bottom = rect.top + metrics.gmBlackBoxY;
621 if (intersect_rect( &clipped_rect, &rect, &src->visrect ))
623 src_origin.x = clipped_rect.left - rect.left;
624 src_origin.y = clipped_rect.top - rect.top;
626 dib.funcs->draw_glyph( &dib, &clipped_rect, &glyph_dib, &src_origin,
627 fg_pixel, glyph_intensities );
630 if (image.free) image.free( &image );
644 x += metrics.gmCellIncX;
645 y += metrics.gmCellIncY;
651 /***********************************************************************
654 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
655 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
657 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
658 struct clipped_rects clipped_rects;
662 DWORD text_color, err;
663 struct intensity_range ranges[17];
665 init_clipped_rects( &clipped_rects );
666 reset_bounds( &bounds );
668 if (flags & ETO_OPAQUE)
670 rop_mask bkgnd_color;
671 get_text_bkgnd_masks( pdev, &bkgnd_color );
672 add_bounds_rect( &bounds, rect );
673 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
674 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
675 bkgnd_color.and, bkgnd_color.xor );
678 if (count == 0) goto done;
680 if (flags & ETO_CLIPPED)
682 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
683 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
687 free_clipped_rects( &clipped_rects );
688 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
690 if (!clipped_rects.count) goto done;
692 text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
693 get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
695 aa_flags = get_font_aa_flags( dev->hdc );
698 for (i = 0; i < count; i++)
700 GLYPHMETRICS metrics;
701 struct gdi_image_bits image;
703 err = get_glyph_bitmap( dev->hdc, (UINT)str[i], aa_flags, &metrics, &image );
707 draw_glyph( pdev, &origin, &metrics, &image, text_color, ranges, &clipped_rects, &bounds );
709 if (image.free) image.free( &image );
715 origin.x += dx[ i * 2 ];
716 origin.y += dx[ i * 2 + 1];
723 origin.x += metrics.gmCellIncX;
724 origin.y += metrics.gmCellIncY;
729 add_clipped_bounds( pdev, &bounds, pdev->clip );
730 free_clipped_rects( &clipped_rects );
734 /***********************************************************************
737 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
738 INT start_x, INT start_y, INT end_x, INT end_y )
740 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
743 /***********************************************************************
746 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
747 INT start_x, INT start_y, INT end_x, INT end_y )
749 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
752 /***********************************************************************
755 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
756 INT start_x, INT start_y, INT end_x, INT end_y )
758 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
761 /***********************************************************************
764 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
766 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
769 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
771 /* the clip rgn stops the flooding */
772 if (clip && !PtInRegion( clip, x, y )) return FALSE;
774 if (type == FLOODFILLBORDER)
775 return dib->funcs->get_pixel( dib, x, y ) != pixel;
777 return dib->funcs->get_pixel( dib, x, y ) == pixel;
780 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
782 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
786 next.top = row->top + offset;
787 next.bottom = next.top + 1;
788 next.left = next.right = row->left;
789 while (next.right < row->right)
791 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
794 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
795 fill_row( dib, clip, &next, pixel, type, rgn );
796 next.left = ++next.right;
799 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
800 fill_row( dib, clip, &next, pixel, type, rgn );
803 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
805 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
806 while (row->right < dib->rect.right - dib->rect.left &&
807 is_interior( dib, clip, row->right, row->top, pixel, type))
810 add_rect_to_region( rgn, row );
812 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
813 if (row->top < dib->rect.bottom - dib->rect.top - 1)
814 do_next_row( dib, clip, row, 1, pixel, type, rgn );
817 /***********************************************************************
818 * dibdrv_ExtFloodFill
820 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
822 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
823 DWORD pixel = get_pixel_color( pdev, color, FALSE );
827 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
829 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
831 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
837 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
839 add_clipped_bounds( pdev, NULL, rgn );
840 brush_region( pdev, rgn );
846 /***********************************************************************
847 * dibdrv_GetNearestColor
849 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
851 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
854 TRACE( "(%p, %08x)\n", dev, color );
856 pixel = get_pixel_color( pdev, color, FALSE );
857 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
860 /***********************************************************************
863 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
865 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
869 TRACE( "(%p, %d, %d)\n", dev, x, y );
873 LPtoDP( dev->hdc, &pt, 1 );
875 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
876 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
879 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
880 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
883 /***********************************************************************
886 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
888 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
893 GetCurrentPositionEx(dev->hdc, pts);
897 LPtoDP(dev->hdc, pts, 2);
899 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
901 reset_dash_origin(pdev);
903 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
904 add_pen_lines_bounds( pdev, 2, pts, region );
908 ret = pen_region( pdev, region );
909 DeleteObject( region );
914 /***********************************************************************
917 * Returns the binary rop that is equivalent to the provided ternary rop
918 * if the src bits are ignored.
920 static inline INT get_rop2_from_rop(INT rop)
922 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
925 /***********************************************************************
928 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
930 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
931 dib_brush *brush = &pdev->brush;
932 int rop2 = get_rop2_from_rop( rop );
933 struct clipped_rects clipped_rects;
934 DWORD and = 0, xor = 0;
937 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
939 add_clipped_bounds( pdev, &dst->visrect, 0 );
940 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
942 switch (rop2) /* shortcuts for rops that don't involve the brush */
944 case R2_NOT: and = ~0u;
946 case R2_WHITE: xor = ~0u;
949 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
954 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
957 free_clipped_rects( &clipped_rects );
961 /***********************************************************************
964 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
966 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
967 const WINEREGION *region;
971 TRACE("%p, %p\n", dev, rgn);
973 reset_bounds( &bounds );
975 region = get_wine_region( rgn );
976 if(!region) return FALSE;
978 for(i = 0; i < region->numRects; i++)
980 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
981 region->rects[i].right, region->rects[i].bottom, FALSE );
982 add_bounds_rect( &bounds, &rect );
983 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
986 release_wine_region( rgn );
987 add_clipped_bounds( pdev, &bounds, pdev->clip );
991 /***********************************************************************
994 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
996 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1000 HRGN outline = 0, interior = 0;
1002 for (i = total = 0; i < polygons; i++)
1004 if (counts[i] < 2) return FALSE;
1008 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1009 if (!points) return FALSE;
1010 memcpy( points, pt, total * sizeof(*pt) );
1011 LPtoDP( dev->hdc, points, total );
1013 if (pdev->brush.style != BS_NULL &&
1014 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
1016 HeapFree( GetProcessHeap(), 0, points );
1020 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1022 /* if not using a region, paint the interior first so the outline can overlap it */
1023 if (interior && !outline)
1025 ret = brush_region( pdev, interior );
1026 DeleteObject( interior );
1030 for (i = pos = 0; i < polygons; i++)
1032 reset_dash_origin( pdev );
1033 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1036 add_pen_lines_bounds( pdev, total, points, outline );
1040 CombineRgn( interior, interior, outline, RGN_DIFF );
1041 ret = brush_region( pdev, interior );
1042 DeleteObject( interior );
1046 if (ret) ret = pen_region( pdev, outline );
1047 DeleteObject( outline );
1049 HeapFree( GetProcessHeap(), 0, points );
1053 /***********************************************************************
1054 * dibdrv_PolyPolyline
1056 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1058 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1059 DWORD total, pos, i;
1064 for (i = total = 0; i < polylines; i++)
1066 if (counts[i] < 2) return FALSE;
1070 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1071 if (!points) return FALSE;
1072 memcpy( points, pt, total * sizeof(*pt) );
1073 LPtoDP( dev->hdc, points, total );
1075 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1077 HeapFree( GetProcessHeap(), 0, points );
1081 for (i = pos = 0; i < polylines; i++)
1083 reset_dash_origin( pdev );
1084 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1087 add_pen_lines_bounds( pdev, total, points, outline );
1091 ret = pen_region( pdev, outline );
1092 DeleteObject( outline );
1095 HeapFree( GetProcessHeap(), 0, points );
1099 /***********************************************************************
1102 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1104 INT counts[1] = { count };
1106 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1109 /***********************************************************************
1112 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1114 DWORD counts[1] = { count };
1116 if (count < 0) return FALSE;
1117 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1120 /***********************************************************************
1123 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1125 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1131 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1133 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1135 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1139 reset_dash_origin(pdev);
1141 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1143 /* 4 pts going clockwise starting from bottom-right */
1144 pts[0].x = pts[3].x = rect.right;
1145 pts[0].y = pts[1].y = rect.bottom;
1146 pts[1].x = pts[2].x = rect.left;
1147 pts[2].y = pts[3].y = rect.top;
1151 /* 4 pts going anti-clockwise starting from top-right */
1152 pts[0].x = pts[3].x = rect.right;
1153 pts[0].y = pts[1].y = rect.top;
1154 pts[1].x = pts[2].x = rect.left;
1155 pts[2].y = pts[3].y = rect.bottom;
1158 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1159 add_pen_lines_bounds( pdev, 4, pts, outline );
1163 if (pdev->brush.style != BS_NULL)
1165 HRGN interior = CreateRectRgnIndirect( &rect );
1167 CombineRgn( interior, interior, outline, RGN_DIFF );
1168 brush_region( pdev, interior );
1169 DeleteObject( interior );
1171 ret = pen_region( pdev, outline );
1172 DeleteObject( outline );
1176 rect.left += (pdev->pen_width + 1) / 2;
1177 rect.top += (pdev->pen_width + 1) / 2;
1178 rect.right -= pdev->pen_width / 2;
1179 rect.bottom -= pdev->pen_width / 2;
1180 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1185 /***********************************************************************
1188 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1189 INT ellipse_width, INT ellipse_height )
1191 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1193 POINT pt[2], *points;
1196 HRGN outline = 0, interior = 0;
1198 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1200 pt[0].x = pt[0].y = 0;
1201 pt[1].x = ellipse_width;
1202 pt[1].y = ellipse_height;
1203 LPtoDP( dev->hdc, pt, 2 );
1204 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1205 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1206 if (ellipse_width <= 2|| ellipse_height <= 2)
1207 return dibdrv_Rectangle( dev, left, top, right, bottom );
1209 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1210 if (!points) return FALSE;
1212 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1214 HeapFree( GetProcessHeap(), 0, points );
1218 if (pdev->brush.style != BS_NULL &&
1219 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1220 ellipse_width, ellipse_height )))
1222 HeapFree( GetProcessHeap(), 0, points );
1223 if (outline) DeleteObject( outline );
1227 /* if not using a region, paint the interior first so the outline can overlap it */
1228 if (interior && !outline)
1230 ret = brush_region( pdev, interior );
1231 DeleteObject( interior );
1235 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1237 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1239 for (i = 0; i < count; i++)
1241 points[i].x = rect.right - ellipse_width + points[i].x;
1242 points[i].y = rect.bottom - ellipse_height + points[i].y;
1247 for (i = 0; i < count; i++)
1249 points[i].x = rect.right - ellipse_width + points[i].x;
1250 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1254 /* horizontal symmetry */
1256 end = 2 * count - 1;
1257 /* avoid duplicating the midpoint */
1258 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1259 for (i = 0; i < count; i++)
1261 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1262 points[end - i].y = points[i].y;
1266 /* vertical symmetry */
1268 end = 2 * count - 1;
1269 /* avoid duplicating the midpoint */
1270 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1271 for (i = 0; i < count; i++)
1273 points[end - i].x = points[i].x;
1274 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1278 reset_dash_origin( pdev );
1279 pdev->pen_lines( pdev, count, points, TRUE, outline );
1280 add_pen_lines_bounds( pdev, count, points, outline );
1284 CombineRgn( interior, interior, outline, RGN_DIFF );
1285 ret = brush_region( pdev, interior );
1286 DeleteObject( interior );
1290 if (ret) ret = pen_region( pdev, outline );
1291 DeleteObject( outline );
1293 HeapFree( GetProcessHeap(), 0, points );
1297 /***********************************************************************
1300 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1301 INT start_x, INT start_y, INT end_x, INT end_y )
1303 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1306 /***********************************************************************
1309 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1311 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1312 struct clipped_rects clipped_rects;
1317 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1321 LPtoDP( dev->hdc, &pt, 1 );
1324 rect.right = rect.left + 1;
1325 rect.bottom = rect.top + 1;
1326 add_clipped_bounds( pdev, &rect, pdev->clip );
1328 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1329 pixel = get_pixel_color( pdev, color, FALSE );
1330 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1332 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1333 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1334 free_clipped_rects( &clipped_rects );