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( const dibdrv_physdev
*pdev
, rop_mask
*mask
)
121 if (pdev
->dib
.bit_count
!= 1)
122 mask
->xor = pdev
->bkgnd_color
;
125 mask
->xor = ~pdev
->text_color
;
126 if (GetTextColor( pdev
->dev
.hdc
) == GetBkColor( pdev
->dev
.hdc
))
127 mask
->xor = pdev
->text_color
;
131 static void draw_glyph( dibdrv_physdev
*pdev
, const POINT
*origin
, const GLYPHMETRICS
*metrics
,
132 const struct gdi_image_bits
*image
)
134 const WINEREGION
*clip
= get_wine_region( pdev
->clip
);
136 RECT rect
, clipped_rect
;
140 glyph_dib
.bit_count
= 8;
141 glyph_dib
.width
= metrics
->gmBlackBoxX
;
142 glyph_dib
.height
= metrics
->gmBlackBoxY
;
143 glyph_dib
.stride
= get_dib_stride( metrics
->gmBlackBoxX
, 8 );
144 glyph_dib
.bits
= *image
;
146 rect
.left
= origin
->x
+ metrics
->gmptGlyphOrigin
.x
;
147 rect
.top
= origin
->y
- metrics
->gmptGlyphOrigin
.y
;
148 rect
.right
= rect
.left
+ metrics
->gmBlackBoxX
;
149 rect
.bottom
= rect
.top
+ metrics
->gmBlackBoxY
;
151 for (i
= 0; i
< clip
->numRects
; i
++)
153 if (intersect_rect( &clipped_rect
, &rect
, clip
->rects
+ i
))
155 src_origin
.x
= clipped_rect
.left
- rect
.left
;
156 src_origin
.y
= clipped_rect
.top
- rect
.top
;
158 pdev
->dib
.funcs
->draw_glyph( &pdev
->dib
, &clipped_rect
, &glyph_dib
, &src_origin
,
159 pdev
->text_color
, pdev
->glyph_intensities
);
163 release_wine_region( pdev
->clip
);
166 static const BYTE masks
[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
167 static const int padding
[4] = {0, 3, 2, 1};
169 /***********************************************************************
172 * Retrieve a 17-level bitmap for the appropiate glyph.
174 * For non-antialiased bitmaps convert them to the 17-level format
175 * using only values 0 or 16.
177 static DWORD
get_glyph_bitmap( HDC hdc
, UINT index
, UINT aa_flags
, GLYPHMETRICS
*metrics
,
178 struct gdi_image_bits
*image
)
180 UINT ggo_flags
= aa_flags
| GGO_GLYPH_INDEX
;
181 static const MAT2 identity
= { {0,1}, {0,0}, {0,0}, {0,1} };
182 UINT indices
[3] = {0, 0, 0x20};
185 BYTE
*buf
, *dst
, *src
;
189 image
->is_copy
= FALSE
;
190 image
->free
= free_heap_bits
;
195 for (i
= 0; i
< sizeof(indices
) / sizeof(indices
[0]); i
++)
198 ret
= GetGlyphOutlineW( hdc
, index
, ggo_flags
, metrics
, 0, NULL
, &identity
);
199 if (ret
!= GDI_ERROR
) break;
202 if (ret
== GDI_ERROR
) return ERROR_NOT_FOUND
;
203 if (!ret
) return ERROR_SUCCESS
; /* empty glyph */
205 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
206 pad
= padding
[ metrics
->gmBlackBoxX
% 4 ];
207 stride
= get_dib_stride( metrics
->gmBlackBoxX
, 8 );
208 size
= metrics
->gmBlackBoxY
* stride
;
210 buf
= HeapAlloc( GetProcessHeap(), 0, size
);
211 if (!buf
) return ERROR_OUTOFMEMORY
;
213 ret
= GetGlyphOutlineW( hdc
, index
, ggo_flags
, metrics
, size
, buf
, &identity
);
214 if (ret
== GDI_ERROR
)
216 HeapFree( GetProcessHeap(), 0, buf
);
217 return ERROR_NOT_FOUND
;
220 if (aa_flags
== GGO_BITMAP
)
222 for (y
= metrics
->gmBlackBoxY
- 1; y
>= 0; y
--)
224 src
= buf
+ y
* get_dib_stride( metrics
->gmBlackBoxX
, 1 );
225 dst
= buf
+ y
* stride
;
227 if (pad
) memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
229 for (x
= metrics
->gmBlackBoxX
- 1; x
>= 0; x
--)
230 dst
[x
] = (src
[x
/ 8] & masks
[x
% 8]) ? 0x10 : 0;
235 for (y
= 0, dst
= buf
; y
< metrics
->gmBlackBoxY
; y
++, dst
+= stride
)
236 memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
240 return ERROR_SUCCESS
;
243 BOOL
render_aa_text_bitmapinfo( HDC hdc
, BITMAPINFO
*info
, struct gdi_image_bits
*bits
,
244 struct bitblt_coords
*src
, INT x
, INT y
, UINT flags
,
245 UINT aa_flags
, LPCWSTR str
, UINT count
, const INT
*dx
)
252 DWORD fg_pixel
, bg_pixel
;
253 struct intensity_range glyph_intensities
[17];
255 assert( info
->bmiHeader
.biBitCount
> 8 ); /* mono and indexed formats don't support anti-aliasing */
257 init_dib_info_from_bitmapinfo( &dib
, info
, bits
->ptr
, 0 );
259 fg
= make_rgb_colorref( hdc
, &dib
, GetTextColor( hdc
), &got_pixel
, &fg_pixel
);
260 if (!got_pixel
) fg_pixel
= dib
.funcs
->colorref_to_pixel( &dib
, fg
);
262 get_aa_ranges( fg
, glyph_intensities
);
264 if (flags
& ETO_OPAQUE
)
266 rop_mask bkgnd_color
;
268 bg
= make_rgb_colorref( hdc
, &dib
, GetBkColor( hdc
), &got_pixel
, &bg_pixel
);
269 if (!got_pixel
) bg_pixel
= dib
.funcs
->colorref_to_pixel( &dib
, bg
);
272 bkgnd_color
.xor = bg_pixel
;
273 solid_rects( &dib
, 1, &src
->visrect
, &bkgnd_color
, 0 );
276 for (i
= 0; i
< count
; i
++)
278 GLYPHMETRICS metrics
;
279 struct gdi_image_bits image
;
281 err
= get_glyph_bitmap( hdc
, (UINT
)str
[i
], aa_flags
, &metrics
, &image
);
286 RECT rect
, clipped_rect
;
290 glyph_dib
.bit_count
= 8;
291 glyph_dib
.width
= metrics
.gmBlackBoxX
;
292 glyph_dib
.height
= metrics
.gmBlackBoxY
;
293 glyph_dib
.stride
= get_dib_stride( metrics
.gmBlackBoxX
, 8 );
294 glyph_dib
.bits
= image
;
296 rect
.left
= x
+ metrics
.gmptGlyphOrigin
.x
;
297 rect
.top
= y
- metrics
.gmptGlyphOrigin
.y
;
298 rect
.right
= rect
.left
+ metrics
.gmBlackBoxX
;
299 rect
.bottom
= rect
.top
+ metrics
.gmBlackBoxY
;
301 if (intersect_rect( &clipped_rect
, &rect
, &src
->visrect
))
303 src_origin
.x
= clipped_rect
.left
- rect
.left
;
304 src_origin
.y
= clipped_rect
.top
- rect
.top
;
306 dib
.funcs
->draw_glyph( &dib
, &clipped_rect
, &glyph_dib
, &src_origin
,
307 fg_pixel
, glyph_intensities
);
310 if (image
.free
) image
.free( &image
);
324 x
+= metrics
.gmCellIncX
;
325 y
+= metrics
.gmCellIncY
;
331 /***********************************************************************
334 BOOL
dibdrv_ExtTextOut( PHYSDEV dev
, INT x
, INT y
, UINT flags
,
335 const RECT
*rect
, LPCWSTR str
, UINT count
, const INT
*dx
)
337 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
341 HRGN saved_clip
= NULL
;
343 if (flags
& ETO_OPAQUE
)
345 rop_mask bkgnd_color
;
346 get_text_bkgnd_masks( pdev
, &bkgnd_color
);
347 solid_rects( &pdev
->dib
, 1, rect
, &bkgnd_color
, pdev
->clip
);
350 if (count
== 0) return TRUE
;
352 if (flags
& ETO_CLIPPED
)
354 HRGN clip
= CreateRectRgnIndirect( rect
);
355 saved_clip
= add_extra_clipping_region( pdev
, clip
);
356 DeleteObject( clip
);
359 aa_flags
= get_font_aa_flags( dev
->hdc
);
362 for (i
= 0; i
< count
; i
++)
364 GLYPHMETRICS metrics
;
365 struct gdi_image_bits image
;
367 err
= get_glyph_bitmap( dev
->hdc
, (UINT
)str
[i
], aa_flags
, &metrics
, &image
);
370 if (image
.ptr
) draw_glyph( pdev
, &origin
, &metrics
, &image
);
371 if (image
.free
) image
.free( &image
);
377 origin
.x
+= dx
[ i
* 2 ];
378 origin
.y
+= dx
[ i
* 2 + 1];
385 origin
.x
+= metrics
.gmCellIncX
;
386 origin
.y
+= metrics
.gmCellIncY
;
390 restore_clipping_region( pdev
, saved_clip
);
394 /***********************************************************************
397 COLORREF
dibdrv_GetPixel( PHYSDEV dev
, INT x
, INT y
)
399 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
403 TRACE( "(%p, %d, %d)\n", dev
, x
, y
);
407 LPtoDP( dev
->hdc
, &pt
, 1 );
409 if (pt
.x
< 0 || pt
.x
>= pdev
->dib
.width
||
410 pt
.y
< 0 || pt
.y
>= pdev
->dib
.height
)
413 pixel
= pdev
->dib
.funcs
->get_pixel( &pdev
->dib
, &pt
);
414 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
417 /***********************************************************************
420 BOOL
dibdrv_LineTo( PHYSDEV dev
, INT x
, INT y
)
422 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pLineTo
);
423 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
426 GetCurrentPositionEx(dev
->hdc
, pts
);
430 LPtoDP(dev
->hdc
, pts
, 2);
432 reset_dash_origin(pdev
);
434 if(defer_pen(pdev
) || !pdev
->pen_lines(pdev
, 2, pts
, FALSE
))
435 return next
->funcs
->pLineTo( next
, x
, y
);
440 /***********************************************************************
443 * Returns the binary rop that is equivalent to the provided ternary rop
444 * if the src bits are ignored.
446 static inline INT
get_rop2_from_rop(INT rop
)
448 return (((rop
>> 18) & 0x0c) | ((rop
>> 16) & 0x03)) + 1;
451 /***********************************************************************
454 BOOL
dibdrv_PatBlt( PHYSDEV dev
, struct bitblt_coords
*dst
, DWORD rop
)
456 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPatBlt
);
457 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
458 INT rop2
= get_rop2_from_rop(rop
);
461 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev
, dst
->x
, dst
->y
, dst
->width
, dst
->height
, rop
);
463 if(defer_brush(pdev
))
464 return next
->funcs
->pPatBlt( next
, dst
, rop
);
466 update_brush_rop( pdev
, rop2
);
468 done
= brush_rects( pdev
, 1, &dst
->visrect
);
470 update_brush_rop( pdev
, GetROP2(dev
->hdc
) );
473 return next
->funcs
->pPatBlt( next
, dst
, rop
);
478 /***********************************************************************
481 BOOL
dibdrv_PaintRgn( PHYSDEV dev
, HRGN rgn
)
483 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPaintRgn
);
484 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
485 const WINEREGION
*region
;
489 TRACE("%p, %p\n", dev
, rgn
);
491 if(defer_brush(pdev
)) return next
->funcs
->pPaintRgn( next
, rgn
);
493 region
= get_wine_region( rgn
);
494 if(!region
) return FALSE
;
496 for(i
= 0; i
< region
->numRects
; i
++)
498 rect
= get_device_rect( dev
->hdc
, region
->rects
[i
].left
, region
->rects
[i
].top
,
499 region
->rects
[i
].right
, region
->rects
[i
].bottom
, FALSE
);
500 brush_rects( pdev
, 1, &rect
);
503 release_wine_region( rgn
);
507 /***********************************************************************
508 * dibdrv_PolyPolyline
510 BOOL
dibdrv_PolyPolyline( PHYSDEV dev
, const POINT
* pt
, const DWORD
* counts
, DWORD polylines
)
512 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
513 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPolyPolyline
);
514 DWORD max_points
= 0, i
;
517 if (defer_pen( pdev
)) return next
->funcs
->pPolyPolyline( next
, pt
, counts
, polylines
);
519 for (i
= 0; i
< polylines
; i
++) max_points
= max( counts
[i
], max_points
);
521 points
= HeapAlloc( GetProcessHeap(), 0, max_points
* sizeof(*pt
) );
522 if (!points
) return FALSE
;
524 for (i
= 0; i
< polylines
; i
++)
526 memcpy( points
, pt
, counts
[i
] * sizeof(*pt
) );
528 LPtoDP( dev
->hdc
, points
, counts
[i
] );
530 reset_dash_origin( pdev
);
531 pdev
->pen_lines( pdev
, counts
[i
], points
, FALSE
);
534 HeapFree( GetProcessHeap(), 0, points
);
538 /***********************************************************************
541 BOOL
dibdrv_Polyline( PHYSDEV dev
, const POINT
* pt
, INT count
)
543 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
544 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPolyline
);
547 if (defer_pen( pdev
)) return next
->funcs
->pPolyline( next
, pt
, count
);
549 points
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*pt
) );
550 if (!points
) return FALSE
;
552 memcpy( points
, pt
, count
* sizeof(*pt
) );
553 LPtoDP( dev
->hdc
, points
, count
);
555 reset_dash_origin( pdev
);
556 pdev
->pen_lines( pdev
, count
, points
, FALSE
);
558 HeapFree( GetProcessHeap(), 0, points
);
562 /***********************************************************************
565 BOOL
dibdrv_Rectangle( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
567 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pRectangle
);
568 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
569 RECT rect
= get_device_rect( dev
->hdc
, left
, top
, right
, bottom
, TRUE
);
572 TRACE("(%p, %d, %d, %d, %d)\n", dev
, left
, top
, right
, bottom
);
574 if(rect
.left
== rect
.right
|| rect
.top
== rect
.bottom
) return TRUE
;
576 if(defer_pen(pdev
) || defer_brush(pdev
))
577 return next
->funcs
->pRectangle( next
, left
, top
, right
, bottom
);
579 reset_dash_origin(pdev
);
581 /* 4 pts going anti-clockwise starting from top-right */
582 pts
[0].x
= pts
[3].x
= rect
.right
- 1;
583 pts
[0].y
= pts
[1].y
= rect
.top
;
584 pts
[1].x
= pts
[2].x
= rect
.left
;
585 pts
[2].y
= pts
[3].y
= rect
.bottom
- 1;
587 pdev
->pen_lines(pdev
, 4, pts
, TRUE
);
589 /* FIXME: Will need updating when we support wide pens */
596 brush_rects(pdev
, 1, &rect
);
601 /***********************************************************************
604 COLORREF
dibdrv_SetPixel( PHYSDEV dev
, INT x
, INT y
, COLORREF color
)
606 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
610 const WINEREGION
*clip
= get_wine_region( pdev
->clip
);
612 TRACE( "(%p, %d, %d, %08x)\n", dev
, x
, y
, color
);
616 LPtoDP( dev
->hdc
, &pt
, 1 );
618 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
619 pixel
= get_pixel_color( pdev
, color
, FALSE
);
620 color
= pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
622 for (i
= 0; i
< clip
->numRects
; i
++)
624 if (pt_in_rect( clip
->rects
+ i
, pt
))
629 rect
.right
= rect
.left
+ 1;
630 rect
.bottom
= rect
.top
+ 1;
632 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, 1, &rect
, 0, pixel
);
637 release_wine_region( pdev
->clip
);