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 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
466 text_color, ranges );
471 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
472 static const int padding[4] = {0, 3, 2, 1};
474 /***********************************************************************
477 * Retrieve a 17-level bitmap for the appropriate glyph.
479 * For non-antialiased bitmaps convert them to the 17-level format
480 * using only values 0 or 16.
482 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
483 dib_info *glyph_dib )
485 UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
486 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
487 UINT indices[3] = {0, 0, 0x20};
490 BYTE *buf, *dst, *src;
493 glyph_dib->bits.ptr = NULL;
494 glyph_dib->bits.is_copy = FALSE;
495 glyph_dib->bits.free = free_heap_bits;
496 glyph_dib->bits.param = NULL;
500 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
503 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
504 if (ret != GDI_ERROR) break;
507 if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
508 if (!ret) return ERROR_SUCCESS; /* empty glyph */
510 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
511 glyph_dib->bit_count = 8;
512 glyph_dib->width = metrics->gmBlackBoxX;
513 glyph_dib->height = metrics->gmBlackBoxY;
514 glyph_dib->rect.left = 0;
515 glyph_dib->rect.top = 0;
516 glyph_dib->rect.right = metrics->gmBlackBoxX;
517 glyph_dib->rect.bottom = metrics->gmBlackBoxY;
518 glyph_dib->stride = get_dib_stride( metrics->gmBlackBoxX, glyph_dib->bit_count );
520 pad = padding[ metrics->gmBlackBoxX % 4 ];
521 size = metrics->gmBlackBoxY * glyph_dib->stride;
523 buf = HeapAlloc( GetProcessHeap(), 0, size );
524 if (!buf) return ERROR_OUTOFMEMORY;
526 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
527 if (ret == GDI_ERROR)
529 HeapFree( GetProcessHeap(), 0, buf );
530 return ERROR_NOT_FOUND;
533 if (aa_flags == GGO_BITMAP)
535 for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
537 src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
538 dst = buf + y * glyph_dib->stride;
540 if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
542 for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
543 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
548 for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += glyph_dib->stride)
549 memset( dst + metrics->gmBlackBoxX, 0, pad );
552 glyph_dib->bits.ptr = buf;
553 return ERROR_SUCCESS;
556 static void render_string( HDC hdc, dib_info *dib, INT x, INT y, UINT flags, UINT aa_flags,
557 const WCHAR *str, UINT count, const INT *dx, DWORD text_color,
558 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
563 GLYPHMETRICS metrics;
566 for (i = 0; i < count; i++)
568 err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &glyph_dib );
571 if (glyph_dib.bits.ptr)
572 draw_glyph( dib, x, y, &metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
574 free_dib_info( &glyph_dib );
588 x += metrics.gmCellIncX;
589 y += metrics.gmCellIncY;
594 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
595 struct bitblt_coords *src, INT x, INT y, UINT flags,
596 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
601 DWORD fg_pixel, bg_pixel;
602 struct intensity_range glyph_intensities[17];
603 struct clipped_rects visrect;
605 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
607 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
610 visrect.rects = &src->visrect;
612 fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
613 if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
615 get_aa_ranges( fg, glyph_intensities );
617 if (flags & ETO_OPAQUE)
619 rop_mask bkgnd_color;
621 bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
622 if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
625 bkgnd_color.xor = bg_pixel;
626 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
629 render_string( hdc, &dib, x, y, flags, aa_flags, str, count, dx,
630 fg_pixel, glyph_intensities, &visrect, NULL );
634 /***********************************************************************
637 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
638 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
640 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
641 struct clipped_rects clipped_rects;
645 struct intensity_range ranges[17];
647 init_clipped_rects( &clipped_rects );
648 reset_bounds( &bounds );
650 if (flags & ETO_OPAQUE)
652 rop_mask bkgnd_color;
653 get_text_bkgnd_masks( pdev, &bkgnd_color );
654 add_bounds_rect( &bounds, rect );
655 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
656 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
657 bkgnd_color.and, bkgnd_color.xor );
660 if (count == 0) goto done;
662 if (flags & ETO_CLIPPED)
664 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
665 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
669 free_clipped_rects( &clipped_rects );
670 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
672 if (!clipped_rects.count) goto done;
674 text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
675 get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
677 aa_flags = get_font_aa_flags( dev->hdc );
679 render_string( dev->hdc, &pdev->dib, x, y, flags, aa_flags, str, count, dx,
680 text_color, ranges, &clipped_rects, &bounds );
683 add_clipped_bounds( pdev, &bounds, pdev->clip );
684 free_clipped_rects( &clipped_rects );
688 /***********************************************************************
691 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
692 INT start_x, INT start_y, INT end_x, INT end_y )
694 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
697 /***********************************************************************
700 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
701 INT start_x, INT start_y, INT end_x, INT end_y )
703 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
706 /***********************************************************************
709 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
710 INT start_x, INT start_y, INT end_x, INT end_y )
712 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
715 /***********************************************************************
718 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
720 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
723 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
725 /* the clip rgn stops the flooding */
726 if (clip && !PtInRegion( clip, x, y )) return FALSE;
728 if (type == FLOODFILLBORDER)
729 return dib->funcs->get_pixel( dib, x, y ) != pixel;
731 return dib->funcs->get_pixel( dib, x, y ) == pixel;
734 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
736 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
740 next.top = row->top + offset;
741 next.bottom = next.top + 1;
742 next.left = next.right = row->left;
743 while (next.right < row->right)
745 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
748 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
749 fill_row( dib, clip, &next, pixel, type, rgn );
750 next.left = ++next.right;
753 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
754 fill_row( dib, clip, &next, pixel, type, rgn );
757 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
759 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
760 while (row->right < dib->rect.right - dib->rect.left &&
761 is_interior( dib, clip, row->right, row->top, pixel, type))
764 add_rect_to_region( rgn, row );
766 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
767 if (row->top < dib->rect.bottom - dib->rect.top - 1)
768 do_next_row( dib, clip, row, 1, pixel, type, rgn );
771 /***********************************************************************
772 * dibdrv_ExtFloodFill
774 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
776 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
777 DWORD pixel = get_pixel_color( pdev, color, FALSE );
781 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
783 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
785 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
791 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
793 add_clipped_bounds( pdev, NULL, rgn );
794 brush_region( pdev, rgn );
800 /***********************************************************************
801 * dibdrv_GetNearestColor
803 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
805 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
808 TRACE( "(%p, %08x)\n", dev, color );
810 pixel = get_pixel_color( pdev, color, FALSE );
811 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
814 /***********************************************************************
817 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
819 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
823 TRACE( "(%p, %d, %d)\n", dev, x, y );
827 LPtoDP( dev->hdc, &pt, 1 );
829 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
830 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
833 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
834 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
837 /***********************************************************************
840 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
842 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
847 GetCurrentPositionEx(dev->hdc, pts);
851 LPtoDP(dev->hdc, pts, 2);
853 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
855 reset_dash_origin(pdev);
857 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
858 add_pen_lines_bounds( pdev, 2, pts, region );
862 ret = pen_region( pdev, region );
863 DeleteObject( region );
868 /***********************************************************************
871 * Returns the binary rop that is equivalent to the provided ternary rop
872 * if the src bits are ignored.
874 static inline INT get_rop2_from_rop(INT rop)
876 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
879 /***********************************************************************
882 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
884 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
885 dib_brush *brush = &pdev->brush;
886 int rop2 = get_rop2_from_rop( rop );
887 struct clipped_rects clipped_rects;
888 DWORD and = 0, xor = 0;
891 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
893 add_clipped_bounds( pdev, &dst->visrect, 0 );
894 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
896 switch (rop2) /* shortcuts for rops that don't involve the brush */
898 case R2_NOT: and = ~0u;
900 case R2_WHITE: xor = ~0u;
903 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
908 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
911 free_clipped_rects( &clipped_rects );
915 /***********************************************************************
918 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
920 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
921 const WINEREGION *region;
925 TRACE("%p, %p\n", dev, rgn);
927 reset_bounds( &bounds );
929 region = get_wine_region( rgn );
930 if(!region) return FALSE;
932 for(i = 0; i < region->numRects; i++)
934 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
935 region->rects[i].right, region->rects[i].bottom, FALSE );
936 add_bounds_rect( &bounds, &rect );
937 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
940 release_wine_region( rgn );
941 add_clipped_bounds( pdev, &bounds, pdev->clip );
945 /***********************************************************************
948 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
950 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
954 HRGN outline = 0, interior = 0;
956 for (i = total = 0; i < polygons; i++)
958 if (counts[i] < 2) return FALSE;
962 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
963 if (!points) return FALSE;
964 memcpy( points, pt, total * sizeof(*pt) );
965 LPtoDP( dev->hdc, points, total );
967 if (pdev->brush.style != BS_NULL &&
968 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
970 HeapFree( GetProcessHeap(), 0, points );
974 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
976 /* if not using a region, paint the interior first so the outline can overlap it */
977 if (interior && !outline)
979 ret = brush_region( pdev, interior );
980 DeleteObject( interior );
984 for (i = pos = 0; i < polygons; i++)
986 reset_dash_origin( pdev );
987 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
990 add_pen_lines_bounds( pdev, total, points, outline );
994 CombineRgn( interior, interior, outline, RGN_DIFF );
995 ret = brush_region( pdev, interior );
996 DeleteObject( interior );
1000 if (ret) ret = pen_region( pdev, outline );
1001 DeleteObject( outline );
1003 HeapFree( GetProcessHeap(), 0, points );
1007 /***********************************************************************
1008 * dibdrv_PolyPolyline
1010 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1012 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1013 DWORD total, pos, i;
1018 for (i = total = 0; i < polylines; i++)
1020 if (counts[i] < 2) return FALSE;
1024 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1025 if (!points) return FALSE;
1026 memcpy( points, pt, total * sizeof(*pt) );
1027 LPtoDP( dev->hdc, points, total );
1029 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1031 HeapFree( GetProcessHeap(), 0, points );
1035 for (i = pos = 0; i < polylines; i++)
1037 reset_dash_origin( pdev );
1038 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1041 add_pen_lines_bounds( pdev, total, points, outline );
1045 ret = pen_region( pdev, outline );
1046 DeleteObject( outline );
1049 HeapFree( GetProcessHeap(), 0, points );
1053 /***********************************************************************
1056 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1058 INT counts[1] = { count };
1060 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1063 /***********************************************************************
1066 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1068 DWORD counts[1] = { count };
1070 if (count < 0) return FALSE;
1071 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1074 /***********************************************************************
1077 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1079 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1085 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1087 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1089 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1093 reset_dash_origin(pdev);
1095 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1097 /* 4 pts going clockwise starting from bottom-right */
1098 pts[0].x = pts[3].x = rect.right;
1099 pts[0].y = pts[1].y = rect.bottom;
1100 pts[1].x = pts[2].x = rect.left;
1101 pts[2].y = pts[3].y = rect.top;
1105 /* 4 pts going anti-clockwise starting from top-right */
1106 pts[0].x = pts[3].x = rect.right;
1107 pts[0].y = pts[1].y = rect.top;
1108 pts[1].x = pts[2].x = rect.left;
1109 pts[2].y = pts[3].y = rect.bottom;
1112 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1113 add_pen_lines_bounds( pdev, 4, pts, outline );
1117 if (pdev->brush.style != BS_NULL)
1119 HRGN interior = CreateRectRgnIndirect( &rect );
1121 CombineRgn( interior, interior, outline, RGN_DIFF );
1122 brush_region( pdev, interior );
1123 DeleteObject( interior );
1125 ret = pen_region( pdev, outline );
1126 DeleteObject( outline );
1130 rect.left += (pdev->pen_width + 1) / 2;
1131 rect.top += (pdev->pen_width + 1) / 2;
1132 rect.right -= pdev->pen_width / 2;
1133 rect.bottom -= pdev->pen_width / 2;
1134 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1139 /***********************************************************************
1142 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1143 INT ellipse_width, INT ellipse_height )
1145 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1147 POINT pt[2], *points;
1150 HRGN outline = 0, interior = 0;
1152 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1154 pt[0].x = pt[0].y = 0;
1155 pt[1].x = ellipse_width;
1156 pt[1].y = ellipse_height;
1157 LPtoDP( dev->hdc, pt, 2 );
1158 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1159 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1160 if (ellipse_width <= 2|| ellipse_height <= 2)
1161 return dibdrv_Rectangle( dev, left, top, right, bottom );
1163 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1164 if (!points) return FALSE;
1166 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1168 HeapFree( GetProcessHeap(), 0, points );
1172 if (pdev->brush.style != BS_NULL &&
1173 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1174 ellipse_width, ellipse_height )))
1176 HeapFree( GetProcessHeap(), 0, points );
1177 if (outline) DeleteObject( outline );
1181 /* if not using a region, paint the interior first so the outline can overlap it */
1182 if (interior && !outline)
1184 ret = brush_region( pdev, interior );
1185 DeleteObject( interior );
1189 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1191 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1193 for (i = 0; i < count; i++)
1195 points[i].x = rect.right - ellipse_width + points[i].x;
1196 points[i].y = rect.bottom - ellipse_height + points[i].y;
1201 for (i = 0; i < count; i++)
1203 points[i].x = rect.right - ellipse_width + points[i].x;
1204 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1208 /* horizontal symmetry */
1210 end = 2 * count - 1;
1211 /* avoid duplicating the midpoint */
1212 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1213 for (i = 0; i < count; i++)
1215 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1216 points[end - i].y = points[i].y;
1220 /* vertical symmetry */
1222 end = 2 * count - 1;
1223 /* avoid duplicating the midpoint */
1224 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1225 for (i = 0; i < count; i++)
1227 points[end - i].x = points[i].x;
1228 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1232 reset_dash_origin( pdev );
1233 pdev->pen_lines( pdev, count, points, TRUE, outline );
1234 add_pen_lines_bounds( pdev, count, points, outline );
1238 CombineRgn( interior, interior, outline, RGN_DIFF );
1239 ret = brush_region( pdev, interior );
1240 DeleteObject( interior );
1244 if (ret) ret = pen_region( pdev, outline );
1245 DeleteObject( outline );
1247 HeapFree( GetProcessHeap(), 0, points );
1251 /***********************************************************************
1254 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1255 INT start_x, INT start_y, INT end_x, INT end_y )
1257 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1260 /***********************************************************************
1263 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1265 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1266 struct clipped_rects clipped_rects;
1271 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1275 LPtoDP( dev->hdc, &pt, 1 );
1278 rect.right = rect.left + 1;
1279 rect.bottom = rect.top + 1;
1280 add_clipped_bounds( pdev, &rect, pdev->clip );
1282 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1283 pixel = get_pixel_color( pdev, color, FALSE );
1284 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1286 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1287 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1288 free_clipped_rects( &clipped_rects );