gdi32: Fixed Rectangle() rotation with GM_ADVANCED graphics mode in dib driver.
[wine.git] / dlls / gdi32 / dibdrv / graphics.c
blobf3aaeb6a7383ceba96967157f65b7787615d8080
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;
698 bit_count = get_glyph_depth( font->aa_flags );
699 stride = get_dib_stride( metrics.gmBlackBoxX, bit_count );
700 size = metrics.gmBlackBoxY * stride;
701 glyph = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( struct cached_glyph, bits[size] ));
702 if (!glyph) return NULL;
703 if (!ret) goto done; /* zero-size glyph */
705 if (bit_count == 8) pad = padding[ metrics.gmBlackBoxX % 4 ];
707 ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, size, glyph->bits, &identity );
708 if (ret == GDI_ERROR)
710 HeapFree( GetProcessHeap(), 0, glyph );
711 return NULL;
713 assert( ret <= size );
714 if (font->aa_flags == GGO_BITMAP)
716 for (y = metrics.gmBlackBoxY - 1; y >= 0; y--)
718 src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 );
719 dst = glyph->bits + y * stride;
721 if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad );
723 for (x = metrics.gmBlackBoxX - 1; x >= 0; x--)
724 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
727 else if (pad)
729 for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride)
730 memset( dst + metrics.gmBlackBoxX, 0, pad );
733 done:
734 glyph->metrics = metrics;
735 return add_cached_glyph( font, index, flags, glyph );
738 static void render_string( HDC hdc, dib_info *dib, struct cached_font *font, INT x, INT y,
739 UINT flags, const WCHAR *str, UINT count, const INT *dx,
740 const struct clipped_rects *clipped_rects, RECT *bounds )
742 UINT i;
743 struct cached_glyph *glyph;
744 dib_info glyph_dib;
745 DWORD text_color;
746 struct intensity_range ranges[17];
748 glyph_dib.bit_count = get_glyph_depth( font->aa_flags );
749 glyph_dib.rect.left = 0;
750 glyph_dib.rect.top = 0;
751 glyph_dib.bits.is_copy = FALSE;
752 glyph_dib.bits.free = NULL;
754 text_color = get_pixel_color( hdc, dib, GetTextColor( hdc ), TRUE );
756 if (glyph_dib.bit_count == 8)
757 get_aa_ranges( dib->funcs->pixel_to_colorref( dib, text_color ), ranges );
759 for (i = 0; i < count; i++)
761 if (!(glyph = get_cached_glyph( font, str[i], flags )) &&
762 !(glyph = cache_glyph_bitmap( hdc, font, str[i], flags ))) continue;
764 glyph_dib.width = glyph->metrics.gmBlackBoxX;
765 glyph_dib.height = glyph->metrics.gmBlackBoxY;
766 glyph_dib.rect.right = glyph->metrics.gmBlackBoxX;
767 glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY;
768 glyph_dib.stride = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count );
769 glyph_dib.bits.ptr = glyph->bits;
771 draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
773 if (dx)
775 if (flags & ETO_PDY)
777 x += dx[ i * 2 ];
778 y += dx[ i * 2 + 1];
780 else
781 x += dx[ i ];
783 else
785 x += glyph->metrics.gmCellIncX;
786 y += glyph->metrics.gmCellIncY;
791 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
792 struct bitblt_coords *src, INT x, INT y, UINT flags,
793 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
795 dib_info dib;
796 struct clipped_rects visrect;
797 struct cached_font *font;
799 assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
801 init_dib_info_from_bitmapinfo( &dib, info, bits->ptr );
803 visrect.count = 1;
804 visrect.rects = &src->visrect;
806 if (flags & ETO_OPAQUE)
808 rop_mask bkgnd_color;
809 get_text_bkgnd_masks( hdc, &dib, &bkgnd_color );
810 dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
813 if (!(font = add_cached_font( hdc, GetCurrentObject( hdc, OBJ_FONT ), aa_flags ))) return FALSE;
815 render_string( hdc, &dib, font, x, y, flags, str, count, dx, &visrect, NULL );
816 release_cached_font( font );
817 return TRUE;
820 /***********************************************************************
821 * dibdrv_ExtTextOut
823 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
824 const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
826 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
827 struct clipped_rects clipped_rects;
828 RECT bounds;
830 if (!pdev->font) return FALSE;
832 init_clipped_rects( &clipped_rects );
833 reset_bounds( &bounds );
835 if (flags & ETO_OPAQUE)
837 rop_mask bkgnd_color;
838 get_text_bkgnd_masks( dev->hdc, &pdev->dib, &bkgnd_color );
839 add_bounds_rect( &bounds, rect );
840 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
841 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
842 bkgnd_color.and, bkgnd_color.xor );
845 if (count == 0) goto done;
847 if (flags & ETO_CLIPPED)
849 if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */
850 get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
852 else
854 free_clipped_rects( &clipped_rects );
855 get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
857 if (!clipped_rects.count) goto done;
859 render_string( dev->hdc, &pdev->dib, pdev->font, x, y, flags, str, count, dx,
860 &clipped_rects, &bounds );
862 done:
863 add_clipped_bounds( pdev, &bounds, pdev->clip );
864 free_clipped_rects( &clipped_rects );
865 return TRUE;
868 /***********************************************************************
869 * dibdrv_SelectFont
871 HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
873 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
874 HFONT ret;
876 if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */
878 dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
879 ret = dev->funcs->pSelectFont( dev, font, aa_flags );
880 if (ret)
882 struct cached_font *prev = pdev->font;
883 pdev->font = add_cached_font( dev->hdc, font, *aa_flags ? *aa_flags : GGO_BITMAP );
884 release_cached_font( prev );
886 return ret;
889 /***********************************************************************
890 * dibdrv_Arc
892 BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
893 INT start_x, INT start_y, INT end_x, INT end_y )
895 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 );
898 /***********************************************************************
899 * dibdrv_ArcTo
901 BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
902 INT start_x, INT start_y, INT end_x, INT end_y )
904 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 );
907 /***********************************************************************
908 * dibdrv_Chord
910 BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
911 INT start_x, INT start_y, INT end_x, INT end_y )
913 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 );
916 /***********************************************************************
917 * dibdrv_Ellipse
919 BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
921 return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top );
924 static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type)
926 /* the clip rgn stops the flooding */
927 if (clip && !PtInRegion( clip, x, y )) return FALSE;
929 if (type == FLOODFILLBORDER)
930 return dib->funcs->get_pixel( dib, x, y ) != pixel;
931 else
932 return dib->funcs->get_pixel( dib, x, y ) == pixel;
935 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn );
937 static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn )
939 RECT next;
941 next.top = row->top + offset;
942 next.bottom = next.top + 1;
943 next.left = next.right = row->left;
944 while (next.right < row->right)
946 if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++;
947 else
949 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
950 fill_row( dib, clip, &next, pixel, type, rgn );
951 next.left = ++next.right;
954 if (next.left != next.right && !PtInRegion( rgn, next.left, next.top ))
955 fill_row( dib, clip, &next, pixel, type, rgn );
958 static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn )
960 while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--;
961 while (row->right < dib->rect.right - dib->rect.left &&
962 is_interior( dib, clip, row->right, row->top, pixel, type))
963 row->right++;
965 add_rect_to_region( rgn, row );
967 if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn );
968 if (row->top < dib->rect.bottom - dib->rect.top - 1)
969 do_next_row( dib, clip, row, 1, pixel, type, rgn );
972 /***********************************************************************
973 * dibdrv_ExtFloodFill
975 BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type )
977 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
978 DWORD pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
979 RECT row;
980 HRGN rgn;
982 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type );
984 if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE;
986 if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
987 row.left = x;
988 row.right = x + 1;
989 row.top = y;
990 row.bottom = y + 1;
992 fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn );
994 add_clipped_bounds( pdev, NULL, rgn );
995 brush_region( pdev, rgn );
997 DeleteObject( rgn );
998 return TRUE;
1001 /***********************************************************************
1002 * dibdrv_GetNearestColor
1004 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
1006 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1007 DWORD pixel;
1009 TRACE( "(%p, %08x)\n", dev, color );
1011 pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
1012 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1015 /***********************************************************************
1016 * dibdrv_GetPixel
1018 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
1020 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1021 POINT pt;
1022 DWORD pixel;
1024 TRACE( "(%p, %d, %d)\n", dev, x, y );
1026 pt.x = x;
1027 pt.y = y;
1028 LPtoDP( dev->hdc, &pt, 1 );
1030 if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left ||
1031 pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top)
1032 return CLR_INVALID;
1034 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y );
1035 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1038 /***********************************************************************
1039 * dibdrv_LineTo
1041 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
1043 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1044 POINT pts[2];
1045 HRGN region = 0;
1046 BOOL ret;
1048 GetCurrentPositionEx(dev->hdc, pts);
1049 pts[1].x = x;
1050 pts[1].y = y;
1052 LPtoDP(dev->hdc, pts, 2);
1054 if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1056 reset_dash_origin(pdev);
1058 ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
1059 add_pen_lines_bounds( pdev, 2, pts, region );
1061 if (region)
1063 ret = pen_region( pdev, region );
1064 DeleteObject( region );
1066 return ret;
1069 /***********************************************************************
1070 * get_rop2_from_rop
1072 * Returns the binary rop that is equivalent to the provided ternary rop
1073 * if the src bits are ignored.
1075 static inline INT get_rop2_from_rop(INT rop)
1077 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
1080 /***********************************************************************
1081 * dibdrv_PatBlt
1083 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
1085 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1086 dib_brush *brush = &pdev->brush;
1087 int rop2 = get_rop2_from_rop( rop );
1088 struct clipped_rects clipped_rects;
1089 DWORD and = 0, xor = 0;
1090 BOOL ret = TRUE;
1092 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
1094 add_clipped_bounds( pdev, &dst->visrect, 0 );
1095 if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE;
1097 switch (rop2) /* shortcuts for rops that don't involve the brush */
1099 case R2_NOT: and = ~0u;
1100 /* fall through */
1101 case R2_WHITE: xor = ~0u;
1102 /* fall through */
1103 case R2_BLACK:
1104 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor );
1105 /* fall through */
1106 case R2_NOP:
1107 break;
1108 default:
1109 ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 );
1110 break;
1112 free_clipped_rects( &clipped_rects );
1113 return ret;
1116 /***********************************************************************
1117 * dibdrv_PaintRgn
1119 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
1121 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1122 const WINEREGION *region;
1123 int i;
1124 RECT rect, bounds;
1126 TRACE("%p, %p\n", dev, rgn);
1128 reset_bounds( &bounds );
1130 region = get_wine_region( rgn );
1131 if(!region) return FALSE;
1133 for(i = 0; i < region->numRects; i++)
1135 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
1136 region->rects[i].right, region->rects[i].bottom, FALSE );
1137 add_bounds_rect( &bounds, &rect );
1138 brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1141 release_wine_region( rgn );
1142 add_clipped_bounds( pdev, &bounds, pdev->clip );
1143 return TRUE;
1146 /***********************************************************************
1147 * dibdrv_PolyPolygon
1149 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
1151 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1152 DWORD total, i, pos;
1153 BOOL ret = TRUE;
1154 POINT *points;
1155 HRGN outline = 0, interior = 0;
1157 for (i = total = 0; i < polygons; i++)
1159 if (counts[i] < 2) return FALSE;
1160 total += counts[i];
1163 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1164 if (!points) return FALSE;
1165 memcpy( points, pt, total * sizeof(*pt) );
1166 LPtoDP( dev->hdc, points, total );
1168 if (pdev->brush.style != BS_NULL &&
1169 !(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
1171 HeapFree( GetProcessHeap(), 0, points );
1172 return FALSE;
1175 if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 );
1177 /* if not using a region, paint the interior first so the outline can overlap it */
1178 if (interior && !outline)
1180 ret = brush_region( pdev, interior );
1181 DeleteObject( interior );
1182 interior = 0;
1185 for (i = pos = 0; i < polygons; i++)
1187 reset_dash_origin( pdev );
1188 pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
1189 pos += counts[i];
1191 add_pen_lines_bounds( pdev, total, points, outline );
1193 if (interior)
1195 CombineRgn( interior, interior, outline, RGN_DIFF );
1196 ret = brush_region( pdev, interior );
1197 DeleteObject( interior );
1199 if (outline)
1201 if (ret) ret = pen_region( pdev, outline );
1202 DeleteObject( outline );
1204 HeapFree( GetProcessHeap(), 0, points );
1205 return ret;
1208 /***********************************************************************
1209 * dibdrv_PolyPolyline
1211 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1213 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1214 DWORD total, pos, i;
1215 POINT *points;
1216 BOOL ret = TRUE;
1217 HRGN outline = 0;
1219 for (i = total = 0; i < polylines; i++)
1221 if (counts[i] < 2) return FALSE;
1222 total += counts[i];
1225 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1226 if (!points) return FALSE;
1227 memcpy( points, pt, total * sizeof(*pt) );
1228 LPtoDP( dev->hdc, points, total );
1230 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1232 HeapFree( GetProcessHeap(), 0, points );
1233 return FALSE;
1236 for (i = pos = 0; i < polylines; i++)
1238 reset_dash_origin( pdev );
1239 pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline );
1240 pos += counts[i];
1242 add_pen_lines_bounds( pdev, total, points, outline );
1244 if (outline)
1246 ret = pen_region( pdev, outline );
1247 DeleteObject( outline );
1250 HeapFree( GetProcessHeap(), 0, points );
1251 return ret;
1254 /***********************************************************************
1255 * dibdrv_Polygon
1257 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
1259 INT counts[1] = { count };
1261 return dibdrv_PolyPolygon( dev, pt, counts, 1 );
1264 /***********************************************************************
1265 * dibdrv_Polyline
1267 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
1269 DWORD counts[1] = { count };
1271 if (count < 0) return FALSE;
1272 return dibdrv_PolyPolyline( dev, pt, counts, 1 );
1275 /***********************************************************************
1276 * dibdrv_Rectangle
1278 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
1280 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1281 RECT rect;
1282 POINT pts[4];
1283 BOOL ret;
1284 HRGN outline = 0;
1286 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
1288 if (GetGraphicsMode( dev->hdc ) == GM_ADVANCED)
1290 pts[0].x = pts[3].x = left;
1291 pts[0].y = pts[1].y = top;
1292 pts[1].x = pts[2].x = right;
1293 pts[2].y = pts[3].y = bottom;
1294 return dibdrv_Polygon( dev, pts, 4 );
1297 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1299 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
1301 rect.right--;
1302 rect.bottom--;
1303 reset_dash_origin(pdev);
1305 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1307 /* 4 pts going clockwise starting from bottom-right */
1308 pts[0].x = pts[3].x = rect.right;
1309 pts[0].y = pts[1].y = rect.bottom;
1310 pts[1].x = pts[2].x = rect.left;
1311 pts[2].y = pts[3].y = rect.top;
1313 else
1315 /* 4 pts going anti-clockwise starting from top-right */
1316 pts[0].x = pts[3].x = rect.right;
1317 pts[0].y = pts[1].y = rect.top;
1318 pts[1].x = pts[2].x = rect.left;
1319 pts[2].y = pts[3].y = rect.bottom;
1322 pdev->pen_lines(pdev, 4, pts, TRUE, outline);
1323 add_pen_lines_bounds( pdev, 4, pts, outline );
1325 if (outline)
1327 if (pdev->brush.style != BS_NULL)
1329 HRGN interior = CreateRectRgnIndirect( &rect );
1331 CombineRgn( interior, interior, outline, RGN_DIFF );
1332 brush_region( pdev, interior );
1333 DeleteObject( interior );
1335 ret = pen_region( pdev, outline );
1336 DeleteObject( outline );
1338 else
1340 rect.left += (pdev->pen_width + 1) / 2;
1341 rect.top += (pdev->pen_width + 1) / 2;
1342 rect.right -= pdev->pen_width / 2;
1343 rect.bottom -= pdev->pen_width / 2;
1344 ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip );
1346 return ret;
1349 /***********************************************************************
1350 * dibdrv_RoundRect
1352 BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1353 INT ellipse_width, INT ellipse_height )
1355 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1356 RECT rect;
1357 POINT pt[2], *points;
1358 int i, end, count;
1359 BOOL ret = TRUE;
1360 HRGN outline = 0, interior = 0;
1362 if (!get_pen_device_rect( pdev, &rect, left, top, right, bottom )) return TRUE;
1364 pt[0].x = pt[0].y = 0;
1365 pt[1].x = ellipse_width;
1366 pt[1].y = ellipse_height;
1367 LPtoDP( dev->hdc, pt, 2 );
1368 ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x ));
1369 ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y ));
1370 if (ellipse_width <= 2|| ellipse_height <= 2)
1371 return dibdrv_Rectangle( dev, left, top, right, bottom );
1373 points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) );
1374 if (!points) return FALSE;
1376 if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
1378 HeapFree( GetProcessHeap(), 0, points );
1379 return FALSE;
1382 if (pdev->brush.style != BS_NULL &&
1383 !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1,
1384 ellipse_width, ellipse_height )))
1386 HeapFree( GetProcessHeap(), 0, points );
1387 if (outline) DeleteObject( outline );
1388 return FALSE;
1391 /* if not using a region, paint the interior first so the outline can overlap it */
1392 if (interior && !outline)
1394 ret = brush_region( pdev, interior );
1395 DeleteObject( interior );
1396 interior = 0;
1399 count = ellipse_first_quadrant( ellipse_width, ellipse_height, points );
1401 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
1403 for (i = 0; i < count; i++)
1405 points[i].x = rect.right - ellipse_width + points[i].x;
1406 points[i].y = rect.bottom - ellipse_height + points[i].y;
1409 else
1411 for (i = 0; i < count; i++)
1413 points[i].x = rect.right - ellipse_width + points[i].x;
1414 points[i].y = rect.top + ellipse_height - 1 - points[i].y;
1418 /* horizontal symmetry */
1420 end = 2 * count - 1;
1421 /* avoid duplicating the midpoint */
1422 if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--;
1423 for (i = 0; i < count; i++)
1425 points[end - i].x = rect.left + rect.right - 1 - points[i].x;
1426 points[end - i].y = points[i].y;
1428 count = end + 1;
1430 /* vertical symmetry */
1432 end = 2 * count - 1;
1433 /* avoid duplicating the midpoint */
1434 if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--;
1435 for (i = 0; i < count; i++)
1437 points[end - i].x = points[i].x;
1438 points[end - i].y = rect.top + rect.bottom - 1 - points[i].y;
1440 count = end + 1;
1442 reset_dash_origin( pdev );
1443 pdev->pen_lines( pdev, count, points, TRUE, outline );
1444 add_pen_lines_bounds( pdev, count, points, outline );
1446 if (interior)
1448 CombineRgn( interior, interior, outline, RGN_DIFF );
1449 ret = brush_region( pdev, interior );
1450 DeleteObject( interior );
1452 if (outline)
1454 if (ret) ret = pen_region( pdev, outline );
1455 DeleteObject( outline );
1457 HeapFree( GetProcessHeap(), 0, points );
1458 return ret;
1461 /***********************************************************************
1462 * dibdrv_Pie
1464 BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
1465 INT start_x, INT start_y, INT end_x, INT end_y )
1467 return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 );
1470 /***********************************************************************
1471 * dibdrv_SetPixel
1473 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1475 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
1476 struct clipped_rects clipped_rects;
1477 RECT rect;
1478 POINT pt;
1479 DWORD pixel;
1481 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
1483 pt.x = x;
1484 pt.y = y;
1485 LPtoDP( dev->hdc, &pt, 1 );
1486 rect.left = pt.x;
1487 rect.top = pt.y;
1488 rect.right = rect.left + 1;
1489 rect.bottom = rect.top + 1;
1490 add_clipped_bounds( pdev, &rect, pdev->clip );
1492 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1493 pixel = get_pixel_color( dev->hdc, &pdev->dib, color, FALSE );
1494 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
1496 if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
1497 pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
1498 free_clipped_rects( &clipped_rects );
1499 return color;