gdi32: Recompute the background masks on every use to support PALETTEINDEX colors.
[wine/multimedia.git] / dlls / gdi32 / dibdrv / graphics.c
blob33e7d6d19a13fabd7e3020e2a7a93cfa47c1ff40
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( dibdrv_physdev *pdev, rop_mask *mask )
119 COLORREF bg = GetBkColor( pdev->dev.hdc );
121 mask->and = 0;
123 if (pdev->dib.bit_count != 1)
124 mask->xor = get_pixel_color( pdev, bg, FALSE );
125 else
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 );
137 int i;
138 RECT rect, clipped_rect;
139 POINT src_origin;
140 dib_info glyph_dib;
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 /***********************************************************************
172 * get_glyph_bitmap
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};
185 int i, x, y;
186 DWORD ret, size;
187 BYTE *buf, *dst, *src;
188 int pad, stride;
190 image->ptr = NULL;
191 image->is_copy = FALSE;
192 image->free = free_heap_bits;
193 image->param = NULL;
195 indices[0] = index;
197 for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
199 index = indices[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;
235 else if (pad)
237 for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
238 memset( dst + metrics->gmBlackBoxX, 0, pad );
241 image->ptr = buf;
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 )
249 dib_info dib;
250 UINT i;
251 DWORD err;
252 BOOL got_pixel;
253 COLORREF fg, bg;
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 );
273 bkgnd_color.and = 0;
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 );
284 if (err) continue;
286 if (image.ptr)
288 RECT rect, clipped_rect;
289 POINT src_origin;
290 dib_info glyph_dib;
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 );
314 if (dx)
316 if (flags & ETO_PDY)
318 x += dx[ i * 2 ];
319 y += dx[ i * 2 + 1];
321 else
322 x += dx[ i ];
324 else
326 x += metrics.gmCellIncX;
327 y += metrics.gmCellIncY;
330 return TRUE;
333 /***********************************************************************
334 * dibdrv_ExtTextOut
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);
340 UINT aa_flags, i;
341 POINT origin;
342 DWORD err;
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 );
362 origin.x = x;
363 origin.y = y;
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 );
370 if (err) continue;
372 if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image );
373 if (image.free) image.free( &image );
375 if (dx)
377 if (flags & ETO_PDY)
379 origin.x += dx[ i * 2 ];
380 origin.y += dx[ i * 2 + 1];
382 else
383 origin.x += dx[ i ];
385 else
387 origin.x += metrics.gmCellIncX;
388 origin.y += metrics.gmCellIncY;
392 restore_clipping_region( pdev, saved_clip );
393 return TRUE;
396 /***********************************************************************
397 * dibdrv_GetNearestColor
399 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
401 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
402 DWORD pixel;
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 /***********************************************************************
411 * dibdrv_GetPixel
413 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
415 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
416 POINT pt;
417 DWORD pixel;
419 TRACE( "(%p, %d, %d)\n", dev, x, y );
421 pt.x = x;
422 pt.y = 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)
427 return CLR_INVALID;
429 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
430 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
433 /***********************************************************************
434 * dibdrv_LineTo
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);
440 POINT pts[2];
442 GetCurrentPositionEx(dev->hdc, pts);
443 pts[1].x = x;
444 pts[1].y = y;
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 );
453 return TRUE;
456 /***********************************************************************
457 * get_rop2_from_rop
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 /***********************************************************************
468 * dibdrv_PatBlt
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);
474 BOOL ret;
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) );
481 return ret;
484 /***********************************************************************
485 * dibdrv_PaintRgn
487 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
489 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
490 const WINEREGION *region;
491 int i;
492 RECT rect;
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 );
507 return TRUE;
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;
518 POINT *points;
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) );
530 pt += counts[i];
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 );
538 return TRUE;
541 /***********************************************************************
542 * dibdrv_Polyline
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 );
548 POINT *points;
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 );
562 return TRUE;
565 /***********************************************************************
566 * dibdrv_Rectangle
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 );
573 POINT pts[4];
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;
579 if(defer_pen(pdev))
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 */
594 rect.left += 1;
595 rect.top += 1;
596 rect.right -= 1;
597 rect.bottom -= 1;
599 brush_rects(pdev, 1, &rect);
601 return TRUE;
604 /***********************************************************************
605 * dibdrv_SetPixel
607 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
609 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
610 int i;
611 POINT pt;
612 DWORD pixel;
613 const WINEREGION *clip = get_wine_region( pdev->clip );
615 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
617 pt.x = x;
618 pt.y = y;
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 ))
629 RECT rect;
630 rect.left = pt.x;
631 rect.top = pt.y;
632 rect.right = rect.left + 1;
633 rect.bottom = rect.top + 1;
635 pdev->dib.funcs->solid_rects( &pdev->dib, 1, &rect, 0, pixel );
636 break;
640 release_wine_region( pdev->clip );
641 return color;