gdi32: Don't store the default color table in dib_info objects.
[wine/multimedia.git] / dlls / gdi32 / dibdrv / graphics.c
blob835f7d78d0f2851d7e885084432ad2889bdc7951
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 /* paint a region with the brush (note: the region can be modified) */
30 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
32 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
33 return brush_rect( pdev, &pdev->brush, NULL, region, GetROP2( pdev->dev.hdc ));
36 /* paint a region with the pen (note: the region can be modified) */
37 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
39 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
40 return brush_rect( pdev, &pdev->pen_brush, NULL, region, GetROP2( pdev->dev.hdc ));
43 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
45 RECT rect;
47 rect.left = left;
48 rect.top = top;
49 rect.right = right;
50 rect.bottom = bottom;
51 if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
53 /* shift the rectangle so that the right border is included after mirroring */
54 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
55 rect.left--;
56 rect.right--;
58 LPtoDP( hdc, (POINT *)&rect, 2 );
59 if (rect.left > rect.right)
61 int tmp = rect.left;
62 rect.left = rect.right;
63 rect.right = tmp;
65 if (rect.top > rect.bottom)
67 int tmp = rect.top;
68 rect.top = rect.bottom;
69 rect.bottom = tmp;
71 return rect;
74 static BOOL get_pen_device_rect( dibdrv_physdev *dev, RECT *rect, int left, int top, int right, int bottom )
76 *rect = get_device_rect( dev->dev.hdc, left, top, right, bottom, TRUE );
77 if (rect->left == rect->right || rect->top == rect->bottom) return FALSE;
79 if (dev->pen_style == PS_INSIDEFRAME)
81 rect->left += dev->pen_width / 2;
82 rect->top += dev->pen_width / 2;
83 rect->right -= (dev->pen_width - 1) / 2;
84 rect->bottom -= (dev->pen_width - 1) / 2;
86 return TRUE;
89 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
91 const WINEREGION *region;
92 RECT bounds, rect;
93 int width = 0;
95 if (!dev->bounds) return;
96 reset_bounds( &bounds );
98 if (dev->pen_uses_region)
100 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
101 width = dev->pen_width + 2;
102 if (dev->pen_join == PS_JOIN_MITER)
104 width *= 5;
105 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
107 else
109 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4;
110 else width = (width + 1) / 2;
113 /* in case the heuristics are wrong, add the actual region too */
114 if ((region = get_wine_region( rgn )))
116 add_bounds_rect( &bounds, &region->extents );
117 release_wine_region( rgn );
121 while (count-- > 0)
123 rect.left = points->x - width;
124 rect.top = points->y - width;
125 rect.right = points->x + width + 1;
126 rect.bottom = points->y + width + 1;
127 add_bounds_rect( &bounds, &rect );
128 points++;
131 add_clipped_bounds( dev, &bounds, dev->clip );
134 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
135 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
136 static int ellipse_first_quadrant( int width, int height, POINT *data )
138 const int a = width - 1;
139 const int b = height - 1;
140 const INT64 asq = (INT64)8 * a * a;
141 const INT64 bsq = (INT64)8 * b * b;
142 INT64 dx = (INT64)4 * b * b * (1 - a);
143 INT64 dy = (INT64)4 * a * a * (1 + (b % 2));
144 INT64 err = dx + dy + a * a * (b % 2);
145 int pos = 0;
146 POINT pt;
148 pt.x = a;
149 pt.y = height / 2;
151 /* based on an algorithm by Alois Zingl */
153 while (pt.x >= width / 2)
155 INT64 e2 = 2 * err;
156 data[pos++] = pt;
157 if (e2 >= dx)
159 pt.x--;
160 err += dx += bsq;
162 if (e2 <= dy)
164 pt.y++;
165 err += dy += asq;
168 return pos;
171 static int find_intersection( const POINT *points, int x, int y, int count )
173 int i;
175 if (y >= 0)
177 if (x >= 0) /* first quadrant */
179 for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
180 return i;
182 /* second quadrant */
183 for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break;
184 return 2 * count - i;
186 if (x >= 0) /* fourth quadrant */
188 for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break;
189 return 4 * count - i;
191 /* third quadrant */
192 for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break;
193 return 2 * count + i;
196 static int get_arc_points( PHYSDEV dev, const RECT *rect, POINT start, POINT end, POINT *points )
198 int i, pos, count, start_pos, end_pos;
199 int width = rect->right - rect->left;
200 int height = rect->bottom - rect->top;
202 count = ellipse_first_quadrant( width, height, points );
203 for (i = 0; i < count; i++)
205 points[i].x -= width / 2;
206 points[i].y -= height / 2;
208 if (GetArcDirection( dev->hdc ) != AD_CLOCKWISE)
210 start.y = -start.y;
211 end.y = -end.y;
213 start_pos = find_intersection( points, start.x, start.y, count );
214 end_pos = find_intersection( points, end.x, end.y, count );
215 if (end_pos <= start_pos) end_pos += 4 * count;
217 pos = count;
218 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
220 for (i = start_pos; i < end_pos; i++, pos++)
222 switch ((i / count) % 4)
224 case 0:
225 points[pos].x = rect->left + width/2 + points[i % count].x;
226 points[pos].y = rect->top + height/2 + points[i % count].y;
227 break;
228 case 1:
229 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
230 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
231 break;
232 case 2:
233 points[pos].x = rect->left + width/2 - points[i % count].x;
234 points[pos].y = rect->top + height/2 - points[i % count].y;
235 break;
236 case 3:
237 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
238 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
239 break;
243 else
245 for (i = start_pos; i < end_pos; i++, pos++)
247 switch ((i / count) % 4)
249 case 0:
250 points[pos].x = rect->left + width/2 + points[i % count].x;
251 points[pos].y = rect->top + height/2 - points[i % count].y;
252 break;
253 case 1:
254 points[pos].x = rect->left + width/2 - points[count - 1 - i % count].x;
255 points[pos].y = rect->top + height/2 - points[count - 1 - i % count].y;
256 break;
257 case 2:
258 points[pos].x = rect->left + width/2 - points[i % count].x;
259 points[pos].y = rect->top + height/2 + points[i % count].y;
260 break;
261 case 3:
262 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
263 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
264 break;
269 memmove( points, points + count, (pos - count) * sizeof(POINT) );
270 return pos - count;
273 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
274 static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
275 INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines )
277 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
278 RECT rect;
279 POINT pt[2], *points;
280 int width, height, count;
281 BOOL ret = TRUE;
282 HRGN outline = 0, interior = 0;
284 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
286 width = rect.right - rect.left;
287 height = rect.bottom - rect.top;
289 pt[0].x = start_x;
290 pt[0].y = start_y;
291 pt[1].x = end_x;
292 pt[1].y = end_y;
293 LPtoDP( dev->hdc, pt, 2 );
294 /* make them relative to the ellipse center */
295 pt[0].x -= left + width / 2;
296 pt[0].y -= top + height / 2;
297 pt[1].x -= left + width / 2;
298 pt[1].y -= top + height / 2;
300 points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) );
301 if (!points) return FALSE;
303 if (extra_lines == -1)
305 GetCurrentPositionEx( dev->hdc, points );
306 LPtoDP( dev->hdc, points, 1 );
307 count = 1 + get_arc_points( dev, &rect, pt[0], pt[1], points + 1 );
309 else count = get_arc_points( dev, &rect, pt[0], pt[1], points );
311 if (extra_lines == 2)
313 points[count].x = rect.left + width / 2;
314 points[count].y = rect.top + height / 2;
315 count++;
317 if (count < 2)
319 HeapFree( GetProcessHeap(), 0, points );
320 return TRUE;
323 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
325 HeapFree( GetProcessHeap(), 0, points );
326 return FALSE;
329 if (pdev->brush.style != BS_NULL && extra_lines > 0 &&
330 !(interior = CreatePolygonRgn( points, count, WINDING )))
332 HeapFree( GetProcessHeap(), 0, points );
333 if (outline) DeleteObject( outline );
334 return FALSE;
337 /* if not using a region, paint the interior first so the outline can overlap it */
338 if (interior && !outline)
340 ret = brush_region( pdev, interior );
341 DeleteObject( interior );
342 interior = 0;
345 reset_dash_origin( pdev );
346 pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
347 add_pen_lines_bounds( pdev, count, points, outline );
349 if (interior)
351 CombineRgn( interior, interior, outline, RGN_DIFF );
352 ret = brush_region( pdev, interior );
353 DeleteObject( interior );
355 if (outline)
357 if (ret) ret = pen_region( pdev, outline );
358 DeleteObject( outline );
360 HeapFree( GetProcessHeap(), 0, points );
361 return ret;
364 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
365 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
366 static const BYTE ramp[17] =
368 0, 0x4d, 0x68, 0x7c,
369 0x8c, 0x9a, 0xa7, 0xb2,
370 0xbd, 0xc7, 0xd0, 0xd9,
371 0xe1, 0xe9, 0xf0, 0xf8,
372 0xff
375 /* For a give text-color component and a glyph level, calculate the
376 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
377 components respectively.
379 The minimum is a linear interpolation between 0 and the value in
380 the ramp table.
382 The maximum is a linear interpolation between the value from the
383 ramp table read in reverse and 0xff.
385 To find the resulting pixel intensity, we note that if the text and
386 bkgnd intensities are the same then the result must be that
387 intensity. Otherwise we linearly interpolate between either the
388 min or the max value and this intermediate value depending on which
389 side of the inequality we lie.
392 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
394 *min_comp = (ramp[aa] * text_comp) / 0xff;
395 *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
398 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
400 int i;
402 for (i = 0; i < 17; i++)
404 get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
405 get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
406 get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
410 /**********************************************************************
411 * get_text_bkgnd_masks
413 * See the comment above get_pen_bkgnd_masks
415 static inline void get_text_bkgnd_masks( dibdrv_physdev *pdev, rop_mask *mask )
417 COLORREF bg = GetBkColor( pdev->dev.hdc );
419 mask->and = 0;
421 if (pdev->dib.bit_count != 1)
422 mask->xor = get_pixel_color( pdev, bg, FALSE );
423 else
425 COLORREF fg = GetTextColor( pdev->dev.hdc );
426 mask->xor = get_pixel_color( pdev, fg, TRUE );
427 if (fg != bg) mask->xor = ~mask->xor;
431 static void draw_glyph( dibdrv_physdev *pdev, const POINT *origin, const GLYPHMETRICS *metrics,
432 const struct gdi_image_bits *image, DWORD text_color,
433 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
434 RECT *bounds )
436 int i;
437 RECT rect, clipped_rect;
438 POINT src_origin;
439 dib_info glyph_dib;
441 glyph_dib.bit_count = 8;
442 glyph_dib.width = metrics->gmBlackBoxX;
443 glyph_dib.height = metrics->gmBlackBoxY;
444 glyph_dib.rect.left = 0;
445 glyph_dib.rect.top = 0;
446 glyph_dib.rect.right = metrics->gmBlackBoxX;
447 glyph_dib.rect.bottom = metrics->gmBlackBoxY;
448 glyph_dib.stride = get_dib_stride( metrics->gmBlackBoxX, 8 );
449 glyph_dib.bits = *image;
451 rect.left = origin->x + metrics->gmptGlyphOrigin.x;
452 rect.top = origin->y - metrics->gmptGlyphOrigin.y;
453 rect.right = rect.left + metrics->gmBlackBoxX;
454 rect.bottom = rect.top + metrics->gmBlackBoxY;
455 add_bounds_rect( bounds, &rect );
457 for (i = 0; i < clipped_rects->count; i++)
459 if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
461 src_origin.x = clipped_rect.left - rect.left;
462 src_origin.y = clipped_rect.top - rect.top;
464 pdev->dib.funcs->draw_glyph( &pdev->dib, &clipped_rect, &glyph_dib, &src_origin,
465 text_color, ranges );
470 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
471 static const int padding[4] = {0, 3, 2, 1};
473 /***********************************************************************
474 * get_glyph_bitmap
476 * Retrieve a 17-level bitmap for the appropriate glyph.
478 * For non-antialiased bitmaps convert them to the 17-level format
479 * using only values 0 or 16.
481 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
482 struct gdi_image_bits *image )
484 UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
485 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
486 UINT indices[3] = {0, 0, 0x20};
487 int i, x, y;
488 DWORD ret, size;
489 BYTE *buf, *dst, *src;
490 int pad, stride;
492 image->ptr = NULL;
493 image->is_copy = FALSE;
494 image->free = free_heap_bits;
495 image->param = NULL;
497 indices[0] = index;
499 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
501 index = indices[i];
502 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
503 if (ret != GDI_ERROR) break;
506 if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
507 if (!ret) return ERROR_SUCCESS; /* empty glyph */
509 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
510 pad = padding[ metrics->gmBlackBoxX % 4 ];
511 stride = get_dib_stride( metrics->gmBlackBoxX, 8 );
512 size = metrics->gmBlackBoxY * stride;
514 buf = HeapAlloc( GetProcessHeap(), 0, size );
515 if (!buf) return ERROR_OUTOFMEMORY;
517 ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
518 if (ret == GDI_ERROR)
520 HeapFree( GetProcessHeap(), 0, buf );
521 return ERROR_NOT_FOUND;
524 if (aa_flags == GGO_BITMAP)
526 for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
528 src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
529 dst = buf + y * stride;
531 if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
533 for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
534 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
537 else if (pad)
539 for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
540 memset( dst + metrics->gmBlackBoxX, 0, pad );
543 image->ptr = buf;
544 return ERROR_SUCCESS;
547 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
548 struct bitblt_coords *src, INT x, INT y, UINT flags,
549 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
551 dib_info dib;
552 UINT i;
553 DWORD err;
554 BOOL got_pixel;
555 COLORREF fg, bg;
556 DWORD fg_pixel, bg_pixel;
557 struct intensity_range glyph_intensities[17];
559 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
561 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
563 fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
564 if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
566 get_aa_ranges( fg, glyph_intensities );
568 if (flags & ETO_OPAQUE)
570 rop_mask bkgnd_color;
572 bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
573 if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
575 bkgnd_color.and = 0;
576 bkgnd_color.xor = bg_pixel;
577 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
580 for (i = 0; i < count; i++)
582 GLYPHMETRICS metrics;
583 struct gdi_image_bits image;
585 err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &image );
586 if (err) continue;
588 if (image.ptr)
590 RECT rect, clipped_rect;
591 POINT src_origin;
592 dib_info glyph_dib;
594 glyph_dib.bit_count = 8;
595 glyph_dib.width = metrics.gmBlackBoxX;
596 glyph_dib.height = metrics.gmBlackBoxY;
597 glyph_dib.rect.left = 0;
598 glyph_dib.rect.top = 0;
599 glyph_dib.rect.right = metrics.gmBlackBoxX;
600 glyph_dib.rect.bottom = metrics.gmBlackBoxY;
601 glyph_dib.stride = get_dib_stride( metrics.gmBlackBoxX, 8 );
602 glyph_dib.bits = image;
604 rect.left = x + metrics.gmptGlyphOrigin.x;
605 rect.top = y - metrics.gmptGlyphOrigin.y;
606 rect.right = rect.left + metrics.gmBlackBoxX;
607 rect.bottom = rect.top + metrics.gmBlackBoxY;
609 if (intersect_rect( &clipped_rect, &rect, &src->visrect ))
611 src_origin.x = clipped_rect.left - rect.left;
612 src_origin.y = clipped_rect.top - rect.top;
614 dib.funcs->draw_glyph( &dib, &clipped_rect, &glyph_dib, &src_origin,
615 fg_pixel, glyph_intensities );
618 if (image.free) image.free( &image );
620 if (dx)
622 if (flags & ETO_PDY)
624 x += dx[ i * 2 ];
625 y += dx[ i * 2 + 1];
627 else
628 x += dx[ i ];
630 else
632 x += metrics.gmCellIncX;
633 y += metrics.gmCellIncY;
636 return TRUE;
639 /***********************************************************************
640 * dibdrv_ExtTextOut
642 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
643 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
645 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
646 struct clipped_rects clipped_rects;
647 UINT aa_flags, i;
648 POINT origin;
649 RECT bounds;
650 DWORD text_color, err;
651 struct intensity_range ranges[17];
653 init_clipped_rects( &clipped_rects );
654 reset_bounds( &bounds );
656 if (flags & ETO_OPAQUE)
658 rop_mask bkgnd_color;
659 get_text_bkgnd_masks( pdev, &bkgnd_color );
660 add_bounds_rect( &bounds, rect );
661 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
662 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
663 bkgnd_color.and, bkgnd_color.xor );
666 if (count == 0) goto done;
668 if (flags & ETO_CLIPPED)
670 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
671 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
673 else
675 free_clipped_rects( &clipped_rects );
676 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
678 if (!clipped_rects.count) return TRUE;
680 text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
681 get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
683 aa_flags = get_font_aa_flags( dev->hdc );
684 origin.x = x;
685 origin.y = y;
686 for (i = 0; i < count; i++)
688 GLYPHMETRICS metrics;
689 struct gdi_image_bits image;
691 err = get_glyph_bitmap( dev->hdc, (UINT)str[i], aa_flags, &metrics, &image );
692 if (err) continue;
694 if (image.ptr)
695 draw_glyph( pdev, &origin, &metrics, &image, text_color, ranges, &clipped_rects, &bounds );
697 if (image.free) image.free( &image );
699 if (dx)
701 if (flags & ETO_PDY)
703 origin.x += dx[ i * 2 ];
704 origin.y += dx[ i * 2 + 1];
706 else
707 origin.x += dx[ i ];
709 else
711 origin.x += metrics.gmCellIncX;
712 origin.y += metrics.gmCellIncY;
715 add_clipped_bounds( pdev, &bounds, pdev->clip );
717 done:
718 free_clipped_rects( &clipped_rects );
719 return TRUE;
722 /***********************************************************************
723 * dibdrv_Arc
725 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
726 INT start_x, INT start_y, INT end_x, INT end_y )
728 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
731 /***********************************************************************
732 * dibdrv_ArcTo
734 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
735 INT start_x, INT start_y, INT end_x, INT end_y )
737 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
740 /***********************************************************************
741 * dibdrv_Chord
743 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
744 INT start_x, INT start_y, INT end_x, INT end_y )
746 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
749 /***********************************************************************
750 * dibdrv_Ellipse
752 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
754 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
757 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
759 /* the clip rgn stops the flooding */
760 if (clip && !PtInRegion( clip, x, y )) return FALSE;
762 if (type == FLOODFILLBORDER)
763 return dib->funcs->get_pixel( dib, x, y ) != pixel;
764 else
765 return dib->funcs->get_pixel( dib, x, y ) == pixel;
768 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
770 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
772 RECT next;
774 next.top = row->top + offset;
775 next.bottom = next.top + 1;
776 next.left = next.right = row->left;
777 while (next.right < row->right)
779 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
780 else
782 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
783 fill_row( dib, clip, &next, pixel, type, rgn );
784 next.left = ++next.right;
787 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
788 fill_row( dib, clip, &next, pixel, type, rgn );
791 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
793 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
794 while (row->right < dib->rect.right - dib->rect.left &&
795 is_interior( dib, clip, row->right, row->top, pixel, type))
796 row->right++;
798 add_rect_to_region( rgn, row );
800 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
801 if (row->top < dib->rect.bottom - dib->rect.top - 1)
802 do_next_row( dib, clip, row, 1, pixel, type, rgn );
805 /***********************************************************************
806 * dibdrv_ExtFloodFill
808 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
810 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
811 DWORD pixel = get_pixel_color( pdev, color, FALSE );
812 RECT row;
813 HRGN rgn;
815 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
817 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
819 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
820 row.left = x;
821 row.right = x + 1;
822 row.top = y;
823 row.bottom = y + 1;
825 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
827 add_clipped_bounds( pdev, NULL, rgn );
828 brush_region( pdev, rgn );
830 DeleteObject( rgn );
831 return TRUE;
834 /***********************************************************************
835 * dibdrv_GetNearestColor
837 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
839 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
840 DWORD pixel;
842 TRACE( "(%p, %08x)\n", dev, color );
844 pixel = get_pixel_color( pdev, color, FALSE );
845 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
848 /***********************************************************************
849 * dibdrv_GetPixel
851 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
853 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
854 POINT pt;
855 DWORD pixel;
857 TRACE( "(%p, %d, %d)\n", dev, x, y );
859 pt.x = x;
860 pt.y = y;
861 LPtoDP( dev->hdc, &pt, 1 );
863 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
864 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
865 return CLR_INVALID;
867 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
868 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
871 /***********************************************************************
872 * dibdrv_LineTo
874 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
876 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
877 POINT pts[2];
878 HRGN region = 0;
879 BOOL ret;
881 GetCurrentPositionEx(dev->hdc, pts);
882 pts[1].x = x;
883 pts[1].y = y;
885 LPtoDP(dev->hdc, pts, 2);
887 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
889 reset_dash_origin(pdev);
891 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
892 add_pen_lines_bounds( pdev, 2, pts, region );
894 if (region)
896 ret = pen_region( pdev, region );
897 DeleteObject( region );
899 return ret;
902 /***********************************************************************
903 * get_rop2_from_rop
905 * Returns the binary rop that is equivalent to the provided ternary rop
906 * if the src bits are ignored.
908 static inline INT get_rop2_from_rop(INT rop)
910 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
913 /***********************************************************************
914 * dibdrv_PatBlt
916 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
918 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
920 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
922 add_clipped_bounds( pdev, &dst->visrect, 0 );
923 return brush_rect( pdev, &pdev->brush, &dst->visrect, pdev->clip, get_rop2_from_rop(rop) );
926 /***********************************************************************
927 * dibdrv_PaintRgn
929 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
931 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
932 const WINEREGION *region;
933 int i;
934 RECT rect, bounds;
936 TRACE("%p, %p\n", dev, rgn);
938 reset_bounds( &bounds );
940 region = get_wine_region( rgn );
941 if(!region) return FALSE;
943 for(i = 0; i < region->numRects; i++)
945 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
946 region->rects[i].right, region->rects[i].bottom, FALSE );
947 add_bounds_rect( &bounds, &rect );
948 brush_rect( pdev, &pdev->brush, &rect, pdev->clip, GetROP2( dev->hdc ) );
951 release_wine_region( rgn );
952 add_clipped_bounds( pdev, &bounds, pdev->clip );
953 return TRUE;
956 /***********************************************************************
957 * dibdrv_PolyPolygon
959 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
961 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
962 DWORD total, i, pos;
963 BOOL ret = TRUE;
964 POINT *points;
965 HRGN outline = 0, interior = 0;
967 for (i = total = 0; i < polygons; i++)
969 if (counts[i] < 2) return FALSE;
970 total += counts[i];
973 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
974 if (!points) return FALSE;
975 memcpy( points, pt, total * sizeof(*pt) );
976 LPtoDP( dev->hdc, points, total );
978 if (pdev->brush.style != BS_NULL &&
979 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
981 HeapFree( GetProcessHeap(), 0, points );
982 return FALSE;
985 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
987 /* if not using a region, paint the interior first so the outline can overlap it */
988 if (interior && !outline)
990 ret = brush_region( pdev, interior );
991 DeleteObject( interior );
992 interior = 0;
995 for (i = pos = 0; i < polygons; i++)
997 reset_dash_origin( pdev );
998 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
999 pos += counts[i];
1001 add_pen_lines_bounds( pdev, total, points, outline );
1003 if (interior)
1005 CombineRgn( interior, interior, outline, RGN_DIFF );
1006 ret = brush_region( pdev, interior );
1007 DeleteObject( interior );
1009 if (outline)
1011 if (ret) ret = pen_region( pdev, outline );
1012 DeleteObject( outline );
1014 HeapFree( GetProcessHeap(), 0, points );
1015 return ret;
1018 /***********************************************************************
1019 * dibdrv_PolyPolyline
1021 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1023 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1024 DWORD total, pos, i;
1025 POINT *points;
1026 BOOL ret = TRUE;
1027 HRGN outline = 0;
1029 for (i = total = 0; i < polylines; i++)
1031 if (counts[i] < 2) return FALSE;
1032 total += counts[i];
1035 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1036 if (!points) return FALSE;
1037 memcpy( points, pt, total * sizeof(*pt) );
1038 LPtoDP( dev->hdc, points, total );
1040 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1042 HeapFree( GetProcessHeap(), 0, points );
1043 return FALSE;
1046 for (i = pos = 0; i < polylines; i++)
1048 reset_dash_origin( pdev );
1049 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1050 pos += counts[i];
1052 add_pen_lines_bounds( pdev, total, points, outline );
1054 if (outline)
1056 ret = pen_region( pdev, outline );
1057 DeleteObject( outline );
1060 HeapFree( GetProcessHeap(), 0, points );
1061 return ret;
1064 /***********************************************************************
1065 * dibdrv_Polygon
1067 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1069 INT counts[1] = { count };
1071 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1074 /***********************************************************************
1075 * dibdrv_Polyline
1077 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1079 DWORD counts[1] = { count };
1081 if (count < 0) return FALSE;
1082 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1085 /***********************************************************************
1086 * dibdrv_Rectangle
1088 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1090 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1091 RECT rect;
1092 POINT pts[4];
1093 BOOL ret;
1094 HRGN outline = 0;
1096 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1098 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1100 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1102 rect.right--;
1103 rect.bottom--;
1104 reset_dash_origin(pdev);
1106 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1108 /* 4 pts going clockwise starting from bottom-right */
1109 pts[0].x = pts[3].x = rect.right;
1110 pts[0].y = pts[1].y = rect.bottom;
1111 pts[1].x = pts[2].x = rect.left;
1112 pts[2].y = pts[3].y = rect.top;
1114 else
1116 /* 4 pts going anti-clockwise starting from top-right */
1117 pts[0].x = pts[3].x = rect.right;
1118 pts[0].y = pts[1].y = rect.top;
1119 pts[1].x = pts[2].x = rect.left;
1120 pts[2].y = pts[3].y = rect.bottom;
1123 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1124 add_pen_lines_bounds( pdev, 4, pts, outline );
1126 if (outline)
1128 if (pdev->brush.style != BS_NULL)
1130 HRGN interior = CreateRectRgnIndirect( &rect );
1132 CombineRgn( interior, interior, outline, RGN_DIFF );
1133 brush_region( pdev, interior );
1134 DeleteObject( interior );
1136 ret = pen_region( pdev, outline );
1137 DeleteObject( outline );
1139 else
1141 rect.left += (pdev->pen_width + 1) / 2;
1142 rect.top += (pdev->pen_width + 1) / 2;
1143 rect.right -= pdev->pen_width / 2;
1144 rect.bottom -= pdev->pen_width / 2;
1145 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip, GetROP2(dev->hdc) );
1147 return ret;
1150 /***********************************************************************
1151 * dibdrv_RoundRect
1153 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1154 INT ellipse_width, INT ellipse_height )
1156 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1157 RECT rect;
1158 POINT pt[2], *points;
1159 int i, end, count;
1160 BOOL ret = TRUE;
1161 HRGN outline = 0, interior = 0;
1163 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1165 pt[0].x = pt[0].y = 0;
1166 pt[1].x = ellipse_width;
1167 pt[1].y = ellipse_height;
1168 LPtoDP( dev->hdc, pt, 2 );
1169 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1170 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1171 if (ellipse_width <= 2|| ellipse_height <= 2)
1172 return dibdrv_Rectangle( dev, left, top, right, bottom );
1174 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1175 if (!points) return FALSE;
1177 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1179 HeapFree( GetProcessHeap(), 0, points );
1180 return FALSE;
1183 if (pdev->brush.style != BS_NULL &&
1184 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1185 ellipse_width, ellipse_height )))
1187 HeapFree( GetProcessHeap(), 0, points );
1188 if (outline) DeleteObject( outline );
1189 return FALSE;
1192 /* if not using a region, paint the interior first so the outline can overlap it */
1193 if (interior && !outline)
1195 ret = brush_region( pdev, interior );
1196 DeleteObject( interior );
1197 interior = 0;
1200 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1202 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1204 for (i = 0; i < count; i++)
1206 points[i].x = rect.right - ellipse_width + points[i].x;
1207 points[i].y = rect.bottom - ellipse_height + points[i].y;
1210 else
1212 for (i = 0; i < count; i++)
1214 points[i].x = rect.right - ellipse_width + points[i].x;
1215 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1219 /* horizontal symmetry */
1221 end = 2 * count - 1;
1222 /* avoid duplicating the midpoint */
1223 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1224 for (i = 0; i < count; i++)
1226 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1227 points[end - i].y = points[i].y;
1229 count = end + 1;
1231 /* vertical symmetry */
1233 end = 2 * count - 1;
1234 /* avoid duplicating the midpoint */
1235 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1236 for (i = 0; i < count; i++)
1238 points[end - i].x = points[i].x;
1239 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1241 count = end + 1;
1243 reset_dash_origin( pdev );
1244 pdev->pen_lines( pdev, count, points, TRUE, outline );
1245 add_pen_lines_bounds( pdev, count, points, outline );
1247 if (interior)
1249 CombineRgn( interior, interior, outline, RGN_DIFF );
1250 ret = brush_region( pdev, interior );
1251 DeleteObject( interior );
1253 if (outline)
1255 if (ret) ret = pen_region( pdev, outline );
1256 DeleteObject( outline );
1258 HeapFree( GetProcessHeap(), 0, points );
1259 return ret;
1262 /***********************************************************************
1263 * dibdrv_Pie
1265 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1266 INT start_x, INT start_y, INT end_x, INT end_y )
1268 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1271 /***********************************************************************
1272 * dibdrv_SetPixel
1274 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1276 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1277 struct clipped_rects clipped_rects;
1278 RECT rect;
1279 POINT pt;
1280 DWORD pixel;
1282 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1284 pt.x = x;
1285 pt.y = y;
1286 LPtoDP( dev->hdc, &pt, 1 );
1287 rect.left = pt.x;
1288 rect.top = pt.y;
1289 rect.right = rect.left + 1;
1290 rect.bottom = rect.top + 1;
1291 add_clipped_bounds( pdev, &rect, pdev->clip );
1293 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1294 pixel = get_pixel_color( pdev, color, FALSE );
1295 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1297 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1298 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1299 free_clipped_rects( &clipped_rects );
1300 return color;