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( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics,
444 const dib_info *glyph_dib, DWORD text_color,
445 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
449 RECT rect, clipped_rect;
452 rect.left = x + metrics->gmptGlyphOrigin.x;
453 rect.top = y - metrics->gmptGlyphOrigin.y;
454 rect.right = rect.left + metrics->gmBlackBoxX;
455 rect.bottom = rect.top + metrics->gmBlackBoxY;
456 if (bounds) add_bounds_rect( bounds, &rect );
458 for (i = 0; i < clipped_rects->count; i++)
460 if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
462 src_origin.x = clipped_rect.left - rect.left;
463 src_origin.y = clipped_rect.top - rect.top;
465 if (glyph_dib->bit_count == 32)
466 dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
469 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
470 text_color, ranges );
475 static int get_glyph_depth( UINT aa_flags )
479 case GGO_BITMAP: return 1;
481 case GGO_GRAY2_BITMAP:
482 case GGO_GRAY4_BITMAP:
483 case GGO_GRAY8_BITMAP:
484 case WINE_GGO_GRAY16_BITMAP: return 8;
486 case WINE_GGO_HRGB_BITMAP:
487 case WINE_GGO_HBGR_BITMAP:
488 case WINE_GGO_VRGB_BITMAP:
489 case WINE_GGO_VBGR_BITMAP: return 32;
492 ERR("Unexpected flags %08x\n", aa_flags);
497 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
498 static const int padding[4] = {0, 3, 2, 1};
500 /***********************************************************************
503 * Retrieve a 17-level bitmap for the appropriate glyph.
505 * For non-antialiased bitmaps convert them to the 17-level format
506 * using only values 0 or 16.
508 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
509 dib_info *glyph_dib )
511 UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
512 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
513 UINT indices[3] = {0, 0, 0x20};
516 BYTE *buf, *dst, *src;
517 int pad = 0, depth = get_glyph_depth( aa_flags );
519 glyph_dib->bits.ptr = NULL;
520 glyph_dib->bits.is_copy = FALSE;
521 glyph_dib->bits.free = free_heap_bits;
522 glyph_dib->bits.param = NULL;
526 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
529 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
530 if (ret != GDI_ERROR) break;
533 if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
534 if (!ret) return ERROR_SUCCESS; /* empty glyph */
536 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
537 glyph_dib->bit_count = depth == 1 ? 8 : depth;
538 glyph_dib->width = metrics->gmBlackBoxX;
539 glyph_dib->height = metrics->gmBlackBoxY;
540 glyph_dib->rect.left = 0;
541 glyph_dib->rect.top = 0;
542 glyph_dib->rect.right = metrics->gmBlackBoxX;
543 glyph_dib->rect.bottom = metrics->gmBlackBoxY;
544 glyph_dib->stride = get_dib_stride( metrics->gmBlackBoxX, glyph_dib->bit_count );
546 if (glyph_dib->bit_count == 8) pad = padding[ metrics->gmBlackBoxX % 4 ];
547 size = metrics->gmBlackBoxY * glyph_dib->stride;
549 buf = HeapAlloc( GetProcessHeap(), 0, size );
550 if (!buf) return ERROR_OUTOFMEMORY;
552 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
553 if (ret == GDI_ERROR)
555 HeapFree( GetProcessHeap(), 0, buf );
556 return ERROR_NOT_FOUND;
559 if (aa_flags == GGO_BITMAP)
561 for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
563 src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
564 dst = buf + y * glyph_dib->stride;
566 if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
568 for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
569 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
574 for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += glyph_dib->stride)
575 memset( dst + metrics->gmBlackBoxX, 0, pad );
578 glyph_dib->bits.ptr = buf;
579 return ERROR_SUCCESS;
582 static void render_string( HDC hdc, dib_info *dib, INT x, INT y, UINT flags, UINT aa_flags,
583 const WCHAR *str, UINT count, const INT *dx, DWORD text_color,
584 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
589 GLYPHMETRICS metrics;
592 for (i = 0; i < count; i++)
594 err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &glyph_dib );
597 if (glyph_dib.bits.ptr)
598 draw_glyph( dib, x, y, &metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
600 free_dib_info( &glyph_dib );
614 x += metrics.gmCellIncX;
615 y += metrics.gmCellIncY;
620 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
621 struct bitblt_coords *src, INT x, INT y, UINT flags,
622 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
627 DWORD fg_pixel, bg_pixel;
628 struct intensity_range glyph_intensities[17];
629 struct clipped_rects visrect;
631 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
633 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
636 visrect.rects = &src->visrect;
638 fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
639 if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
641 get_aa_ranges( fg, glyph_intensities );
643 if (flags & ETO_OPAQUE)
645 rop_mask bkgnd_color;
647 bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
648 if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
651 bkgnd_color.xor = bg_pixel;
652 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
655 render_string( hdc, &dib, x, y, flags, aa_flags, str, count, dx,
656 fg_pixel, glyph_intensities, &visrect, NULL );
660 /***********************************************************************
663 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
664 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
666 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
667 struct clipped_rects clipped_rects;
671 struct intensity_range ranges[17];
673 init_clipped_rects( &clipped_rects );
674 reset_bounds( &bounds );
676 if (flags & ETO_OPAQUE)
678 rop_mask bkgnd_color;
679 get_text_bkgnd_masks( pdev, &bkgnd_color );
680 add_bounds_rect( &bounds, rect );
681 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
682 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
683 bkgnd_color.and, bkgnd_color.xor );
686 if (count == 0) goto done;
688 if (flags & ETO_CLIPPED)
690 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
691 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
695 free_clipped_rects( &clipped_rects );
696 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
698 if (!clipped_rects.count) goto done;
700 text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
701 get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
703 dc = get_dc_ptr( dev->hdc );
704 render_string( dev->hdc, &pdev->dib, x, y, flags, dc->aa_flags, str, count, dx,
705 text_color, ranges, &clipped_rects, &bounds );
706 release_dc_ptr( dc );
709 add_clipped_bounds( pdev, &bounds, pdev->clip );
710 free_clipped_rects( &clipped_rects );
714 /***********************************************************************
717 HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
719 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
721 if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */
723 dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
724 return dev->funcs->pSelectFont( dev, font, aa_flags );
727 /***********************************************************************
730 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
731 INT start_x, INT start_y, INT end_x, INT end_y )
733 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
736 /***********************************************************************
739 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
740 INT start_x, INT start_y, INT end_x, INT end_y )
742 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
745 /***********************************************************************
748 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
749 INT start_x, INT start_y, INT end_x, INT end_y )
751 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
754 /***********************************************************************
757 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
759 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
762 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
764 /* the clip rgn stops the flooding */
765 if (clip && !PtInRegion( clip, x, y )) return FALSE;
767 if (type == FLOODFILLBORDER)
768 return dib->funcs->get_pixel( dib, x, y ) != pixel;
770 return dib->funcs->get_pixel( dib, x, y ) == pixel;
773 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
775 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
779 next.top = row->top + offset;
780 next.bottom = next.top + 1;
781 next.left = next.right = row->left;
782 while (next.right < row->right)
784 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
787 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
788 fill_row( dib, clip, &next, pixel, type, rgn );
789 next.left = ++next.right;
792 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
793 fill_row( dib, clip, &next, pixel, type, rgn );
796 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
798 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
799 while (row->right < dib->rect.right - dib->rect.left &&
800 is_interior( dib, clip, row->right, row->top, pixel, type))
803 add_rect_to_region( rgn, row );
805 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
806 if (row->top < dib->rect.bottom - dib->rect.top - 1)
807 do_next_row( dib, clip, row, 1, pixel, type, rgn );
810 /***********************************************************************
811 * dibdrv_ExtFloodFill
813 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
815 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
816 DWORD pixel = get_pixel_color( pdev, color, FALSE );
820 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
822 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
824 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
830 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
832 add_clipped_bounds( pdev, NULL, rgn );
833 brush_region( pdev, rgn );
839 /***********************************************************************
840 * dibdrv_GetNearestColor
842 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
844 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
847 TRACE( "(%p, %08x)\n", dev, color );
849 pixel = get_pixel_color( pdev, color, FALSE );
850 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
853 /***********************************************************************
856 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
858 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
862 TRACE( "(%p, %d, %d)\n", dev, x, y );
866 LPtoDP( dev->hdc, &pt, 1 );
868 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
869 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
872 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
873 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
876 /***********************************************************************
879 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
881 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
886 GetCurrentPositionEx(dev->hdc, pts);
890 LPtoDP(dev->hdc, pts, 2);
892 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
894 reset_dash_origin(pdev);
896 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
897 add_pen_lines_bounds( pdev, 2, pts, region );
901 ret = pen_region( pdev, region );
902 DeleteObject( region );
907 /***********************************************************************
910 * Returns the binary rop that is equivalent to the provided ternary rop
911 * if the src bits are ignored.
913 static inline INT get_rop2_from_rop(INT rop)
915 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
918 /***********************************************************************
921 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
923 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
924 dib_brush *brush = &pdev->brush;
925 int rop2 = get_rop2_from_rop( rop );
926 struct clipped_rects clipped_rects;
927 DWORD and = 0, xor = 0;
930 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
932 add_clipped_bounds( pdev, &dst->visrect, 0 );
933 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
935 switch (rop2) /* shortcuts for rops that don't involve the brush */
937 case R2_NOT: and = ~0u;
939 case R2_WHITE: xor = ~0u;
942 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
947 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
950 free_clipped_rects( &clipped_rects );
954 /***********************************************************************
957 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
959 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
960 const WINEREGION *region;
964 TRACE("%p, %p\n", dev, rgn);
966 reset_bounds( &bounds );
968 region = get_wine_region( rgn );
969 if(!region) return FALSE;
971 for(i = 0; i < region->numRects; i++)
973 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
974 region->rects[i].right, region->rects[i].bottom, FALSE );
975 add_bounds_rect( &bounds, &rect );
976 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
979 release_wine_region( rgn );
980 add_clipped_bounds( pdev, &bounds, pdev->clip );
984 /***********************************************************************
987 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
989 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
993 HRGN outline = 0, interior = 0;
995 for (i = total = 0; i < polygons; i++)
997 if (counts[i] < 2) return FALSE;
1001 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1002 if (!points) return FALSE;
1003 memcpy( points, pt, total * sizeof(*pt) );
1004 LPtoDP( dev->hdc, points, total );
1006 if (pdev->brush.style != BS_NULL &&
1007 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
1009 HeapFree( GetProcessHeap(), 0, points );
1013 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1015 /* if not using a region, paint the interior first so the outline can overlap it */
1016 if (interior && !outline)
1018 ret = brush_region( pdev, interior );
1019 DeleteObject( interior );
1023 for (i = pos = 0; i < polygons; i++)
1025 reset_dash_origin( pdev );
1026 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1029 add_pen_lines_bounds( pdev, total, points, outline );
1033 CombineRgn( interior, interior, outline, RGN_DIFF );
1034 ret = brush_region( pdev, interior );
1035 DeleteObject( interior );
1039 if (ret) ret = pen_region( pdev, outline );
1040 DeleteObject( outline );
1042 HeapFree( GetProcessHeap(), 0, points );
1046 /***********************************************************************
1047 * dibdrv_PolyPolyline
1049 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1051 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1052 DWORD total, pos, i;
1057 for (i = total = 0; i < polylines; i++)
1059 if (counts[i] < 2) return FALSE;
1063 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1064 if (!points) return FALSE;
1065 memcpy( points, pt, total * sizeof(*pt) );
1066 LPtoDP( dev->hdc, points, total );
1068 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1070 HeapFree( GetProcessHeap(), 0, points );
1074 for (i = pos = 0; i < polylines; i++)
1076 reset_dash_origin( pdev );
1077 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1080 add_pen_lines_bounds( pdev, total, points, outline );
1084 ret = pen_region( pdev, outline );
1085 DeleteObject( outline );
1088 HeapFree( GetProcessHeap(), 0, points );
1092 /***********************************************************************
1095 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1097 INT counts[1] = { count };
1099 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1102 /***********************************************************************
1105 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1107 DWORD counts[1] = { count };
1109 if (count < 0) return FALSE;
1110 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1113 /***********************************************************************
1116 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1118 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1124 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1126 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1128 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1132 reset_dash_origin(pdev);
1134 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1136 /* 4 pts going clockwise starting from bottom-right */
1137 pts[0].x = pts[3].x = rect.right;
1138 pts[0].y = pts[1].y = rect.bottom;
1139 pts[1].x = pts[2].x = rect.left;
1140 pts[2].y = pts[3].y = rect.top;
1144 /* 4 pts going anti-clockwise starting from top-right */
1145 pts[0].x = pts[3].x = rect.right;
1146 pts[0].y = pts[1].y = rect.top;
1147 pts[1].x = pts[2].x = rect.left;
1148 pts[2].y = pts[3].y = rect.bottom;
1151 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1152 add_pen_lines_bounds( pdev, 4, pts, outline );
1156 if (pdev->brush.style != BS_NULL)
1158 HRGN interior = CreateRectRgnIndirect( &rect );
1160 CombineRgn( interior, interior, outline, RGN_DIFF );
1161 brush_region( pdev, interior );
1162 DeleteObject( interior );
1164 ret = pen_region( pdev, outline );
1165 DeleteObject( outline );
1169 rect.left += (pdev->pen_width + 1) / 2;
1170 rect.top += (pdev->pen_width + 1) / 2;
1171 rect.right -= pdev->pen_width / 2;
1172 rect.bottom -= pdev->pen_width / 2;
1173 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1178 /***********************************************************************
1181 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1182 INT ellipse_width, INT ellipse_height )
1184 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1186 POINT pt[2], *points;
1189 HRGN outline = 0, interior = 0;
1191 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1193 pt[0].x = pt[0].y = 0;
1194 pt[1].x = ellipse_width;
1195 pt[1].y = ellipse_height;
1196 LPtoDP( dev->hdc, pt, 2 );
1197 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1198 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1199 if (ellipse_width <= 2|| ellipse_height <= 2)
1200 return dibdrv_Rectangle( dev, left, top, right, bottom );
1202 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1203 if (!points) return FALSE;
1205 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1207 HeapFree( GetProcessHeap(), 0, points );
1211 if (pdev->brush.style != BS_NULL &&
1212 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1213 ellipse_width, ellipse_height )))
1215 HeapFree( GetProcessHeap(), 0, points );
1216 if (outline) DeleteObject( outline );
1220 /* if not using a region, paint the interior first so the outline can overlap it */
1221 if (interior && !outline)
1223 ret = brush_region( pdev, interior );
1224 DeleteObject( interior );
1228 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1230 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1232 for (i = 0; i < count; i++)
1234 points[i].x = rect.right - ellipse_width + points[i].x;
1235 points[i].y = rect.bottom - ellipse_height + points[i].y;
1240 for (i = 0; i < count; i++)
1242 points[i].x = rect.right - ellipse_width + points[i].x;
1243 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1247 /* horizontal symmetry */
1249 end = 2 * count - 1;
1250 /* avoid duplicating the midpoint */
1251 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1252 for (i = 0; i < count; i++)
1254 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1255 points[end - i].y = points[i].y;
1259 /* vertical symmetry */
1261 end = 2 * count - 1;
1262 /* avoid duplicating the midpoint */
1263 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1264 for (i = 0; i < count; i++)
1266 points[end - i].x = points[i].x;
1267 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1271 reset_dash_origin( pdev );
1272 pdev->pen_lines( pdev, count, points, TRUE, outline );
1273 add_pen_lines_bounds( pdev, count, points, outline );
1277 CombineRgn( interior, interior, outline, RGN_DIFF );
1278 ret = brush_region( pdev, interior );
1279 DeleteObject( interior );
1283 if (ret) ret = pen_region( pdev, outline );
1284 DeleteObject( outline );
1286 HeapFree( GetProcessHeap(), 0, points );
1290 /***********************************************************************
1293 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1294 INT start_x, INT start_y, INT end_x, INT end_y )
1296 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1299 /***********************************************************************
1302 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1304 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1305 struct clipped_rects clipped_rects;
1310 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1314 LPtoDP( dev->hdc, &pt, 1 );
1317 rect.right = rect.left + 1;
1318 rect.bottom = rect.top + 1;
1319 add_clipped_bounds( pdev, &rect, pdev->clip );
1321 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1322 pixel = get_pixel_color( pdev, color, FALSE );
1323 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1325 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1326 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1327 free_clipped_rects( &clipped_rects );