gdi32: Recompute the pen masks on every use to support PALETTEINDEX colors.
[wine/multimedia.git] / dlls / gdi32 / dibdrv / graphics.c
blob69ce50b2d2ecc1863914a1506fd9d4daba5948d6
1 /*
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 <assert.h>
22 #include "gdi_private.h"
23 #include "dibdrv.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 )
31 RECT rect;
33 rect.left = left;
34 rect.top = top;
35 rect.right = right;
36 rect.bottom = bottom;
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 */
41 rect.left--;
42 rect.right--;
44 LPtoDP( hdc, (POINT *)&rect, 2 );
45 if (rect.left > rect.right)
47 int tmp = rect.left;
48 rect.left = rect.right;
49 rect.right = tmp;
51 if (rect.top > rect.bottom)
53 int tmp = rect.top;
54 rect.top = rect.bottom;
55 rect.bottom = tmp;
57 return rect;
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] =
64 0, 0x4d, 0x68, 0x7c,
65 0x8c, 0x9a, 0xa7, 0xb2,
66 0xbd, 0xc7, 0xd0, 0xd9,
67 0xe1, 0xe9, 0xf0, 0xf8,
68 0xff
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
76 the ramp table.
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] )
96 int i;
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 )
119 mask->and = 0;
121 if (pdev->dib.bit_count != 1)
122 mask->xor = pdev->bkgnd_color;
123 else
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 );
135 int i;
136 RECT rect, clipped_rect;
137 POINT src_origin;
138 dib_info glyph_dib;
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 /***********************************************************************
170 * get_glyph_bitmap
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};
183 int i, x, y;
184 DWORD ret, size;
185 BYTE *buf, *dst, *src;
186 int pad, stride;
188 image->ptr = NULL;
189 image->is_copy = FALSE;
190 image->free = free_heap_bits;
191 image->param = NULL;
193 indices[0] = index;
195 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
197 index = indices[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;
233 else if (pad)
235 for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
236 memset( dst + metrics->gmBlackBoxX, 0, pad );
239 image->ptr = buf;
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 )
247 dib_info dib;
248 UINT i;
249 DWORD err;
250 BOOL got_pixel;
251 COLORREF fg, bg;
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 );
271 bkgnd_color.and = 0;
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 );
282 if (err) continue;
284 if (image.ptr)
286 RECT rect, clipped_rect;
287 POINT src_origin;
288 dib_info glyph_dib;
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 );
312 if (dx)
314 if (flags & ETO_PDY)
316 x += dx[ i * 2 ];
317 y += dx[ i * 2 + 1];
319 else
320 x += dx[ i ];
322 else
324 x += metrics.gmCellIncX;
325 y += metrics.gmCellIncY;
328 return TRUE;
331 /***********************************************************************
332 * dibdrv_ExtTextOut
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);
338 UINT aa_flags, i;
339 POINT origin;
340 DWORD err;
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 );
360 origin.x = x;
361 origin.y = y;
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 );
368 if (err) continue;
370 if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image );
371 if (image.free) image.free( &image );
373 if (dx)
375 if (flags & ETO_PDY)
377 origin.x += dx[ i * 2 ];
378 origin.y += dx[ i * 2 + 1];
380 else
381 origin.x += dx[ i ];
383 else
385 origin.x += metrics.gmCellIncX;
386 origin.y += metrics.gmCellIncY;
390 restore_clipping_region( pdev, saved_clip );
391 return TRUE;
394 /***********************************************************************
395 * dibdrv_GetNearestColor
397 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
399 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
400 DWORD pixel;
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 /***********************************************************************
409 * dibdrv_GetPixel
411 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
413 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
414 POINT pt;
415 DWORD pixel;
417 TRACE( "(%p, %d, %d)\n", dev, x, y );
419 pt.x = x;
420 pt.y = 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)
425 return CLR_INVALID;
427 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
428 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
431 /***********************************************************************
432 * dibdrv_LineTo
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);
438 POINT pts[2];
440 GetCurrentPositionEx(dev->hdc, pts);
441 pts[1].x = x;
442 pts[1].y = y;
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 );
451 return TRUE;
454 /***********************************************************************
455 * get_rop2_from_rop
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 /***********************************************************************
466 * dibdrv_PatBlt
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);
472 BOOL ret;
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) );
479 return ret;
482 /***********************************************************************
483 * dibdrv_PaintRgn
485 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
487 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
488 const WINEREGION *region;
489 int i;
490 RECT rect;
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 );
505 return TRUE;
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;
516 POINT *points;
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) );
528 pt += counts[i];
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 );
536 return TRUE;
539 /***********************************************************************
540 * dibdrv_Polyline
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 );
546 POINT *points;
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 );
560 return TRUE;
563 /***********************************************************************
564 * dibdrv_Rectangle
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 );
571 POINT pts[4];
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;
577 if(defer_pen(pdev))
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 */
592 rect.left += 1;
593 rect.top += 1;
594 rect.right -= 1;
595 rect.bottom -= 1;
597 brush_rects(pdev, 1, &rect);
599 return TRUE;
602 /***********************************************************************
603 * dibdrv_SetPixel
605 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
607 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
608 int i;
609 POINT pt;
610 DWORD pixel;
611 const WINEREGION *clip = get_wine_region( pdev->clip );
613 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
615 pt.x = x;
616 pt.y = y;
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 ))
627 RECT rect;
628 rect.left = pt.x;
629 rect.top = pt.y;
630 rect.right = rect.left + 1;
631 rect.bottom = rect.top + 1;
633 pdev->dib.funcs->solid_rects( &pdev->dib, 1, &rect, 0, pixel );
634 break;
638 release_wine_region( pdev->clip );
639 return color;