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 /***********************************************************************
395 * dibdrv_GetNearestColor
397 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
399 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
402 TRACE( "(%p, %08x)\n", dev, color );
404 pixel = get_pixel_color( pdev, color, FALSE );
405 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
408 /***********************************************************************
411 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
413 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
417 TRACE( "(%p, %d, %d)\n", dev, x, y );
421 LPtoDP( dev->hdc, &pt, 1 );
423 if (pt.x < 0 || pt.x >= pdev->dib.width ||
424 pt.y < 0 || pt.y >= pdev->dib.height)
427 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
428 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
431 /***********************************************************************
434 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
436 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pLineTo );
437 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
440 GetCurrentPositionEx(dev->hdc, pts);
444 LPtoDP(dev->hdc, pts, 2);
446 reset_dash_origin(pdev);
448 if(defer_pen(pdev) || !pdev->pen_lines(pdev, 2, pts, FALSE))
449 return next->funcs->pLineTo( next, x, y );
454 /***********************************************************************
457 * Returns the binary rop that is equivalent to the provided ternary rop
458 * if the src bits are ignored.
460 static inline INT get_rop2_from_rop(INT rop)
462 return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
465 /***********************************************************************
468 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
470 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
471 INT rop2 = get_rop2_from_rop(rop);
474 TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
476 update_brush_rop( pdev, rop2 );
477 ret = brush_rects( pdev, 1, &dst->visrect );
478 update_brush_rop( pdev, GetROP2(dev->hdc) );
482 /***********************************************************************
485 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
487 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
488 const WINEREGION *region;
492 TRACE("%p, %p\n", dev, rgn);
494 region = get_wine_region( rgn );
495 if(!region) return FALSE;
497 for(i = 0; i < region->numRects; i++)
499 rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
500 region->rects[i].right, region->rects[i].bottom, FALSE );
501 brush_rects( pdev, 1, &rect );
504 release_wine_region( rgn );
508 /***********************************************************************
509 * dibdrv_PolyPolyline
511 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
513 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
514 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPolyPolyline );
515 DWORD max_points = 0, i;
518 if (defer_pen( pdev )) return next->funcs->pPolyPolyline( next, pt, counts, polylines );
520 for (i = 0; i < polylines; i++) max_points = max( counts[i], max_points );
522 points = HeapAlloc( GetProcessHeap(), 0, max_points * sizeof(*pt) );
523 if (!points) return FALSE;
525 for (i = 0; i < polylines; i++)
527 memcpy( points, pt, counts[i] * sizeof(*pt) );
529 LPtoDP( dev->hdc, points, counts[i] );
531 reset_dash_origin( pdev );
532 pdev->pen_lines( pdev, counts[i], points, FALSE );
535 HeapFree( GetProcessHeap(), 0, points );
539 /***********************************************************************
542 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
544 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
545 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPolyline );
548 if (defer_pen( pdev )) return next->funcs->pPolyline( next, pt, count );
550 points = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pt) );
551 if (!points) return FALSE;
553 memcpy( points, pt, count * sizeof(*pt) );
554 LPtoDP( dev->hdc, points, count );
556 reset_dash_origin( pdev );
557 pdev->pen_lines( pdev, count, points, FALSE );
559 HeapFree( GetProcessHeap(), 0, points );
563 /***********************************************************************
566 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
568 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pRectangle );
569 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
570 RECT rect = get_device_rect( dev->hdc, left, top, right, bottom, TRUE );
573 TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
575 if(rect.left == rect.right || rect.top == rect.bottom) return TRUE;
578 return next->funcs->pRectangle( next, left, top, right, bottom );
580 reset_dash_origin(pdev);
582 /* 4 pts going anti-clockwise starting from top-right */
583 pts[0].x = pts[3].x = rect.right - 1;
584 pts[0].y = pts[1].y = rect.top;
585 pts[1].x = pts[2].x = rect.left;
586 pts[2].y = pts[3].y = rect.bottom - 1;
588 pdev->pen_lines(pdev, 4, pts, TRUE);
590 /* FIXME: Will need updating when we support wide pens */
597 brush_rects(pdev, 1, &rect);
602 /***********************************************************************
605 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
607 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
611 const WINEREGION *clip = get_wine_region( pdev->clip );
613 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
617 LPtoDP( dev->hdc, &pt, 1 );
619 /* SetPixel doesn't do the 1bpp massaging like other fg colors */
620 pixel = get_pixel_color( pdev, color, FALSE );
621 color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
623 for (i = 0; i < clip->numRects; i++)
625 if (pt_in_rect( clip->rects + i, pt ))
630 rect.right = rect.left + 1;
631 rect.bottom = rect.top + 1;
633 pdev->dib.funcs->solid_rects( &pdev->dib, 1, &rect, 0, pixel );
638 release_wine_region( pdev->clip );