mmdevapi/tests: Add tests for IAudioClient::GetCurrentPadding.
[wine] / dlls / gdi32 / dibdrv / objects.c
1 /*
2  * DIB driver GDI objects.
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 "gdi_private.h"
22 #include "dibdrv.h"
23
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(dib);
27
28 /*
29  *
30  * Decompose the 16 ROP2s into an expression of the form
31  *
32  * D = (D & A) ^ X
33  *
34  * Where A and X depend only on P (and so can be precomputed).
35  *
36  *                                       A    X
37  *
38  * R2_BLACK         0                    0    0
39  * R2_NOTMERGEPEN   ~(D | P)            ~P   ~P
40  * R2_MASKNOTPEN    ~P & D              ~P    0
41  * R2_NOTCOPYPEN    ~P                   0   ~P
42  * R2_MASKPENNOT    P & ~D               P    P
43  * R2_NOT           ~D                   1    1
44  * R2_XORPEN        P ^ D                1    P
45  * R2_NOTMASKPEN    ~(P & D)             P    1
46  * R2_MASKPEN       P & D                P    0
47  * R2_NOTXORPEN     ~(P ^ D)             1   ~P
48  * R2_NOP           D                    1    0
49  * R2_MERGENOTPEN   ~P | D               P   ~P
50  * R2_COPYPEN       P                    0    P
51  * R2_MERGEPENNOT   P | ~D              ~P    1
52  * R2_MERGEPEN      P | D               ~P    P
53  * R2_WHITE         1                    0    1
54  *
55  */
56
57 /* A = (P & A1) | (~P & A2) */
58 #define ZERO {0, 0}
59 #define ONE {0xffffffff, 0xffffffff}
60 #define P {0xffffffff, 0}
61 #define NOT_P {0, 0xffffffff}
62
63 static const DWORD rop2_and_array[16][2] =
64 {
65     ZERO, NOT_P, NOT_P, ZERO,
66     P,    ONE,   ONE,   P,
67     P,    ONE,   ONE,   P,
68     ZERO, NOT_P, NOT_P, ZERO
69 };
70
71 /* X = (P & X1) | (~P & X2) */
72 static const DWORD rop2_xor_array[16][2] =
73 {
74     ZERO, NOT_P, ZERO, NOT_P,
75     P,    ONE,   P,    ONE,
76     ZERO, NOT_P, ZERO, NOT_P,
77     P,    ONE,   P,    ONE
78 };
79
80 #undef NOT_P
81 #undef P
82 #undef ONE
83 #undef ZERO
84
85 void calc_and_xor_masks(INT rop, DWORD color, DWORD *and, DWORD *xor)
86 {
87     /* NB The ROP2 codes start at one and the arrays are zero-based */
88     *and = (color & rop2_and_array[rop-1][0]) | ((~color) & rop2_and_array[rop-1][1]);
89     *xor = (color & rop2_xor_array[rop-1][0]) | ((~color) & rop2_xor_array[rop-1][1]);
90 }
91
92 static inline void order_end_points(int *s, int *e)
93 {
94     if(*s > *e)
95     {
96         int tmp;
97         tmp = *s + 1;
98         *s = *e + 1;
99         *e = tmp;
100     }
101 }
102
103 static inline BOOL pt_in_rect( const RECT *rect, const POINT *pt )
104 {
105     return ((pt->x >= rect->left) && (pt->x < rect->right) &&
106             (pt->y >= rect->top) && (pt->y < rect->bottom));
107 }
108
109 /**********************************************************************
110  *                  get_octant_number
111  *
112  * Return the octant number starting clockwise from the +ve x-axis.
113  */
114 static inline int get_octant_number(int dx, int dy)
115 {
116     if(dy > 0)
117         if(dx > 0)
118             return ( dx >  dy) ? 1 : 2;
119         else
120             return (-dx >  dy) ? 4 : 3;
121     else
122         if(dx < 0)
123             return (-dx > -dy) ? 5 : 6;
124         else
125             return ( dx > -dy) ? 8 : 7;
126 }
127
128 static inline DWORD get_octant_mask(int dx, int dy)
129 {
130     return 1 << (get_octant_number(dx, dy) - 1);
131 }
132
133 static void solid_pen_line_callback(INT x, INT y, LPARAM lparam)
134 {
135     dibdrv_physdev *pdev = (dibdrv_physdev *)lparam;
136     RECT rect;
137
138     rect.left   = x;
139     rect.right  = x + 1;
140     rect.top    = y;
141     rect.bottom = y + 1;
142     pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
143     return;
144 }
145
146 static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, void (* callback)(INT,INT,LPARAM), LPARAM lParam)
147 {
148     INT xadd = 1, yadd = 1;
149     INT err, erradd;
150     INT cnt;
151     INT dx = x2 - x1;
152     INT dy = y2 - y1;
153     DWORD octant = get_octant_mask(dx, dy);
154     INT bias = 0;
155
156     /* Octants 3, 5, 6 and 8 take a bias */
157     if(octant & 0xb4) bias = 1;
158
159     if (dx < 0)
160     {
161         dx = -dx;
162         xadd = -1;
163     }
164     if (dy < 0)
165     {
166         dy = -dy;
167         yadd = -1;
168     }
169     if (dx > dy)  /* line is "more horizontal" */
170     {
171         err = 2*dy - dx; erradd = 2*dy - 2*dx;
172         for(cnt = 0; cnt < dx; cnt++)
173         {
174             callback(x1, y1, lParam);
175             if (err + bias > 0)
176             {
177                 y1 += yadd;
178                 err += erradd;
179             }
180             else err += 2*dy;
181             x1 += xadd;
182         }
183     }
184     else   /* line is "more vertical" */
185     {
186         err = 2*dx - dy; erradd = 2*dx - 2*dy;
187         for(cnt = 0; cnt < dy; cnt++)
188         {
189             callback(x1, y1, lParam);
190             if (err + bias > 0)
191             {
192                 x1 += xadd;
193                 err += erradd;
194             }
195             else err += 2*dx;
196             y1 += yadd;
197         }
198     }
199 }
200
201 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
202 {
203     const WINEREGION *clip = get_wine_region(pdev->clip);
204     BOOL ret = TRUE;
205
206     if(start->y == end->y)
207     {
208         RECT rect;
209         int i;
210
211         rect.left   = start->x;
212         rect.top    = start->y;
213         rect.right  = end->x;
214         rect.bottom = end->y + 1;
215         order_end_points(&rect.left, &rect.right);
216         for(i = 0; i < clip->numRects; i++)
217         {
218             if(clip->rects[i].top >= rect.bottom) break;
219             if(clip->rects[i].bottom <= rect.top) continue;
220             /* Optimize the unclipped case */
221             if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
222             {
223                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
224                 break;
225             }
226             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
227             {
228                 RECT tmp = rect;
229                 tmp.left = max(rect.left, clip->rects[i].left);
230                 tmp.right = min(rect.right, clip->rects[i].right);
231                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
232             }
233         }
234     }
235     else if(start->x == end->x)
236     {
237         RECT rect;
238         int i;
239
240         rect.left   = start->x;
241         rect.top    = start->y;
242         rect.right  = end->x + 1;
243         rect.bottom = end->y;
244         order_end_points(&rect.top, &rect.bottom);
245         for(i = 0; i < clip->numRects; i++)
246         {
247             /* Optimize unclipped case */
248             if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
249                clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
250             {
251                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
252                 break;
253             }
254             if(clip->rects[i].top >= rect.bottom) break;
255             if(clip->rects[i].bottom <= rect.top) continue;
256             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
257             {
258                 RECT tmp = rect;
259                 tmp.top = max(rect.top, clip->rects[i].top);
260                 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
261                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
262             }
263         }
264     }
265     else
266     {
267         if(clip->numRects == 1 && pt_in_rect(&clip->extents, start) && pt_in_rect(&clip->extents, end))
268             /* FIXME: Optimize by moving Bresenham algorithm to the primitive functions,
269                or at least cache adjacent points in the callback */
270             bres_line_with_bias(start->x, start->y, end->x, end->y, solid_pen_line_callback, (LPARAM)pdev);
271         else if(clip->numRects >= 1)
272             ret = FALSE;
273     }
274     release_wine_region(pdev->clip);
275     return ret;
276 }
277
278 /***********************************************************************
279  *           dibdrv_SelectPen
280  */
281 HPEN CDECL dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
282 {
283     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
284     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
285     LOGPEN logpen;
286
287     TRACE("(%p, %p)\n", dev, hpen);
288
289     if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
290     {
291         /* must be an extended pen */
292         EXTLOGPEN *elp;
293         INT size = GetObjectW( hpen, 0, NULL );
294
295         if (!size) return 0;
296
297         elp = HeapAlloc( GetProcessHeap(), 0, size );
298
299         GetObjectW( hpen, size, elp );
300         /* FIXME: add support for user style pens */
301         logpen.lopnStyle = elp->elpPenStyle;
302         logpen.lopnWidth.x = elp->elpWidth;
303         logpen.lopnWidth.y = 0;
304         logpen.lopnColor = elp->elpColor;
305
306         HeapFree( GetProcessHeap(), 0, elp );
307     }
308
309     if (hpen == GetStockObject( DC_PEN ))
310         logpen.lopnColor = GetDCPenColor( dev->hdc );
311
312     pdev->pen_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, logpen.lopnColor);
313     calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
314
315     pdev->defer |= DEFER_PEN;
316
317     switch(logpen.lopnStyle & PS_STYLE_MASK)
318     {
319     case PS_SOLID:
320         if(logpen.lopnStyle & PS_GEOMETRIC) break;
321         if(logpen.lopnWidth.x > 1) break;
322         pdev->pen_line = solid_pen_line;
323         pdev->defer &= ~DEFER_PEN;
324         break;
325     default:
326         break;
327     }
328
329     return next->funcs->pSelectPen( next, hpen );
330 }
331
332 /***********************************************************************
333  *           dibdrv_SetDCPenColor
334  */
335 COLORREF CDECL dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
336 {
337     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
338     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
339
340     if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
341     {
342         pdev->pen_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, color);
343         calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
344     }
345
346     return next->funcs->pSetDCPenColor( next, color );
347 }
348
349 /**********************************************************************
350  *             solid_brush
351  *
352  * Fill a number of rectangles with the solid brush
353  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
354  */
355 static BOOL solid_brush(dibdrv_physdev *pdev, int num, RECT *rects)
356 {
357     int i, j;
358     const WINEREGION *clip = get_wine_region(pdev->clip);
359
360     for(i = 0; i < num; i++)
361     {
362         for(j = 0; j < clip->numRects; j++)
363         {
364             RECT rect = rects[i];
365
366             /* Optimize unclipped case */
367             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
368                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
369             {
370                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
371                 break;
372             }
373
374             if(clip->rects[j].top >= rect.bottom) break;
375             if(clip->rects[j].bottom <= rect.top) continue;
376
377             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
378             {
379                 rect.left   = max(rect.left,   clip->rects[j].left);
380                 rect.top    = max(rect.top,    clip->rects[j].top);
381                 rect.right  = min(rect.right,  clip->rects[j].right);
382                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
383
384                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
385             }
386         }
387     }
388     release_wine_region(pdev->clip);
389     return TRUE;
390 }
391
392 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
393 {
394     if(pdev->brush_style == BS_SOLID)
395         calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
396 }
397
398 /***********************************************************************
399  *           dibdrv_SelectBrush
400  */
401 HBRUSH CDECL dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
402 {
403     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
404     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
405     LOGBRUSH logbrush;
406
407     TRACE("(%p, %p)\n", dev, hbrush);
408
409     if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
410
411     if (hbrush == GetStockObject( DC_BRUSH ))
412         logbrush.lbColor = GetDCBrushColor( dev->hdc );
413
414     pdev->brush_style = logbrush.lbStyle;
415
416     pdev->defer |= DEFER_BRUSH;
417
418     switch(logbrush.lbStyle)
419     {
420     case BS_SOLID:
421         pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, logbrush.lbColor);
422         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
423         pdev->brush_rects = solid_brush;
424         pdev->defer &= ~DEFER_BRUSH;
425         break;
426     default:
427         break;
428     }
429
430     return next->funcs->pSelectBrush( next, hbrush );
431 }
432
433 /***********************************************************************
434  *           dibdrv_SetDCBrushColor
435  */
436 COLORREF CDECL dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
437 {
438     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
439     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
440
441     if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
442     {
443         pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, color);
444         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
445     }
446
447     return next->funcs->pSetDCBrushColor( next, color );
448 }