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
27 #include "ntgdi_private.h"
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(dib
);
47 #define GLYPH_CACHE_PAGE_SIZE 0x100
48 #define GLYPH_CACHE_PAGES (0x10000 / GLYPH_CACHE_PAGE_SIZE)
58 struct cached_glyph
**glyphs
[GLYPH_NBTYPES
][GLYPH_CACHE_PAGES
];
61 static struct list font_cache
= LIST_INIT( font_cache
);
63 static pthread_mutex_t font_cache_lock
= PTHREAD_MUTEX_INITIALIZER
;
66 static BOOL
brush_rect( dibdrv_physdev
*pdev
, dib_brush
*brush
, const RECT
*rect
, HRGN clip
)
68 DC
*dc
= get_physdev_dc( &pdev
->dev
);
69 struct clipped_rects clipped_rects
;
72 if (!get_clipped_rects( &pdev
->dib
, rect
, clip
, &clipped_rects
)) return TRUE
;
73 ret
= brush
->rects( pdev
, brush
, &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
,
74 &dc
->attr
->brush_org
, dc
->attr
->rop_mode
);
75 free_clipped_rects( &clipped_rects
);
79 /* paint a region with the brush (note: the region can be modified) */
80 static BOOL
brush_region( dibdrv_physdev
*pdev
, HRGN region
)
82 if (pdev
->clip
) NtGdiCombineRgn( region
, region
, pdev
->clip
, RGN_AND
);
83 return brush_rect( pdev
, &pdev
->brush
, NULL
, region
);
86 /* paint a region with the pen (note: the region can be modified) */
87 static BOOL
pen_region( dibdrv_physdev
*pdev
, HRGN region
)
89 if (pdev
->clip
) NtGdiCombineRgn( region
, region
, pdev
->clip
, RGN_AND
);
90 return brush_rect( pdev
, &pdev
->pen_brush
, NULL
, region
);
93 static RECT
get_device_rect( DC
*dc
, int left
, int top
, int right
, int bottom
, BOOL rtl_correction
)
100 rect
.bottom
= bottom
;
101 if (rtl_correction
&& dc
->attr
->layout
& LAYOUT_RTL
)
103 /* shift the rectangle so that the right border is included after mirroring */
104 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
108 lp_to_dp( dc
, (POINT
*)&rect
, 2 );
113 static BOOL
get_pen_device_rect( DC
*dc
, dibdrv_physdev
*dev
, RECT
*rect
,
114 int left
, int top
, int right
, int bottom
)
116 *rect
= get_device_rect( dc
, left
, top
, right
, bottom
, TRUE
);
117 if (rect
->left
== rect
->right
|| rect
->top
== rect
->bottom
) return FALSE
;
119 if (dev
->pen_style
== PS_INSIDEFRAME
)
121 rect
->left
+= dev
->pen_width
/ 2;
122 rect
->top
+= dev
->pen_width
/ 2;
123 rect
->right
-= (dev
->pen_width
- 1) / 2;
124 rect
->bottom
-= (dev
->pen_width
- 1) / 2;
129 static void add_pen_lines_bounds( dibdrv_physdev
*dev
, int count
, const POINT
*points
, HRGN rgn
)
131 const WINEREGION
*region
;
135 if (!dev
->bounds
) return;
136 reset_bounds( &bounds
);
138 if (dev
->pen_uses_region
)
140 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
141 width
= dev
->pen_width
+ 2;
142 if (dev
->pen_join
== PS_JOIN_MITER
)
145 if (dev
->pen_endcap
== PS_ENDCAP_SQUARE
) width
= (width
* 3 + 1) / 2;
149 if (dev
->pen_endcap
== PS_ENDCAP_SQUARE
) width
-= width
/ 4;
150 else width
= (width
+ 1) / 2;
153 /* in case the heuristics are wrong, add the actual region too */
154 if ((region
= get_wine_region( rgn
)))
156 add_bounds_rect( &bounds
, ®ion
->extents
);
157 release_wine_region( rgn
);
163 rect
.left
= points
->x
- width
;
164 rect
.top
= points
->y
- width
;
165 rect
.right
= points
->x
+ width
+ 1;
166 rect
.bottom
= points
->y
+ width
+ 1;
167 add_bounds_rect( &bounds
, &rect
);
171 add_clipped_bounds( dev
, &bounds
, dev
->clip
);
174 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
175 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
176 static int ellipse_first_quadrant( int width
, int height
, POINT
*data
)
178 const int a
= width
- 1;
179 const int b
= height
- 1;
180 const INT64 asq
= (INT64
)8 * a
* a
;
181 const INT64 bsq
= (INT64
)8 * b
* b
;
182 INT64 dx
= (INT64
)4 * b
* b
* (1 - a
);
183 INT64 dy
= (INT64
)4 * a
* a
* (1 + (b
% 2));
184 INT64 err
= dx
+ dy
+ a
* a
* (b
% 2);
191 /* based on an algorithm by Alois Zingl */
193 while (pt
.x
>= width
/ 2)
211 static int find_intersection( const POINT
*points
, int x
, int y
, int count
)
217 if (x
>= 0) /* first quadrant */
219 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* y
<= points
[i
].y
* x
) break;
222 /* second quadrant */
223 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* y
< points
[i
].y
* -x
) break;
224 return 2 * count
- i
;
226 if (x
>= 0) /* fourth quadrant */
228 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* -y
<= points
[i
].y
* x
) break;
229 return 4 * count
- i
;
232 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* -y
< points
[i
].y
* -x
) break;
233 return 2 * count
+ i
;
236 static int get_arc_points( int arc_dir
, const RECT
*rect
, POINT start
, POINT end
, POINT
*points
)
238 int i
, pos
, count
, start_pos
, end_pos
;
239 int width
= rect
->right
- rect
->left
;
240 int height
= rect
->bottom
- rect
->top
;
242 count
= ellipse_first_quadrant( width
, height
, points
);
243 for (i
= 0; i
< count
; i
++)
245 points
[i
].x
-= width
/ 2;
246 points
[i
].y
-= height
/ 2;
248 if (arc_dir
!= AD_CLOCKWISE
)
253 start_pos
= find_intersection( points
, start
.x
, start
.y
, count
);
254 end_pos
= find_intersection( points
, end
.x
, end
.y
, count
);
255 if (end_pos
<= start_pos
) end_pos
+= 4 * count
;
258 if (arc_dir
== AD_CLOCKWISE
)
260 for (i
= start_pos
; i
< end_pos
; i
++, pos
++)
262 switch ((i
/ count
) % 4)
265 points
[pos
].x
= rect
->left
+ width
/2 + points
[i
% count
].x
;
266 points
[pos
].y
= rect
->top
+ height
/2 + points
[i
% count
].y
;
269 points
[pos
].x
= rect
->right
-1 - width
/2 - points
[count
- 1 - i
% count
].x
;
270 points
[pos
].y
= rect
->top
+ height
/2 + points
[count
- 1 - i
% count
].y
;
273 points
[pos
].x
= rect
->right
-1 - width
/2 - points
[i
% count
].x
;
274 points
[pos
].y
= rect
->bottom
-1 - height
/2 - points
[i
% count
].y
;
277 points
[pos
].x
= rect
->left
+ width
/2 + points
[count
- 1 - i
% count
].x
;
278 points
[pos
].y
= rect
->bottom
-1 - height
/2 - points
[count
- 1 - i
% count
].y
;
285 for (i
= start_pos
; i
< end_pos
; i
++, pos
++)
287 switch ((i
/ count
) % 4)
290 points
[pos
].x
= rect
->left
+ width
/2 + points
[i
% count
].x
;
291 points
[pos
].y
= rect
->bottom
-1 - height
/2 - points
[i
% count
].y
;
294 points
[pos
].x
= rect
->right
-1 - width
/2 - points
[count
- 1 - i
% count
].x
;
295 points
[pos
].y
= rect
->bottom
-1 - height
/2 - points
[count
- 1 - i
% count
].y
;
298 points
[pos
].x
= rect
->right
-1 - width
/2 - points
[i
% count
].x
;
299 points
[pos
].y
= rect
->top
+ height
/2 + points
[i
% count
].y
;
302 points
[pos
].x
= rect
->left
+ width
/2 + points
[count
- 1 - i
% count
].x
;
303 points
[pos
].y
= rect
->top
+ height
/2 + points
[count
- 1 - i
% count
].y
;
309 memmove( points
, points
+ count
, (pos
- count
) * sizeof(POINT
) );
313 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
314 static BOOL
draw_arc( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
315 INT start_x
, INT start_y
, INT end_x
, INT end_y
, INT extra_lines
)
317 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
318 DC
*dc
= get_physdev_dc( dev
);
320 POINT pt
[2], *points
;
321 int width
, height
, count
;
323 HRGN outline
= 0, interior
= 0;
325 if (!get_pen_device_rect( dc
, pdev
, &rect
, left
, top
, right
, bottom
)) return TRUE
;
327 width
= rect
.right
- rect
.left
;
328 height
= rect
.bottom
- rect
.top
;
334 lp_to_dp( dc
, 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
= malloc( (width
+ height
) * 3 * sizeof(*points
) );
342 if (!points
) return FALSE
;
344 if (extra_lines
== -1)
346 points
[0] = dc
->attr
->cur_pos
;
347 lp_to_dp( dc
, points
, 1 );
348 count
= 1 + get_arc_points( dc
->attr
->arc_direction
, &rect
, pt
[0], pt
[1], points
+ 1 );
350 else count
= get_arc_points( dc
->attr
->arc_direction
, &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;
364 if (pdev
->pen_uses_region
&& !(outline
= NtGdiCreateRectRgn( 0, 0, 0, 0 )))
370 if (pdev
->brush
.style
!= BS_NULL
&&
372 get_dib_rect( &pdev
->dib
, &rc
) &&
373 !(interior
= create_polypolygon_region( points
, &count
, 1, WINDING
, &rc
)))
376 if (outline
) NtGdiDeleteObjectApp( outline
);
380 /* if not using a region, paint the interior first so the outline can overlap it */
381 if (interior
&& !outline
)
383 ret
= brush_region( pdev
, interior
);
384 NtGdiDeleteObjectApp( interior
);
388 reset_dash_origin( pdev
);
389 pdev
->pen_lines( pdev
, count
, points
, extra_lines
> 0, outline
);
390 add_pen_lines_bounds( pdev
, count
, points
, outline
);
394 NtGdiCombineRgn( interior
, interior
, outline
, RGN_DIFF
);
395 ret
= brush_region( pdev
, interior
);
396 NtGdiDeleteObjectApp( interior
);
400 if (ret
) ret
= pen_region( pdev
, outline
);
401 NtGdiDeleteObjectApp( outline
);
407 /* helper for path stroking and filling functions */
408 static BOOL
stroke_and_fill_path( dibdrv_physdev
*dev
, BOOL stroke
, BOOL fill
)
410 DC
*dc
= get_physdev_dc( &dev
->dev
);
411 struct gdi_path
*path
;
415 HRGN outline
= 0, interior
= 0;
418 if (dev
->brush
.style
== BS_NULL
) fill
= FALSE
;
420 if (!(path
= get_gdi_flat_path( dc
, fill
? &interior
: NULL
))) return FALSE
;
421 if (!(total
= get_gdi_path_data( path
, &points
, &types
))) goto done
;
423 if (stroke
&& dev
->pen_uses_region
) outline
= NtGdiCreateRectRgn( 0, 0, 0, 0 );
425 /* if not using a region, paint the interior first so the outline can overlap it */
426 if (interior
&& !outline
)
428 ret
= brush_region( dev
, interior
);
429 NtGdiDeleteObjectApp( interior
);
436 for (i
= 1; i
< total
; i
++)
438 if (types
[i
] != PT_MOVETO
) continue;
441 reset_dash_origin( dev
);
442 dev
->pen_lines( dev
, i
- pos
, points
+ pos
,
443 fill
|| types
[i
- 1] & PT_CLOSEFIGURE
, outline
);
449 reset_dash_origin( dev
);
450 dev
->pen_lines( dev
, i
- pos
, points
+ pos
,
451 fill
|| types
[i
- 1] & PT_CLOSEFIGURE
, outline
);
455 add_pen_lines_bounds( dev
, total
, points
, outline
);
459 NtGdiCombineRgn( interior
, interior
, outline
, RGN_DIFF
);
460 ret
= brush_region( dev
, interior
);
461 NtGdiDeleteObjectApp( interior
);
465 if (ret
) ret
= pen_region( dev
, outline
);
466 NtGdiDeleteObjectApp( outline
);
470 free_gdi_path( path
);
474 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
475 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
476 static const BYTE ramp
[17] =
479 0x8c, 0x9a, 0xa7, 0xb2,
480 0xbd, 0xc7, 0xd0, 0xd9,
481 0xe1, 0xe9, 0xf0, 0xf8,
485 /* For a give text-color component and a glyph level, calculate the
486 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
487 components respectively.
489 The minimum is a linear interpolation between 0 and the value in
492 The maximum is a linear interpolation between the value from the
493 ramp table read in reverse and 0xff.
495 To find the resulting pixel intensity, we note that if the text and
496 bkgnd intensities are the same then the result must be that
497 intensity. Otherwise we linearly interpolate between either the
498 min or the max value and this intermediate value depending on which
499 side of the inequality we lie.
502 static inline void get_range( BYTE aa
, DWORD text_comp
, BYTE
*min_comp
, BYTE
*max_comp
)
504 *min_comp
= (ramp
[aa
] * text_comp
) / 0xff;
505 *max_comp
= ramp
[16 - aa
] + ((0xff - ramp
[16 - aa
]) * text_comp
) / 0xff;
508 static inline void get_aa_ranges( COLORREF col
, struct intensity_range intensities
[17] )
512 for (i
= 0; i
< 17; i
++)
514 get_range( i
, GetRValue(col
), &intensities
[i
].r_min
, &intensities
[i
].r_max
);
515 get_range( i
, GetGValue(col
), &intensities
[i
].g_min
, &intensities
[i
].g_max
);
516 get_range( i
, GetBValue(col
), &intensities
[i
].b_min
, &intensities
[i
].b_max
);
520 static DWORD
font_cache_hash( struct cached_font
*font
)
522 DWORD hash
= 0, *ptr
, two_chars
;
526 hash
^= font
->aa_flags
;
527 for(i
= 0, ptr
= (DWORD
*)&font
->xform
; i
< sizeof(XFORM
)/sizeof(DWORD
); i
++, ptr
++)
529 for(i
= 0, ptr
= (DWORD
*)&font
->lf
; i
< 7; i
++, ptr
++)
531 for(i
= 0, ptr
= (DWORD
*)font
->lf
.lfFaceName
; i
< LF_FACESIZE
/2; i
++, ptr
++) {
533 pwc
= (WCHAR
*)&two_chars
;
535 *pwc
= towupper(*pwc
);
537 *pwc
= towupper(*pwc
);
544 static int font_cache_cmp( const struct cached_font
*p1
, const struct cached_font
*p2
)
546 int ret
= p1
->hash
- p2
->hash
;
547 if (!ret
) ret
= p1
->aa_flags
- p2
->aa_flags
;
548 if (!ret
) ret
= memcmp( &p1
->xform
, &p2
->xform
, sizeof(p1
->xform
) );
549 if (!ret
) ret
= memcmp( &p1
->lf
, &p2
->lf
, FIELD_OFFSET( LOGFONTW
, lfFaceName
));
550 if (!ret
) ret
= wcsicmp( p1
->lf
.lfFaceName
, p2
->lf
.lfFaceName
);
554 static struct cached_font
*add_cached_font( DC
*dc
, HFONT hfont
, UINT aa_flags
)
556 struct cached_font font
, *ptr
, *last_unused
= NULL
;
559 NtGdiExtGetObjectW( hfont
, sizeof(font
.lf
), &font
.lf
);
560 font
.xform
= dc
->xformWorld2Vport
;
561 font
.xform
.eDx
= font
.xform
.eDy
= 0; /* unused, would break hashing */
562 if (dc
->attr
->graphics_mode
== GM_COMPATIBLE
)
564 font
.lf
.lfOrientation
= font
.lf
.lfEscapement
;
565 if (font
.xform
.eM11
* font
.xform
.eM22
< 0)
566 font
.lf
.lfOrientation
= -font
.lf
.lfOrientation
;
568 font
.lf
.lfWidth
= abs( font
.lf
.lfWidth
);
569 font
.aa_flags
= aa_flags
;
570 font
.hash
= font_cache_hash( &font
);
572 pthread_mutex_lock( &font_cache_lock
);
573 LIST_FOR_EACH_ENTRY( ptr
, &font_cache
, struct cached_font
, entry
)
575 if (!font_cache_cmp( &font
, ptr
))
577 InterlockedIncrement( &ptr
->ref
);
578 list_remove( &ptr
->entry
);
588 if (i
> 5) /* keep at least 5 of the most-recently used fonts around */
591 for (i
= 0; i
< GLYPH_NBTYPES
; i
++)
593 for (j
= 0; j
< GLYPH_CACHE_PAGES
; j
++)
595 if (!ptr
->glyphs
[i
][j
]) continue;
596 for (k
= 0; k
< GLYPH_CACHE_PAGE_SIZE
; k
++)
597 free( ptr
->glyphs
[i
][j
][k
] );
598 free( ptr
->glyphs
[i
][j
] );
601 list_remove( &ptr
->entry
);
603 else if (!(ptr
= malloc( sizeof(*ptr
) )))
605 pthread_mutex_unlock( &font_cache_lock
);
611 memset( ptr
->glyphs
, 0, sizeof(ptr
->glyphs
) );
613 list_add_head( &font_cache
, &ptr
->entry
);
614 pthread_mutex_unlock( &font_cache_lock
);
615 TRACE( "%d %s -> %p\n", (int)ptr
->lf
.lfHeight
, debugstr_w(ptr
->lf
.lfFaceName
), ptr
);
619 void release_cached_font( struct cached_font
*font
)
621 if (font
) InterlockedDecrement( &font
->ref
);
624 static struct cached_glyph
*add_cached_glyph( struct cached_font
*font
, UINT index
, UINT flags
,
625 struct cached_glyph
*glyph
)
627 struct cached_glyph
*ret
;
628 enum glyph_type type
= (flags
& ETO_GLYPH_INDEX
) ? GLYPH_INDEX
: GLYPH_WCHAR
;
629 UINT page
= index
/ GLYPH_CACHE_PAGE_SIZE
;
630 UINT entry
= index
% GLYPH_CACHE_PAGE_SIZE
;
632 if (!font
->glyphs
[type
][page
])
634 struct cached_glyph
**ptr
;
636 ptr
= calloc( 1, GLYPH_CACHE_PAGE_SIZE
* sizeof(*ptr
) );
642 if (InterlockedCompareExchangePointer( (void **)&font
->glyphs
[type
][page
], ptr
, NULL
))
645 ret
= InterlockedCompareExchangePointer( (void **)&font
->glyphs
[type
][page
][entry
], glyph
, NULL
);
646 if (!ret
) ret
= glyph
;
651 static struct cached_glyph
*get_cached_glyph( struct cached_font
*font
, UINT index
, UINT flags
)
653 enum glyph_type type
= (flags
& ETO_GLYPH_INDEX
) ? GLYPH_INDEX
: GLYPH_WCHAR
;
654 UINT page
= index
/ GLYPH_CACHE_PAGE_SIZE
;
656 if (!font
->glyphs
[type
][page
]) return NULL
;
657 return font
->glyphs
[type
][page
][index
% GLYPH_CACHE_PAGE_SIZE
];
660 /**********************************************************************
661 * get_text_bkgnd_masks
663 * See the comment above get_pen_bkgnd_masks
665 static inline void get_text_bkgnd_masks( DC
*dc
, const dib_info
*dib
, rop_mask
*mask
)
667 COLORREF bg
= dc
->attr
->background_color
;
671 if (dib
->bit_count
!= 1)
672 mask
->xor = get_pixel_color( dc
, dib
, bg
, FALSE
);
675 COLORREF fg
= dc
->attr
->text_color
;
676 mask
->xor = get_pixel_color( dc
, dib
, fg
, TRUE
);
677 if (fg
!= bg
) mask
->xor = ~mask
->xor;
681 static void draw_glyph( dib_info
*dib
, int x
, int y
, const GLYPHMETRICS
*metrics
,
682 const dib_info
*glyph_dib
, DWORD text_color
,
683 const struct font_intensities
*intensity
,
684 const struct clipped_rects
*clipped_rects
, RECT
*bounds
)
687 RECT rect
, clipped_rect
;
690 rect
.left
= x
+ metrics
->gmptGlyphOrigin
.x
;
691 rect
.top
= y
- metrics
->gmptGlyphOrigin
.y
;
692 rect
.right
= rect
.left
+ metrics
->gmBlackBoxX
;
693 rect
.bottom
= rect
.top
+ metrics
->gmBlackBoxY
;
694 if (bounds
) add_bounds_rect( bounds
, &rect
);
696 for (i
= 0; i
< clipped_rects
->count
; i
++)
698 if (intersect_rect( &clipped_rect
, &rect
, clipped_rects
->rects
+ i
))
700 src_origin
.x
= clipped_rect
.left
- rect
.left
;
701 src_origin
.y
= clipped_rect
.top
- rect
.top
;
703 if (glyph_dib
->bit_count
== 32)
704 dib
->funcs
->draw_subpixel_glyph( dib
, &clipped_rect
, glyph_dib
, &src_origin
,
705 text_color
, intensity
->gamma_ramp
);
707 dib
->funcs
->draw_glyph( dib
, &clipped_rect
, glyph_dib
, &src_origin
,
708 text_color
, intensity
->ranges
);
713 static int get_glyph_depth( UINT aa_flags
)
717 case GGO_BITMAP
: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */
718 case GGO_GRAY2_BITMAP
:
719 case GGO_GRAY4_BITMAP
:
720 case GGO_GRAY8_BITMAP
:
721 case WINE_GGO_GRAY16_BITMAP
: return 8;
723 case WINE_GGO_HRGB_BITMAP
:
724 case WINE_GGO_HBGR_BITMAP
:
725 case WINE_GGO_VRGB_BITMAP
:
726 case WINE_GGO_VBGR_BITMAP
: return 32;
729 ERR("Unexpected flags %08x\n", aa_flags
);
734 static const BYTE masks
[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
735 static const int padding
[4] = {0, 3, 2, 1};
737 /***********************************************************************
740 * Retrieve a 17-level bitmap for the appropriate glyph.
742 * For non-antialiased bitmaps convert them to the 17-level format
743 * using only values 0 or 16.
745 static struct cached_glyph
*cache_glyph_bitmap( DC
*dc
, struct cached_font
*font
, UINT index
, UINT flags
)
747 UINT ggo_flags
= font
->aa_flags
;
748 static const MAT2 identity
= { {0,1}, {0,0}, {0,0}, {0,1} };
749 UINT indices
[3] = {0, 0, 0x20};
753 int pad
= 0, stride
, bit_count
;
754 GLYPHMETRICS metrics
;
755 struct cached_glyph
*glyph
;
757 if (flags
& ETO_GLYPH_INDEX
) ggo_flags
|= GGO_GLYPH_INDEX
;
759 for (i
= 0; i
< ARRAY_SIZE( indices
); i
++)
762 ret
= NtGdiGetGlyphOutline( dc
->hSelf
, index
, ggo_flags
, &metrics
, 0, NULL
,
764 if (ret
!= GDI_ERROR
) break;
766 if (ret
== GDI_ERROR
) return NULL
;
767 if (!ret
) metrics
.gmBlackBoxX
= metrics
.gmBlackBoxY
= 0; /* empty glyph */
769 bit_count
= get_glyph_depth( font
->aa_flags
);
770 stride
= get_dib_stride( metrics
.gmBlackBoxX
, bit_count
);
771 size
= metrics
.gmBlackBoxY
* stride
;
772 glyph
= malloc( FIELD_OFFSET( struct cached_glyph
, bits
[size
] ));
773 if (!glyph
) return NULL
;
774 if (!size
) goto done
; /* empty glyph */
776 if (bit_count
== 8) pad
= padding
[ metrics
.gmBlackBoxX
% 4 ];
778 ret
= NtGdiGetGlyphOutline( dc
->hSelf
, index
, ggo_flags
, &metrics
, size
, glyph
->bits
,
780 if (ret
== GDI_ERROR
)
785 assert( ret
<= size
);
786 if (font
->aa_flags
== GGO_BITMAP
)
788 for (y
= metrics
.gmBlackBoxY
- 1; y
>= 0; y
--)
790 src
= glyph
->bits
+ y
* get_dib_stride( metrics
.gmBlackBoxX
, 1 );
791 dst
= glyph
->bits
+ y
* stride
;
793 if (pad
) memset( dst
+ metrics
.gmBlackBoxX
, 0, pad
);
795 for (x
= metrics
.gmBlackBoxX
- 1; x
>= 0; x
--)
796 dst
[x
] = (src
[x
/ 8] & masks
[x
% 8]) ? 0x10 : 0;
801 for (y
= 0, dst
= glyph
->bits
; y
< metrics
.gmBlackBoxY
; y
++, dst
+= stride
)
802 memset( dst
+ metrics
.gmBlackBoxX
, 0, pad
);
806 glyph
->metrics
= metrics
;
807 return add_cached_glyph( font
, index
, flags
, glyph
);
810 static void render_string( DC
*dc
, dib_info
*dib
, struct cached_font
*font
, INT x
, INT y
,
811 UINT flags
, const WCHAR
*str
, UINT count
, const INT
*dx
,
812 const struct clipped_rects
*clipped_rects
, RECT
*bounds
)
815 struct cached_glyph
*glyph
;
818 struct font_intensities intensity
;
820 glyph_dib
.bit_count
= get_glyph_depth( font
->aa_flags
);
821 glyph_dib
.rect
.left
= 0;
822 glyph_dib
.rect
.top
= 0;
823 glyph_dib
.bits
.is_copy
= FALSE
;
824 glyph_dib
.bits
.free
= NULL
;
826 text_color
= get_pixel_color( dc
, dib
, dc
->attr
->text_color
, TRUE
);
828 if (glyph_dib
.bit_count
== 32)
829 intensity
.gamma_ramp
= dc
->font_gamma_ramp
;
831 get_aa_ranges( dib
->funcs
->pixel_to_colorref( dib
, text_color
), intensity
.ranges
);
833 for (i
= 0; i
< count
; i
++)
835 if (!(glyph
= get_cached_glyph( font
, str
[i
], flags
)) &&
836 !(glyph
= cache_glyph_bitmap( dc
, font
, str
[i
], flags
))) continue;
838 glyph_dib
.width
= glyph
->metrics
.gmBlackBoxX
;
839 glyph_dib
.height
= glyph
->metrics
.gmBlackBoxY
;
840 glyph_dib
.rect
.right
= glyph
->metrics
.gmBlackBoxX
;
841 glyph_dib
.rect
.bottom
= glyph
->metrics
.gmBlackBoxY
;
842 glyph_dib
.stride
= get_dib_stride( glyph
->metrics
.gmBlackBoxX
, glyph_dib
.bit_count
);
843 glyph_dib
.bits
.ptr
= glyph
->bits
;
845 draw_glyph( dib
, x
, y
, &glyph
->metrics
, &glyph_dib
, text_color
, &intensity
, clipped_rects
, bounds
);
859 x
+= glyph
->metrics
.gmCellIncX
;
860 y
+= glyph
->metrics
.gmCellIncY
;
865 BOOL
render_aa_text_bitmapinfo( DC
*dc
, BITMAPINFO
*info
, struct gdi_image_bits
*bits
,
866 struct bitblt_coords
*src
, INT x
, INT y
, UINT flags
,
867 UINT aa_flags
, LPCWSTR str
, UINT count
, const INT
*dx
)
870 struct clipped_rects visrect
;
871 struct cached_font
*font
;
873 assert( info
->bmiHeader
.biBitCount
> 8 ); /* mono and indexed formats don't support anti-aliasing */
875 init_dib_info_from_bitmapinfo( &dib
, info
, bits
->ptr
);
878 visrect
.rects
= &src
->visrect
;
880 if (flags
& ETO_OPAQUE
)
882 rop_mask bkgnd_color
;
883 get_text_bkgnd_masks( dc
, &dib
, &bkgnd_color
);
884 dib
.funcs
->solid_rects( &dib
, 1, &src
->visrect
, bkgnd_color
.and, bkgnd_color
.xor );
887 if (!(font
= add_cached_font( dc
, dc
->hFont
, aa_flags
))) return FALSE
;
889 render_string( dc
, &dib
, font
, x
, y
, flags
, str
, count
, dx
, &visrect
, NULL
);
890 release_cached_font( font
);
894 /***********************************************************************
897 BOOL
dibdrv_ExtTextOut( PHYSDEV dev
, INT x
, INT y
, UINT flags
,
898 const RECT
*rect
, LPCWSTR str
, UINT count
, const INT
*dx
)
900 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
901 DC
*dc
= get_physdev_dc( dev
);
902 struct clipped_rects clipped_rects
;
905 if (!pdev
->font
) return FALSE
;
907 init_clipped_rects( &clipped_rects
);
908 reset_bounds( &bounds
);
910 if (flags
& ETO_OPAQUE
)
912 rop_mask bkgnd_color
;
913 get_text_bkgnd_masks( dc
, &pdev
->dib
, &bkgnd_color
);
914 add_bounds_rect( &bounds
, rect
);
915 get_clipped_rects( &pdev
->dib
, rect
, pdev
->clip
, &clipped_rects
);
916 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
,
917 bkgnd_color
.and, bkgnd_color
.xor );
920 if (count
== 0) goto done
;
922 if (flags
& ETO_CLIPPED
)
924 if (!(flags
& ETO_OPAQUE
)) /* otherwise we have done it already */
925 get_clipped_rects( &pdev
->dib
, rect
, pdev
->clip
, &clipped_rects
);
929 free_clipped_rects( &clipped_rects
);
930 get_clipped_rects( &pdev
->dib
, NULL
, pdev
->clip
, &clipped_rects
);
932 if (!clipped_rects
.count
) goto done
;
934 render_string( dc
, &pdev
->dib
, pdev
->font
, x
, y
, flags
, str
, count
, dx
,
935 &clipped_rects
, &bounds
);
938 add_clipped_bounds( pdev
, &bounds
, pdev
->clip
);
939 free_clipped_rects( &clipped_rects
);
943 /***********************************************************************
946 HFONT
dibdrv_SelectFont( PHYSDEV dev
, HFONT font
, UINT
*aa_flags
)
948 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
949 DC
*dc
= get_physdev_dc( dev
);
952 if (pdev
->dib
.bit_count
<= 8) *aa_flags
= GGO_BITMAP
; /* no anti-aliasing on <= 8bpp */
954 dev
= GET_NEXT_PHYSDEV( dev
, pSelectFont
);
955 ret
= dev
->funcs
->pSelectFont( dev
, font
, aa_flags
);
958 struct cached_font
*prev
= pdev
->font
;
959 pdev
->font
= add_cached_font( dc
, font
, *aa_flags
? *aa_flags
: GGO_BITMAP
);
960 release_cached_font( prev
);
965 /***********************************************************************
968 BOOL
dibdrv_Arc( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
969 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
971 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, 0 );
974 /***********************************************************************
977 BOOL
dibdrv_ArcTo( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
978 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
980 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, -1 );
983 /***********************************************************************
986 BOOL
dibdrv_Chord( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
987 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
989 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, 1 );
992 /***********************************************************************
995 BOOL
dibdrv_Ellipse( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
997 return dibdrv_RoundRect( dev
, left
, top
, right
, bottom
, right
- left
, bottom
- top
);
1000 static inline BOOL
is_interior( dib_info
*dib
, HRGN clip
, int x
, int y
, DWORD pixel
, UINT type
)
1002 /* the clip rgn stops the flooding */
1003 if (clip
&& !NtGdiPtInRegion( clip
, x
, y
)) return FALSE
;
1005 if (type
== FLOODFILLBORDER
)
1006 return dib
->funcs
->get_pixel( dib
, x
, y
) != pixel
;
1008 return dib
->funcs
->get_pixel( dib
, x
, y
) == pixel
;
1011 static void fill_row( dib_info
*dib
, HRGN clip
, RECT
*row
, DWORD pixel
, UINT type
, HRGN rgn
);
1013 static inline void do_next_row( dib_info
*dib
, HRGN clip
, const RECT
*row
, int offset
, DWORD pixel
, UINT type
, HRGN rgn
)
1017 next
.top
= row
->top
+ offset
;
1018 next
.bottom
= next
.top
+ 1;
1019 next
.left
= next
.right
= row
->left
;
1020 while (next
.right
< row
->right
)
1022 if (is_interior( dib
, clip
, next
.right
, next
.top
, pixel
, type
)) next
.right
++;
1025 if (next
.left
!= next
.right
&& !NtGdiPtInRegion( rgn
, next
.left
, next
.top
))
1026 fill_row( dib
, clip
, &next
, pixel
, type
, rgn
);
1027 next
.left
= ++next
.right
;
1030 if (next
.left
!= next
.right
&& !NtGdiPtInRegion( rgn
, next
.left
, next
.top
))
1031 fill_row( dib
, clip
, &next
, pixel
, type
, rgn
);
1034 static void fill_row( dib_info
*dib
, HRGN clip
, RECT
*row
, DWORD pixel
, UINT type
, HRGN rgn
)
1036 while (row
->left
> 0 && is_interior( dib
, clip
, row
->left
- 1, row
->top
, pixel
, type
)) row
->left
--;
1037 while (row
->right
< dib
->rect
.right
- dib
->rect
.left
&&
1038 is_interior( dib
, clip
, row
->right
, row
->top
, pixel
, type
))
1041 add_rect_to_region( rgn
, row
);
1043 if (row
->top
> 0) do_next_row( dib
, clip
, row
, -1, pixel
, type
, rgn
);
1044 if (row
->top
< dib
->rect
.bottom
- dib
->rect
.top
- 1)
1045 do_next_row( dib
, clip
, row
, 1, pixel
, type
, rgn
);
1048 /***********************************************************************
1049 * dibdrv_ExtFloodFill
1051 BOOL
dibdrv_ExtFloodFill( PHYSDEV dev
, INT x
, INT y
, COLORREF color
, UINT type
)
1053 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1054 DC
*dc
= get_physdev_dc( dev
);
1055 DWORD pixel
= get_pixel_color( dc
, &pdev
->dib
, color
, FALSE
);
1059 TRACE( "(%p, %d, %d, %s, %d)\n", pdev
, x
, y
, debugstr_color(color
), type
);
1061 if (x
< 0 || x
>= pdev
->dib
.rect
.right
- pdev
->dib
.rect
.left
||
1062 y
< 0 || y
>= pdev
->dib
.rect
.bottom
- pdev
->dib
.rect
.top
) return FALSE
;
1064 if (!is_interior( &pdev
->dib
, pdev
->clip
, x
, y
, pixel
, type
)) return FALSE
;
1066 if (!(rgn
= NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return FALSE
;
1072 fill_row( &pdev
->dib
, pdev
->clip
, &row
, pixel
, type
, rgn
);
1074 add_clipped_bounds( pdev
, NULL
, rgn
);
1075 brush_region( pdev
, rgn
);
1077 NtGdiDeleteObjectApp( rgn
);
1081 /***********************************************************************
1084 BOOL
dibdrv_FillPath( PHYSDEV dev
)
1086 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1088 return stroke_and_fill_path( pdev
, FALSE
, TRUE
);
1091 /***********************************************************************
1092 * dibdrv_GetNearestColor
1094 COLORREF
dibdrv_GetNearestColor( PHYSDEV dev
, COLORREF color
)
1096 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1097 DC
*dc
= get_physdev_dc( dev
);
1100 TRACE( "(%p, %s)\n", dev
, debugstr_color(color
) );
1102 pixel
= get_pixel_color( dc
, &pdev
->dib
, color
, FALSE
);
1103 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
1106 /***********************************************************************
1109 COLORREF
dibdrv_GetPixel( PHYSDEV dev
, INT x
, INT y
)
1111 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1112 DC
*dc
= get_physdev_dc( dev
);
1117 TRACE( "(%p, %d, %d)\n", dev
, x
, y
);
1121 lp_to_dp( dc
, &pt
, 1 );
1124 rect
.right
= rect
.left
+ 1;
1125 rect
.bottom
= rect
.top
+ 1;
1126 if (!clip_rect_to_dib( &pdev
->dib
, &rect
)) return CLR_INVALID
;
1128 pixel
= pdev
->dib
.funcs
->get_pixel( &pdev
->dib
, pt
.x
, pt
.y
);
1129 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
1132 /***********************************************************************
1135 BOOL
dibdrv_LineTo( PHYSDEV dev
, INT x
, INT y
)
1137 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1138 DC
*dc
= get_physdev_dc( dev
);
1143 pts
[0] = dc
->attr
->cur_pos
;
1147 lp_to_dp(dc
, pts
, 2);
1149 if (pdev
->pen_uses_region
&& !(region
= NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return FALSE
;
1151 reset_dash_origin(pdev
);
1153 ret
= pdev
->pen_lines(pdev
, 2, pts
, FALSE
, region
);
1154 add_pen_lines_bounds( pdev
, 2, pts
, region
);
1158 ret
= pen_region( pdev
, region
);
1159 NtGdiDeleteObjectApp( region
);
1164 /***********************************************************************
1167 * Returns the binary rop that is equivalent to the provided ternary rop
1168 * if the src bits are ignored.
1170 static inline INT
get_rop2_from_rop(INT rop
)
1172 return (((rop
>> 18) & 0x0c) | ((rop
>> 16) & 0x03)) + 1;
1175 /***********************************************************************
1178 BOOL
dibdrv_PatBlt( PHYSDEV dev
, struct bitblt_coords
*dst
, DWORD rop
)
1180 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1181 dib_brush
*brush
= &pdev
->brush
;
1182 DC
*dc
= get_physdev_dc( dev
);
1183 int rop2
= get_rop2_from_rop( rop
);
1184 struct clipped_rects clipped_rects
;
1185 DWORD
and = 0, xor = 0;
1188 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev
, dst
->x
, dst
->y
, dst
->width
, dst
->height
, (int)rop
);
1190 add_clipped_bounds( pdev
, &dst
->visrect
, 0 );
1191 if (!get_clipped_rects( &pdev
->dib
, &dst
->visrect
, pdev
->clip
, &clipped_rects
)) return TRUE
;
1193 switch (rop2
) /* shortcuts for rops that don't involve the brush */
1195 case R2_NOT
: and = ~0u;
1197 case R2_WHITE
: xor = ~0u;
1200 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
, and, xor );
1205 ret
= brush
->rects( pdev
, brush
, &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
,
1206 &dc
->attr
->brush_org
, rop2
);
1209 free_clipped_rects( &clipped_rects
);
1213 /***********************************************************************
1216 BOOL
dibdrv_PaintRgn( PHYSDEV dev
, HRGN rgn
)
1218 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1219 const WINEREGION
*region
;
1222 DC
*dc
= get_physdev_dc( dev
);
1224 TRACE("%p, %p\n", dev
, rgn
);
1226 reset_bounds( &bounds
);
1228 region
= get_wine_region( rgn
);
1229 if(!region
) return FALSE
;
1231 for(i
= 0; i
< region
->numRects
; i
++)
1233 rect
= get_device_rect( dc
, region
->rects
[i
].left
, region
->rects
[i
].top
,
1234 region
->rects
[i
].right
, region
->rects
[i
].bottom
, FALSE
);
1235 add_bounds_rect( &bounds
, &rect
);
1236 brush_rect( pdev
, &pdev
->brush
, &rect
, pdev
->clip
);
1239 release_wine_region( rgn
);
1240 add_clipped_bounds( pdev
, &bounds
, pdev
->clip
);
1244 /***********************************************************************
1245 * dibdrv_PolyPolygon
1247 BOOL
dibdrv_PolyPolygon( PHYSDEV dev
, const POINT
*pt
, const INT
*counts
, UINT polygons
)
1249 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1250 DC
*dc
= get_physdev_dc( dev
);
1251 DWORD total
, i
, pos
;
1255 POINT
*points
= pt_buf
;
1256 HRGN outline
= 0, interior
= 0;
1258 for (i
= total
= 0; i
< polygons
; i
++)
1260 if (counts
[i
] < 2) return FALSE
;
1264 if (total
> ARRAY_SIZE( pt_buf
))
1266 points
= malloc( total
* sizeof(*pt
) );
1267 if (!points
) return FALSE
;
1269 memcpy( points
, pt
, total
* sizeof(*pt
) );
1270 lp_to_dp( dc
, points
, total
);
1272 if (pdev
->brush
.style
!= BS_NULL
&&
1273 get_dib_rect( &pdev
->dib
, &rc
) &&
1274 !(interior
= create_polypolygon_region( points
, counts
, polygons
,
1275 dc
->attr
->poly_fill_mode
, &rc
)))
1281 if (pdev
->pen_uses_region
) outline
= NtGdiCreateRectRgn( 0, 0, 0, 0 );
1283 /* if not using a region, paint the interior first so the outline can overlap it */
1284 if (interior
&& !outline
)
1286 ret
= brush_region( pdev
, interior
);
1287 NtGdiDeleteObjectApp( interior
);
1291 for (i
= pos
= 0; i
< polygons
; i
++)
1293 reset_dash_origin( pdev
);
1294 pdev
->pen_lines( pdev
, counts
[i
], points
+ pos
, TRUE
, outline
);
1297 add_pen_lines_bounds( pdev
, total
, points
, outline
);
1301 NtGdiCombineRgn( interior
, interior
, outline
, RGN_DIFF
);
1302 ret
= brush_region( pdev
, interior
);
1303 NtGdiDeleteObjectApp( interior
);
1307 if (ret
) ret
= pen_region( pdev
, outline
);
1308 NtGdiDeleteObjectApp( outline
);
1312 if (points
!= pt_buf
) free( points
);
1316 /***********************************************************************
1317 * dibdrv_PolyPolyline
1319 BOOL
dibdrv_PolyPolyline( PHYSDEV dev
, const POINT
* pt
, const DWORD
* counts
, DWORD polylines
)
1321 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1322 DC
*dc
= get_physdev_dc( dev
);
1323 DWORD total
, pos
, i
;
1325 POINT
*points
= pt_buf
;
1329 for (i
= total
= 0; i
< polylines
; i
++)
1331 if (counts
[i
] < 2) return FALSE
;
1335 if (total
> ARRAY_SIZE( pt_buf
))
1337 points
= malloc( total
* sizeof(*pt
) );
1338 if (!points
) return FALSE
;
1340 memcpy( points
, pt
, total
* sizeof(*pt
) );
1341 lp_to_dp( dc
, points
, total
);
1343 if (pdev
->pen_uses_region
&& !(outline
= NtGdiCreateRectRgn( 0, 0, 0, 0 )))
1349 for (i
= pos
= 0; i
< polylines
; i
++)
1351 reset_dash_origin( pdev
);
1352 pdev
->pen_lines( pdev
, counts
[i
], points
+ pos
, FALSE
, outline
);
1355 add_pen_lines_bounds( pdev
, total
, points
, outline
);
1359 ret
= pen_region( pdev
, outline
);
1360 NtGdiDeleteObjectApp( outline
);
1364 if (points
!= pt_buf
) free( points
);
1368 /***********************************************************************
1371 BOOL
dibdrv_Rectangle( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
1373 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1374 DC
*dc
= get_physdev_dc( dev
);
1380 TRACE("(%p, %d, %d, %d, %d)\n", dev
, left
, top
, right
, bottom
);
1382 if (dc
->attr
->graphics_mode
== GM_ADVANCED
)
1384 const INT count
= 4;
1385 pts
[0].x
= pts
[3].x
= left
;
1386 pts
[0].y
= pts
[1].y
= top
;
1387 pts
[1].x
= pts
[2].x
= right
;
1388 pts
[2].y
= pts
[3].y
= bottom
;
1389 return dibdrv_PolyPolygon( dev
, pts
, &count
, 1 );
1392 if (!get_pen_device_rect( dc
, pdev
, &rect
, left
, top
, right
, bottom
)) return TRUE
;
1394 if (pdev
->pen_uses_region
&& !(outline
= NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return FALSE
;
1398 reset_dash_origin(pdev
);
1400 if (dc
->attr
->arc_direction
== AD_CLOCKWISE
)
1402 /* 4 pts going clockwise starting from bottom-right */
1403 pts
[0].x
= pts
[3].x
= rect
.right
;
1404 pts
[0].y
= pts
[1].y
= rect
.bottom
;
1405 pts
[1].x
= pts
[2].x
= rect
.left
;
1406 pts
[2].y
= pts
[3].y
= rect
.top
;
1410 /* 4 pts going anti-clockwise starting from top-right */
1411 pts
[0].x
= pts
[3].x
= rect
.right
;
1412 pts
[0].y
= pts
[1].y
= rect
.top
;
1413 pts
[1].x
= pts
[2].x
= rect
.left
;
1414 pts
[2].y
= pts
[3].y
= rect
.bottom
;
1417 pdev
->pen_lines(pdev
, 4, pts
, TRUE
, outline
);
1418 add_pen_lines_bounds( pdev
, 4, pts
, outline
);
1422 if (pdev
->brush
.style
!= BS_NULL
)
1424 HRGN interior
= NtGdiCreateRectRgn( rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1426 NtGdiCombineRgn( interior
, interior
, outline
, RGN_DIFF
);
1427 brush_region( pdev
, interior
);
1428 NtGdiDeleteObjectApp( interior
);
1430 ret
= pen_region( pdev
, outline
);
1431 NtGdiDeleteObjectApp( outline
);
1435 rect
.left
+= (pdev
->pen_width
+ 1) / 2;
1436 rect
.top
+= (pdev
->pen_width
+ 1) / 2;
1437 rect
.right
-= pdev
->pen_width
/ 2;
1438 rect
.bottom
-= pdev
->pen_width
/ 2;
1439 ret
= brush_rect( pdev
, &pdev
->brush
, &rect
, pdev
->clip
);
1444 /***********************************************************************
1447 BOOL
dibdrv_RoundRect( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
1448 INT ellipse_width
, INT ellipse_height
)
1450 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1451 DC
*dc
= get_physdev_dc( dev
);
1453 POINT pt
[2], *points
;
1456 HRGN outline
= 0, interior
= 0;
1458 if (!get_pen_device_rect( dc
, pdev
, &rect
, left
, top
, right
, bottom
)) return TRUE
;
1460 pt
[0].x
= pt
[0].y
= 0;
1461 pt
[1].x
= ellipse_width
;
1462 pt
[1].y
= ellipse_height
;
1463 lp_to_dp( dc
, pt
, 2 );
1464 ellipse_width
= min( rect
.right
- rect
.left
, abs( pt
[1].x
- pt
[0].x
));
1465 ellipse_height
= min( rect
.bottom
- rect
.top
, abs( pt
[1].y
- pt
[0].y
));
1466 if (ellipse_width
<= 2|| ellipse_height
<= 2)
1467 return dibdrv_Rectangle( dev
, left
, top
, right
, bottom
);
1469 points
= malloc( (ellipse_width
+ ellipse_height
) * 2 * sizeof(*points
) );
1470 if (!points
) return FALSE
;
1472 if (pdev
->pen_uses_region
&& !(outline
= NtGdiCreateRectRgn( 0, 0, 0, 0 )))
1478 if (pdev
->brush
.style
!= BS_NULL
&&
1479 !(interior
= NtGdiCreateRoundRectRgn( rect
.left
, rect
.top
, rect
.right
+ 1, rect
.bottom
+ 1,
1480 ellipse_width
, ellipse_height
)))
1483 if (outline
) NtGdiDeleteObjectApp( outline
);
1487 /* if not using a region, paint the interior first so the outline can overlap it */
1488 if (interior
&& !outline
)
1490 ret
= brush_region( pdev
, interior
);
1491 NtGdiDeleteObjectApp( interior
);
1495 count
= ellipse_first_quadrant( ellipse_width
, ellipse_height
, points
);
1497 if (dc
->attr
->arc_direction
== AD_CLOCKWISE
)
1499 for (i
= 0; i
< count
; i
++)
1501 points
[i
].x
= rect
.right
- ellipse_width
+ points
[i
].x
;
1502 points
[i
].y
= rect
.bottom
- ellipse_height
+ points
[i
].y
;
1507 for (i
= 0; i
< count
; i
++)
1509 points
[i
].x
= rect
.right
- ellipse_width
+ points
[i
].x
;
1510 points
[i
].y
= rect
.top
+ ellipse_height
- 1 - points
[i
].y
;
1514 /* horizontal symmetry */
1516 end
= 2 * count
- 1;
1517 /* avoid duplicating the midpoint */
1518 if (ellipse_width
% 2 && ellipse_width
== rect
.right
- rect
.left
) end
--;
1519 for (i
= 0; i
< count
; i
++)
1521 points
[end
- i
].x
= rect
.left
+ rect
.right
- 1 - points
[i
].x
;
1522 points
[end
- i
].y
= points
[i
].y
;
1526 /* vertical symmetry */
1528 end
= 2 * count
- 1;
1529 /* avoid duplicating the midpoint */
1530 if (ellipse_height
% 2 && ellipse_height
== rect
.bottom
- rect
.top
) end
--;
1531 for (i
= 0; i
< count
; i
++)
1533 points
[end
- i
].x
= points
[i
].x
;
1534 points
[end
- i
].y
= rect
.top
+ rect
.bottom
- 1 - points
[i
].y
;
1538 reset_dash_origin( pdev
);
1539 pdev
->pen_lines( pdev
, count
, points
, TRUE
, outline
);
1540 add_pen_lines_bounds( pdev
, count
, points
, outline
);
1544 NtGdiCombineRgn( interior
, interior
, outline
, RGN_DIFF
);
1545 ret
= brush_region( pdev
, interior
);
1546 NtGdiDeleteObjectApp( interior
);
1550 if (ret
) ret
= pen_region( pdev
, outline
);
1551 NtGdiDeleteObjectApp( outline
);
1557 /***********************************************************************
1560 BOOL
dibdrv_Pie( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
1561 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
1563 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, 2 );
1566 /***********************************************************************
1569 COLORREF
dibdrv_SetPixel( PHYSDEV dev
, INT x
, INT y
, COLORREF color
)
1571 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1572 DC
*dc
= get_physdev_dc( dev
);
1573 struct clipped_rects clipped_rects
;
1578 TRACE( "(%p, %d, %d, %s)\n", dev
, x
, y
, debugstr_color(color
) );
1582 lp_to_dp( dc
, &pt
, 1 );
1585 rect
.right
= rect
.left
+ 1;
1586 rect
.bottom
= rect
.top
+ 1;
1587 add_clipped_bounds( pdev
, &rect
, pdev
->clip
);
1589 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1590 pixel
= get_pixel_color( dc
, &pdev
->dib
, color
, FALSE
);
1591 color
= pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
1593 if (!get_clipped_rects( &pdev
->dib
, &rect
, pdev
->clip
, &clipped_rects
)) return color
;
1594 fill_with_pixel( dc
, &pdev
->dib
, pixel
, clipped_rects
.count
, clipped_rects
.rects
, dc
->attr
->rop_mode
);
1595 free_clipped_rects( &clipped_rects
);
1599 /***********************************************************************
1600 * dibdrv_StrokeAndFillPath
1602 BOOL
dibdrv_StrokeAndFillPath( PHYSDEV dev
)
1604 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1606 return stroke_and_fill_path( pdev
, TRUE
, TRUE
);
1609 /***********************************************************************
1612 BOOL
dibdrv_StrokePath( PHYSDEV dev
)
1614 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1616 return stroke_and_fill_path( pdev
, TRUE
, FALSE
);