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
22 #include "gdi_private.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dib
);
29 static BOOL
brush_rect( dibdrv_physdev
*pdev
, dib_brush
*brush
, const RECT
*rect
, HRGN clip
)
31 struct clipped_rects clipped_rects
;
34 if (!get_clipped_rects( &pdev
->dib
, rect
, clip
, &clipped_rects
)) return TRUE
;
35 ret
= brush
->rects( pdev
, brush
, &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
,
36 GetROP2( pdev
->dev
.hdc
));
37 free_clipped_rects( &clipped_rects
);
41 /* paint a region with the brush (note: the region can be modified) */
42 static BOOL
brush_region( dibdrv_physdev
*pdev
, HRGN region
)
44 if (pdev
->clip
) CombineRgn( region
, region
, pdev
->clip
, RGN_AND
);
45 return brush_rect( pdev
, &pdev
->brush
, NULL
, region
);
48 /* paint a region with the pen (note: the region can be modified) */
49 static BOOL
pen_region( dibdrv_physdev
*pdev
, HRGN region
)
51 if (pdev
->clip
) CombineRgn( region
, region
, pdev
->clip
, RGN_AND
);
52 return brush_rect( pdev
, &pdev
->pen_brush
, NULL
, region
);
55 static RECT
get_device_rect( HDC hdc
, int left
, int top
, int right
, int bottom
, BOOL rtl_correction
)
63 if (rtl_correction
&& GetLayout( hdc
) & LAYOUT_RTL
)
65 /* shift the rectangle so that the right border is included after mirroring */
66 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
70 LPtoDP( hdc
, (POINT
*)&rect
, 2 );
71 if (rect
.left
> rect
.right
)
74 rect
.left
= rect
.right
;
77 if (rect
.top
> rect
.bottom
)
80 rect
.top
= rect
.bottom
;
86 static BOOL
get_pen_device_rect( dibdrv_physdev
*dev
, RECT
*rect
, int left
, int top
, int right
, int bottom
)
88 *rect
= get_device_rect( dev
->dev
.hdc
, left
, top
, right
, bottom
, TRUE
);
89 if (rect
->left
== rect
->right
|| rect
->top
== rect
->bottom
) return FALSE
;
91 if (dev
->pen_style
== PS_INSIDEFRAME
)
93 rect
->left
+= dev
->pen_width
/ 2;
94 rect
->top
+= dev
->pen_width
/ 2;
95 rect
->right
-= (dev
->pen_width
- 1) / 2;
96 rect
->bottom
-= (dev
->pen_width
- 1) / 2;
101 static void add_pen_lines_bounds( dibdrv_physdev
*dev
, int count
, const POINT
*points
, HRGN rgn
)
103 const WINEREGION
*region
;
107 if (!dev
->bounds
) return;
108 reset_bounds( &bounds
);
110 if (dev
->pen_uses_region
)
112 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
113 width
= dev
->pen_width
+ 2;
114 if (dev
->pen_join
== PS_JOIN_MITER
)
117 if (dev
->pen_endcap
== PS_ENDCAP_SQUARE
) width
= (width
* 3 + 1) / 2;
121 if (dev
->pen_endcap
== PS_ENDCAP_SQUARE
) width
-= width
/ 4;
122 else width
= (width
+ 1) / 2;
125 /* in case the heuristics are wrong, add the actual region too */
126 if ((region
= get_wine_region( rgn
)))
128 add_bounds_rect( &bounds
, ®ion
->extents
);
129 release_wine_region( rgn
);
135 rect
.left
= points
->x
- width
;
136 rect
.top
= points
->y
- width
;
137 rect
.right
= points
->x
+ width
+ 1;
138 rect
.bottom
= points
->y
+ width
+ 1;
139 add_bounds_rect( &bounds
, &rect
);
143 add_clipped_bounds( dev
, &bounds
, dev
->clip
);
146 /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */
147 /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */
148 static int ellipse_first_quadrant( int width
, int height
, POINT
*data
)
150 const int a
= width
- 1;
151 const int b
= height
- 1;
152 const INT64 asq
= (INT64
)8 * a
* a
;
153 const INT64 bsq
= (INT64
)8 * b
* b
;
154 INT64 dx
= (INT64
)4 * b
* b
* (1 - a
);
155 INT64 dy
= (INT64
)4 * a
* a
* (1 + (b
% 2));
156 INT64 err
= dx
+ dy
+ a
* a
* (b
% 2);
163 /* based on an algorithm by Alois Zingl */
165 while (pt
.x
>= width
/ 2)
183 static int find_intersection( const POINT
*points
, int x
, int y
, int count
)
189 if (x
>= 0) /* first quadrant */
191 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* y
<= points
[i
].y
* x
) break;
194 /* second quadrant */
195 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* y
< points
[i
].y
* -x
) break;
196 return 2 * count
- i
;
198 if (x
>= 0) /* fourth quadrant */
200 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* -y
<= points
[i
].y
* x
) break;
201 return 4 * count
- i
;
204 for (i
= 0; i
< count
; i
++) if (points
[i
].x
* -y
< points
[i
].y
* -x
) break;
205 return 2 * count
+ i
;
208 static int get_arc_points( PHYSDEV dev
, const RECT
*rect
, POINT start
, POINT end
, POINT
*points
)
210 int i
, pos
, count
, start_pos
, end_pos
;
211 int width
= rect
->right
- rect
->left
;
212 int height
= rect
->bottom
- rect
->top
;
214 count
= ellipse_first_quadrant( width
, height
, points
);
215 for (i
= 0; i
< count
; i
++)
217 points
[i
].x
-= width
/ 2;
218 points
[i
].y
-= height
/ 2;
220 if (GetArcDirection( dev
->hdc
) != AD_CLOCKWISE
)
225 start_pos
= find_intersection( points
, start
.x
, start
.y
, count
);
226 end_pos
= find_intersection( points
, end
.x
, end
.y
, count
);
227 if (end_pos
<= start_pos
) end_pos
+= 4 * count
;
230 if (GetArcDirection( dev
->hdc
) == AD_CLOCKWISE
)
232 for (i
= start_pos
; i
< end_pos
; i
++, pos
++)
234 switch ((i
/ count
) % 4)
237 points
[pos
].x
= rect
->left
+ width
/2 + points
[i
% count
].x
;
238 points
[pos
].y
= rect
->top
+ height
/2 + points
[i
% count
].y
;
241 points
[pos
].x
= rect
->left
+ width
/2 - points
[count
- 1 - i
% count
].x
;
242 points
[pos
].y
= rect
->top
+ height
/2 + points
[count
- 1 - i
% count
].y
;
245 points
[pos
].x
= rect
->left
+ width
/2 - points
[i
% count
].x
;
246 points
[pos
].y
= rect
->top
+ height
/2 - points
[i
% count
].y
;
249 points
[pos
].x
= rect
->left
+ width
/2 + points
[count
- 1 - i
% count
].x
;
250 points
[pos
].y
= rect
->top
+ height
/2 - points
[count
- 1 - i
% count
].y
;
257 for (i
= start_pos
; i
< end_pos
; i
++, pos
++)
259 switch ((i
/ count
) % 4)
262 points
[pos
].x
= rect
->left
+ width
/2 + points
[i
% count
].x
;
263 points
[pos
].y
= rect
->top
+ height
/2 - points
[i
% count
].y
;
266 points
[pos
].x
= rect
->left
+ width
/2 - points
[count
- 1 - i
% count
].x
;
267 points
[pos
].y
= rect
->top
+ height
/2 - points
[count
- 1 - i
% count
].y
;
270 points
[pos
].x
= rect
->left
+ width
/2 - points
[i
% count
].x
;
271 points
[pos
].y
= rect
->top
+ height
/2 + points
[i
% count
].y
;
274 points
[pos
].x
= rect
->left
+ width
/2 + points
[count
- 1 - i
% count
].x
;
275 points
[pos
].y
= rect
->top
+ height
/2 + points
[count
- 1 - i
% count
].y
;
281 memmove( points
, points
+ count
, (pos
- count
) * sizeof(POINT
) );
285 /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */
286 static BOOL
draw_arc( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
287 INT start_x
, INT start_y
, INT end_x
, INT end_y
, INT extra_lines
)
289 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
291 POINT pt
[2], *points
;
292 int width
, height
, count
;
294 HRGN outline
= 0, interior
= 0;
296 if (!get_pen_device_rect( pdev
, &rect
, left
, top
, right
, bottom
)) return TRUE
;
298 width
= rect
.right
- rect
.left
;
299 height
= rect
.bottom
- rect
.top
;
305 LPtoDP( dev
->hdc
, pt
, 2 );
306 /* make them relative to the ellipse center */
307 pt
[0].x
-= left
+ width
/ 2;
308 pt
[0].y
-= top
+ height
/ 2;
309 pt
[1].x
-= left
+ width
/ 2;
310 pt
[1].y
-= top
+ height
/ 2;
312 points
= HeapAlloc( GetProcessHeap(), 0, (width
+ height
) * 3 * sizeof(*points
) );
313 if (!points
) return FALSE
;
315 if (extra_lines
== -1)
317 GetCurrentPositionEx( dev
->hdc
, points
);
318 LPtoDP( dev
->hdc
, points
, 1 );
319 count
= 1 + get_arc_points( dev
, &rect
, pt
[0], pt
[1], points
+ 1 );
321 else count
= get_arc_points( dev
, &rect
, pt
[0], pt
[1], points
);
323 if (extra_lines
== 2)
325 points
[count
].x
= rect
.left
+ width
/ 2;
326 points
[count
].y
= rect
.top
+ height
/ 2;
331 HeapFree( GetProcessHeap(), 0, points
);
335 if (pdev
->pen_uses_region
&& !(outline
= CreateRectRgn( 0, 0, 0, 0 )))
337 HeapFree( GetProcessHeap(), 0, points
);
341 if (pdev
->brush
.style
!= BS_NULL
&& extra_lines
> 0 &&
342 !(interior
= CreatePolygonRgn( points
, count
, WINDING
)))
344 HeapFree( GetProcessHeap(), 0, points
);
345 if (outline
) DeleteObject( outline
);
349 /* if not using a region, paint the interior first so the outline can overlap it */
350 if (interior
&& !outline
)
352 ret
= brush_region( pdev
, interior
);
353 DeleteObject( interior
);
357 reset_dash_origin( pdev
);
358 pdev
->pen_lines( pdev
, count
, points
, extra_lines
> 0, outline
);
359 add_pen_lines_bounds( pdev
, count
, points
, outline
);
363 CombineRgn( interior
, interior
, outline
, RGN_DIFF
);
364 ret
= brush_region( pdev
, interior
);
365 DeleteObject( interior
);
369 if (ret
) ret
= pen_region( pdev
, outline
);
370 DeleteObject( outline
);
372 HeapFree( GetProcessHeap(), 0, points
);
376 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
377 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
378 static const BYTE ramp
[17] =
381 0x8c, 0x9a, 0xa7, 0xb2,
382 0xbd, 0xc7, 0xd0, 0xd9,
383 0xe1, 0xe9, 0xf0, 0xf8,
387 /* For a give text-color component and a glyph level, calculate the
388 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
389 components respectively.
391 The minimum is a linear interpolation between 0 and the value in
394 The maximum is a linear interpolation between the value from the
395 ramp table read in reverse and 0xff.
397 To find the resulting pixel intensity, we note that if the text and
398 bkgnd intensities are the same then the result must be that
399 intensity. Otherwise we linearly interpolate between either the
400 min or the max value and this intermediate value depending on which
401 side of the inequality we lie.
404 static inline void get_range( BYTE aa
, DWORD text_comp
, BYTE
*min_comp
, BYTE
*max_comp
)
406 *min_comp
= (ramp
[aa
] * text_comp
) / 0xff;
407 *max_comp
= ramp
[16 - aa
] + ((0xff - ramp
[16 - aa
]) * text_comp
) / 0xff;
410 static inline void get_aa_ranges( COLORREF col
, struct intensity_range intensities
[17] )
414 for (i
= 0; i
< 17; i
++)
416 get_range( i
, GetRValue(col
), &intensities
[i
].r_min
, &intensities
[i
].r_max
);
417 get_range( i
, GetGValue(col
), &intensities
[i
].g_min
, &intensities
[i
].g_max
);
418 get_range( i
, GetBValue(col
), &intensities
[i
].b_min
, &intensities
[i
].b_max
);
422 /**********************************************************************
423 * get_text_bkgnd_masks
425 * See the comment above get_pen_bkgnd_masks
427 static inline void get_text_bkgnd_masks( dibdrv_physdev
*pdev
, rop_mask
*mask
)
429 COLORREF bg
= GetBkColor( pdev
->dev
.hdc
);
433 if (pdev
->dib
.bit_count
!= 1)
434 mask
->xor = get_pixel_color( pdev
, bg
, FALSE
);
437 COLORREF fg
= GetTextColor( pdev
->dev
.hdc
);
438 mask
->xor = get_pixel_color( pdev
, fg
, TRUE
);
439 if (fg
!= bg
) mask
->xor = ~mask
->xor;
443 static void draw_glyph( dib_info
*dib
, int x
, int y
, const GLYPHMETRICS
*metrics
,
444 const dib_info
*glyph_dib
, DWORD text_color
,
445 const struct intensity_range
*ranges
, const struct clipped_rects
*clipped_rects
,
449 RECT rect
, clipped_rect
;
452 rect
.left
= x
+ metrics
->gmptGlyphOrigin
.x
;
453 rect
.top
= y
- metrics
->gmptGlyphOrigin
.y
;
454 rect
.right
= rect
.left
+ metrics
->gmBlackBoxX
;
455 rect
.bottom
= rect
.top
+ metrics
->gmBlackBoxY
;
456 if (bounds
) add_bounds_rect( bounds
, &rect
);
458 for (i
= 0; i
< clipped_rects
->count
; i
++)
460 if (intersect_rect( &clipped_rect
, &rect
, clipped_rects
->rects
+ i
))
462 src_origin
.x
= clipped_rect
.left
- rect
.left
;
463 src_origin
.y
= clipped_rect
.top
- rect
.top
;
465 if (glyph_dib
->bit_count
== 32)
466 dib
->funcs
->draw_subpixel_glyph( dib
, &clipped_rect
, glyph_dib
, &src_origin
,
469 dib
->funcs
->draw_glyph( dib
, &clipped_rect
, glyph_dib
, &src_origin
,
470 text_color
, ranges
);
475 static int get_glyph_depth( UINT aa_flags
)
479 case GGO_BITMAP
: return 1;
481 case GGO_GRAY2_BITMAP
:
482 case GGO_GRAY4_BITMAP
:
483 case GGO_GRAY8_BITMAP
:
484 case WINE_GGO_GRAY16_BITMAP
: return 8;
486 case WINE_GGO_HRGB_BITMAP
:
487 case WINE_GGO_HBGR_BITMAP
:
488 case WINE_GGO_VRGB_BITMAP
:
489 case WINE_GGO_VBGR_BITMAP
: return 32;
492 ERR("Unexpected flags %08x\n", aa_flags
);
497 static const BYTE masks
[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
498 static const int padding
[4] = {0, 3, 2, 1};
500 /***********************************************************************
503 * Retrieve a 17-level bitmap for the appropriate glyph.
505 * For non-antialiased bitmaps convert them to the 17-level format
506 * using only values 0 or 16.
508 static DWORD
get_glyph_bitmap( HDC hdc
, UINT index
, UINT aa_flags
, GLYPHMETRICS
*metrics
,
509 dib_info
*glyph_dib
)
511 UINT ggo_flags
= aa_flags
| GGO_GLYPH_INDEX
;
512 static const MAT2 identity
= { {0,1}, {0,0}, {0,0}, {0,1} };
513 UINT indices
[3] = {0, 0, 0x20};
516 BYTE
*buf
, *dst
, *src
;
517 int pad
= 0, depth
= get_glyph_depth( aa_flags
);
519 glyph_dib
->bits
.ptr
= NULL
;
520 glyph_dib
->bits
.is_copy
= FALSE
;
521 glyph_dib
->bits
.free
= free_heap_bits
;
522 glyph_dib
->bits
.param
= NULL
;
526 for (i
= 0; i
< sizeof(indices
) / sizeof(indices
[0]); i
++)
529 ret
= GetGlyphOutlineW( hdc
, index
, ggo_flags
, metrics
, 0, NULL
, &identity
);
530 if (ret
!= GDI_ERROR
) break;
533 if (ret
== GDI_ERROR
) return ERROR_NOT_FOUND
;
534 if (!ret
) return ERROR_SUCCESS
; /* empty glyph */
536 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
537 glyph_dib
->bit_count
= depth
== 1 ? 8 : depth
;
538 glyph_dib
->width
= metrics
->gmBlackBoxX
;
539 glyph_dib
->height
= metrics
->gmBlackBoxY
;
540 glyph_dib
->rect
.left
= 0;
541 glyph_dib
->rect
.top
= 0;
542 glyph_dib
->rect
.right
= metrics
->gmBlackBoxX
;
543 glyph_dib
->rect
.bottom
= metrics
->gmBlackBoxY
;
544 glyph_dib
->stride
= get_dib_stride( metrics
->gmBlackBoxX
, glyph_dib
->bit_count
);
546 if (glyph_dib
->bit_count
== 8) pad
= padding
[ metrics
->gmBlackBoxX
% 4 ];
547 size
= metrics
->gmBlackBoxY
* glyph_dib
->stride
;
549 buf
= HeapAlloc( GetProcessHeap(), 0, size
);
550 if (!buf
) return ERROR_OUTOFMEMORY
;
552 ret
= GetGlyphOutlineW( hdc
, index
, ggo_flags
, metrics
, size
, buf
, &identity
);
553 if (ret
== GDI_ERROR
)
555 HeapFree( GetProcessHeap(), 0, buf
);
556 return ERROR_NOT_FOUND
;
559 if (aa_flags
== GGO_BITMAP
)
561 for (y
= metrics
->gmBlackBoxY
- 1; y
>= 0; y
--)
563 src
= buf
+ y
* get_dib_stride( metrics
->gmBlackBoxX
, 1 );
564 dst
= buf
+ y
* glyph_dib
->stride
;
566 if (pad
) memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
568 for (x
= metrics
->gmBlackBoxX
- 1; x
>= 0; x
--)
569 dst
[x
] = (src
[x
/ 8] & masks
[x
% 8]) ? 0x10 : 0;
574 for (y
= 0, dst
= buf
; y
< metrics
->gmBlackBoxY
; y
++, dst
+= glyph_dib
->stride
)
575 memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
578 glyph_dib
->bits
.ptr
= buf
;
579 return ERROR_SUCCESS
;
582 static void render_string( HDC hdc
, dib_info
*dib
, INT x
, INT y
, UINT flags
, UINT aa_flags
,
583 const WCHAR
*str
, UINT count
, const INT
*dx
, DWORD text_color
,
584 const struct intensity_range
*ranges
, const struct clipped_rects
*clipped_rects
,
589 GLYPHMETRICS metrics
;
592 for (i
= 0; i
< count
; i
++)
594 err
= get_glyph_bitmap( hdc
, (UINT
)str
[i
], aa_flags
, &metrics
, &glyph_dib
);
597 if (glyph_dib
.bits
.ptr
)
598 draw_glyph( dib
, x
, y
, &metrics
, &glyph_dib
, text_color
, ranges
, clipped_rects
, bounds
);
600 free_dib_info( &glyph_dib
);
614 x
+= metrics
.gmCellIncX
;
615 y
+= metrics
.gmCellIncY
;
620 BOOL
render_aa_text_bitmapinfo( HDC hdc
, BITMAPINFO
*info
, struct gdi_image_bits
*bits
,
621 struct bitblt_coords
*src
, INT x
, INT y
, UINT flags
,
622 UINT aa_flags
, LPCWSTR str
, UINT count
, const INT
*dx
)
627 DWORD fg_pixel
, bg_pixel
;
628 struct intensity_range glyph_intensities
[17];
629 struct clipped_rects visrect
;
631 assert( info
->bmiHeader
.biBitCount
> 8 ); /* mono and indexed formats don't support anti-aliasing */
633 init_dib_info_from_bitmapinfo( &dib
, info
, bits
->ptr
);
636 visrect
.rects
= &src
->visrect
;
638 fg
= make_rgb_colorref( hdc
, &dib
, GetTextColor( hdc
), &got_pixel
, &fg_pixel
);
639 if (!got_pixel
) fg_pixel
= dib
.funcs
->colorref_to_pixel( &dib
, fg
);
641 get_aa_ranges( fg
, glyph_intensities
);
643 if (flags
& ETO_OPAQUE
)
645 rop_mask bkgnd_color
;
647 bg
= make_rgb_colorref( hdc
, &dib
, GetBkColor( hdc
), &got_pixel
, &bg_pixel
);
648 if (!got_pixel
) bg_pixel
= dib
.funcs
->colorref_to_pixel( &dib
, bg
);
651 bkgnd_color
.xor = bg_pixel
;
652 dib
.funcs
->solid_rects( &dib
, 1, &src
->visrect
, bkgnd_color
.and, bkgnd_color
.xor );
655 render_string( hdc
, &dib
, x
, y
, flags
, aa_flags
, str
, count
, dx
,
656 fg_pixel
, glyph_intensities
, &visrect
, NULL
);
660 /***********************************************************************
663 BOOL
dibdrv_ExtTextOut( PHYSDEV dev
, INT x
, INT y
, UINT flags
,
664 const RECT
*rect
, LPCWSTR str
, UINT count
, const INT
*dx
)
666 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
667 struct clipped_rects clipped_rects
;
671 struct intensity_range ranges
[17];
673 init_clipped_rects( &clipped_rects
);
674 reset_bounds( &bounds
);
676 if (flags
& ETO_OPAQUE
)
678 rop_mask bkgnd_color
;
679 get_text_bkgnd_masks( pdev
, &bkgnd_color
);
680 add_bounds_rect( &bounds
, rect
);
681 get_clipped_rects( &pdev
->dib
, rect
, pdev
->clip
, &clipped_rects
);
682 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
,
683 bkgnd_color
.and, bkgnd_color
.xor );
686 if (count
== 0) goto done
;
688 if (flags
& ETO_CLIPPED
)
690 if (!(flags
& ETO_OPAQUE
)) /* otherwise we have done it already */
691 get_clipped_rects( &pdev
->dib
, rect
, pdev
->clip
, &clipped_rects
);
695 free_clipped_rects( &clipped_rects
);
696 get_clipped_rects( &pdev
->dib
, NULL
, pdev
->clip
, &clipped_rects
);
698 if (!clipped_rects
.count
) goto done
;
700 text_color
= get_pixel_color( pdev
, GetTextColor( pdev
->dev
.hdc
), TRUE
);
701 get_aa_ranges( pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, text_color
), ranges
);
703 dc
= get_dc_ptr( dev
->hdc
);
704 render_string( dev
->hdc
, &pdev
->dib
, x
, y
, flags
, dc
->aa_flags
, str
, count
, dx
,
705 text_color
, ranges
, &clipped_rects
, &bounds
);
706 release_dc_ptr( dc
);
709 add_clipped_bounds( pdev
, &bounds
, pdev
->clip
);
710 free_clipped_rects( &clipped_rects
);
714 /***********************************************************************
717 BOOL
dibdrv_Arc( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
718 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
720 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, 0 );
723 /***********************************************************************
726 BOOL
dibdrv_ArcTo( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
727 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
729 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, -1 );
732 /***********************************************************************
735 BOOL
dibdrv_Chord( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
736 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
738 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, 1 );
741 /***********************************************************************
744 BOOL
dibdrv_Ellipse( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
746 return dibdrv_RoundRect( dev
, left
, top
, right
, bottom
, right
- left
, bottom
- top
);
749 static inline BOOL
is_interior( dib_info
*dib
, HRGN clip
, int x
, int y
, DWORD pixel
, UINT type
)
751 /* the clip rgn stops the flooding */
752 if (clip
&& !PtInRegion( clip
, x
, y
)) return FALSE
;
754 if (type
== FLOODFILLBORDER
)
755 return dib
->funcs
->get_pixel( dib
, x
, y
) != pixel
;
757 return dib
->funcs
->get_pixel( dib
, x
, y
) == pixel
;
760 static void fill_row( dib_info
*dib
, HRGN clip
, RECT
*row
, DWORD pixel
, UINT type
, HRGN rgn
);
762 static inline void do_next_row( dib_info
*dib
, HRGN clip
, const RECT
*row
, int offset
, DWORD pixel
, UINT type
, HRGN rgn
)
766 next
.top
= row
->top
+ offset
;
767 next
.bottom
= next
.top
+ 1;
768 next
.left
= next
.right
= row
->left
;
769 while (next
.right
< row
->right
)
771 if (is_interior( dib
, clip
, next
.right
, next
.top
, pixel
, type
)) next
.right
++;
774 if (next
.left
!= next
.right
&& !PtInRegion( rgn
, next
.left
, next
.top
))
775 fill_row( dib
, clip
, &next
, pixel
, type
, rgn
);
776 next
.left
= ++next
.right
;
779 if (next
.left
!= next
.right
&& !PtInRegion( rgn
, next
.left
, next
.top
))
780 fill_row( dib
, clip
, &next
, pixel
, type
, rgn
);
783 static void fill_row( dib_info
*dib
, HRGN clip
, RECT
*row
, DWORD pixel
, UINT type
, HRGN rgn
)
785 while (row
->left
> 0 && is_interior( dib
, clip
, row
->left
- 1, row
->top
, pixel
, type
)) row
->left
--;
786 while (row
->right
< dib
->rect
.right
- dib
->rect
.left
&&
787 is_interior( dib
, clip
, row
->right
, row
->top
, pixel
, type
))
790 add_rect_to_region( rgn
, row
);
792 if (row
->top
> 0) do_next_row( dib
, clip
, row
, -1, pixel
, type
, rgn
);
793 if (row
->top
< dib
->rect
.bottom
- dib
->rect
.top
- 1)
794 do_next_row( dib
, clip
, row
, 1, pixel
, type
, rgn
);
797 /***********************************************************************
798 * dibdrv_ExtFloodFill
800 BOOL
dibdrv_ExtFloodFill( PHYSDEV dev
, INT x
, INT y
, COLORREF color
, UINT type
)
802 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
803 DWORD pixel
= get_pixel_color( pdev
, color
, FALSE
);
807 TRACE( "(%p, %d, %d, %08x, %d)\n", pdev
, x
, y
, color
, type
);
809 if (!is_interior( &pdev
->dib
, pdev
->clip
, x
, y
, pixel
, type
)) return FALSE
;
811 if (!(rgn
= CreateRectRgn( 0, 0, 0, 0 ))) return FALSE
;
817 fill_row( &pdev
->dib
, pdev
->clip
, &row
, pixel
, type
, rgn
);
819 add_clipped_bounds( pdev
, NULL
, rgn
);
820 brush_region( pdev
, rgn
);
826 /***********************************************************************
827 * dibdrv_GetNearestColor
829 COLORREF
dibdrv_GetNearestColor( PHYSDEV dev
, COLORREF color
)
831 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
834 TRACE( "(%p, %08x)\n", dev
, color
);
836 pixel
= get_pixel_color( pdev
, color
, FALSE
);
837 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
840 /***********************************************************************
843 COLORREF
dibdrv_GetPixel( PHYSDEV dev
, INT x
, INT y
)
845 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
849 TRACE( "(%p, %d, %d)\n", dev
, x
, y
);
853 LPtoDP( dev
->hdc
, &pt
, 1 );
855 if (pt
.x
< 0 || pt
.x
>= pdev
->dib
.rect
.right
- pdev
->dib
.rect
.left
||
856 pt
.y
< 0 || pt
.y
>= pdev
->dib
.rect
.bottom
- pdev
->dib
.rect
.top
)
859 pixel
= pdev
->dib
.funcs
->get_pixel( &pdev
->dib
, pt
.x
, pt
.y
);
860 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
863 /***********************************************************************
866 BOOL
dibdrv_LineTo( PHYSDEV dev
, INT x
, INT y
)
868 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
873 GetCurrentPositionEx(dev
->hdc
, pts
);
877 LPtoDP(dev
->hdc
, pts
, 2);
879 if (pdev
->pen_uses_region
&& !(region
= CreateRectRgn( 0, 0, 0, 0 ))) return FALSE
;
881 reset_dash_origin(pdev
);
883 ret
= pdev
->pen_lines(pdev
, 2, pts
, FALSE
, region
);
884 add_pen_lines_bounds( pdev
, 2, pts
, region
);
888 ret
= pen_region( pdev
, region
);
889 DeleteObject( region
);
894 /***********************************************************************
897 * Returns the binary rop that is equivalent to the provided ternary rop
898 * if the src bits are ignored.
900 static inline INT
get_rop2_from_rop(INT rop
)
902 return (((rop
>> 18) & 0x0c) | ((rop
>> 16) & 0x03)) + 1;
905 /***********************************************************************
908 BOOL
dibdrv_PatBlt( PHYSDEV dev
, struct bitblt_coords
*dst
, DWORD rop
)
910 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
911 dib_brush
*brush
= &pdev
->brush
;
912 int rop2
= get_rop2_from_rop( rop
);
913 struct clipped_rects clipped_rects
;
914 DWORD
and = 0, xor = 0;
917 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev
, dst
->x
, dst
->y
, dst
->width
, dst
->height
, rop
);
919 add_clipped_bounds( pdev
, &dst
->visrect
, 0 );
920 if (!get_clipped_rects( &pdev
->dib
, &dst
->visrect
, pdev
->clip
, &clipped_rects
)) return TRUE
;
922 switch (rop2
) /* shortcuts for rops that don't involve the brush */
924 case R2_NOT
: and = ~0u;
926 case R2_WHITE
: xor = ~0u;
929 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
, and, xor );
934 ret
= brush
->rects( pdev
, brush
, &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
, rop2
);
937 free_clipped_rects( &clipped_rects
);
941 /***********************************************************************
944 BOOL
dibdrv_PaintRgn( PHYSDEV dev
, HRGN rgn
)
946 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
947 const WINEREGION
*region
;
951 TRACE("%p, %p\n", dev
, rgn
);
953 reset_bounds( &bounds
);
955 region
= get_wine_region( rgn
);
956 if(!region
) return FALSE
;
958 for(i
= 0; i
< region
->numRects
; i
++)
960 rect
= get_device_rect( dev
->hdc
, region
->rects
[i
].left
, region
->rects
[i
].top
,
961 region
->rects
[i
].right
, region
->rects
[i
].bottom
, FALSE
);
962 add_bounds_rect( &bounds
, &rect
);
963 brush_rect( pdev
, &pdev
->brush
, &rect
, pdev
->clip
);
966 release_wine_region( rgn
);
967 add_clipped_bounds( pdev
, &bounds
, pdev
->clip
);
971 /***********************************************************************
974 BOOL
dibdrv_PolyPolygon( PHYSDEV dev
, const POINT
*pt
, const INT
*counts
, DWORD polygons
)
976 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
980 HRGN outline
= 0, interior
= 0;
982 for (i
= total
= 0; i
< polygons
; i
++)
984 if (counts
[i
] < 2) return FALSE
;
988 points
= HeapAlloc( GetProcessHeap(), 0, total
* sizeof(*pt
) );
989 if (!points
) return FALSE
;
990 memcpy( points
, pt
, total
* sizeof(*pt
) );
991 LPtoDP( dev
->hdc
, points
, total
);
993 if (pdev
->brush
.style
!= BS_NULL
&&
994 !(interior
= CreatePolyPolygonRgn( points
, counts
, polygons
, GetPolyFillMode( dev
->hdc
))))
996 HeapFree( GetProcessHeap(), 0, points
);
1000 if (pdev
->pen_uses_region
) outline
= CreateRectRgn( 0, 0, 0, 0 );
1002 /* if not using a region, paint the interior first so the outline can overlap it */
1003 if (interior
&& !outline
)
1005 ret
= brush_region( pdev
, interior
);
1006 DeleteObject( interior
);
1010 for (i
= pos
= 0; i
< polygons
; i
++)
1012 reset_dash_origin( pdev
);
1013 pdev
->pen_lines( pdev
, counts
[i
], points
+ pos
, TRUE
, outline
);
1016 add_pen_lines_bounds( pdev
, total
, points
, outline
);
1020 CombineRgn( interior
, interior
, outline
, RGN_DIFF
);
1021 ret
= brush_region( pdev
, interior
);
1022 DeleteObject( interior
);
1026 if (ret
) ret
= pen_region( pdev
, outline
);
1027 DeleteObject( outline
);
1029 HeapFree( GetProcessHeap(), 0, points
);
1033 /***********************************************************************
1034 * dibdrv_PolyPolyline
1036 BOOL
dibdrv_PolyPolyline( PHYSDEV dev
, const POINT
* pt
, const DWORD
* counts
, DWORD polylines
)
1038 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1039 DWORD total
, pos
, i
;
1044 for (i
= total
= 0; i
< polylines
; i
++)
1046 if (counts
[i
] < 2) return FALSE
;
1050 points
= HeapAlloc( GetProcessHeap(), 0, total
* sizeof(*pt
) );
1051 if (!points
) return FALSE
;
1052 memcpy( points
, pt
, total
* sizeof(*pt
) );
1053 LPtoDP( dev
->hdc
, points
, total
);
1055 if (pdev
->pen_uses_region
&& !(outline
= CreateRectRgn( 0, 0, 0, 0 )))
1057 HeapFree( GetProcessHeap(), 0, points
);
1061 for (i
= pos
= 0; i
< polylines
; i
++)
1063 reset_dash_origin( pdev
);
1064 pdev
->pen_lines( pdev
, counts
[i
], points
+ pos
, FALSE
, outline
);
1067 add_pen_lines_bounds( pdev
, total
, points
, outline
);
1071 ret
= pen_region( pdev
, outline
);
1072 DeleteObject( outline
);
1075 HeapFree( GetProcessHeap(), 0, points
);
1079 /***********************************************************************
1082 BOOL
dibdrv_Polygon( PHYSDEV dev
, const POINT
*pt
, INT count
)
1084 INT counts
[1] = { count
};
1086 return dibdrv_PolyPolygon( dev
, pt
, counts
, 1 );
1089 /***********************************************************************
1092 BOOL
dibdrv_Polyline( PHYSDEV dev
, const POINT
* pt
, INT count
)
1094 DWORD counts
[1] = { count
};
1096 if (count
< 0) return FALSE
;
1097 return dibdrv_PolyPolyline( dev
, pt
, counts
, 1 );
1100 /***********************************************************************
1103 BOOL
dibdrv_Rectangle( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
1105 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1111 TRACE("(%p, %d, %d, %d, %d)\n", dev
, left
, top
, right
, bottom
);
1113 if (!get_pen_device_rect( pdev
, &rect
, left
, top
, right
, bottom
)) return TRUE
;
1115 if (pdev
->pen_uses_region
&& !(outline
= CreateRectRgn( 0, 0, 0, 0 ))) return FALSE
;
1119 reset_dash_origin(pdev
);
1121 if (GetArcDirection( dev
->hdc
) == AD_CLOCKWISE
)
1123 /* 4 pts going clockwise starting from bottom-right */
1124 pts
[0].x
= pts
[3].x
= rect
.right
;
1125 pts
[0].y
= pts
[1].y
= rect
.bottom
;
1126 pts
[1].x
= pts
[2].x
= rect
.left
;
1127 pts
[2].y
= pts
[3].y
= rect
.top
;
1131 /* 4 pts going anti-clockwise starting from top-right */
1132 pts
[0].x
= pts
[3].x
= rect
.right
;
1133 pts
[0].y
= pts
[1].y
= rect
.top
;
1134 pts
[1].x
= pts
[2].x
= rect
.left
;
1135 pts
[2].y
= pts
[3].y
= rect
.bottom
;
1138 pdev
->pen_lines(pdev
, 4, pts
, TRUE
, outline
);
1139 add_pen_lines_bounds( pdev
, 4, pts
, outline
);
1143 if (pdev
->brush
.style
!= BS_NULL
)
1145 HRGN interior
= CreateRectRgnIndirect( &rect
);
1147 CombineRgn( interior
, interior
, outline
, RGN_DIFF
);
1148 brush_region( pdev
, interior
);
1149 DeleteObject( interior
);
1151 ret
= pen_region( pdev
, outline
);
1152 DeleteObject( outline
);
1156 rect
.left
+= (pdev
->pen_width
+ 1) / 2;
1157 rect
.top
+= (pdev
->pen_width
+ 1) / 2;
1158 rect
.right
-= pdev
->pen_width
/ 2;
1159 rect
.bottom
-= pdev
->pen_width
/ 2;
1160 ret
= brush_rect( pdev
, &pdev
->brush
, &rect
, pdev
->clip
);
1165 /***********************************************************************
1168 BOOL
dibdrv_RoundRect( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
1169 INT ellipse_width
, INT ellipse_height
)
1171 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1173 POINT pt
[2], *points
;
1176 HRGN outline
= 0, interior
= 0;
1178 if (!get_pen_device_rect( pdev
, &rect
, left
, top
, right
, bottom
)) return TRUE
;
1180 pt
[0].x
= pt
[0].y
= 0;
1181 pt
[1].x
= ellipse_width
;
1182 pt
[1].y
= ellipse_height
;
1183 LPtoDP( dev
->hdc
, pt
, 2 );
1184 ellipse_width
= min( rect
.right
- rect
.left
, abs( pt
[1].x
- pt
[0].x
));
1185 ellipse_height
= min( rect
.bottom
- rect
.top
, abs( pt
[1].y
- pt
[0].y
));
1186 if (ellipse_width
<= 2|| ellipse_height
<= 2)
1187 return dibdrv_Rectangle( dev
, left
, top
, right
, bottom
);
1189 points
= HeapAlloc( GetProcessHeap(), 0, (ellipse_width
+ ellipse_height
) * 2 * sizeof(*points
) );
1190 if (!points
) return FALSE
;
1192 if (pdev
->pen_uses_region
&& !(outline
= CreateRectRgn( 0, 0, 0, 0 )))
1194 HeapFree( GetProcessHeap(), 0, points
);
1198 if (pdev
->brush
.style
!= BS_NULL
&&
1199 !(interior
= CreateRoundRectRgn( rect
.left
, rect
.top
, rect
.right
+ 1, rect
.bottom
+ 1,
1200 ellipse_width
, ellipse_height
)))
1202 HeapFree( GetProcessHeap(), 0, points
);
1203 if (outline
) DeleteObject( outline
);
1207 /* if not using a region, paint the interior first so the outline can overlap it */
1208 if (interior
&& !outline
)
1210 ret
= brush_region( pdev
, interior
);
1211 DeleteObject( interior
);
1215 count
= ellipse_first_quadrant( ellipse_width
, ellipse_height
, points
);
1217 if (GetArcDirection( dev
->hdc
) == AD_CLOCKWISE
)
1219 for (i
= 0; i
< count
; i
++)
1221 points
[i
].x
= rect
.right
- ellipse_width
+ points
[i
].x
;
1222 points
[i
].y
= rect
.bottom
- ellipse_height
+ points
[i
].y
;
1227 for (i
= 0; i
< count
; i
++)
1229 points
[i
].x
= rect
.right
- ellipse_width
+ points
[i
].x
;
1230 points
[i
].y
= rect
.top
+ ellipse_height
- 1 - points
[i
].y
;
1234 /* horizontal symmetry */
1236 end
= 2 * count
- 1;
1237 /* avoid duplicating the midpoint */
1238 if (ellipse_width
% 2 && ellipse_width
== rect
.right
- rect
.left
) end
--;
1239 for (i
= 0; i
< count
; i
++)
1241 points
[end
- i
].x
= rect
.left
+ rect
.right
- 1 - points
[i
].x
;
1242 points
[end
- i
].y
= points
[i
].y
;
1246 /* vertical symmetry */
1248 end
= 2 * count
- 1;
1249 /* avoid duplicating the midpoint */
1250 if (ellipse_height
% 2 && ellipse_height
== rect
.bottom
- rect
.top
) end
--;
1251 for (i
= 0; i
< count
; i
++)
1253 points
[end
- i
].x
= points
[i
].x
;
1254 points
[end
- i
].y
= rect
.top
+ rect
.bottom
- 1 - points
[i
].y
;
1258 reset_dash_origin( pdev
);
1259 pdev
->pen_lines( pdev
, count
, points
, TRUE
, outline
);
1260 add_pen_lines_bounds( pdev
, count
, points
, outline
);
1264 CombineRgn( interior
, interior
, outline
, RGN_DIFF
);
1265 ret
= brush_region( pdev
, interior
);
1266 DeleteObject( interior
);
1270 if (ret
) ret
= pen_region( pdev
, outline
);
1271 DeleteObject( outline
);
1273 HeapFree( GetProcessHeap(), 0, points
);
1277 /***********************************************************************
1280 BOOL
dibdrv_Pie( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
1281 INT start_x
, INT start_y
, INT end_x
, INT end_y
)
1283 return draw_arc( dev
, left
, top
, right
, bottom
, start_x
, start_y
, end_x
, end_y
, 2 );
1286 /***********************************************************************
1289 COLORREF
dibdrv_SetPixel( PHYSDEV dev
, INT x
, INT y
, COLORREF color
)
1291 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
1292 struct clipped_rects clipped_rects
;
1297 TRACE( "(%p, %d, %d, %08x)\n", dev
, x
, y
, color
);
1301 LPtoDP( dev
->hdc
, &pt
, 1 );
1304 rect
.right
= rect
.left
+ 1;
1305 rect
.bottom
= rect
.top
+ 1;
1306 add_clipped_bounds( pdev
, &rect
, pdev
->clip
);
1308 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
1309 pixel
= get_pixel_color( pdev
, color
, FALSE
);
1310 color
= pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
1312 if (!get_clipped_rects( &pdev
->dib
, &rect
, pdev
->clip
, &clipped_rects
)) return color
;
1313 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, clipped_rects
.count
, clipped_rects
.rects
, 0, pixel
);
1314 free_clipped_rects( &clipped_rects
);