gdi32: Don't mark a local variable static.
[wine/multimedia.git] / dlls / gdi32 / dibdrv / graphics.c
blob0e55af13e2526c60ed72a89bae83c52314bd0d61
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 "gdi_private.h"
22 #include "dibdrv.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 )
30 RECT rect;
32 rect.left = left;
33 rect.top = top;
34 rect.right = right;
35 rect.bottom = bottom;
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 */
40 rect.left--;
41 rect.right--;
43 LPtoDP( hdc, (POINT *)&rect, 2 );
44 if (rect.left > rect.right)
46 int tmp = rect.left;
47 rect.left = rect.right;
48 rect.right = tmp;
50 if (rect.top > rect.bottom)
52 int tmp = rect.top;
53 rect.top = rect.bottom;
54 rect.bottom = tmp;
56 return rect;
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] =
63 0, 0x4d, 0x68, 0x7c,
64 0x8c, 0x9a, 0xa7, 0xb2,
65 0xbd, 0xc7, 0xd0, 0xd9,
66 0xe1, 0xe9, 0xf0, 0xf8,
67 0xff
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
75 the ramp table.
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 )
95 int i;
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 )
113 mask->and = 0;
115 if (pdev->dib.bit_count != 1)
116 mask->xor = pdev->bkgnd_color;
117 else
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 );
129 int i;
130 RECT rect, clipped_rect;
131 POINT src_origin;
132 dib_info glyph_dib;
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 )
162 LOGFONTW lf;
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 /***********************************************************************
177 * get_glyph_bitmap
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};
190 int i, x, y;
191 DWORD ret, size;
192 BYTE *buf, *dst, *src;
193 int pad, stride;
195 image->ptr = NULL;
196 image->is_copy = FALSE;
197 image->free = free_heap_bits;
198 image->param = NULL;
200 indices[0] = index;
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;
239 else if (pad)
241 for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
242 memset( dst + metrics->gmBlackBoxX, 0, pad );
245 image->ptr = buf;
246 return ERROR_SUCCESS;
249 /***********************************************************************
250 * dibdrv_ExtTextOut
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);
256 UINT i;
257 POINT origin;
258 DWORD err;
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 );
277 origin.x = x;
278 origin.y = y;
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 );
285 if (err) continue;
287 if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image );
288 if (image.free) image.free( &image );
290 if (dx)
292 if (flags & ETO_PDY)
294 origin.x += dx[ i * 2 ];
295 origin.y += dx[ i * 2 + 1];
297 else
298 origin.x += dx[ i ];
300 else
302 origin.x += metrics.gmCellIncX;
303 origin.y += metrics.gmCellIncY;
307 restore_clipping_region( pdev, saved_clip );
308 return TRUE;
311 /***********************************************************************
312 * dibdrv_GetPixel
314 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
316 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
317 POINT pt;
318 DWORD pixel;
320 TRACE( "(%p, %d, %d)\n", dev, x, y );
322 pt.x = x;
323 pt.y = 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)
328 return CLR_INVALID;
330 pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
331 return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
334 /***********************************************************************
335 * dibdrv_LineTo
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);
341 POINT pts[2];
343 GetCurrentPositionEx(dev->hdc, pts);
344 pts[1].x = x;
345 pts[1].y = y;
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 );
354 return TRUE;
357 /***********************************************************************
358 * get_rop2_from_rop
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 /***********************************************************************
369 * dibdrv_PatBlt
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);
376 BOOL done;
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) );
389 if(!done)
390 return next->funcs->pPatBlt( next, dst, rop );
392 return TRUE;
395 /***********************************************************************
396 * dibdrv_PaintRgn
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;
403 int i;
404 RECT rect;
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 );
421 return TRUE;
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;
432 POINT *points;
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) );
444 pt += counts[i];
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 );
452 return TRUE;
455 /***********************************************************************
456 * dibdrv_Polyline
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 );
462 POINT *points;
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 );
476 return TRUE;
479 /***********************************************************************
480 * dibdrv_Rectangle
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 );
487 POINT pts[5];
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;
503 pts[4] = pts[0];
505 pdev->pen_lines(pdev, 5, pts);
507 /* FIXME: Will need updating when we support wide pens */
509 rect.left += 1;
510 rect.top += 1;
511 rect.right -= 1;
512 rect.bottom -= 1;
514 brush_rects(pdev, 1, &rect);
516 return TRUE;
519 /***********************************************************************
520 * dibdrv_SetPixel
522 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
524 dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
525 int i;
526 POINT pt;
527 DWORD pixel;
528 const WINEREGION *clip = get_wine_region( pdev->clip );
530 TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
532 pt.x = x;
533 pt.y = y;
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 ))
544 RECT rect;
545 rect.left = pt.x;
546 rect.top = pt.y;
547 rect.right = rect.left + 1;
548 rect.bottom = rect.top + 1;
550 pdev->dib.funcs->solid_rects( &pdev->dib, 1, &rect, 0, pixel );
551 break;
555 release_wine_region( pdev->clip );
556 return color;