2 * DIB driver graphics operations.
4 * Copyright 2011 Huw Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "gdi_private.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(dib
);
28 static RECT
get_device_rect( HDC hdc
, int left
, int top
, int right
, int bottom
, BOOL rtl_correction
)
36 if (rtl_correction
&& GetLayout( hdc
) & LAYOUT_RTL
)
38 /* shift the rectangle so that the right border is included after mirroring */
39 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
43 LPtoDP( hdc
, (POINT
*)&rect
, 2 );
44 if (rect
.left
> rect
.right
)
47 rect
.left
= rect
.right
;
50 if (rect
.top
> rect
.bottom
)
53 rect
.top
= rect
.bottom
;
59 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
60 black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
61 static const BYTE ramp
[17] =
64 0x8c, 0x9a, 0xa7, 0xb2,
65 0xbd, 0xc7, 0xd0, 0xd9,
66 0xe1, 0xe9, 0xf0, 0xf8,
70 /* For a give text-color component and a glyph level, calculate the
71 range of dst intensities, the min/max corresponding to 0/0xff bkgnd
72 components respectively.
74 The minimum is a linear interpolation between 0 and the value in
77 The maximum is a linear interpolation between the value from the
78 ramp table read in reverse and 0xff.
80 To find the resulting pixel intensity, we note that if the text and
81 bkgnd intensities are the same then the result must be that
82 intensity. Otherwise we linearly interpolate between either the
83 min or the max value and this intermediate value depending on which
84 side of the inequality we lie.
87 static inline void get_range( BYTE aa
, DWORD text_comp
, BYTE
*min_comp
, BYTE
*max_comp
)
89 *min_comp
= (ramp
[aa
] * text_comp
) / 0xff;
90 *max_comp
= ramp
[16 - aa
] + ((0xff - ramp
[16 - aa
]) * text_comp
) / 0xff;
93 void update_aa_ranges( dibdrv_physdev
*pdev
)
96 COLORREF text
= pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pdev
->text_color
);
98 for (i
= 0; i
< 17; i
++)
100 get_range( i
, GetRValue(text
), &pdev
->glyph_intensities
[i
].r_min
, &pdev
->glyph_intensities
[i
].r_max
);
101 get_range( i
, GetGValue(text
), &pdev
->glyph_intensities
[i
].g_min
, &pdev
->glyph_intensities
[i
].g_max
);
102 get_range( i
, GetBValue(text
), &pdev
->glyph_intensities
[i
].b_min
, &pdev
->glyph_intensities
[i
].b_max
);
106 /**********************************************************************
107 * get_text_bkgnd_masks
109 * See the comment above get_pen_bkgnd_masks
111 static inline void get_text_bkgnd_masks( const dibdrv_physdev
*pdev
, rop_mask
*mask
)
115 if (pdev
->dib
.bit_count
!= 1)
116 mask
->xor = pdev
->bkgnd_color
;
119 mask
->xor = ~pdev
->text_color
;
120 if (GetTextColor( pdev
->dev
.hdc
) == GetBkColor( pdev
->dev
.hdc
))
121 mask
->xor = pdev
->text_color
;
125 static void draw_glyph( dibdrv_physdev
*pdev
, const POINT
*origin
, const GLYPHMETRICS
*metrics
,
126 const struct gdi_image_bits
*image
)
128 const WINEREGION
*clip
= get_wine_region( pdev
->clip
);
130 RECT rect
, clipped_rect
;
134 glyph_dib
.bit_count
= 8;
135 glyph_dib
.width
= metrics
->gmBlackBoxX
;
136 glyph_dib
.height
= metrics
->gmBlackBoxY
;
137 glyph_dib
.stride
= get_dib_stride( metrics
->gmBlackBoxX
, 8 );
138 glyph_dib
.bits
= *image
;
140 rect
.left
= origin
->x
+ metrics
->gmptGlyphOrigin
.x
;
141 rect
.top
= origin
->y
- metrics
->gmptGlyphOrigin
.y
;
142 rect
.right
= rect
.left
+ metrics
->gmBlackBoxX
;
143 rect
.bottom
= rect
.top
+ metrics
->gmBlackBoxY
;
145 for (i
= 0; i
< clip
->numRects
; i
++)
147 if (intersect_rect( &clipped_rect
, &rect
, clip
->rects
+ i
))
149 src_origin
.x
= clipped_rect
.left
- rect
.left
;
150 src_origin
.y
= clipped_rect
.top
- rect
.top
;
152 pdev
->dib
.funcs
->draw_glyph( &pdev
->dib
, &clipped_rect
, &glyph_dib
, &src_origin
,
153 pdev
->text_color
, pdev
->glyph_intensities
);
157 release_wine_region( pdev
->clip
);
160 static inline UINT
get_aa_flags( dibdrv_physdev
*pdev
)
164 if (pdev
->dib
.bit_count
<= 8) return GGO_BITMAP
;
166 GetObjectW( GetCurrentObject( pdev
->dev
.hdc
, OBJ_FONT
), sizeof(lf
), &lf
);
167 if (lf
.lfQuality
== NONANTIALIASED_QUALITY
) return GGO_BITMAP
;
169 /* FIXME, check gasp and user prefs */
170 return GGO_GRAY4_BITMAP
;
173 static const BYTE masks
[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
174 static const int padding
[4] = {0, 3, 2, 1};
176 /***********************************************************************
179 * Retrieve a 17-level bitmap for the appropiate glyph.
181 * For non-antialiased bitmaps convert them to the 17-level format
182 * using only values 0 or 16.
184 static DWORD
get_glyph_bitmap( dibdrv_physdev
*pdev
, UINT index
, GLYPHMETRICS
*metrics
,
185 struct gdi_image_bits
*image
)
187 UINT aa_flags
= get_aa_flags( pdev
), ggo_flags
= aa_flags
| GGO_GLYPH_INDEX
;
188 static const MAT2 identity
= { {0,1}, {0,0}, {0,0}, {0,1} };
189 UINT indices
[3] = {0, 0, 0x20};
192 BYTE
*buf
, *dst
, *src
;
196 image
->is_copy
= FALSE
;
197 image
->free
= free_heap_bits
;
202 for (i
= 0; i
< sizeof(indices
) / sizeof(indices
[0]); index
= indices
[++i
])
204 ret
= GetGlyphOutlineW( pdev
->dev
.hdc
, index
, ggo_flags
, metrics
, 0, NULL
, &identity
);
205 if (ret
!= GDI_ERROR
) break;
208 if (ret
== GDI_ERROR
) return ERROR_NOT_FOUND
;
209 if (!ret
) return ERROR_SUCCESS
; /* empty glyph */
211 /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
212 pad
= padding
[ metrics
->gmBlackBoxX
% 4 ];
213 stride
= get_dib_stride( metrics
->gmBlackBoxX
, 8 );
214 size
= metrics
->gmBlackBoxY
* stride
;
216 buf
= HeapAlloc( GetProcessHeap(), 0, size
);
217 if (!buf
) return ERROR_OUTOFMEMORY
;
219 ret
= GetGlyphOutlineW( pdev
->dev
.hdc
, index
, ggo_flags
, metrics
, size
, buf
, &identity
);
220 if (ret
== GDI_ERROR
)
222 HeapFree( GetProcessHeap(), 0, buf
);
223 return ERROR_NOT_FOUND
;
226 if (aa_flags
== GGO_BITMAP
)
228 for (y
= metrics
->gmBlackBoxY
- 1; y
>= 0; y
--)
230 src
= buf
+ y
* get_dib_stride( metrics
->gmBlackBoxX
, 1 );
231 dst
= buf
+ y
* stride
;
233 if (pad
) memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
235 for (x
= metrics
->gmBlackBoxX
- 1; x
>= 0; x
--)
236 dst
[x
] = (src
[x
/ 8] & masks
[x
% 8]) ? 0x10 : 0;
241 for (y
= 0, dst
= buf
; y
< metrics
->gmBlackBoxY
; y
++, dst
+= stride
)
242 memset( dst
+ metrics
->gmBlackBoxX
, 0, pad
);
246 return ERROR_SUCCESS
;
249 /***********************************************************************
252 BOOL
dibdrv_ExtTextOut( PHYSDEV dev
, INT x
, INT y
, UINT flags
,
253 const RECT
*rect
, LPCWSTR str
, UINT count
, const INT
*dx
)
255 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
259 HRGN saved_clip
= NULL
;
261 if (flags
& ETO_OPAQUE
)
263 rop_mask bkgnd_color
;
264 get_text_bkgnd_masks( pdev
, &bkgnd_color
);
265 solid_rects( &pdev
->dib
, 1, rect
, &bkgnd_color
, pdev
->clip
);
268 if (count
== 0) return TRUE
;
270 if (flags
& ETO_CLIPPED
)
272 HRGN clip
= CreateRectRgnIndirect( rect
);
273 saved_clip
= add_extra_clipping_region( pdev
, clip
);
274 DeleteObject( clip
);
279 for (i
= 0; i
< count
; i
++)
281 GLYPHMETRICS metrics
;
282 struct gdi_image_bits image
;
284 err
= get_glyph_bitmap( pdev
, (UINT
)str
[i
], &metrics
, &image
);
287 if (image
.ptr
) draw_glyph( pdev
, &origin
, &metrics
, &image
);
288 if (image
.free
) image
.free( &image
);
294 origin
.x
+= dx
[ i
* 2 ];
295 origin
.y
+= dx
[ i
* 2 + 1];
302 origin
.x
+= metrics
.gmCellIncX
;
303 origin
.y
+= metrics
.gmCellIncY
;
307 restore_clipping_region( pdev
, saved_clip
);
311 /***********************************************************************
314 COLORREF
dibdrv_GetPixel( PHYSDEV dev
, INT x
, INT y
)
316 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
320 TRACE( "(%p, %d, %d)\n", dev
, x
, y
);
324 LPtoDP( dev
->hdc
, &pt
, 1 );
326 if (pt
.x
< 0 || pt
.x
>= pdev
->dib
.width
||
327 pt
.y
< 0 || pt
.y
>= pdev
->dib
.height
)
330 pixel
= pdev
->dib
.funcs
->get_pixel( &pdev
->dib
, &pt
);
331 return pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
334 /***********************************************************************
337 BOOL
dibdrv_LineTo( PHYSDEV dev
, INT x
, INT y
)
339 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pLineTo
);
340 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
343 GetCurrentPositionEx(dev
->hdc
, pts
);
347 LPtoDP(dev
->hdc
, pts
, 2);
349 reset_dash_origin(pdev
);
351 if(defer_pen(pdev
) || !pdev
->pen_lines(pdev
, 2, pts
))
352 return next
->funcs
->pLineTo( next
, x
, y
);
357 /***********************************************************************
360 * Returns the binary rop that is equivalent to the provided ternary rop
361 * if the src bits are ignored.
363 static inline INT
get_rop2_from_rop(INT rop
)
365 return (((rop
>> 18) & 0x0c) | ((rop
>> 16) & 0x03)) + 1;
368 /***********************************************************************
371 BOOL
dibdrv_PatBlt( PHYSDEV dev
, struct bitblt_coords
*dst
, DWORD rop
)
373 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPatBlt
);
374 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
375 INT rop2
= get_rop2_from_rop(rop
);
378 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev
, dst
->x
, dst
->y
, dst
->width
, dst
->height
, rop
);
380 if(defer_brush(pdev
))
381 return next
->funcs
->pPatBlt( next
, dst
, rop
);
383 update_brush_rop( pdev
, rop2
);
385 done
= brush_rects( pdev
, 1, &dst
->visrect
);
387 update_brush_rop( pdev
, GetROP2(dev
->hdc
) );
390 return next
->funcs
->pPatBlt( next
, dst
, rop
);
395 /***********************************************************************
398 BOOL
dibdrv_PaintRgn( PHYSDEV dev
, HRGN rgn
)
400 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPaintRgn
);
401 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
402 const WINEREGION
*region
;
406 TRACE("%p, %p\n", dev
, rgn
);
408 if(defer_brush(pdev
)) return next
->funcs
->pPaintRgn( next
, rgn
);
410 region
= get_wine_region( rgn
);
411 if(!region
) return FALSE
;
413 for(i
= 0; i
< region
->numRects
; i
++)
415 rect
= get_device_rect( dev
->hdc
, region
->rects
[i
].left
, region
->rects
[i
].top
,
416 region
->rects
[i
].right
, region
->rects
[i
].bottom
, FALSE
);
417 brush_rects( pdev
, 1, &rect
);
420 release_wine_region( rgn
);
424 /***********************************************************************
425 * dibdrv_PolyPolyline
427 BOOL
dibdrv_PolyPolyline( PHYSDEV dev
, const POINT
* pt
, const DWORD
* counts
, DWORD polylines
)
429 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
430 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPolyPolyline
);
431 DWORD max_points
= 0, i
;
434 if (defer_pen( pdev
)) return next
->funcs
->pPolyPolyline( next
, pt
, counts
, polylines
);
436 for (i
= 0; i
< polylines
; i
++) max_points
= max( counts
[i
], max_points
);
438 points
= HeapAlloc( GetProcessHeap(), 0, max_points
* sizeof(*pt
) );
439 if (!points
) return FALSE
;
441 for (i
= 0; i
< polylines
; i
++)
443 memcpy( points
, pt
, counts
[i
] * sizeof(*pt
) );
445 LPtoDP( dev
->hdc
, points
, counts
[i
] );
447 reset_dash_origin( pdev
);
448 pdev
->pen_lines( pdev
, counts
[i
], points
);
451 HeapFree( GetProcessHeap(), 0, points
);
455 /***********************************************************************
458 BOOL
dibdrv_Polyline( PHYSDEV dev
, const POINT
* pt
, INT count
)
460 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
461 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pPolyline
);
464 if (defer_pen( pdev
)) return next
->funcs
->pPolyline( next
, pt
, count
);
466 points
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*pt
) );
467 if (!points
) return FALSE
;
469 memcpy( points
, pt
, count
* sizeof(*pt
) );
470 LPtoDP( dev
->hdc
, points
, count
);
472 reset_dash_origin( pdev
);
473 pdev
->pen_lines( pdev
, count
, points
);
475 HeapFree( GetProcessHeap(), 0, points
);
479 /***********************************************************************
482 BOOL
dibdrv_Rectangle( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
484 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pRectangle
);
485 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
486 RECT rect
= get_device_rect( dev
->hdc
, left
, top
, right
, bottom
, TRUE
);
489 TRACE("(%p, %d, %d, %d, %d)\n", dev
, left
, top
, right
, bottom
);
491 if(rect
.left
== rect
.right
|| rect
.top
== rect
.bottom
) return TRUE
;
493 if(defer_pen(pdev
) || defer_brush(pdev
))
494 return next
->funcs
->pRectangle( next
, left
, top
, right
, bottom
);
496 reset_dash_origin(pdev
);
498 /* 4 pts going anti-clockwise starting from top-right */
499 pts
[0].x
= pts
[3].x
= rect
.right
- 1;
500 pts
[0].y
= pts
[1].y
= rect
.top
;
501 pts
[1].x
= pts
[2].x
= rect
.left
;
502 pts
[2].y
= pts
[3].y
= rect
.bottom
- 1;
505 pdev
->pen_lines(pdev
, 5, pts
);
507 /* FIXME: Will need updating when we support wide pens */
514 brush_rects(pdev
, 1, &rect
);
519 /***********************************************************************
522 COLORREF
dibdrv_SetPixel( PHYSDEV dev
, INT x
, INT y
, COLORREF color
)
524 dibdrv_physdev
*pdev
= get_dibdrv_pdev( dev
);
528 const WINEREGION
*clip
= get_wine_region( pdev
->clip
);
530 TRACE( "(%p, %d, %d, %08x)\n", dev
, x
, y
, color
);
534 LPtoDP( dev
->hdc
, &pt
, 1 );
536 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
537 pixel
= get_pixel_color( pdev
, color
, FALSE
);
538 color
= pdev
->dib
.funcs
->pixel_to_colorref( &pdev
->dib
, pixel
);
540 for (i
= 0; i
< clip
->numRects
; i
++)
542 if (pt_in_rect( clip
->rects
+ i
, pt
))
547 rect
.right
= rect
.left
+ 1;
548 rect
.bottom
= rect
.top
+ 1;
550 pdev
->dib
.funcs
->solid_rects( &pdev
->dib
, 1, &rect
, 0, pixel
);
555 release_wine_region( pdev
->clip
);