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 RECT
get_device_rect( HDC hdc
, int left
, int top
, int right
, int bottom
, BOOL rtl_correction
)
37 if (rtl_correction
&& GetLayout( hdc
) & LAYOUT_RTL
)
39 /* shift the rectangle so that the right border is included after mirroring */
40 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
44 LPtoDP( hdc
, (POINT
*)&rect
, 2 );
45 if (rect
.left
> rect
.right
)
48 rect
.left
= rect
.right
;
51 if (rect
.top
> rect
.bottom
)
54 rect
.top
= rect
.bottom
;
60 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
61 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
62 static const BYTE ramp
[17] =
65 0x8c, 0x9a, 0xa7, 0xb2,
66 0xbd, 0xc7, 0xd0, 0xd9,
67 0xe1, 0xe9, 0xf0, 0xf8,
71 /* For a give text-color component and a glyph level, calculate the
72 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
73 components respectively.
75 The minimum is a linear interpolation between 0 and the value in
78 The maximum is a linear interpolation between the value from the
79 ramp table read in reverse and 0xff.
81 To find the resulting pixel intensity, we note that if the text and
82 bkgnd intensities are the same then the result must be that
83 intensity. Otherwise we linearly interpolate between either the
84 min or the max value and this intermediate value depending on which
85 side of the inequality we lie.
88 static inline void get_range( BYTE aa
, DWORD text_comp
, BYTE
*min_comp
, BYTE
*max_comp
)
90 *min_comp
= (ramp
[aa
] * text_comp
) / 0xff;
91 *max_comp
= ramp
[16 - aa
] + ((0xff - ramp
[16 - aa
]) * text_comp
) / 0xff;
94 static inline void get_aa_ranges( COLORREF col
, struct intensity_range intensities
[17] )
98 for (i
= 0; i
< 17; i
++)
100 get_range( i
, GetRValue(col
), &intensities
[i
].r_min
, &intensities
[i
].r_max
);
101 get_range( i
, GetGValue(col
), &intensities
[i
].g_min
, &intensities
[i
].g_max
);
102 get_range( i
, GetBValue(col
), &intensities
[i
].b_min
, &intensities
[i
].b_max
);
106 void update_aa_ranges( dibdrv_physdev
*pdev
)
108 COLORREF text
= pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pdev
->text_color
);
109 get_aa_ranges( text
, pdev
->glyph_intensities
);
112 /**********************************************************************
113 * get_text_bkgnd_masks
115 * See the comment above get_pen_bkgnd_masks
117 static inline void get_text_bkgnd_masks( dibdrv_physdev
*pdev
, rop_mask
*mask
)
119 COLORREF bg
= GetBkColor( pdev
->dev
.hdc
);
123 if (pdev
->dib
.bit_count
!= 1)
124 mask
->xor = get_pixel_color( pdev
, bg
, FALSE
);
127 mask
->xor = ~pdev
->text_color
;
128 if (GetTextColor( pdev
->dev
.hdc
) == bg
)
129 mask
->xor = pdev
->text_color
;
133 static void draw_glyph( dibdrv_physdev
*pdev
, const POINT
*origin
, const GLYPHMETRICS
*metrics
,
134 const struct gdi_image_bits
*image
)
136 const WINEREGION
*clip
= get_wine_region( pdev
->clip
);
138 RECT rect
, clipped_rect
;
142 glyph_dib
.bit_count
= 8;
143 glyph_dib
.width
= metrics
->gmBlackBoxX
;
144 glyph_dib
.height
= metrics
->gmBlackBoxY
;
145 glyph_dib
.stride
= get_dib_stride( metrics
->gmBlackBoxX
, 8 );
146 glyph_dib
.bits
= *image
;
148 rect
.left
= origin
->x
+ metrics
->gmptGlyphOrigin
.x
;
149 rect
.top
= origin
->y
- metrics
->gmptGlyphOrigin
.y
;
150 rect
.right
= rect
.left
+ metrics
->gmBlackBoxX
;
151 rect
.bottom
= rect
.top
+ metrics
->gmBlackBoxY
;
153 for (i
= 0; i
< clip
->numRects
; i
++)
155 if (intersect_rect( &clipped_rect
, &rect
, clip
->rects
+ i
))
157 src_origin
.x
= clipped_rect
.left
- rect
.left
;
158 src_origin
.y
= clipped_rect
.top
- rect
.top
;
160 pdev
->dib
.funcs
->draw_glyph( &pdev
->dib
, &clipped_rect
, &glyph_dib
, &src_origin
,
161 pdev
->text_color
, pdev
->glyph_intensities
);
165 release_wine_region( pdev
->clip
);
168 static const BYTE masks
[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
169 static const int padding
[4] = {0, 3, 2, 1};
171 /***********************************************************************
174 * Retrieve a 17-level bitmap for the appropiate glyph.
176 * For non-antialiased bitmaps convert them to the 17-level format
177 * using only values 0 or 16.
179 static DWORD
get_glyph_bitmap( HDC hdc
, UINT index
, UINT aa_flags
, GLYPHMETRICS
*metrics
,
180 struct gdi_image_bits
*image
)
182 UINT ggo_flags
= aa_flags
| GGO_GLYPH_INDEX
;
183 static const MAT2 identity
= { {0,1}, {0,0}, {0,0}, {0,1} };
184 UINT indices
[3] = {0, 0, 0x20};
187 BYTE
*buf
, *dst
, *src
;
191 image
->is_copy
= FALSE
;
192 image
->free
= free_heap_bits
;
197 for (i
= 0; i
< sizeof(indices
) / sizeof(indices
[0]); i
++)
200 ret
= GetGlyphOutlineW( hdc
, index
, ggo_flags
, metrics
, 0, NULL
, &identity
);
201 if (ret
!= GDI_ERROR
) break;
204 if (ret
== GDI_ERROR
) return ERROR_NOT_FOUND
;
205 if (!ret
) return ERROR_SUCCESS
; /* empty glyph */
207 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
208 pad
= padding
[ metrics
->gmBlackBoxX
% 4 ];
209 stride
= get_dib_stride( metrics
->gmBlackBoxX
, 8 );
210 size
= metrics
->gmBlackBoxY
* stride
;
212 buf
= HeapAlloc( GetProcessHeap(), 0, size
);
213 if (!buf
) return ERROR_OUTOFMEMORY
;
215 ret
= GetGlyphOutlineW( hdc
, index
, ggo_flags
, metrics
, size
, buf
, &identity
);
216 if (ret
== GDI_ERROR
)
218 HeapFree( GetProcessHeap(), 0, buf
);
219 return ERROR_NOT_FOUND
;
222 if (aa_flags
== GGO_BITMAP
)
224 for (y
= metrics
->gmBlackBoxY
- 1; y
>= 0; y
--)
226 src
= buf
+ y
* get_dib_stride( metrics
->gmBlackBoxX
, 1 );
227 dst
= buf
+ y
* stride
;
229 if (pad
) memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
231 for (x
= metrics
->gmBlackBoxX
- 1; x
>= 0; x
--)
232 dst
[x
] = (src
[x
/ 8] & masks
[x
% 8]) ? 0x10 : 0;
237 for (y
= 0, dst
= buf
; y
< metrics
->gmBlackBoxY
; y
++, dst
+= stride
)
238 memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
242 return ERROR_SUCCESS
;
245 BOOL
render_aa_text_bitmapinfo( HDC hdc
, BITMAPINFO
*info
, struct gdi_image_bits
*bits
,
246 struct bitblt_coords
*src
, INT x
, INT y
, UINT flags
,
247 UINT aa_flags
, LPCWSTR str
, UINT count
, const INT
*dx
)
254 DWORD fg_pixel
, bg_pixel
;
255 struct intensity_range glyph_intensities
[17];
257 assert( info
->bmiHeader
.biBitCount
> 8 ); /* mono and indexed formats don't support anti-aliasing */
259 init_dib_info_from_bitmapinfo( &dib
, info
, bits
->ptr
, 0 );
261 fg
= make_rgb_colorref( hdc
, &dib
, GetTextColor( hdc
), &got_pixel
, &fg_pixel
);
262 if (!got_pixel
) fg_pixel
= dib
.funcs
->colorref_to_pixel( &dib
, fg
);
264 get_aa_ranges( fg
, glyph_intensities
);
266 if (flags
& ETO_OPAQUE
)
268 rop_mask bkgnd_color
;
270 bg
= make_rgb_colorref( hdc
, &dib
, GetBkColor( hdc
), &got_pixel
, &bg_pixel
);
271 if (!got_pixel
) bg_pixel
= dib
.funcs
->colorref_to_pixel( &dib
, bg
);
274 bkgnd_color
.xor = bg_pixel
;
275 solid_rects( &dib
, 1, &src
->visrect
, &bkgnd_color
, 0 );
278 for (i
= 0; i
< count
; i
++)
280 GLYPHMETRICS metrics
;
281 struct gdi_image_bits image
;
283 err
= get_glyph_bitmap( hdc
, (UINT
)str
[i
], aa_flags
, &metrics
, &image
);
288 RECT rect
, clipped_rect
;
292 glyph_dib
.bit_count
= 8;
293 glyph_dib
.width
= metrics
.gmBlackBoxX
;
294 glyph_dib
.height
= metrics
.gmBlackBoxY
;
295 glyph_dib
.stride
= get_dib_stride( metrics
.gmBlackBoxX
, 8 );
296 glyph_dib
.bits
= image
;
298 rect
.left
= x
+ metrics
.gmptGlyphOrigin
.x
;
299 rect
.top
= y
- metrics
.gmptGlyphOrigin
.y
;
300 rect
.right
= rect
.left
+ metrics
.gmBlackBoxX
;
301 rect
.bottom
= rect
.top
+ metrics
.gmBlackBoxY
;
303 if (intersect_rect( &clipped_rect
, &rect
, &src
->visrect
))
305 src_origin
.x
= clipped_rect
.left
- rect
.left
;
306 src_origin
.y
= clipped_rect
.top
- rect
.top
;
308 dib
.funcs
->draw_glyph( &dib
, &clipped_rect
, &glyph_dib
, &src_origin
,
309 fg_pixel
, glyph_intensities
);
312 if (image
.free
) image
.free( &image
);
326 x
+= metrics
.gmCellIncX
;
327 y
+= metrics
.gmCellIncY
;
333 /***********************************************************************
336 BOOL
dibdrv_ExtTextOut( PHYSDEV dev
, INT x
, INT y
, UINT flags
,
337 const RECT
*rect
, LPCWSTR str
, UINT count
, const INT
*dx
)
339 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
343 HRGN saved_clip
= NULL
;
345 if (flags
& ETO_OPAQUE
)
347 rop_mask bkgnd_color
;
348 get_text_bkgnd_masks( pdev
, &bkgnd_color
);
349 solid_rects( &pdev
->dib
, 1, rect
, &bkgnd_color
, pdev
->clip
);
352 if (count
== 0) return TRUE
;
354 if (flags
& ETO_CLIPPED
)
356 HRGN clip
= CreateRectRgnIndirect( rect
);
357 saved_clip
= add_extra_clipping_region( pdev
, clip
);
358 DeleteObject( clip
);
361 aa_flags
= get_font_aa_flags( dev
->hdc
);
364 for (i
= 0; i
< count
; i
++)
366 GLYPHMETRICS metrics
;
367 struct gdi_image_bits image
;
369 err
= get_glyph_bitmap( dev
->hdc
, (UINT
)str
[i
], aa_flags
, &metrics
, &image
);
372 if (image
.ptr
) draw_glyph( pdev
, &origin
, &metrics
, &image
);
373 if (image
.free
) image
.free( &image
);
379 origin
.x
+= dx
[ i
* 2 ];
380 origin
.y
+= dx
[ i
* 2 + 1];
387 origin
.x
+= metrics
.gmCellIncX
;
388 origin
.y
+= metrics
.gmCellIncY
;
392 restore_clipping_region( pdev
, saved_clip
);
396 /***********************************************************************
397 * dibdrv_GetNearestColor
399 COLORREF
dibdrv_GetNearestColor( PHYSDEV dev
, COLORREF color
)
401 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
404 TRACE( "(%p, %08x)\n", dev
, color
);
406 pixel
= get_pixel_color( pdev
, color
, FALSE
);
407 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
410 /***********************************************************************
413 COLORREF
dibdrv_GetPixel( PHYSDEV dev
, INT x
, INT y
)
415 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
419 TRACE( "(%p, %d, %d)\n", dev
, x
, y
);
423 LPtoDP( dev
->hdc
, &pt
, 1 );
425 if (pt
.x
< 0 || pt
.x
>= pdev
->dib
.width
||
426 pt
.y
< 0 || pt
.y
>= pdev
->dib
.height
)
429 pixel
= pdev
->dib
.funcs
->get_pixel( &pdev
->dib
, &pt
);
430 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
433 /***********************************************************************
436 BOOL
dibdrv_LineTo( PHYSDEV dev
, INT x
, INT y
)
438 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pLineTo
);
439 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
442 GetCurrentPositionEx(dev
->hdc
, pts
);
446 LPtoDP(dev
->hdc
, pts
, 2);
448 reset_dash_origin(pdev
);
450 if(defer_pen(pdev
) || !pdev
->pen_lines(pdev
, 2, pts
, FALSE
))
451 return next
->funcs
->pLineTo( next
, x
, y
);
456 /***********************************************************************
459 * Returns the binary rop that is equivalent to the provided ternary rop
460 * if the src bits are ignored.
462 static inline INT
get_rop2_from_rop(INT rop
)
464 return (((rop
>> 18) & 0x0c) | ((rop
>> 16) & 0x03)) + 1;
467 /***********************************************************************
470 BOOL
dibdrv_PatBlt( PHYSDEV dev
, struct bitblt_coords
*dst
, DWORD rop
)
472 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
473 INT rop2
= get_rop2_from_rop(rop
);
476 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev
, dst
->x
, dst
->y
, dst
->width
, dst
->height
, rop
);
478 update_brush_rop( pdev
, rop2
);
479 ret
= brush_rects( pdev
, 1, &dst
->visrect
);
480 update_brush_rop( pdev
, GetROP2(dev
->hdc
) );
484 /***********************************************************************
487 BOOL
dibdrv_PaintRgn( PHYSDEV dev
, HRGN rgn
)
489 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
490 const WINEREGION
*region
;
494 TRACE("%p, %p\n", dev
, rgn
);
496 region
= get_wine_region( rgn
);
497 if(!region
) return FALSE
;
499 for(i
= 0; i
< region
->numRects
; i
++)
501 rect
= get_device_rect( dev
->hdc
, region
->rects
[i
].left
, region
->rects
[i
].top
,
502 region
->rects
[i
].right
, region
->rects
[i
].bottom
, FALSE
);
503 brush_rects( pdev
, 1, &rect
);
506 release_wine_region( rgn
);
510 /***********************************************************************
511 * dibdrv_PolyPolyline
513 BOOL
dibdrv_PolyPolyline( PHYSDEV dev
, const POINT
* pt
, const DWORD
* counts
, DWORD polylines
)
515 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
516 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPolyPolyline
);
517 DWORD max_points
= 0, i
;
520 if (defer_pen( pdev
)) return next
->funcs
->pPolyPolyline( next
, pt
, counts
, polylines
);
522 for (i
= 0; i
< polylines
; i
++) max_points
= max( counts
[i
], max_points
);
524 points
= HeapAlloc( GetProcessHeap(), 0, max_points
* sizeof(*pt
) );
525 if (!points
) return FALSE
;
527 for (i
= 0; i
< polylines
; i
++)
529 memcpy( points
, pt
, counts
[i
] * sizeof(*pt
) );
531 LPtoDP( dev
->hdc
, points
, counts
[i
] );
533 reset_dash_origin( pdev
);
534 pdev
->pen_lines( pdev
, counts
[i
], points
, FALSE
);
537 HeapFree( GetProcessHeap(), 0, points
);
541 /***********************************************************************
544 BOOL
dibdrv_Polyline( PHYSDEV dev
, const POINT
* pt
, INT count
)
546 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
547 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPolyline
);
550 if (defer_pen( pdev
)) return next
->funcs
->pPolyline( next
, pt
, count
);
552 points
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*pt
) );
553 if (!points
) return FALSE
;
555 memcpy( points
, pt
, count
* sizeof(*pt
) );
556 LPtoDP( dev
->hdc
, points
, count
);
558 reset_dash_origin( pdev
);
559 pdev
->pen_lines( pdev
, count
, points
, FALSE
);
561 HeapFree( GetProcessHeap(), 0, points
);
565 /***********************************************************************
568 BOOL
dibdrv_Rectangle( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
570 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pRectangle
);
571 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
572 RECT rect
= get_device_rect( dev
->hdc
, left
, top
, right
, bottom
, TRUE
);
575 TRACE("(%p, %d, %d, %d, %d)\n", dev
, left
, top
, right
, bottom
);
577 if(rect
.left
== rect
.right
|| rect
.top
== rect
.bottom
) return TRUE
;
580 return next
->funcs
->pRectangle( next
, left
, top
, right
, bottom
);
582 reset_dash_origin(pdev
);
584 /* 4 pts going anti-clockwise starting from top-right */
585 pts
[0].x
= pts
[3].x
= rect
.right
- 1;
586 pts
[0].y
= pts
[1].y
= rect
.top
;
587 pts
[1].x
= pts
[2].x
= rect
.left
;
588 pts
[2].y
= pts
[3].y
= rect
.bottom
- 1;
590 pdev
->pen_lines(pdev
, 4, pts
, TRUE
);
592 /* FIXME: Will need updating when we support wide pens */
599 brush_rects(pdev
, 1, &rect
);
604 /***********************************************************************
607 COLORREF
dibdrv_SetPixel( PHYSDEV dev
, INT x
, INT y
, COLORREF color
)
609 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
613 const WINEREGION
*clip
= get_wine_region( pdev
->clip
);
615 TRACE( "(%p, %d, %d, %08x)\n", dev
, x
, y
, color
);
619 LPtoDP( dev
->hdc
, &pt
, 1 );
621 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
622 pixel
= get_pixel_color( pdev
, color
, FALSE
);
623 color
= pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
625 for (i
= 0; i
< clip
->numRects
; i
++)
627 if (pt_in_rect( clip
->rects
+ i
, pt
))
632 rect
.right
= rect
.left
+ 1;
633 rect
.bottom
= rect
.top
+ 1;
635 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, 1, &rect
, 0, pixel
);
640 release_wine_region( pdev
->clip
);