winecfg: Widen "Folder" column to accommodate Catalan translation.
[wine.git] / dlls / gdi32 / dibdrv / graphics.c
blob2c865c02250014fea4e0574089dd35ba7570af62
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/unicode.h"
26 #include "wine/debug.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(dib);
30 struct cached_glyph
32 GLYPHMETRICS metrics;
33 BYTE bits[1];
36 enum glyph_type
38 GLYPH_INDEX,
39 GLYPH_WCHAR,
40 GLYPH_NBTYPES
43 #define GLYPH_CACHE_PAGE_SIZE 0x100
44 #define GLYPH_CACHE_PAGES (0x10000 / GLYPH_CACHE_PAGE_SIZE)
46 struct cached_font
48 struct list entry;
49 LONG ref;
50 DWORD hash;
51 LOGFONTW lf;
52 XFORM xform;
53 UINT aa_flags;
54 struct cached_glyph **glyphs[GLYPH_NBTYPES][GLYPH_CACHE_PAGES];
57 static struct list font_cache = LIST_INIT( font_cache );
59 static CRITICAL_SECTION font_cache_cs;
60 static CRITICAL_SECTION_DEBUG critsect_debug =
62 0, 0, &font_cache_cs,
63 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
64 0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_cs") }
66 static CRITICAL_SECTION font_cache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
69 static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
71 struct clipped_rects clipped_rects;
72 BOOL ret;
74 if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
75 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects,
76 GetROP2( pdev->dev.hdc ));
77 free_clipped_rects( &clipped_rects );
78 return ret;
81 /* paint a region with the brush (note: the region can be modified) */
82 static BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
84 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
85 return brush_rect( pdev, &pdev->brush, NULL, region );
88 /* paint a region with the pen (note: the region can be modified) */
89 static BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
91 if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
92 return brush_rect( pdev, &pdev->pen_brush, NULL, region );
95 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
97 RECT rect;
99 rect.left = left;
100 rect.top = top;
101 rect.right = right;
102 rect.bottom = bottom;
103 if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
105 /* shift the rectangle so that the right border is included after mirroring */
106 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
107 rect.left--;
108 rect.right--;
110 LPtoDP( hdc, (POINT *)&rect, 2 );
111 order_rect( &rect );
112 return rect;
115 static BOOL get_pen_device_rect( dibdrv_physdev *dev, RECT *rect, int left, int top, int right, int bottom )
117 *rect = get_device_rect( dev->dev.hdc, left, top, right, bottom, TRUE );
118 if (rect->left == rect->right || rect->top == rect->bottom) return FALSE;
120 if (dev->pen_style == PS_INSIDEFRAME)
122 rect->left += dev->pen_width / 2;
123 rect->top += dev->pen_width / 2;
124 rect->right -= (dev->pen_width - 1) / 2;
125 rect->bottom -= (dev->pen_width - 1) / 2;
127 return TRUE;
130 static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn )
132 const WINEREGION *region;
133 RECT bounds, rect;
134 int width = 0;
136 if (!dev->bounds) return;
137 reset_bounds( &bounds );
139 if (dev->pen_uses_region)
141 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
142 width = dev->pen_width + 2;
143 if (dev->pen_join == PS_JOIN_MITER)
145 width *= 5;
146 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
148 else
150 if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4;
151 else width = (width + 1) / 2;
154 /* in case the heuristics are wrong, add the actual region too */
155 if ((region = get_wine_region( rgn )))
157 add_bounds_rect( &bounds, &region->extents );
158 release_wine_region( rgn );
162 while (count-- > 0)
164 rect.left = points->x - width;
165 rect.top = points->y - width;
166 rect.right = points->x + width + 1;
167 rect.bottom = points->y + width + 1;
168 add_bounds_rect( &bounds, &rect );
169 points++;
172 add_clipped_bounds( dev, &bounds, dev->clip );
175 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
176 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
177 static int ellipse_first_quadrant( int width, int height, POINT *data )
179 const int a = width - 1;
180 const int b = height - 1;
181 const INT64 asq = (INT64)8 * a * a;
182 const INT64 bsq = (INT64)8 * b * b;
183 INT64 dx = (INT64)4 * b * b * (1 - a);
184 INT64 dy = (INT64)4 * a * a * (1 + (b % 2));
185 INT64 err = dx + dy + a * a * (b % 2);
186 int pos = 0;
187 POINT pt;
189 pt.x = a;
190 pt.y = height / 2;
192 /* based on an algorithm by Alois Zingl */
194 while (pt.x >= width / 2)
196 INT64 e2 = 2 * err;
197 data[pos++] = pt;
198 if (e2 >= dx)
200 pt.x--;
201 err += dx += bsq;
203 if (e2 <= dy)
205 pt.y++;
206 err += dy += asq;
209 return pos;
212 static int find_intersection( const POINT *points, int x, int y, int count )
214 int i;
216 if (y >= 0)
218 if (x >= 0) /* first quadrant */
220 for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break;
221 return i;
223 /* second quadrant */
224 for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break;
225 return 2 * count - i;
227 if (x >= 0) /* fourth quadrant */
229 for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break;
230 return 4 * count - i;
232 /* third quadrant */
233 for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break;
234 return 2 * count + i;
237 static int get_arc_points( PHYSDEV dev, const RECT *rect, POINT start, POINT end, POINT *points )
239 int i, pos, count, start_pos, end_pos;
240 int width = rect->right - rect->left;
241 int height = rect->bottom - rect->top;
243 count = ellipse_first_quadrant( width, height, points );
244 for (i = 0; i < count; i++)
246 points[i].x -= width / 2;
247 points[i].y -= height / 2;
249 if (GetArcDirection( dev->hdc ) != AD_CLOCKWISE)
251 start.y = -start.y;
252 end.y = -end.y;
254 start_pos = find_intersection( points, start.x, start.y, count );
255 end_pos = find_intersection( points, end.x, end.y, count );
256 if (end_pos <= start_pos) end_pos += 4 * count;
258 pos = count;
259 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
261 for (i = start_pos; i < end_pos; i++, pos++)
263 switch ((i / count) % 4)
265 case 0:
266 points[pos].x = rect->left + width/2 + points[i % count].x;
267 points[pos].y = rect->top + height/2 + points[i % count].y;
268 break;
269 case 1:
270 points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x;
271 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
272 break;
273 case 2:
274 points[pos].x = rect->right-1 - width/2 - points[i % count].x;
275 points[pos].y = rect->bottom-1 - height/2 - points[i % count].y;
276 break;
277 case 3:
278 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
279 points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y;
280 break;
284 else
286 for (i = start_pos; i < end_pos; i++, pos++)
288 switch ((i / count) % 4)
290 case 0:
291 points[pos].x = rect->left + width/2 + points[i % count].x;
292 points[pos].y = rect->bottom-1 - height/2 - points[i % count].y;
293 break;
294 case 1:
295 points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x;
296 points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y;
297 break;
298 case 2:
299 points[pos].x = rect->right-1 - width/2 - points[i % count].x;
300 points[pos].y = rect->top + height/2 + points[i % count].y;
301 break;
302 case 3:
303 points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x;
304 points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y;
305 break;
310 memmove( points, points + count, (pos - count) * sizeof(POINT) );
311 return pos - count;
314 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
315 static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
316 INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines )
318 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
319 RECT rect;
320 POINT pt[2], *points;
321 int width, height, count;
322 BOOL ret = TRUE;
323 HRGN outline = 0, interior = 0;
325 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
327 width = rect.right - rect.left;
328 height = rect.bottom - rect.top;
330 pt[0].x = start_x;
331 pt[0].y = start_y;
332 pt[1].x = end_x;
333 pt[1].y = end_y;
334 LPtoDP( dev->hdc, pt, 2 );
335 /* make them relative to the ellipse center */
336 pt[0].x -= rect.left + width / 2;
337 pt[0].y -= rect.top + height / 2;
338 pt[1].x -= rect.left + width / 2;
339 pt[1].y -= rect.top + height / 2;
341 points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) );
342 if (!points) return FALSE;
344 if (extra_lines == -1)
346 GetCurrentPositionEx( dev->hdc, points );
347 LPtoDP( dev->hdc, points, 1 );
348 count = 1 + get_arc_points( dev, &rect, pt[0], pt[1], points + 1 );
350 else count = get_arc_points( dev, &rect, pt[0], pt[1], points );
352 if (extra_lines == 2)
354 points[count].x = rect.left + width / 2;
355 points[count].y = rect.top + height / 2;
356 count++;
358 if (count < 2)
360 HeapFree( GetProcessHeap(), 0, points );
361 return TRUE;
364 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
366 HeapFree( GetProcessHeap(), 0, points );
367 return FALSE;
370 if (pdev->brush.style != BS_NULL && extra_lines > 0 &&
371 !(interior = CreatePolygonRgn( points, count, WINDING )))
373 HeapFree( GetProcessHeap(), 0, points );
374 if (outline) DeleteObject( outline );
375 return FALSE;
378 /* if not using a region, paint the interior first so the outline can overlap it */
379 if (interior && !outline)
381 ret = brush_region( pdev, interior );
382 DeleteObject( interior );
383 interior = 0;
386 reset_dash_origin( pdev );
387 pdev->pen_lines( pdev, count, points, extra_lines > 0, outline );
388 add_pen_lines_bounds( pdev, count, points, outline );
390 if (interior)
392 CombineRgn( interior, interior, outline, RGN_DIFF );
393 ret = brush_region( pdev, interior );
394 DeleteObject( interior );
396 if (outline)
398 if (ret) ret = pen_region( pdev, outline );
399 DeleteObject( outline );
401 HeapFree( GetProcessHeap(), 0, points );
402 return ret;
405 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
406 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
407 static const BYTE ramp[17] =
409 0, 0x4d, 0x68, 0x7c,
410 0x8c, 0x9a, 0xa7, 0xb2,
411 0xbd, 0xc7, 0xd0, 0xd9,
412 0xe1, 0xe9, 0xf0, 0xf8,
413 0xff
416 /* For a give text-color component and a glyph level, calculate the
417 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
418 components respectively.
420 The minimum is a linear interpolation between 0 and the value in
421 the ramp table.
423 The maximum is a linear interpolation between the value from the
424 ramp table read in reverse and 0xff.
426 To find the resulting pixel intensity, we note that if the text and
427 bkgnd intensities are the same then the result must be that
428 intensity. Otherwise we linearly interpolate between either the
429 min or the max value and this intermediate value depending on which
430 side of the inequality we lie.
433 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
435 *min_comp = (ramp[aa] * text_comp) / 0xff;
436 *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
439 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
441 int i;
443 for (i = 0; i < 17; i++)
445 get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
446 get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
447 get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
451 static DWORD font_cache_hash( struct cached_font *font )
453 DWORD hash = 0, *ptr, two_chars;
454 WORD *pwc;
455 int i;
457 hash ^= font->aa_flags;
458 for(i = 0, ptr = (DWORD*)&font->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
459 hash ^= *ptr;
460 for(i = 0, ptr = (DWORD*)&font->lf; i < 7; i++, ptr++)
461 hash ^= *ptr;
462 for(i = 0, ptr = (DWORD*)font->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
463 two_chars = *ptr;
464 pwc = (WCHAR *)&two_chars;
465 if (!*pwc) break;
466 *pwc = toupperW(*pwc);
467 pwc++;
468 *pwc = toupperW(*pwc);
469 hash ^= two_chars;
470 if (!*pwc) break;
472 return hash;
475 static int font_cache_cmp( const struct cached_font *p1, const struct cached_font *p2 )
477 int ret = p1->hash - p2->hash;
478 if (!ret) ret = p1->aa_flags - p2->aa_flags;
479 if (!ret) ret = memcmp( &p1->xform, &p2->xform, sizeof(p1->xform) );
480 if (!ret) ret = memcmp( &p1->lf, &p2->lf, FIELD_OFFSET( LOGFONTW, lfFaceName ));
481 if (!ret) ret = strcmpiW( p1->lf.lfFaceName, p2->lf.lfFaceName );
482 return ret;
485 static struct cached_font *add_cached_font( HDC hdc, HFONT hfont, UINT aa_flags )
487 struct cached_font font, *ptr, *last_unused = NULL;
488 UINT i = 0, j, k;
490 GetObjectW( hfont, sizeof(font.lf), &font.lf );
491 GetTransform( hdc, 0x204, &font.xform );
492 font.xform.eDx = font.xform.eDy = 0; /* unused, would break hashing */
493 if (GetGraphicsMode( hdc ) == GM_COMPATIBLE)
495 font.lf.lfOrientation = font.lf.lfEscapement;
496 if (font.xform.eM11 * font.xform.eM22 < 0)
497 font.lf.lfOrientation = -font.lf.lfOrientation;
499 font.lf.lfWidth = abs( font.lf.lfWidth );
500 font.aa_flags = aa_flags;
501 font.hash = font_cache_hash( &font );
503 EnterCriticalSection( &font_cache_cs );
504 LIST_FOR_EACH_ENTRY( ptr, &font_cache, struct cached_font, entry )
506 if (!font_cache_cmp( &font, ptr ))
508 InterlockedIncrement( &ptr->ref );
509 list_remove( &ptr->entry );
510 goto done;
512 if (!ptr->ref)
514 i++;
515 last_unused = ptr;
519 if (i > 5) /* keep at least 5 of the most-recently used fonts around */
521 ptr = last_unused;
522 for (i = 0; i < GLYPH_NBTYPES; i++)
524 for (j = 0; j < GLYPH_CACHE_PAGES; j++)
526 if (!ptr->glyphs[i][j]) continue;
527 for (k = 0; k < GLYPH_CACHE_PAGE_SIZE; k++)
528 HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j][k] );
529 HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j] );
532 list_remove( &ptr->entry );
534 else if (!(ptr = HeapAlloc( GetProcessHeap(), 0, sizeof(*ptr) )))
536 LeaveCriticalSection( &font_cache_cs );
537 return NULL;
540 *ptr = font;
541 ptr->ref = 1;
542 memset( ptr->glyphs, 0, sizeof(ptr->glyphs) );
543 done:
544 list_add_head( &font_cache, &ptr->entry );
545 LeaveCriticalSection( &font_cache_cs );
546 TRACE( "%d %s -> %p\n", ptr->lf.lfHeight, debugstr_w(ptr->lf.lfFaceName), ptr );
547 return ptr;
550 void release_cached_font( struct cached_font *font )
552 if (font) InterlockedDecrement( &font->ref );
555 static struct cached_glyph *add_cached_glyph( struct cached_font *font, UINT index, UINT flags,
556 struct cached_glyph *glyph )
558 struct cached_glyph *ret;
559 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
560 UINT page = index / GLYPH_CACHE_PAGE_SIZE;
561 UINT entry = index % GLYPH_CACHE_PAGE_SIZE;
563 if (!font->glyphs[type][page])
565 struct cached_glyph **ptr;
567 ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, GLYPH_CACHE_PAGE_SIZE * sizeof(*ptr) );
568 if (!ptr)
570 HeapFree( GetProcessHeap(), 0, glyph );
571 return NULL;
573 if (InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page], ptr, NULL ))
574 HeapFree( GetProcessHeap(), 0, ptr );
576 ret = InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page][entry], glyph, NULL );
577 if (!ret) ret = glyph;
578 else HeapFree( GetProcessHeap(), 0, glyph );
579 return ret;
582 static struct cached_glyph *get_cached_glyph( struct cached_font *font, UINT index, UINT flags )
584 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
585 UINT page = index / GLYPH_CACHE_PAGE_SIZE;
587 if (!font->glyphs[type][page]) return NULL;
588 return font->glyphs[type][page][index % GLYPH_CACHE_PAGE_SIZE];
591 /**********************************************************************
592 * get_text_bkgnd_masks
594 * See the comment above get_pen_bkgnd_masks
596 static inline void get_text_bkgnd_masks( HDC hdc, const dib_info *dib, rop_mask *mask )
598 COLORREF bg = GetBkColor( hdc );
600 mask->and = 0;
602 if (dib->bit_count != 1)
603 mask->xor = get_pixel_color( hdc, dib, bg, FALSE );
604 else
606 COLORREF fg = GetTextColor( hdc );
607 mask->xor = get_pixel_color( hdc, dib, fg, TRUE );
608 if (fg != bg) mask->xor = ~mask->xor;
612 static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics,
613 const dib_info *glyph_dib, DWORD text_color,
614 const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
615 RECT *bounds )
617 int i;
618 RECT rect, clipped_rect;
619 POINT src_origin;
621 rect.left = x + metrics->gmptGlyphOrigin.x;
622 rect.top = y - metrics->gmptGlyphOrigin.y;
623 rect.right = rect.left + metrics->gmBlackBoxX;
624 rect.bottom = rect.top + metrics->gmBlackBoxY;
625 if (bounds) add_bounds_rect( bounds, &rect );
627 for (i = 0; i < clipped_rects->count; i++)
629 if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
631 src_origin.x = clipped_rect.left - rect.left;
632 src_origin.y = clipped_rect.top - rect.top;
634 if (glyph_dib->bit_count == 32)
635 dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
636 text_color );
637 else
638 dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin,
639 text_color, ranges );
644 static int get_glyph_depth( UINT aa_flags )
646 switch (aa_flags)
648 case GGO_BITMAP: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */
649 case GGO_GRAY2_BITMAP:
650 case GGO_GRAY4_BITMAP:
651 case GGO_GRAY8_BITMAP:
652 case WINE_GGO_GRAY16_BITMAP: return 8;
654 case WINE_GGO_HRGB_BITMAP:
655 case WINE_GGO_HBGR_BITMAP:
656 case WINE_GGO_VRGB_BITMAP:
657 case WINE_GGO_VBGR_BITMAP: return 32;
659 default:
660 ERR("Unexpected flags %08x\n", aa_flags);
661 return 0;
665 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
666 static const int padding[4] = {0, 3, 2, 1};
668 /***********************************************************************
669 * cache_glyph_bitmap
671 * Retrieve a 17-level bitmap for the appropriate glyph.
673 * For non-antialiased bitmaps convert them to the 17-level format
674 * using only values 0 or 16.
676 static struct cached_glyph *cache_glyph_bitmap( HDC hdc, struct cached_font *font, UINT index, UINT flags )
678 UINT ggo_flags = font->aa_flags;
679 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
680 UINT indices[3] = {0, 0, 0x20};
681 int i, x, y;
682 DWORD ret, size;
683 BYTE *dst, *src;
684 int pad = 0, stride, bit_count;
685 GLYPHMETRICS metrics;
686 struct cached_glyph *glyph;
688 if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
689 indices[0] = index;
690 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
692 index = indices[i];
693 ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, 0, NULL, &identity );
694 if (ret != GDI_ERROR) break;
696 if (ret == GDI_ERROR) return NULL;
697 if (!ret) metrics.gmBlackBoxX = metrics.gmBlackBoxY = 0; /* empty glyph */
699 bit_count = get_glyph_depth( font->aa_flags );
700 stride = get_dib_stride( metrics.gmBlackBoxX, bit_count );
701 size = metrics.gmBlackBoxY * stride;
702 glyph = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( struct cached_glyph, bits[size] ));
703 if (!glyph) return NULL;
704 if (!size) goto done; /* empty glyph */
706 if (bit_count == 8) pad = padding[ metrics.gmBlackBoxX % 4 ];
708 ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, size, glyph->bits, &identity );
709 if (ret == GDI_ERROR)
711 HeapFree( GetProcessHeap(), 0, glyph );
712 return NULL;
714 assert( ret <= size );
715 if (font->aa_flags == GGO_BITMAP)
717 for (y = metrics.gmBlackBoxY - 1; y >= 0; y--)
719 src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 );
720 dst = glyph->bits + y * stride;
722 if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad );
724 for (x = metrics.gmBlackBoxX - 1; x >= 0; x--)
725 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
728 else if (pad)
730 for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride)
731 memset( dst + metrics.gmBlackBoxX, 0, pad );
734 done:
735 glyph->metrics = metrics;
736 return add_cached_glyph( font, index, flags, glyph );
739 static void render_string( HDC hdc, dib_info *dib, struct cached_font *font, INT x, INT y,
740 UINT flags, const WCHAR *str, UINT count, const INT *dx,
741 const struct clipped_rects *clipped_rects, RECT *bounds )
743 UINT i;
744 struct cached_glyph *glyph;
745 dib_info glyph_dib;
746 DWORD text_color;
747 struct intensity_range ranges[17];
749 glyph_dib.bit_count = get_glyph_depth( font->aa_flags );
750 glyph_dib.rect.left = 0;
751 glyph_dib.rect.top = 0;
752 glyph_dib.bits.is_copy = FALSE;
753 glyph_dib.bits.free = NULL;
755 text_color = get_pixel_color( hdc, dib, GetTextColor( hdc ), TRUE );
757 if (glyph_dib.bit_count == 8)
758 get_aa_ranges( dib->funcs->pixel_to_colorref( dib, text_color ), ranges );
760 for (i = 0; i < count; i++)
762 if (!(glyph = get_cached_glyph( font, str[i], flags )) &&
763 !(glyph = cache_glyph_bitmap( hdc, font, str[i], flags ))) continue;
765 glyph_dib.width = glyph->metrics.gmBlackBoxX;
766 glyph_dib.height = glyph->metrics.gmBlackBoxY;
767 glyph_dib.rect.right = glyph->metrics.gmBlackBoxX;
768 glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY;
769 glyph_dib.stride = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count );
770 glyph_dib.bits.ptr = glyph->bits;
772 draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
774 if (dx)
776 if (flags & ETO_PDY)
778 x += dx[ i * 2 ];
779 y += dx[ i * 2 + 1];
781 else
782 x += dx[ i ];
784 else
786 x += glyph->metrics.gmCellIncX;
787 y += glyph->metrics.gmCellIncY;
792 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
793 struct bitblt_coords *src, INT x, INT y, UINT flags,
794 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
796 dib_info dib;
797 struct clipped_rects visrect;
798 struct cached_font *font;
800 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
802 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
804 visrect.count = 1;
805 visrect.rects = &src->visrect;
807 if (flags & ETO_OPAQUE)
809 rop_mask bkgnd_color;
810 get_text_bkgnd_masks( hdc, &dib, &bkgnd_color );
811 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
814 if (!(font = add_cached_font( hdc, GetCurrentObject( hdc, OBJ_FONT ), aa_flags ))) return FALSE;
816 render_string( hdc, &dib, font, x, y, flags, str, count, dx, &visrect, NULL );
817 release_cached_font( font );
818 return TRUE;
821 /***********************************************************************
822 * dibdrv_ExtTextOut
824 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
825 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
827 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
828 struct clipped_rects clipped_rects;
829 RECT bounds;
831 if (!pdev->font) return FALSE;
833 init_clipped_rects( &clipped_rects );
834 reset_bounds( &bounds );
836 if (flags & ETO_OPAQUE)
838 rop_mask bkgnd_color;
839 get_text_bkgnd_masks( dev->hdc, &pdev->dib, &bkgnd_color );
840 add_bounds_rect( &bounds, rect );
841 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
842 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
843 bkgnd_color.and, bkgnd_color.xor );
846 if (count == 0) goto done;
848 if (flags & ETO_CLIPPED)
850 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
851 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
853 else
855 free_clipped_rects( &clipped_rects );
856 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
858 if (!clipped_rects.count) goto done;
860 render_string( dev->hdc, &pdev->dib, pdev->font, x, y, flags, str, count, dx,
861 &clipped_rects, &bounds );
863 done:
864 add_clipped_bounds( pdev, &bounds, pdev->clip );
865 free_clipped_rects( &clipped_rects );
866 return TRUE;
869 /***********************************************************************
870 * dibdrv_SelectFont
872 HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
874 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
875 HFONT ret;
877 if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */
879 dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
880 ret = dev->funcs->pSelectFont( dev, font, aa_flags );
881 if (ret)
883 struct cached_font *prev = pdev->font;
884 pdev->font = add_cached_font( dev->hdc, font, *aa_flags ? *aa_flags : GGO_BITMAP );
885 release_cached_font( prev );
887 return ret;
890 /***********************************************************************
891 * dibdrv_Arc
893 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
894 INT start_x, INT start_y, INT end_x, INT end_y )
896 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
899 /***********************************************************************
900 * dibdrv_ArcTo
902 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
903 INT start_x, INT start_y, INT end_x, INT end_y )
905 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
908 /***********************************************************************
909 * dibdrv_Chord
911 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
912 INT start_x, INT start_y, INT end_x, INT end_y )
914 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
917 /***********************************************************************
918 * dibdrv_Ellipse
920 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
922 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
925 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
927 /* the clip rgn stops the flooding */
928 if (clip && !PtInRegion( clip, x, y )) return FALSE;
930 if (type == FLOODFILLBORDER)
931 return dib->funcs->get_pixel( dib, x, y ) != pixel;
932 else
933 return dib->funcs->get_pixel( dib, x, y ) == pixel;
936 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
938 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
940 RECT next;
942 next.top = row->top + offset;
943 next.bottom = next.top + 1;
944 next.left = next.right = row->left;
945 while (next.right < row->right)
947 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
948 else
950 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
951 fill_row( dib, clip, &next, pixel, type, rgn );
952 next.left = ++next.right;
955 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
956 fill_row( dib, clip, &next, pixel, type, rgn );
959 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
961 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
962 while (row->right < dib->rect.right - dib->rect.left &&
963 is_interior( dib, clip, row->right, row->top, pixel, type))
964 row->right++;
966 add_rect_to_region( rgn, row );
968 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
969 if (row->top < dib->rect.bottom - dib->rect.top - 1)
970 do_next_row( dib, clip, row, 1, pixel, type, rgn );
973 /***********************************************************************
974 * dibdrv_ExtFloodFill
976 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
978 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
979 DWORD pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
980 RECT row;
981 HRGN rgn;
983 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
985 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
987 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
988 row.left = x;
989 row.right = x + 1;
990 row.top = y;
991 row.bottom = y + 1;
993 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
995 add_clipped_bounds( pdev, NULL, rgn );
996 brush_region( pdev, rgn );
998 DeleteObject( rgn );
999 return TRUE;
1002 /***********************************************************************
1003 * dibdrv_GetNearestColor
1005 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
1007 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1008 DWORD pixel;
1010 TRACE( "(%p, %08x)\n", dev, color );
1012 pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
1013 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1016 /***********************************************************************
1017 * dibdrv_GetPixel
1019 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
1021 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1022 POINT pt;
1023 DWORD pixel;
1025 TRACE( "(%p, %d, %d)\n", dev, x, y );
1027 pt.x = x;
1028 pt.y = y;
1029 LPtoDP( dev->hdc, &pt, 1 );
1031 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
1032 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
1033 return CLR_INVALID;
1035 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
1036 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1039 /***********************************************************************
1040 * dibdrv_LineTo
1042 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
1044 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1045 POINT pts[2];
1046 HRGN region = 0;
1047 BOOL ret;
1049 GetCurrentPositionEx(dev->hdc, pts);
1050 pts[1].x = x;
1051 pts[1].y = y;
1053 LPtoDP(dev->hdc, pts, 2);
1055 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1057 reset_dash_origin(pdev);
1059 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
1060 add_pen_lines_bounds( pdev, 2, pts, region );
1062 if (region)
1064 ret = pen_region( pdev, region );
1065 DeleteObject( region );
1067 return ret;
1070 /***********************************************************************
1071 * get_rop2_from_rop
1073 * Returns the binary rop that is equivalent to the provided ternary rop
1074 * if the src bits are ignored.
1076 static inline INT get_rop2_from_rop(INT rop)
1078 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
1081 /***********************************************************************
1082 * dibdrv_PatBlt
1084 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
1086 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1087 dib_brush *brush = &pdev->brush;
1088 int rop2 = get_rop2_from_rop( rop );
1089 struct clipped_rects clipped_rects;
1090 DWORD and = 0, xor = 0;
1091 BOOL ret = TRUE;
1093 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
1095 add_clipped_bounds( pdev, &dst->visrect, 0 );
1096 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
1098 switch (rop2) /* shortcuts for rops that don't involve the brush */
1100 case R2_NOT: and = ~0u;
1101 /* fall through */
1102 case R2_WHITE: xor = ~0u;
1103 /* fall through */
1104 case R2_BLACK:
1105 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
1106 /* fall through */
1107 case R2_NOP:
1108 break;
1109 default:
1110 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
1111 break;
1113 free_clipped_rects( &clipped_rects );
1114 return ret;
1117 /***********************************************************************
1118 * dibdrv_PaintRgn
1120 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
1122 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1123 const WINEREGION *region;
1124 int i;
1125 RECT rect, bounds;
1127 TRACE("%p, %p\n", dev, rgn);
1129 reset_bounds( &bounds );
1131 region = get_wine_region( rgn );
1132 if(!region) return FALSE;
1134 for(i = 0; i < region->numRects; i++)
1136 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
1137 region->rects[i].right, region->rects[i].bottom, FALSE );
1138 add_bounds_rect( &bounds, &rect );
1139 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1142 release_wine_region( rgn );
1143 add_clipped_bounds( pdev, &bounds, pdev->clip );
1144 return TRUE;
1147 /***********************************************************************
1148 * dibdrv_PolyPolygon
1150 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
1152 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1153 DWORD total, i, pos;
1154 BOOL ret = TRUE;
1155 POINT *points;
1156 HRGN outline = 0, interior = 0;
1158 for (i = total = 0; i < polygons; i++)
1160 if (counts[i] < 2) return FALSE;
1161 total += counts[i];
1164 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1165 if (!points) return FALSE;
1166 memcpy( points, pt, total * sizeof(*pt) );
1167 LPtoDP( dev->hdc, points, total );
1169 if (pdev->brush.style != BS_NULL &&
1170 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
1172 HeapFree( GetProcessHeap(), 0, points );
1173 return FALSE;
1176 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1178 /* if not using a region, paint the interior first so the outline can overlap it */
1179 if (interior && !outline)
1181 ret = brush_region( pdev, interior );
1182 DeleteObject( interior );
1183 interior = 0;
1186 for (i = pos = 0; i < polygons; i++)
1188 reset_dash_origin( pdev );
1189 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1190 pos += counts[i];
1192 add_pen_lines_bounds( pdev, total, points, outline );
1194 if (interior)
1196 CombineRgn( interior, interior, outline, RGN_DIFF );
1197 ret = brush_region( pdev, interior );
1198 DeleteObject( interior );
1200 if (outline)
1202 if (ret) ret = pen_region( pdev, outline );
1203 DeleteObject( outline );
1205 HeapFree( GetProcessHeap(), 0, points );
1206 return ret;
1209 /***********************************************************************
1210 * dibdrv_PolyPolyline
1212 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1214 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1215 DWORD total, pos, i;
1216 POINT *points;
1217 BOOL ret = TRUE;
1218 HRGN outline = 0;
1220 for (i = total = 0; i < polylines; i++)
1222 if (counts[i] < 2) return FALSE;
1223 total += counts[i];
1226 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1227 if (!points) return FALSE;
1228 memcpy( points, pt, total * sizeof(*pt) );
1229 LPtoDP( dev->hdc, points, total );
1231 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1233 HeapFree( GetProcessHeap(), 0, points );
1234 return FALSE;
1237 for (i = pos = 0; i < polylines; i++)
1239 reset_dash_origin( pdev );
1240 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1241 pos += counts[i];
1243 add_pen_lines_bounds( pdev, total, points, outline );
1245 if (outline)
1247 ret = pen_region( pdev, outline );
1248 DeleteObject( outline );
1251 HeapFree( GetProcessHeap(), 0, points );
1252 return ret;
1255 /***********************************************************************
1256 * dibdrv_Polygon
1258 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1260 INT counts[1] = { count };
1262 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1265 /***********************************************************************
1266 * dibdrv_Polyline
1268 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1270 DWORD counts[1] = { count };
1272 if (count < 0) return FALSE;
1273 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1276 /***********************************************************************
1277 * dibdrv_Rectangle
1279 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1281 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1282 RECT rect;
1283 POINT pts[4];
1284 BOOL ret;
1285 HRGN outline = 0;
1287 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1289 if (GetGraphicsMode( dev->hdc ) == GM_ADVANCED)
1291 pts[0].x = pts[3].x = left;
1292 pts[0].y = pts[1].y = top;
1293 pts[1].x = pts[2].x = right;
1294 pts[2].y = pts[3].y = bottom;
1295 return dibdrv_Polygon( dev, pts, 4 );
1298 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1300 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1302 rect.right--;
1303 rect.bottom--;
1304 reset_dash_origin(pdev);
1306 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1308 /* 4 pts going clockwise starting from bottom-right */
1309 pts[0].x = pts[3].x = rect.right;
1310 pts[0].y = pts[1].y = rect.bottom;
1311 pts[1].x = pts[2].x = rect.left;
1312 pts[2].y = pts[3].y = rect.top;
1314 else
1316 /* 4 pts going anti-clockwise starting from top-right */
1317 pts[0].x = pts[3].x = rect.right;
1318 pts[0].y = pts[1].y = rect.top;
1319 pts[1].x = pts[2].x = rect.left;
1320 pts[2].y = pts[3].y = rect.bottom;
1323 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1324 add_pen_lines_bounds( pdev, 4, pts, outline );
1326 if (outline)
1328 if (pdev->brush.style != BS_NULL)
1330 HRGN interior = CreateRectRgnIndirect( &rect );
1332 CombineRgn( interior, interior, outline, RGN_DIFF );
1333 brush_region( pdev, interior );
1334 DeleteObject( interior );
1336 ret = pen_region( pdev, outline );
1337 DeleteObject( outline );
1339 else
1341 rect.left += (pdev->pen_width + 1) / 2;
1342 rect.top += (pdev->pen_width + 1) / 2;
1343 rect.right -= pdev->pen_width / 2;
1344 rect.bottom -= pdev->pen_width / 2;
1345 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1347 return ret;
1350 /***********************************************************************
1351 * dibdrv_RoundRect
1353 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1354 INT ellipse_width, INT ellipse_height )
1356 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1357 RECT rect;
1358 POINT pt[2], *points;
1359 int i, end, count;
1360 BOOL ret = TRUE;
1361 HRGN outline = 0, interior = 0;
1363 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1365 pt[0].x = pt[0].y = 0;
1366 pt[1].x = ellipse_width;
1367 pt[1].y = ellipse_height;
1368 LPtoDP( dev->hdc, pt, 2 );
1369 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1370 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1371 if (ellipse_width <= 2|| ellipse_height <= 2)
1372 return dibdrv_Rectangle( dev, left, top, right, bottom );
1374 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1375 if (!points) return FALSE;
1377 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1379 HeapFree( GetProcessHeap(), 0, points );
1380 return FALSE;
1383 if (pdev->brush.style != BS_NULL &&
1384 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1385 ellipse_width, ellipse_height )))
1387 HeapFree( GetProcessHeap(), 0, points );
1388 if (outline) DeleteObject( outline );
1389 return FALSE;
1392 /* if not using a region, paint the interior first so the outline can overlap it */
1393 if (interior && !outline)
1395 ret = brush_region( pdev, interior );
1396 DeleteObject( interior );
1397 interior = 0;
1400 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1402 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1404 for (i = 0; i < count; i++)
1406 points[i].x = rect.right - ellipse_width + points[i].x;
1407 points[i].y = rect.bottom - ellipse_height + points[i].y;
1410 else
1412 for (i = 0; i < count; i++)
1414 points[i].x = rect.right - ellipse_width + points[i].x;
1415 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1419 /* horizontal symmetry */
1421 end = 2 * count - 1;
1422 /* avoid duplicating the midpoint */
1423 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1424 for (i = 0; i < count; i++)
1426 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1427 points[end - i].y = points[i].y;
1429 count = end + 1;
1431 /* vertical symmetry */
1433 end = 2 * count - 1;
1434 /* avoid duplicating the midpoint */
1435 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1436 for (i = 0; i < count; i++)
1438 points[end - i].x = points[i].x;
1439 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1441 count = end + 1;
1443 reset_dash_origin( pdev );
1444 pdev->pen_lines( pdev, count, points, TRUE, outline );
1445 add_pen_lines_bounds( pdev, count, points, outline );
1447 if (interior)
1449 CombineRgn( interior, interior, outline, RGN_DIFF );
1450 ret = brush_region( pdev, interior );
1451 DeleteObject( interior );
1453 if (outline)
1455 if (ret) ret = pen_region( pdev, outline );
1456 DeleteObject( outline );
1458 HeapFree( GetProcessHeap(), 0, points );
1459 return ret;
1462 /***********************************************************************
1463 * dibdrv_Pie
1465 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1466 INT start_x, INT start_y, INT end_x, INT end_y )
1468 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1471 /***********************************************************************
1472 * dibdrv_SetPixel
1474 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1476 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1477 struct clipped_rects clipped_rects;
1478 RECT rect;
1479 POINT pt;
1480 DWORD pixel;
1482 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1484 pt.x = x;
1485 pt.y = y;
1486 LPtoDP( dev->hdc, &pt, 1 );
1487 rect.left = pt.x;
1488 rect.top = pt.y;
1489 rect.right = rect.left + 1;
1490 rect.bottom = rect.top + 1;
1491 add_clipped_bounds( pdev, &rect, pdev->clip );
1493 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1494 pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
1495 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1497 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1498 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1499 free_clipped_rects( &clipped_rects );
1500 return color;