gdi32: init_dib_info() can no longer fail, and no longer requires freeing.
[wine.git] / dlls / gdi32 / dibdrv / graphics.c
blob439a10f275f4c29c44056f5556fdf376a39c44f9
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_GetPixel
397 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
399 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
400 POINT pt;
401 DWORD pixel;
403 TRACE( "(%p, %d, %d)\n", dev, x, y );
405 pt.x = x;
406 pt.y = 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)
411 return CLR_INVALID;
413 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
414 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
417 /***********************************************************************
418 * dibdrv_LineTo
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);
424 POINT pts[2];
426 GetCurrentPositionEx(dev->hdc, pts);
427 pts[1].x = x;
428 pts[1].y = y;
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 );
437 return TRUE;
440 /***********************************************************************
441 * get_rop2_from_rop
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 /***********************************************************************
452 * dibdrv_PatBlt
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);
459 BOOL done;
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) );
472 if(!done)
473 return next->funcs->pPatBlt( next, dst, rop );
475 return TRUE;
478 /***********************************************************************
479 * dibdrv_PaintRgn
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;
486 int i;
487 RECT rect;
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 );
504 return TRUE;
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;
515 POINT *points;
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) );
527 pt += counts[i];
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 );
535 return TRUE;
538 /***********************************************************************
539 * dibdrv_Polyline
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 );
545 POINT *points;
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 );
559 return TRUE;
562 /***********************************************************************
563 * dibdrv_Rectangle
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 );
570 POINT pts[4];
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 */
591 rect.left += 1;
592 rect.top += 1;
593 rect.right -= 1;
594 rect.bottom -= 1;
596 brush_rects(pdev, 1, &rect);
598 return TRUE;
601 /***********************************************************************
602 * dibdrv_SetPixel
604 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
606 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
607 int i;
608 POINT pt;
609 DWORD pixel;
610 const WINEREGION *clip = get_wine_region( pdev->clip );
612 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
614 pt.x = x;
615 pt.y = y;
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 ))
626 RECT rect;
627 rect.left = pt.x;
628 rect.top = pt.y;
629 rect.right = rect.left + 1;
630 rect.bottom = rect.top + 1;
632 pdev->dib.funcs->solid_rects( &pdev->dib, 1, &rect, 0, pixel );
633 break;
637 release_wine_region( pdev->clip );
638 return color;