dinput: Stub IDirectInputJoyConfig8 interface.
[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     if (!init_dib_info_from_bitmapinfo( &dib, info, bits->ptr, 0 )) return FALSE;
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     free_dib_info( &dib );
329     return TRUE;
330 }
331
332 /***********************************************************************
333  *           dibdrv_ExtTextOut
334  */
335 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
336                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
337 {
338     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
339     UINT aa_flags, i;
340     POINT origin;
341     DWORD err;
342     HRGN saved_clip = NULL;
343
344     if (flags & ETO_OPAQUE)
345     {
346         rop_mask bkgnd_color;
347         get_text_bkgnd_masks( pdev, &bkgnd_color );
348         solid_rects( &pdev->dib, 1, rect, &bkgnd_color, pdev->clip );
349     }
350
351     if (count == 0) return TRUE;
352
353     if (flags & ETO_CLIPPED)
354     {
355         HRGN clip = CreateRectRgnIndirect( rect );
356         saved_clip = add_extra_clipping_region( pdev, clip );
357         DeleteObject( clip );
358     }
359
360     aa_flags = get_font_aa_flags( dev->hdc );
361     origin.x = x;
362     origin.y = y;
363     for (i = 0; i < count; i++)
364     {
365         GLYPHMETRICS metrics;
366         struct gdi_image_bits image;
367
368         err = get_glyph_bitmap( dev->hdc, (UINT)str[i], aa_flags, &metrics, &image );
369         if (err) continue;
370
371         if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image );
372         if (image.free) image.free( &image );
373
374         if (dx)
375         {
376             if (flags & ETO_PDY)
377             {
378                 origin.x += dx[ i * 2 ];
379                 origin.y += dx[ i * 2 + 1];
380             }
381             else
382                 origin.x += dx[ i ];
383         }
384         else
385         {
386             origin.x += metrics.gmCellIncX;
387             origin.y += metrics.gmCellIncY;
388         }
389     }
390
391     restore_clipping_region( pdev, saved_clip );
392     return TRUE;
393 }
394
395 /***********************************************************************
396  *           dibdrv_GetPixel
397  */
398 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
399 {
400     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
401     POINT pt;
402     DWORD pixel;
403
404     TRACE( "(%p, %d, %d)\n", dev, x, y );
405
406     pt.x = x;
407     pt.y = y;
408     LPtoDP( dev->hdc, &pt, 1 );
409
410     if (pt.x < 0 || pt.x >= pdev->dib.width ||
411         pt.y < 0 || pt.y >= pdev->dib.height)
412         return CLR_INVALID;
413
414     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
415     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
416 }
417
418 /***********************************************************************
419  *           dibdrv_LineTo
420  */
421 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
422 {
423     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pLineTo );
424     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
425     POINT pts[2];
426
427     GetCurrentPositionEx(dev->hdc, pts);
428     pts[1].x = x;
429     pts[1].y = y;
430
431     LPtoDP(dev->hdc, pts, 2);
432
433     reset_dash_origin(pdev);
434
435     if(defer_pen(pdev) || !pdev->pen_lines(pdev, 2, pts, FALSE))
436         return next->funcs->pLineTo( next, x, y );
437
438     return TRUE;
439 }
440
441 /***********************************************************************
442  *           get_rop2_from_rop
443  *
444  * Returns the binary rop that is equivalent to the provided ternary rop
445  * if the src bits are ignored.
446  */
447 static inline INT get_rop2_from_rop(INT rop)
448 {
449     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
450 }
451
452 /***********************************************************************
453  *           dibdrv_PatBlt
454  */
455 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
456 {
457     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPatBlt );
458     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
459     INT rop2 = get_rop2_from_rop(rop);
460     BOOL done;
461
462     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
463
464     if(defer_brush(pdev))
465         return next->funcs->pPatBlt( next, dst, rop );
466
467     update_brush_rop( pdev, rop2 );
468
469     done = brush_rects( pdev, 1, &dst->visrect );
470
471     update_brush_rop( pdev, GetROP2(dev->hdc) );
472
473     if(!done)
474         return next->funcs->pPatBlt( next, dst, rop );
475
476     return TRUE;
477 }
478
479 /***********************************************************************
480  *           dibdrv_PaintRgn
481  */
482 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
483 {
484     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPaintRgn );
485     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
486     const WINEREGION *region;
487     int i;
488     RECT rect;
489
490     TRACE("%p, %p\n", dev, rgn);
491
492     if(defer_brush(pdev)) return next->funcs->pPaintRgn( next, 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) || defer_brush(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 }