gdi32: Store the anti-aliasing flags in the DC when selecting a font.
[wine/multimedia.git] / dlls / gdi32 / dibdrv / graphics.c
blob6f936e0cdd45a6797b1c65ab3f491d8a542e9eca
1 /*
2 * DIB driver graphics operations.
4 * Copyright 2011 Huw Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <assert.h>
22 #include "gdi_private.h"
23 #include "dibdrv.h"
25 #include "wine/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;
32 BOOL ret;
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 );
38 return ret;
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 )
57 RECT rect;
59 rect.left = left;
60 rect.top = top;
61 rect.right = right;
62 rect.bottom = bottom;
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 */
67 rect.left--;
68 rect.right--;
70 LPtoDP( hdc, (POINT *)&rect, 2 );
71 if (rect.left > rect.right)
73 int tmp = rect.left;
74 rect.left = rect.right;
75 rect.right = tmp;
77 if (rect.top > rect.bottom)
79 int tmp = rect.top;
80 rect.top = rect.bottom;
81 rect.bottom = tmp;
83 return rect;
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;
98 return TRUE;
101 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
103 const WINEREGION *region;
104 RECT bounds, rect;
105 int width = 0;
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)
116 width *= 5;
117 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
119 else
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, &region->extents );
129 release_wine_region( rgn );
133 while (count-- > 0)
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 );
140 points++;
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);
157 int pos = 0;
158 POINT pt;
160 pt.x = a;
161 pt.y = height / 2;
163 /* based on an algorithm by Alois Zingl */
165 while (pt.x >= width / 2)
167 INT64 e2 = 2 * err;
168 data[pos++] = pt;
169 if (e2 >= dx)
171 pt.x--;
172 err += dx += bsq;
174 if (e2 <= dy)
176 pt.y++;
177 err += dy += asq;
180 return pos;
183 static int find_intersection( const POINT *points, int x, int y, int count )
185 int i;
187 if (y >= 0)
189 if (x >= 0) /* first quadrant */
191 for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
192 return i;
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;
203 /* third quadrant */
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)
222 start.y = -start.y;
223 end.y = -end.y;
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;
229 pos = count;
230 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
232 for (i = start_pos; i < end_pos; i++, pos++)
234 switch ((i / count) % 4)
236 case 0:
237 points[pos].x = rect->left + width/2 + points[i % count].x;
238 points[pos].y = rect->top + height/2 + points[i % count].y;
239 break;
240 case 1:
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;
243 break;
244 case 2:
245 points[pos].x = rect->left + width/2 - points[i % count].x;
246 points[pos].y = rect->top + height/2 - points[i % count].y;
247 break;
248 case 3:
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;
251 break;
255 else
257 for (i = start_pos; i < end_pos; i++, pos++)
259 switch ((i / count) % 4)
261 case 0:
262 points[pos].x = rect->left + width/2 + points[i % count].x;
263 points[pos].y = rect->top + height/2 - points[i % count].y;
264 break;
265 case 1:
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;
268 break;
269 case 2:
270 points[pos].x = rect->left + width/2 - points[i % count].x;
271 points[pos].y = rect->top + height/2 + points[i % count].y;
272 break;
273 case 3:
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;
276 break;
281 memmove( points, points + count, (pos - count) * sizeof(POINT) );
282 return pos - count;
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 );
290 RECT rect;
291 POINT pt[2], *points;
292 int width, height, count;
293 BOOL ret = TRUE;
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;
301 pt[0].x = start_x;
302 pt[0].y = start_y;
303 pt[1].x = end_x;
304 pt[1].y = end_y;
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;
327 count++;
329 if (count < 2)
331 HeapFree( GetProcessHeap(), 0, points );
332 return TRUE;
335 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
337 HeapFree( GetProcessHeap(), 0, points );
338 return FALSE;
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 );
346 return FALSE;
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 );
354 interior = 0;
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 );
361 if (interior)
363 CombineRgn( interior, interior, outline, RGN_DIFF );
364 ret = brush_region( pdev, interior );
365 DeleteObject( interior );
367 if (outline)
369 if (ret) ret = pen_region( pdev, outline );
370 DeleteObject( outline );
372 HeapFree( GetProcessHeap(), 0, points );
373 return ret;
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] =
380 0, 0x4d, 0x68, 0x7c,
381 0x8c, 0x9a, 0xa7, 0xb2,
382 0xbd, 0xc7, 0xd0, 0xd9,
383 0xe1, 0xe9, 0xf0, 0xf8,
384 0xff
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
392 the ramp table.
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] )
412 int i;
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 );
431 mask->and = 0;
433 if (pdev->dib.bit_count != 1)
434 mask->xor = get_pixel_color( pdev, bg, FALSE );
435 else
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,
446 RECT *bounds )
448 int i;
449 RECT rect, clipped_rect;
450 POINT src_origin;
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,
467 text_color );
468 else
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 )
477 switch (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;
491 default:
492 ERR("Unexpected flags %08x\n", aa_flags);
493 return 0;
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 /***********************************************************************
501 * get_glyph_bitmap
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};
514 int i, x, y;
515 DWORD ret, size;
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;
524 indices[0] = index;
526 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
528 index = indices[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;
572 else if (pad)
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,
585 RECT *bounds )
587 UINT i;
588 DWORD err;
589 GLYPHMETRICS metrics;
590 dib_info glyph_dib;
592 for (i = 0; i < count; i++)
594 err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &glyph_dib );
595 if (err) continue;
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 );
602 if (dx)
604 if (flags & ETO_PDY)
606 x += dx[ i * 2 ];
607 y += dx[ i * 2 + 1];
609 else
610 x += dx[ i ];
612 else
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 )
624 dib_info dib;
625 BOOL got_pixel;
626 COLORREF fg, bg;
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 );
635 visrect.count = 1;
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 );
650 bkgnd_color.and = 0;
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 );
657 return TRUE;
660 /***********************************************************************
661 * dibdrv_ExtTextOut
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;
668 DC *dc;
669 RECT bounds;
670 DWORD text_color;
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 );
693 else
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 );
708 done:
709 add_clipped_bounds( pdev, &bounds, pdev->clip );
710 free_clipped_rects( &clipped_rects );
711 return TRUE;
714 /***********************************************************************
715 * dibdrv_Arc
717 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
718 INT start_x, INT start_y, INT end_x, INT end_y )
720 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
723 /***********************************************************************
724 * dibdrv_ArcTo
726 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
727 INT start_x, INT start_y, INT end_x, INT end_y )
729 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
732 /***********************************************************************
733 * dibdrv_Chord
735 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
736 INT start_x, INT start_y, INT end_x, INT end_y )
738 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
741 /***********************************************************************
742 * dibdrv_Ellipse
744 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
746 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
749 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
751 /* the clip rgn stops the flooding */
752 if (clip && !PtInRegion( clip, x, y )) return FALSE;
754 if (type == FLOODFILLBORDER)
755 return dib->funcs->get_pixel( dib, x, y ) != pixel;
756 else
757 return dib->funcs->get_pixel( dib, x, y ) == pixel;
760 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
762 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
764 RECT next;
766 next.top = row->top + offset;
767 next.bottom = next.top + 1;
768 next.left = next.right = row->left;
769 while (next.right < row->right)
771 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
772 else
774 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
775 fill_row( dib, clip, &next, pixel, type, rgn );
776 next.left = ++next.right;
779 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
780 fill_row( dib, clip, &next, pixel, type, rgn );
783 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
785 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
786 while (row->right < dib->rect.right - dib->rect.left &&
787 is_interior( dib, clip, row->right, row->top, pixel, type))
788 row->right++;
790 add_rect_to_region( rgn, row );
792 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
793 if (row->top < dib->rect.bottom - dib->rect.top - 1)
794 do_next_row( dib, clip, row, 1, pixel, type, rgn );
797 /***********************************************************************
798 * dibdrv_ExtFloodFill
800 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
802 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
803 DWORD pixel = get_pixel_color( pdev, color, FALSE );
804 RECT row;
805 HRGN rgn;
807 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
809 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
811 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
812 row.left = x;
813 row.right = x + 1;
814 row.top = y;
815 row.bottom = y + 1;
817 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
819 add_clipped_bounds( pdev, NULL, rgn );
820 brush_region( pdev, rgn );
822 DeleteObject( rgn );
823 return TRUE;
826 /***********************************************************************
827 * dibdrv_GetNearestColor
829 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
831 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
832 DWORD pixel;
834 TRACE( "(%p, %08x)\n", dev, color );
836 pixel = get_pixel_color( pdev, color, FALSE );
837 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
840 /***********************************************************************
841 * dibdrv_GetPixel
843 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
845 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
846 POINT pt;
847 DWORD pixel;
849 TRACE( "(%p, %d, %d)\n", dev, x, y );
851 pt.x = x;
852 pt.y = y;
853 LPtoDP( dev->hdc, &pt, 1 );
855 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
856 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
857 return CLR_INVALID;
859 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
860 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
863 /***********************************************************************
864 * dibdrv_LineTo
866 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
868 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
869 POINT pts[2];
870 HRGN region = 0;
871 BOOL ret;
873 GetCurrentPositionEx(dev->hdc, pts);
874 pts[1].x = x;
875 pts[1].y = y;
877 LPtoDP(dev->hdc, pts, 2);
879 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
881 reset_dash_origin(pdev);
883 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
884 add_pen_lines_bounds( pdev, 2, pts, region );
886 if (region)
888 ret = pen_region( pdev, region );
889 DeleteObject( region );
891 return ret;
894 /***********************************************************************
895 * get_rop2_from_rop
897 * Returns the binary rop that is equivalent to the provided ternary rop
898 * if the src bits are ignored.
900 static inline INT get_rop2_from_rop(INT rop)
902 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
905 /***********************************************************************
906 * dibdrv_PatBlt
908 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
910 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
911 dib_brush *brush = &pdev->brush;
912 int rop2 = get_rop2_from_rop( rop );
913 struct clipped_rects clipped_rects;
914 DWORD and = 0, xor = 0;
915 BOOL ret = TRUE;
917 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
919 add_clipped_bounds( pdev, &dst->visrect, 0 );
920 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
922 switch (rop2) /* shortcuts for rops that don't involve the brush */
924 case R2_NOT: and = ~0u;
925 /* fall through */
926 case R2_WHITE: xor = ~0u;
927 /* fall through */
928 case R2_BLACK:
929 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
930 /* fall through */
931 case R2_NOP:
932 break;
933 default:
934 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
935 break;
937 free_clipped_rects( &clipped_rects );
938 return ret;
941 /***********************************************************************
942 * dibdrv_PaintRgn
944 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
946 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
947 const WINEREGION *region;
948 int i;
949 RECT rect, bounds;
951 TRACE("%p, %p\n", dev, rgn);
953 reset_bounds( &bounds );
955 region = get_wine_region( rgn );
956 if(!region) return FALSE;
958 for(i = 0; i < region->numRects; i++)
960 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
961 region->rects[i].right, region->rects[i].bottom, FALSE );
962 add_bounds_rect( &bounds, &rect );
963 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
966 release_wine_region( rgn );
967 add_clipped_bounds( pdev, &bounds, pdev->clip );
968 return TRUE;
971 /***********************************************************************
972 * dibdrv_PolyPolygon
974 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
976 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
977 DWORD total, i, pos;
978 BOOL ret = TRUE;
979 POINT *points;
980 HRGN outline = 0, interior = 0;
982 for (i = total = 0; i < polygons; i++)
984 if (counts[i] < 2) return FALSE;
985 total += counts[i];
988 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
989 if (!points) return FALSE;
990 memcpy( points, pt, total * sizeof(*pt) );
991 LPtoDP( dev->hdc, points, total );
993 if (pdev->brush.style != BS_NULL &&
994 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
996 HeapFree( GetProcessHeap(), 0, points );
997 return FALSE;
1000 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1002 /* if not using a region, paint the interior first so the outline can overlap it */
1003 if (interior && !outline)
1005 ret = brush_region( pdev, interior );
1006 DeleteObject( interior );
1007 interior = 0;
1010 for (i = pos = 0; i < polygons; i++)
1012 reset_dash_origin( pdev );
1013 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1014 pos += counts[i];
1016 add_pen_lines_bounds( pdev, total, points, outline );
1018 if (interior)
1020 CombineRgn( interior, interior, outline, RGN_DIFF );
1021 ret = brush_region( pdev, interior );
1022 DeleteObject( interior );
1024 if (outline)
1026 if (ret) ret = pen_region( pdev, outline );
1027 DeleteObject( outline );
1029 HeapFree( GetProcessHeap(), 0, points );
1030 return ret;
1033 /***********************************************************************
1034 * dibdrv_PolyPolyline
1036 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1038 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1039 DWORD total, pos, i;
1040 POINT *points;
1041 BOOL ret = TRUE;
1042 HRGN outline = 0;
1044 for (i = total = 0; i < polylines; i++)
1046 if (counts[i] < 2) return FALSE;
1047 total += counts[i];
1050 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1051 if (!points) return FALSE;
1052 memcpy( points, pt, total * sizeof(*pt) );
1053 LPtoDP( dev->hdc, points, total );
1055 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1057 HeapFree( GetProcessHeap(), 0, points );
1058 return FALSE;
1061 for (i = pos = 0; i < polylines; i++)
1063 reset_dash_origin( pdev );
1064 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1065 pos += counts[i];
1067 add_pen_lines_bounds( pdev, total, points, outline );
1069 if (outline)
1071 ret = pen_region( pdev, outline );
1072 DeleteObject( outline );
1075 HeapFree( GetProcessHeap(), 0, points );
1076 return ret;
1079 /***********************************************************************
1080 * dibdrv_Polygon
1082 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1084 INT counts[1] = { count };
1086 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1089 /***********************************************************************
1090 * dibdrv_Polyline
1092 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1094 DWORD counts[1] = { count };
1096 if (count < 0) return FALSE;
1097 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1100 /***********************************************************************
1101 * dibdrv_Rectangle
1103 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1105 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1106 RECT rect;
1107 POINT pts[4];
1108 BOOL ret;
1109 HRGN outline = 0;
1111 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1113 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1115 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1117 rect.right--;
1118 rect.bottom--;
1119 reset_dash_origin(pdev);
1121 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1123 /* 4 pts going clockwise starting from bottom-right */
1124 pts[0].x = pts[3].x = rect.right;
1125 pts[0].y = pts[1].y = rect.bottom;
1126 pts[1].x = pts[2].x = rect.left;
1127 pts[2].y = pts[3].y = rect.top;
1129 else
1131 /* 4 pts going anti-clockwise starting from top-right */
1132 pts[0].x = pts[3].x = rect.right;
1133 pts[0].y = pts[1].y = rect.top;
1134 pts[1].x = pts[2].x = rect.left;
1135 pts[2].y = pts[3].y = rect.bottom;
1138 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1139 add_pen_lines_bounds( pdev, 4, pts, outline );
1141 if (outline)
1143 if (pdev->brush.style != BS_NULL)
1145 HRGN interior = CreateRectRgnIndirect( &rect );
1147 CombineRgn( interior, interior, outline, RGN_DIFF );
1148 brush_region( pdev, interior );
1149 DeleteObject( interior );
1151 ret = pen_region( pdev, outline );
1152 DeleteObject( outline );
1154 else
1156 rect.left += (pdev->pen_width + 1) / 2;
1157 rect.top += (pdev->pen_width + 1) / 2;
1158 rect.right -= pdev->pen_width / 2;
1159 rect.bottom -= pdev->pen_width / 2;
1160 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1162 return ret;
1165 /***********************************************************************
1166 * dibdrv_RoundRect
1168 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1169 INT ellipse_width, INT ellipse_height )
1171 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1172 RECT rect;
1173 POINT pt[2], *points;
1174 int i, end, count;
1175 BOOL ret = TRUE;
1176 HRGN outline = 0, interior = 0;
1178 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1180 pt[0].x = pt[0].y = 0;
1181 pt[1].x = ellipse_width;
1182 pt[1].y = ellipse_height;
1183 LPtoDP( dev->hdc, pt, 2 );
1184 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1185 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1186 if (ellipse_width <= 2|| ellipse_height <= 2)
1187 return dibdrv_Rectangle( dev, left, top, right, bottom );
1189 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1190 if (!points) return FALSE;
1192 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1194 HeapFree( GetProcessHeap(), 0, points );
1195 return FALSE;
1198 if (pdev->brush.style != BS_NULL &&
1199 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1200 ellipse_width, ellipse_height )))
1202 HeapFree( GetProcessHeap(), 0, points );
1203 if (outline) DeleteObject( outline );
1204 return FALSE;
1207 /* if not using a region, paint the interior first so the outline can overlap it */
1208 if (interior && !outline)
1210 ret = brush_region( pdev, interior );
1211 DeleteObject( interior );
1212 interior = 0;
1215 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1217 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1219 for (i = 0; i < count; i++)
1221 points[i].x = rect.right - ellipse_width + points[i].x;
1222 points[i].y = rect.bottom - ellipse_height + points[i].y;
1225 else
1227 for (i = 0; i < count; i++)
1229 points[i].x = rect.right - ellipse_width + points[i].x;
1230 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1234 /* horizontal symmetry */
1236 end = 2 * count - 1;
1237 /* avoid duplicating the midpoint */
1238 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1239 for (i = 0; i < count; i++)
1241 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1242 points[end - i].y = points[i].y;
1244 count = end + 1;
1246 /* vertical symmetry */
1248 end = 2 * count - 1;
1249 /* avoid duplicating the midpoint */
1250 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1251 for (i = 0; i < count; i++)
1253 points[end - i].x = points[i].x;
1254 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1256 count = end + 1;
1258 reset_dash_origin( pdev );
1259 pdev->pen_lines( pdev, count, points, TRUE, outline );
1260 add_pen_lines_bounds( pdev, count, points, outline );
1262 if (interior)
1264 CombineRgn( interior, interior, outline, RGN_DIFF );
1265 ret = brush_region( pdev, interior );
1266 DeleteObject( interior );
1268 if (outline)
1270 if (ret) ret = pen_region( pdev, outline );
1271 DeleteObject( outline );
1273 HeapFree( GetProcessHeap(), 0, points );
1274 return ret;
1277 /***********************************************************************
1278 * dibdrv_Pie
1280 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1281 INT start_x, INT start_y, INT end_x, INT end_y )
1283 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1286 /***********************************************************************
1287 * dibdrv_SetPixel
1289 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1291 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1292 struct clipped_rects clipped_rects;
1293 RECT rect;
1294 POINT pt;
1295 DWORD pixel;
1297 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1299 pt.x = x;
1300 pt.y = y;
1301 LPtoDP( dev->hdc, &pt, 1 );
1302 rect.left = pt.x;
1303 rect.top = pt.y;
1304 rect.right = rect.left + 1;
1305 rect.bottom = rect.top + 1;
1306 add_clipped_bounds( pdev, &rect, pdev->clip );
1308 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1309 pixel = get_pixel_color( pdev, color, FALSE );
1310 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1312 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1313 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1314 free_clipped_rects( &clipped_rects );
1315 return color;