gdi32: Remove support for deferring brush calls to X11, all brush types are supported...
[wine] / dlls / gdi32 / dibdrv / graphics.c
1 /*
2  * DIB driver graphics operations.
3  *
4  * Copyright 2011 Huw Davies
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <assert.h>
22 #include "gdi_private.h"
23 #include "dibdrv.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(dib);
28
29 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
30 {
31     RECT rect;
32
33     rect.left   = left;
34     rect.top    = top;
35     rect.right  = right;
36     rect.bottom = bottom;
37     if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
38     {
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--;
43     }
44     LPtoDP( hdc, (POINT *)&rect, 2 );
45     if (rect.left > rect.right)
46     {
47         int tmp = rect.left;
48         rect.left = rect.right;
49         rect.right = tmp;
50     }
51     if (rect.top > rect.bottom)
52     {
53         int tmp = rect.top;
54         rect.top = rect.bottom;
55         rect.bottom = tmp;
56     }
57     return rect;
58 }
59
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] =
63 {
64     0,    0x4d, 0x68, 0x7c,
65     0x8c, 0x9a, 0xa7, 0xb2,
66     0xbd, 0xc7, 0xd0, 0xd9,
67     0xe1, 0xe9, 0xf0, 0xf8,
68     0xff
69 };
70
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.
74
75    The minimum is a linear interpolation between 0 and the value in
76    the ramp table.
77
78    The maximum is a linear interpolation between the value from the
79    ramp table read in reverse and 0xff.
80
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.
86 */
87
88 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
89 {
90     *min_comp = (ramp[aa] * text_comp) / 0xff;
91     *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
92 }
93
94 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
95 {
96     int i;
97
98     for (i = 0; i < 17; i++)
99     {
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 );
103     }
104 }
105
106 void update_aa_ranges( dibdrv_physdev *pdev )
107 {
108     COLORREF text = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pdev->text_color );
109     get_aa_ranges( text, pdev->glyph_intensities );
110 }
111
112 /**********************************************************************
113  *                 get_text_bkgnd_masks
114  *
115  * See the comment above get_pen_bkgnd_masks
116  */
117 static inline void get_text_bkgnd_masks( const dibdrv_physdev *pdev, rop_mask *mask )
118 {
119     mask->and = 0;
120
121     if (pdev->dib.bit_count != 1)
122         mask->xor = pdev->bkgnd_color;
123     else
124     {
125         mask->xor = ~pdev->text_color;
126         if (GetTextColor( pdev->dev.hdc ) == GetBkColor( pdev->dev.hdc ))
127             mask->xor = pdev->text_color;
128     }
129 }
130
131 static void draw_glyph( dibdrv_physdev *pdev, const POINT *origin, const GLYPHMETRICS *metrics,
132                         const struct gdi_image_bits *image )
133 {
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;
139
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;
145
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;
150
151     for (i = 0; i < clip->numRects; i++)
152     {
153         if (intersect_rect( &clipped_rect, &rect, clip->rects + i ))
154         {
155             src_origin.x = clipped_rect.left - rect.left;
156             src_origin.y = clipped_rect.top  - rect.top;
157
158             pdev->dib.funcs->draw_glyph( &pdev->dib, &clipped_rect, &glyph_dib, &src_origin,
159                                          pdev->text_color, pdev->glyph_intensities );
160         }
161     }
162
163     release_wine_region( pdev->clip );
164 }
165
166 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
167 static const int padding[4] = {0, 3, 2, 1};
168
169 /***********************************************************************
170  *         get_glyph_bitmap
171  *
172  * Retrieve a 17-level bitmap for the appropiate glyph.
173  *
174  * For non-antialiased bitmaps convert them to the 17-level format
175  * using only values 0 or 16.
176  */
177 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
178                                struct gdi_image_bits *image )
179 {
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;
187
188     image->ptr = NULL;
189     image->is_copy = FALSE;
190     image->free = free_heap_bits;
191     image->param = NULL;
192
193     indices[0] = index;
194
195     for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
196     {
197         index = indices[i];
198         ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
199         if (ret != GDI_ERROR) break;
200     }
201
202     if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
203     if (!ret) return ERROR_SUCCESS; /* empty glyph */
204
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;
209
210     buf = HeapAlloc( GetProcessHeap(), 0, size );
211     if (!buf) return ERROR_OUTOFMEMORY;
212
213     ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
214     if (ret == GDI_ERROR)
215     {
216         HeapFree( GetProcessHeap(), 0, buf );
217         return ERROR_NOT_FOUND;
218     }
219
220     if (aa_flags == GGO_BITMAP)
221     {
222         for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
223         {
224             src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
225             dst = buf + y * stride;
226
227             if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
228
229             for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
230                 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
231         }
232     }
233     else if (pad)
234     {
235         for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
236             memset( dst + metrics->gmBlackBoxX, 0, pad );
237     }
238
239     image->ptr = buf;
240     return ERROR_SUCCESS;
241 }
242
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 )
246 {
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];
254
255     assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
256
257     init_dib_info_from_bitmapinfo( &dib, info, bits->ptr, 0 );
258
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 );
261
262     get_aa_ranges( fg, glyph_intensities );
263
264     if (flags & ETO_OPAQUE)
265     {
266         rop_mask bkgnd_color;
267
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 );
270
271         bkgnd_color.and = 0;
272         bkgnd_color.xor = bg_pixel;
273         solid_rects( &dib, 1, &src->visrect, &bkgnd_color, 0 );
274     }
275
276     for (i = 0; i < count; i++)
277     {
278         GLYPHMETRICS metrics;
279         struct gdi_image_bits image;
280
281         err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &image );
282         if (err) continue;
283
284         if (image.ptr)
285         {
286             RECT rect, clipped_rect;
287             POINT src_origin;
288             dib_info glyph_dib;
289
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;
295
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;
300
301             if (intersect_rect( &clipped_rect, &rect, &src->visrect ))
302             {
303                 src_origin.x = clipped_rect.left - rect.left;
304                 src_origin.y = clipped_rect.top  - rect.top;
305
306                 dib.funcs->draw_glyph( &dib, &clipped_rect, &glyph_dib, &src_origin,
307                                        fg_pixel, glyph_intensities );
308             }
309         }
310         if (image.free) image.free( &image );
311
312         if (dx)
313         {
314             if (flags & ETO_PDY)
315             {
316                 x += dx[ i * 2 ];
317                 y += dx[ i * 2 + 1];
318             }
319             else
320                 x += dx[ i ];
321         }
322         else
323         {
324             x += metrics.gmCellIncX;
325             y += metrics.gmCellIncY;
326         }
327     }
328     return TRUE;
329 }
330
331 /***********************************************************************
332  *           dibdrv_ExtTextOut
333  */
334 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
335                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
336 {
337     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
338     UINT aa_flags, i;
339     POINT origin;
340     DWORD err;
341     HRGN saved_clip = NULL;
342
343     if (flags & ETO_OPAQUE)
344     {
345         rop_mask bkgnd_color;
346         get_text_bkgnd_masks( pdev, &bkgnd_color );
347         solid_rects( &pdev->dib, 1, rect, &bkgnd_color, pdev->clip );
348     }
349
350     if (count == 0) return TRUE;
351
352     if (flags & ETO_CLIPPED)
353     {
354         HRGN clip = CreateRectRgnIndirect( rect );
355         saved_clip = add_extra_clipping_region( pdev, clip );
356         DeleteObject( clip );
357     }
358
359     aa_flags = get_font_aa_flags( dev->hdc );
360     origin.x = x;
361     origin.y = y;
362     for (i = 0; i < count; i++)
363     {
364         GLYPHMETRICS metrics;
365         struct gdi_image_bits image;
366
367         err = get_glyph_bitmap( dev->hdc, (UINT)str[i], aa_flags, &metrics, &image );
368         if (err) continue;
369
370         if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image );
371         if (image.free) image.free( &image );
372
373         if (dx)
374         {
375             if (flags & ETO_PDY)
376             {
377                 origin.x += dx[ i * 2 ];
378                 origin.y += dx[ i * 2 + 1];
379             }
380             else
381                 origin.x += dx[ i ];
382         }
383         else
384         {
385             origin.x += metrics.gmCellIncX;
386             origin.y += metrics.gmCellIncY;
387         }
388     }
389
390     restore_clipping_region( pdev, saved_clip );
391     return TRUE;
392 }
393
394 /***********************************************************************
395  *           dibdrv_GetNearestColor
396  */
397 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
398 {
399     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
400     DWORD pixel;
401
402     TRACE( "(%p, %08x)\n", dev, color );
403
404     pixel = get_pixel_color( pdev, color, FALSE );
405     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
406 }
407
408 /***********************************************************************
409  *           dibdrv_GetPixel
410  */
411 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
412 {
413     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
414     POINT pt;
415     DWORD pixel;
416
417     TRACE( "(%p, %d, %d)\n", dev, x, y );
418
419     pt.x = x;
420     pt.y = y;
421     LPtoDP( dev->hdc, &pt, 1 );
422
423     if (pt.x < 0 || pt.x >= pdev->dib.width ||
424         pt.y < 0 || pt.y >= pdev->dib.height)
425         return CLR_INVALID;
426
427     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
428     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
429 }
430
431 /***********************************************************************
432  *           dibdrv_LineTo
433  */
434 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
435 {
436     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pLineTo );
437     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
438     POINT pts[2];
439
440     GetCurrentPositionEx(dev->hdc, pts);
441     pts[1].x = x;
442     pts[1].y = y;
443
444     LPtoDP(dev->hdc, pts, 2);
445
446     reset_dash_origin(pdev);
447
448     if(defer_pen(pdev) || !pdev->pen_lines(pdev, 2, pts, FALSE))
449         return next->funcs->pLineTo( next, x, y );
450
451     return TRUE;
452 }
453
454 /***********************************************************************
455  *           get_rop2_from_rop
456  *
457  * Returns the binary rop that is equivalent to the provided ternary rop
458  * if the src bits are ignored.
459  */
460 static inline INT get_rop2_from_rop(INT rop)
461 {
462     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
463 }
464
465 /***********************************************************************
466  *           dibdrv_PatBlt
467  */
468 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
469 {
470     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
471     INT rop2 = get_rop2_from_rop(rop);
472     BOOL ret;
473
474     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
475
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;
480 }
481
482 /***********************************************************************
483  *           dibdrv_PaintRgn
484  */
485 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
486 {
487     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
488     const WINEREGION *region;
489     int i;
490     RECT rect;
491
492     TRACE("%p, %p\n", dev, rgn);
493
494     region = get_wine_region( rgn );
495     if(!region) return FALSE;
496
497     for(i = 0; i < region->numRects; i++)
498     {
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 );
502     }
503
504     release_wine_region( rgn );
505     return TRUE;
506 }
507
508 /***********************************************************************
509  *           dibdrv_PolyPolyline
510  */
511 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
512 {
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;
517
518     if (defer_pen( pdev )) return next->funcs->pPolyPolyline( next, pt, counts, polylines );
519
520     for (i = 0; i < polylines; i++) max_points = max( counts[i], max_points );
521
522     points = HeapAlloc( GetProcessHeap(), 0, max_points * sizeof(*pt) );
523     if (!points) return FALSE;
524
525     for (i = 0; i < polylines; i++)
526     {
527         memcpy( points, pt, counts[i] * sizeof(*pt) );
528         pt += counts[i];
529         LPtoDP( dev->hdc, points, counts[i] );
530
531         reset_dash_origin( pdev );
532         pdev->pen_lines( pdev, counts[i], points, FALSE );
533     }
534
535     HeapFree( GetProcessHeap(), 0, points );
536     return TRUE;
537 }
538
539 /***********************************************************************
540  *           dibdrv_Polyline
541  */
542 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
543 {
544     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
545     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPolyline );
546     POINT *points;
547
548     if (defer_pen( pdev )) return next->funcs->pPolyline( next, pt, count );
549
550     points = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pt) );
551     if (!points) return FALSE;
552
553     memcpy( points, pt, count * sizeof(*pt) );
554     LPtoDP( dev->hdc, points, count );
555
556     reset_dash_origin( pdev );
557     pdev->pen_lines( pdev, count, points, FALSE );
558
559     HeapFree( GetProcessHeap(), 0, points );
560     return TRUE;
561 }
562
563 /***********************************************************************
564  *           dibdrv_Rectangle
565  */
566 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
567 {
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];
572
573     TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
574
575     if(rect.left == rect.right || rect.top == rect.bottom) return TRUE;
576
577     if(defer_pen(pdev))
578         return next->funcs->pRectangle( next, left, top, right, bottom );
579
580     reset_dash_origin(pdev);
581
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;
587
588     pdev->pen_lines(pdev, 4, pts, TRUE);
589
590     /* FIXME: Will need updating when we support wide pens */
591
592     rect.left   += 1;
593     rect.top    += 1;
594     rect.right  -= 1;
595     rect.bottom -= 1;
596
597     brush_rects(pdev, 1, &rect);
598
599     return TRUE;
600 }
601
602 /***********************************************************************
603  *           dibdrv_SetPixel
604  */
605 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
606 {
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 );
612
613     TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
614
615     pt.x = x;
616     pt.y = y;
617     LPtoDP( dev->hdc, &pt, 1 );
618
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 );
622
623     for (i = 0; i < clip->numRects; i++)
624     {
625         if (pt_in_rect( clip->rects + i, pt ))
626         {
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;
632
633             pdev->dib.funcs->solid_rects( &pdev->dib, 1, &rect, 0, pixel );
634             break;
635         }
636     }
637
638     release_wine_region( pdev->clip );
639     return color;
640 }