msvcp90: Use macro to define RTTI data.
[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 <assert.h>
22 #include <stdlib.h>
23
24 #include "gdi_private.h"
25 #include "dibdrv.h"
26
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(dib);
30
31 /*
32  *
33  * Decompose the 16 ROP2s into an expression of the form
34  *
35  * D = (D & A) ^ X
36  *
37  * Where A and X depend only on P (and so can be precomputed).
38  *
39  *                                       A    X
40  *
41  * R2_BLACK         0                    0    0
42  * R2_NOTMERGEPEN   ~(D | P)            ~P   ~P
43  * R2_MASKNOTPEN    ~P & D              ~P    0
44  * R2_NOTCOPYPEN    ~P                   0   ~P
45  * R2_MASKPENNOT    P & ~D               P    P
46  * R2_NOT           ~D                   1    1
47  * R2_XORPEN        P ^ D                1    P
48  * R2_NOTMASKPEN    ~(P & D)             P    1
49  * R2_MASKPEN       P & D                P    0
50  * R2_NOTXORPEN     ~(P ^ D)             1   ~P
51  * R2_NOP           D                    1    0
52  * R2_MERGENOTPEN   ~P | D               P   ~P
53  * R2_COPYPEN       P                    0    P
54  * R2_MERGEPENNOT   P | ~D              ~P    1
55  * R2_MERGEPEN      P | D               ~P    P
56  * R2_WHITE         1                    0    1
57  *
58  */
59
60 /* A = (P & A1) ^ A2 */
61 #define ZERO  { 0u,  0u}
62 #define ONE   { 0u, ~0u}
63 #define P     {~0u,  0u}
64 #define NOT_P {~0u, ~0u}
65
66 static const DWORD rop2_and_array[16][2] =
67 {
68     ZERO, NOT_P, NOT_P, ZERO,
69     P,    ONE,   ONE,   P,
70     P,    ONE,   ONE,   P,
71     ZERO, NOT_P, NOT_P, ZERO
72 };
73
74 /* X = (P & X1) ^ X2 */
75 static const DWORD rop2_xor_array[16][2] =
76 {
77     ZERO, NOT_P, ZERO, NOT_P,
78     P,    ONE,   P,    ONE,
79     ZERO, NOT_P, ZERO, NOT_P,
80     P,    ONE,   P,    ONE
81 };
82
83 #undef NOT_P
84 #undef P
85 #undef ONE
86 #undef ZERO
87
88 void get_rop_codes(INT rop, struct rop_codes *codes)
89 {
90     /* NB The ROP2 codes start at one and the arrays are zero-based */
91     codes->a1 = rop2_and_array[rop-1][0];
92     codes->a2 = rop2_and_array[rop-1][1];
93     codes->x1 = rop2_xor_array[rop-1][0];
94     codes->x2 = rop2_xor_array[rop-1][1];
95 }
96
97 void calc_and_xor_masks(INT rop, DWORD color, DWORD *and, DWORD *xor)
98 {
99     struct rop_codes codes;
100     get_rop_codes( rop, &codes );
101
102     *and = (color & codes.a1) ^ codes.a2;
103     *xor = (color & codes.x1) ^ codes.x2;
104 }
105
106 static inline RGBQUAD rgbquad_from_colorref(COLORREF c)
107 {
108     RGBQUAD ret;
109
110     ret.rgbRed      = GetRValue(c);
111     ret.rgbGreen    = GetGValue(c);
112     ret.rgbBlue     = GetBValue(c);
113     ret.rgbReserved = 0;
114     return ret;
115 }
116
117 static inline BOOL rgbquad_equal(const RGBQUAD *a, const RGBQUAD *b)
118 {
119     if(a->rgbRed   == b->rgbRed   &&
120        a->rgbGreen == b->rgbGreen &&
121        a->rgbBlue  == b->rgbBlue)
122         return TRUE;
123     return FALSE;
124 }
125
126 COLORREF make_rgb_colorref( HDC hdc, dib_info *dib, COLORREF color, BOOL *got_pixel, DWORD *pixel )
127 {
128     BYTE type = color >> 24;
129     WORD index = LOWORD( color );
130     HPALETTE pal = GetCurrentObject( hdc, OBJ_PAL );
131     PALETTEENTRY pal_ent;
132
133     *pixel = 0;
134     *got_pixel = FALSE;
135
136     switch( type )
137     {
138     case 0: break;
139
140     case 0x10: /* DIBINDEX */
141         *got_pixel = TRUE;
142         *pixel = 0;
143         color = RGB(0, 0, 0);
144
145         if (dib->bit_count <= 8 && index < (1 << dib->bit_count))
146         {
147             *pixel = index;
148             if (index < dib->color_table_size)
149                 color = RGB( dib->color_table[index].rgbRed,
150                              dib->color_table[index].rgbGreen,
151                              dib->color_table[index].rgbBlue );
152         }
153         break;
154
155     case 2: /* PALETTERGB */
156         color &= 0xffffff;
157         break;
158
159     case 1: /* PALETTEINDEX */
160         if (!GetPaletteEntries( pal, index, 1, &pal_ent ))
161             GetPaletteEntries( pal, 0, 1, &pal_ent );
162         color = RGB( pal_ent.peRed, pal_ent.peGreen, pal_ent.peBlue );
163         break;
164
165     default:
166         FIXME("Unhandled color type %08x\n", color);
167         color &= 0xffffff;
168     }
169
170     return color;
171 }
172
173 /******************************************************************
174  *                   get_pixel_color
175  *
176  * 1 bit bitmaps map the fg/bg colors as follows:
177  * If the fg colorref exactly matches one of the color table entries then
178  * that entry is the fg color and the other is the bg.
179  * Otherwise the bg color is mapped to the closest entry in the table and
180  * the fg takes the other one.
181  */
182 DWORD get_pixel_color( dibdrv_physdev *pdev, COLORREF color, BOOL mono_fixup )
183 {
184     RGBQUAD fg_quad;
185     BOOL got_pixel;
186     DWORD pixel;
187     COLORREF rgb_ref;
188
189     rgb_ref = make_rgb_colorref( pdev->dev.hdc, &pdev->dib, color, &got_pixel, &pixel );
190     if (got_pixel) return pixel;
191
192     if (pdev->dib.bit_count != 1 || !mono_fixup)
193         return pdev->dib.funcs->colorref_to_pixel( &pdev->dib, rgb_ref );
194
195     fg_quad = rgbquad_from_colorref( rgb_ref );
196     if(rgbquad_equal(&fg_quad, pdev->dib.color_table))
197         return 0;
198     if(rgbquad_equal(&fg_quad, pdev->dib.color_table + 1))
199         return 1;
200
201     if(color == GetBkColor(pdev->dev.hdc)) return pdev->bkgnd_color;
202     else return pdev->bkgnd_color ? 0 : 1;
203 }
204
205 /***************************************************************************
206  *                get_pen_bkgnd_masks
207  *
208  * Returns the pre-calculated bkgnd color masks unless the dib is 1 bpp.
209  * In this case since there are several fg sources (pen, brush, text)
210  * this makes pdev->bkgnd_color unusable.  So here we take the inverse
211  * of the relevant fg color (which is always set up correctly).
212  */
213 static inline void get_pen_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
214 {
215     if(pdev->dib.bit_count != 1 || GetBkMode(pdev->dev.hdc) == TRANSPARENT)
216     {
217         *and = pdev->bkgnd_and;
218         *xor = pdev->bkgnd_xor;
219     }
220     else
221     {
222         DWORD color = ~pdev->pen_color;
223         if(pdev->pen_colorref == GetBkColor(pdev->dev.hdc)) color = pdev->pen_color;
224         calc_and_xor_masks( GetROP2(pdev->dev.hdc), color, and, xor );
225     }
226 }
227
228 static inline void get_brush_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
229 {
230     if(GetBkMode(pdev->dev.hdc) == TRANSPARENT)
231     {
232         *and = pdev->bkgnd_and;
233         *xor = pdev->bkgnd_xor;
234     }
235     else
236     {
237         DWORD color = pdev->bkgnd_color;
238
239         if(pdev->dib.bit_count == 1)
240         {
241             if(pdev->brush_colorref == GetBkColor(pdev->dev.hdc))
242                 color = pdev->brush_color;
243             else
244                 color = ~pdev->brush_color;
245         }
246         calc_and_xor_masks( pdev->brush_rop, color, and, xor );
247     }
248 }
249
250 static inline void order_end_points(int *s, int *e)
251 {
252     if(*s > *e)
253     {
254         int tmp;
255         tmp = *s + 1;
256         *s = *e + 1;
257         *e = tmp;
258     }
259 }
260
261 #define Y_INCREASING_MASK 0x0f
262 #define X_INCREASING_MASK 0xc3
263 #define X_MAJOR_MASK      0x99
264 #define POS_SLOPE_MASK    0x33
265
266 static inline BOOL is_xmajor(DWORD octant)
267 {
268     return octant & X_MAJOR_MASK;
269 }
270
271 static inline BOOL is_pos_slope(DWORD octant)
272 {
273     return octant & POS_SLOPE_MASK;
274 }
275
276 static inline BOOL is_x_increasing(DWORD octant)
277 {
278     return octant & X_INCREASING_MASK;
279 }
280
281 static inline BOOL is_y_increasing(DWORD octant)
282 {
283     return octant & Y_INCREASING_MASK;
284 }
285
286 /**********************************************************************
287  *                  get_octant_number
288  *
289  * Return the octant number starting clockwise from the +ve x-axis.
290  */
291 static inline int get_octant_number(int dx, int dy)
292 {
293     if(dy > 0)
294         if(dx > 0)
295             return ( dx >  dy) ? 1 : 2;
296         else
297             return (-dx >  dy) ? 4 : 3;
298     else
299         if(dx < 0)
300             return (-dx > -dy) ? 5 : 6;
301         else
302             return ( dx > -dy) ? 8 : 7;
303 }
304
305 static inline DWORD get_octant_mask(int dx, int dy)
306 {
307     return 1 << (get_octant_number(dx, dy) - 1);
308 }
309
310 static inline int get_bias( DWORD mask )
311 {
312     /* Octants 3, 5, 6 and 8 take a bias */
313     return (mask & 0xb4) ? 1 : 0;
314 }
315
316 #define OUT_LEFT    1
317 #define OUT_RIGHT   2
318 #define OUT_TOP     4
319 #define OUT_BOTTOM  8
320
321 static inline DWORD calc_outcode(const POINT *pt, const RECT *clip)
322 {
323     DWORD out = 0;
324     if(pt->x < clip->left)         out |= OUT_LEFT;
325     else if(pt->x >= clip->right)  out |= OUT_RIGHT;
326     if(pt->y < clip->top)          out |= OUT_TOP;
327     else if(pt->y >= clip->bottom) out |= OUT_BOTTOM;
328
329     return out;
330 }
331
332 /******************************************************************************
333  *                clip_line
334  *
335  * Clips the start and end points to a rectangle.
336  *
337  * Note, this treats the end point like the start point.  If the
338  * caller doesn't want it displayed, it should exclude it.  If the end
339  * point is clipped out, then the likelihood is that the new end point
340  * should be displayed.
341  *
342  * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
343  *
344  * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
345  * however the Bresenham error term is defined differently so the equations
346  * will also differ.
347  *
348  * For x major lines we have 2dy >= err + bias > 2dy - 2dx
349  *                           0   >= err + bias - 2dy > -2dx
350  *
351  * Note dx, dy, m and n are all +ve.
352  *
353  * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
354  *                     err = 2dy - dx + 2mdy - 2ndx
355  *                      0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
356  *                      0 >= 2mdy - 2ndx + bias - dx > -2dx
357  *                      which of course will give exactly one solution for n,
358  *                      so looking at the >= inequality
359  *                      n >= (2mdy + bias - dx) / 2dx
360  *                      n = ceiling((2mdy + bias - dx) / 2dx)
361  *                        = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
362  *
363  * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
364  * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
365  *                     0 >= 2mdy - 2ndx + bias - dx > -2dx
366  *                  2mdy > 2ndx - bias - dx
367  *                     m > (2ndx - bias - dx) / 2dy
368  *                     m = floor((2ndx - bias - dx) / 2dy) + 1
369  *                     m = (2ndx - bias - dx) / 2dy + 1
370  *
371  * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
372  *                  err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
373  *                      = 2dy - dx - 2mdy + 2ndx
374  *                   0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
375  *                   0 >= 2ndx - 2mdy + bias - dx > -2dx
376  *                   again exactly one solution.
377  *                   2ndx <= 2mdy - bias + dx
378  *                   n = floor((2mdy - bias + dx) / 2dx)
379  *                     = (2mdy - bias + dx) / 2dx
380  *
381  * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
382  * mininizing m to include all of the points at y = y2 - n.  As above:
383  *                  0 >= 2ndx - 2mdy + bias - dx > -2dx
384  *               2mdy >= 2ndx + bias - dx
385  *                   m = ceiling((2ndx + bias - dx) / 2dy)
386  *                     = (2ndx + bias - dx - 1) / 2dy + 1
387  *
388  * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
389  *
390  * Moving start point from y1 to y1 + n find x1 + m
391  *                     m = (2ndx + bias + dy - 1) / 2dy
392  *
393  * Moving start point from x1 to x1 + m find y1 + n
394  *                     n = (2mdy - bias - dy) / 2ndx + 1
395  *
396  * Moving end point from y2 to y2 - n find x1 - m
397  *                     m = (2ndx - bias + dy) / 2dy
398  *
399  * Moving end point from x2 to x2 - m find y2 - n
400  *                     n = (2mdy + bias - dy - 1) / 2dx + 1
401  */
402 int clip_line(const POINT *start, const POINT *end, const RECT *clip,
403               const bres_params *params, POINT *pt1, POINT *pt2)
404 {
405     int m, n;
406     BOOL clipped = FALSE;
407     DWORD start_oc, end_oc;
408     const int bias = params->bias;
409     const unsigned int dx = params->dx;
410     const unsigned int dy = params->dy;
411     const unsigned int two_dx = params->dx * 2;
412     const unsigned int two_dy = params->dy * 2;
413     const BOOL xmajor = is_xmajor(params->octant);
414     const BOOL neg_slope = !is_pos_slope(params->octant);
415
416     *pt1 = *start;
417     *pt2 = *end;
418
419     start_oc = calc_outcode(start, clip);
420     end_oc = calc_outcode(end, clip);
421
422     while(1)
423     {
424         if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
425         if(start_oc & end_oc)            return 0; /* trivial reject */
426
427         clipped = TRUE;
428         if(start_oc & OUT_LEFT)
429         {
430             m = clip->left - start->x;
431             if(xmajor)
432                 n = (m * two_dy + bias + dx - 1) / two_dx;
433             else
434                 n = (m * two_dy - bias - dy) / two_dx + 1;
435
436             pt1->x = clip->left;
437             if(neg_slope) n = -n;
438             pt1->y = start->y + n;
439             start_oc = calc_outcode(pt1, clip);
440         }
441         else if(start_oc & OUT_RIGHT)
442         {
443             m = start->x - clip->right + 1;
444             if(xmajor)
445                 n = (m * two_dy + bias + dx - 1) / two_dx;
446             else
447                 n = (m * two_dy - bias - dy) / two_dx + 1;
448
449             pt1->x = clip->right - 1;
450             if(neg_slope) n = -n;
451             pt1->y = start->y - n;
452             start_oc = calc_outcode(pt1, clip);
453         }
454         else if(start_oc & OUT_TOP)
455         {
456             n = clip->top - start->y;
457             if(xmajor)
458                 m = (n * two_dx - bias - dx) / two_dy + 1;
459             else
460                 m = (n * two_dx + bias + dy - 1) / two_dy;
461
462             pt1->y = clip->top;
463             if(neg_slope) m = -m;
464             pt1->x = start->x + m;
465             start_oc = calc_outcode(pt1, clip);
466         }
467         else if(start_oc & OUT_BOTTOM)
468         {
469             n = start->y - clip->bottom + 1;
470             if(xmajor)
471                 m = (n * two_dx - bias - dx) / two_dy + 1;
472             else
473                 m = (n * two_dx + bias + dy - 1) / two_dy;
474
475             pt1->y = clip->bottom - 1;
476             if(neg_slope) m = -m;
477             pt1->x = start->x - m;
478             start_oc = calc_outcode(pt1, clip);
479         }
480         else if(end_oc & OUT_LEFT)
481         {
482             m = clip->left - end->x;
483             if(xmajor)
484                 n = (m * two_dy - bias + dx) / two_dx;
485             else
486                 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
487
488             pt2->x = clip->left;
489             if(neg_slope) n = -n;
490             pt2->y = end->y + n;
491             end_oc = calc_outcode(pt2, clip);
492         }
493         else if(end_oc & OUT_RIGHT)
494         {
495             m = end->x - clip->right + 1;
496             if(xmajor)
497                 n = (m * two_dy - bias + dx) / two_dx;
498             else
499                 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
500
501             pt2->x = clip->right - 1;
502             if(neg_slope) n = -n;
503             pt2->y = end->y - n;
504             end_oc = calc_outcode(pt2, clip);
505         }
506         else if(end_oc & OUT_TOP)
507         {
508             n = clip->top - end->y;
509             if(xmajor)
510                 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
511             else
512                 m = (n * two_dx - bias + dy) / two_dy;
513
514             pt2->y = clip->top;
515             if(neg_slope) m = -m;
516             pt2->x = end->x + m;
517             end_oc = calc_outcode(pt2, clip);
518         }
519         else if(end_oc & OUT_BOTTOM)
520         {
521             n = end->y - clip->bottom + 1;
522             if(xmajor)
523                 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
524             else
525                 m = (n * two_dx - bias + dy) / two_dy;
526
527             pt2->y = clip->bottom - 1;
528             if(neg_slope) m = -m;
529             pt2->x = end->x - m;
530             end_oc = calc_outcode(pt2, clip);
531         }
532     }
533 }
534
535 static void bres_line_with_bias(const POINT *start, const struct line_params *params,
536                                 void (* callback)(dibdrv_physdev*,INT,INT), dibdrv_physdev *pdev)
537 {
538     POINT pt = *start;
539     int len = params->length, err = params->err_start;
540
541     if (params->x_major)
542     {
543         while(len--)
544         {
545             callback(pdev, pt.x, pt.y);
546             if (err + params->bias > 0)
547             {
548                 pt.y += params->y_inc;
549                 err += params->err_add_1;
550             }
551             else err += params->err_add_2;
552             pt.x += params->x_inc;
553         }
554     }
555     else
556     {
557         while(len--)
558         {
559             callback(pdev, pt.x, pt.y);
560             if (err + params->bias > 0)
561             {
562                 pt.x += params->x_inc;
563                 err += params->err_add_1;
564             }
565             else err += params->err_add_2;
566             pt.y += params->y_inc;
567         }
568     }
569 }
570
571 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
572 {
573     const WINEREGION *clip = get_wine_region(pdev->clip);
574
575     if(start->y == end->y)
576     {
577         RECT rect;
578         int i;
579
580         rect.left   = start->x;
581         rect.top    = start->y;
582         rect.right  = end->x;
583         rect.bottom = end->y + 1;
584         order_end_points(&rect.left, &rect.right);
585         for(i = 0; i < clip->numRects; i++)
586         {
587             if(clip->rects[i].top >= rect.bottom) break;
588             if(clip->rects[i].bottom <= rect.top) continue;
589             /* Optimize the unclipped case */
590             if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
591             {
592                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
593                 break;
594             }
595             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
596             {
597                 RECT tmp = rect;
598                 tmp.left = max(rect.left, clip->rects[i].left);
599                 tmp.right = min(rect.right, clip->rects[i].right);
600                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
601             }
602         }
603     }
604     else if(start->x == end->x)
605     {
606         RECT rect;
607         int i;
608
609         rect.left   = start->x;
610         rect.top    = start->y;
611         rect.right  = end->x + 1;
612         rect.bottom = end->y;
613         order_end_points(&rect.top, &rect.bottom);
614         for(i = 0; i < clip->numRects; i++)
615         {
616             /* Optimize unclipped case */
617             if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
618                clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
619             {
620                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
621                 break;
622             }
623             if(clip->rects[i].top >= rect.bottom) break;
624             if(clip->rects[i].bottom <= rect.top) continue;
625             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
626             {
627                 RECT tmp = rect;
628                 tmp.top = max(rect.top, clip->rects[i].top);
629                 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
630                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
631             }
632         }
633     }
634     else
635     {
636         bres_params clip_params;
637         struct line_params line_params;
638         INT dx = end->x - start->x, dy = end->y - start->y;
639         INT abs_dx = abs(dx), abs_dy = abs(dy);
640         INT i;
641
642         clip_params.dx = abs_dx;
643         clip_params.dy = abs_dy;
644         clip_params.octant = get_octant_mask(dx, dy);
645         clip_params.bias   = get_bias( clip_params.octant );
646
647         line_params.bias    = clip_params.bias;
648         line_params.x_major = is_xmajor( clip_params.octant );
649         line_params.x_inc   = is_x_increasing( clip_params.octant ) ? 1 : -1;
650         line_params.y_inc   = is_y_increasing( clip_params.octant ) ? 1 : -1;
651
652         if (line_params.x_major)
653         {
654             line_params.err_add_1 = 2 * abs_dy - 2 * abs_dx;
655             line_params.err_add_2 = 2 * abs_dy;
656         }
657         else
658         {
659             line_params.err_add_1 = 2 * abs_dx - 2 * abs_dy;
660             line_params.err_add_2 = 2 * abs_dx;
661         }
662
663         for(i = 0; i < clip->numRects; i++)
664         {
665             POINT clipped_start, clipped_end;
666             int clip_status;
667             clip_status = clip_line(start, end, clip->rects + i, &clip_params, &clipped_start, &clipped_end);
668
669             if(clip_status)
670             {
671                 int m = abs(clipped_start.x - start->x);
672                 int n = abs(clipped_start.y - start->y);
673
674                 if (line_params.x_major)
675                 {
676                     line_params.err_start = 2 * abs_dy - abs_dx + m * 2 * abs_dy - n * 2 * abs_dx;
677                     line_params.length = abs( clipped_end.x - clipped_start.x ) + 1;
678                 }
679                 else
680                 {
681                     line_params.err_start = 2 * abs_dx - abs_dy + n * 2 * abs_dx - m * 2 * abs_dy;
682                     line_params.length = abs( clipped_end.y - clipped_start.y ) + 1;
683                 }
684
685                 if (clipped_end.x == end->x && clipped_end.y == end->y) line_params.length--;
686
687                 pdev->dib.funcs->solid_line( &pdev->dib, &clipped_start, &line_params,
688                                              pdev->pen_and, pdev->pen_xor );
689
690                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
691             }
692         }
693
694     }
695     release_wine_region(pdev->clip);
696     return TRUE;
697 }
698
699 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
700 {
701     int i;
702
703     assert( num >= 2 );
704     for (i = 0; i < num - 1; i++)
705         if (!solid_pen_line( pdev, pts + i, pts + i + 1 ))
706             return FALSE;
707
708     if (close) return solid_pen_line( pdev, pts + num - 1, pts );
709
710     return TRUE;
711 }
712
713 void reset_dash_origin(dibdrv_physdev *pdev)
714 {
715     pdev->dash_pos.cur_dash = 0;
716     pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
717     pdev->dash_pos.mark = TRUE;
718 }
719
720 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
721 {
722     skip %= pdev->pen_pattern.total_len;
723     while(skip)
724     {
725         if(pdev->dash_pos.left_in_dash > skip)
726         {
727             pdev->dash_pos.left_in_dash -= skip;
728             return;
729         }
730         skip -= pdev->dash_pos.left_in_dash;
731         pdev->dash_pos.cur_dash++;
732         if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
733         pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
734         pdev->dash_pos.mark = !pdev->dash_pos.mark;
735     }
736 }
737
738 static inline void get_dash_colors(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
739 {
740     if(pdev->dash_pos.mark)
741     {
742         *and = pdev->pen_and;
743         *xor = pdev->pen_xor;
744     }
745     else /* space */
746     {
747         get_pen_bkgnd_masks( pdev, and, xor );
748     }
749 }
750
751 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
752 {
753     RECT rect;
754     DWORD and, xor;
755
756     get_dash_colors(pdev, &and, &xor);
757     skip_dash(pdev, 1);
758     rect.left   = x;
759     rect.right  = x + 1;
760     rect.top    = y;
761     rect.bottom = y + 1;
762     pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
763     return;
764 }
765
766 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
767 {
768     const WINEREGION *clip = get_wine_region(pdev->clip);
769     DWORD and, xor;
770     int i, dash_len;
771     RECT rect;
772     const dash_pos start_pos = pdev->dash_pos;
773
774     if(start->y == end->y) /* hline */
775     {
776         BOOL l_to_r;
777         INT left, right, cur_x;
778
779         rect.top = start->y;
780         rect.bottom = start->y + 1;
781
782         if(start->x <= end->x)
783         {
784             left = start->x;
785             right = end->x - 1;
786             l_to_r = TRUE;
787         }
788         else
789         {
790             left = end->x + 1;
791             right = start->x;
792             l_to_r = FALSE;
793         }
794
795         for(i = 0; i < clip->numRects; i++)
796         {
797             if(clip->rects[i].top > start->y) break;
798             if(clip->rects[i].bottom <= start->y) continue;
799
800             if(clip->rects[i].right > left && clip->rects[i].left <= right)
801             {
802                 int clipped_left  = max(clip->rects[i].left, left);
803                 int clipped_right = min(clip->rects[i].right - 1, right);
804
805                 pdev->dash_pos = start_pos;
806
807                 if(l_to_r)
808                 {
809                     cur_x = clipped_left;
810                     if(cur_x != left)
811                         skip_dash(pdev, clipped_left - left);
812
813                     while(cur_x <= clipped_right)
814                     {
815                         get_dash_colors(pdev, &and, &xor);
816                         dash_len = pdev->dash_pos.left_in_dash;
817                         if(cur_x + dash_len > clipped_right + 1)
818                             dash_len = clipped_right - cur_x + 1;
819                         rect.left = cur_x;
820                         rect.right = cur_x + dash_len;
821
822                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
823                         cur_x += dash_len;
824                         skip_dash(pdev, dash_len);
825                     }
826                 }
827                 else
828                 {
829                     cur_x = clipped_right;
830                     if(cur_x != right)
831                         skip_dash(pdev, right - clipped_right);
832
833                     while(cur_x >= clipped_left)
834                     {
835                         get_dash_colors(pdev, &and, &xor);
836                         dash_len = pdev->dash_pos.left_in_dash;
837                         if(cur_x - dash_len < clipped_left - 1)
838                             dash_len = cur_x - clipped_left + 1;
839                         rect.left = cur_x - dash_len + 1;
840                         rect.right = cur_x + 1;
841
842                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
843                         cur_x -= dash_len;
844                         skip_dash(pdev, dash_len);
845                     }
846                 }
847             }
848         }
849         pdev->dash_pos = start_pos;
850         skip_dash(pdev, right - left + 1);
851     }
852     else if(start->x == end->x) /* vline */
853     {
854         BOOL t_to_b;
855         INT top, bottom, cur_y;
856
857         rect.left = start->x;
858         rect.right = start->x + 1;
859
860         if(start->y <= end->y)
861         {
862             top = start->y;
863             bottom = end->y - 1;
864             t_to_b = TRUE;
865         }
866         else
867         {
868             top = end->y + 1;
869             bottom = start->y;
870             t_to_b = FALSE;
871         }
872
873         for(i = 0; i < clip->numRects; i++)
874         {
875             if(clip->rects[i].top > bottom) break;
876             if(clip->rects[i].bottom <= top) continue;
877             if(clip->rects[i].right > start->x && clip->rects[i].left <= start->x)
878             {
879                 int clipped_top    = max(clip->rects[i].top, top);
880                 int clipped_bottom = min(clip->rects[i].bottom - 1, bottom);
881
882                 pdev->dash_pos = start_pos;
883
884                 if(t_to_b)
885                 {
886                     cur_y = clipped_top;
887                     if(cur_y != top)
888                         skip_dash(pdev, clipped_top - top);
889
890                     while(cur_y <= clipped_bottom)
891                     {
892                         get_dash_colors(pdev, &and, &xor);
893                         dash_len = pdev->dash_pos.left_in_dash;
894                         if(cur_y + dash_len > clipped_bottom + 1)
895                             dash_len = clipped_bottom - cur_y + 1;
896                         rect.top = cur_y;
897                         rect.bottom = cur_y + dash_len;
898
899                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
900                         cur_y += dash_len;
901                         skip_dash(pdev, dash_len);
902                     }
903                 }
904                 else
905                 {
906                     cur_y = clipped_bottom;
907                     if(cur_y != bottom)
908                         skip_dash(pdev, bottom - clipped_bottom);
909
910                     while(cur_y >= clipped_top)
911                     {
912                         get_dash_colors(pdev, &and, &xor);
913                         dash_len = pdev->dash_pos.left_in_dash;
914                         if(cur_y - dash_len < clipped_top - 1)
915                             dash_len = cur_y - clipped_top + 1;
916                         rect.top = cur_y - dash_len + 1;
917                         rect.bottom = cur_y + 1;
918
919                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
920                         cur_y -= dash_len;
921                         skip_dash(pdev, dash_len);
922                     }
923                 }
924             }
925         }
926         pdev->dash_pos = start_pos;
927         skip_dash(pdev, bottom - top + 1);
928     }
929     else
930     {
931         bres_params clip_params;
932         struct line_params line_params;
933         INT dx = end->x - start->x, dy = end->y - start->y;
934         INT abs_dx = abs(dx), abs_dy = abs(dy);
935         INT i;
936
937         clip_params.dx = abs_dx;
938         clip_params.dy = abs_dy;
939         clip_params.octant = get_octant_mask(dx, dy);
940         clip_params.bias   = get_bias( clip_params.octant );
941
942         line_params.bias    = clip_params.bias;
943         line_params.x_major = is_xmajor( clip_params.octant );
944         line_params.x_inc   = is_x_increasing( clip_params.octant ) ? 1 : -1;
945         line_params.y_inc   = is_y_increasing( clip_params.octant ) ? 1 : -1;
946
947         if (line_params.x_major)
948         {
949             line_params.err_add_1 = 2 * abs_dy - 2 * abs_dx;
950             line_params.err_add_2 = 2 * abs_dy;
951         }
952         else
953         {
954             line_params.err_add_1 = 2 * abs_dx - 2 * abs_dy;
955             line_params.err_add_2 = 2 * abs_dx;
956         }
957
958         for(i = 0; i < clip->numRects; i++)
959         {
960             POINT clipped_start, clipped_end;
961             int clip_status;
962             clip_status = clip_line(start, end, clip->rects + i, &clip_params, &clipped_start, &clipped_end);
963
964             if(clip_status)
965             {
966                 int m = abs(clipped_start.x - start->x);
967                 int n = abs(clipped_start.y - start->y);
968
969                 pdev->dash_pos = start_pos;
970
971                 if (line_params.x_major)
972                 {
973                     line_params.err_start = 2 * abs_dy - abs_dx + m * 2 * abs_dy - n * 2 * abs_dx;
974                     line_params.length = abs( clipped_end.x - clipped_start.x ) + 1;
975                     skip_dash(pdev, m);
976                 }
977                 else
978                 {
979                     line_params.err_start = 2 * abs_dx - abs_dy + n * 2 * abs_dx - m * 2 * abs_dy;
980                     line_params.length = abs( clipped_end.y - clipped_start.y ) + 1;
981                     skip_dash(pdev, n);
982                 }
983                 if (clipped_end.x == end->x && clipped_end.y == end->y) line_params.length--;
984
985                 bres_line_with_bias( &clipped_start, &line_params, dashed_pen_line_callback, pdev );
986
987                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
988             }
989         }
990         pdev->dash_pos = start_pos;
991         if(line_params.x_major)
992             skip_dash(pdev, abs_dx);
993         else
994             skip_dash(pdev, abs_dy);
995     }
996
997     release_wine_region(pdev->clip);
998     return TRUE;
999 }
1000
1001 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
1002 {
1003     int i;
1004
1005     assert( num >= 2 );
1006     for (i = 0; i < num - 1; i++)
1007         if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
1008             return FALSE;
1009
1010     if (close) return dashed_pen_line( pdev, pts + num - 1, pts );
1011
1012     return TRUE;
1013 }
1014
1015 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
1016 {
1017     return TRUE;
1018 }
1019
1020 static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
1021 {
1022     HRGN cap;
1023
1024     switch (pdev->pen_endcap)
1025     {
1026     case PS_ENDCAP_ROUND:
1027         cap = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2,
1028                                  pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 );
1029         break;
1030
1031     default: /* only supporting cosmetic pens so far, so always PS_ENDCAP_ROUND */
1032         return;
1033     }
1034
1035     CombineRgn( region, region, cap, RGN_OR );
1036     DeleteObject( cap );
1037     return;
1038 }
1039
1040 static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
1041 {
1042     HRGN join;
1043
1044     switch (pdev->pen_join)
1045     {
1046     case PS_JOIN_ROUND:
1047         join = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2,
1048                                   pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 );
1049         break;
1050
1051     default: /* only supporting cosmetic pens so far, so always PS_JOIN_ROUND */
1052         return;
1053     }
1054
1055     CombineRgn( region, region, join, RGN_OR );
1056     DeleteObject( join );
1057     return;
1058 }
1059
1060 #define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5)
1061
1062 static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BOOL close )
1063 {
1064     int i;
1065     HRGN total, segment;
1066
1067     assert( num >= 2 );
1068
1069     total = CreateRectRgn( 0, 0, 0, 0 );
1070
1071     if (!close) num--;
1072     for (i = 0; i < num; i++)
1073     {
1074         const POINT *pt_1 = pts + i;
1075         const POINT *pt_2 = pts + ((close && i == num - 1) ? 0 : i + 1);
1076         int dx = pt_2->x - pt_1->x;
1077         int dy = pt_2->y - pt_1->y;
1078         RECT rect;
1079
1080         if (dx == 0 && dy == 0) continue;
1081
1082         if (dy == 0)
1083         {
1084             rect.left = min( pt_1->x, pt_2->x );
1085             rect.right = rect.left + abs( dx );
1086             rect.top = pt_1->y - pdev->pen_width / 2;
1087             rect.bottom = rect.top + pdev->pen_width;
1088             segment = CreateRectRgnIndirect( &rect );
1089         }
1090         else if (dx == 0)
1091         {
1092             rect.top = min( pt_1->y, pt_2->y );
1093             rect.bottom = rect.top + abs( dy );
1094             rect.left = pt_1->x - pdev->pen_width / 2;
1095             rect.right = rect.left + pdev->pen_width;
1096             segment = CreateRectRgnIndirect( &rect );
1097         }
1098         else
1099         {
1100             double len = hypot( dx, dy );
1101             double width_x, width_y;
1102             POINT seg_pts[4];
1103             POINT wide_half, narrow_half;
1104
1105             width_x = pdev->pen_width * abs( dy ) / len;
1106             width_y = pdev->pen_width * abs( dx ) / len;
1107
1108             narrow_half.x = round( width_x / 2 );
1109             narrow_half.y = round( width_y / 2 );
1110             wide_half.x   = round( (width_x + 1) / 2 );
1111             wide_half.y   = round( (width_y + 1) / 2 );
1112
1113             if (dx < 0)
1114             {
1115                 wide_half.y   = -wide_half.y;
1116                 narrow_half.y = -narrow_half.y;
1117             }
1118
1119             if (dy < 0)
1120             {
1121                 POINT tmp = narrow_half; narrow_half = wide_half; wide_half = tmp;
1122                 wide_half.x   = -wide_half.x;
1123                 narrow_half.x = -narrow_half.x;
1124             }
1125
1126             seg_pts[0].x = pt_1->x - narrow_half.x;
1127             seg_pts[0].y = pt_1->y + narrow_half.y;
1128             seg_pts[1].x = pt_1->x + wide_half.x;
1129             seg_pts[1].y = pt_1->y - wide_half.y;
1130             seg_pts[2].x = pt_2->x + wide_half.x;
1131             seg_pts[2].y = pt_2->y - wide_half.y;
1132             seg_pts[3].x = pt_2->x - narrow_half.x;
1133             seg_pts[3].y = pt_2->y + narrow_half.y;
1134
1135             segment = CreatePolygonRgn( seg_pts, 4, ALTERNATE );
1136         }
1137
1138         CombineRgn( total, total, segment, RGN_OR );
1139         DeleteObject( segment );
1140
1141         if (i == 0)
1142         {
1143             if (!close) add_cap( pdev, total, pt_1 );
1144         }
1145         else
1146             add_join( pdev, total, pt_1 );
1147
1148         if (i == num - 1)
1149         {
1150             if (close) add_join( pdev, total, pt_2 );
1151             else add_cap( pdev, total, pt_2 );
1152         }
1153
1154     }
1155     return total;
1156 }
1157
1158 static BOOL wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
1159 {
1160     const WINEREGION *data;
1161     rop_mask color;
1162     HRGN region;
1163
1164     color.and = pdev->pen_and;
1165     color.xor = pdev->pen_xor;
1166
1167     region = get_wide_lines_region( pdev, num, pts, close );
1168
1169     if (CombineRgn( region, region, pdev->clip, RGN_AND ) != ERROR)
1170     {
1171         data = get_wine_region( region );
1172         solid_rects( &pdev->dib, data->numRects, data->rects, &color, NULL );
1173         release_wine_region( region );
1174     }
1175
1176     DeleteObject( region );
1177     return TRUE;
1178 }
1179
1180 static const dash_pattern dash_patterns[5] =
1181 {
1182     {0, {0}, 0},                  /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
1183     {2, {18, 6}, 24},             /* PS_DASH */
1184     {2, {3,  3}, 6},              /* PS_DOT */
1185     {4, {9, 6, 3, 6}, 24},        /* PS_DASHDOT */
1186     {6, {9, 3, 3, 3, 3, 3}, 24}   /* PS_DASHDOTDOT */
1187 };
1188
1189 static inline int get_pen_device_width( dibdrv_physdev *pdev, LOGPEN *pen )
1190 {
1191     int width = pen->lopnWidth.x;
1192
1193     if (pen->lopnStyle & PS_GEOMETRIC && width > 1)
1194     {
1195         POINT pts[2];
1196         pts[0].x = pts[0].y = pts[1].y = 0;
1197         pts[1].x = width;
1198         LPtoDP( pdev->dev.hdc, pts, 2 );
1199         width = max( abs( pts[1].x - pts[0].x ), 1 );
1200     }
1201     return width;
1202 }
1203
1204 /***********************************************************************
1205  *           dibdrv_SelectPen
1206  */
1207 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
1208 {
1209     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
1210     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1211     LOGPEN logpen;
1212     DWORD style;
1213
1214     TRACE("(%p, %p)\n", dev, hpen);
1215
1216     if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
1217     {
1218         /* must be an extended pen */
1219         EXTLOGPEN *elp;
1220         INT size = GetObjectW( hpen, 0, NULL );
1221
1222         if (!size) return 0;
1223
1224         elp = HeapAlloc( GetProcessHeap(), 0, size );
1225
1226         GetObjectW( hpen, size, elp );
1227         /* FIXME: add support for user style pens */
1228         logpen.lopnStyle = elp->elpPenStyle;
1229         logpen.lopnWidth.x = elp->elpWidth;
1230         logpen.lopnWidth.y = 0;
1231         logpen.lopnColor = elp->elpColor;
1232
1233         HeapFree( GetProcessHeap(), 0, elp );
1234     }
1235
1236     pdev->pen_join   = logpen.lopnStyle & PS_JOIN_MASK;
1237     pdev->pen_endcap = logpen.lopnStyle & PS_ENDCAP_MASK;
1238     pdev->pen_width  = get_pen_device_width( pdev, &logpen );
1239
1240     if (hpen == GetStockObject( DC_PEN ))
1241         logpen.lopnColor = GetDCPenColor( dev->hdc );
1242
1243     pdev->pen_colorref = logpen.lopnColor;
1244     pdev->pen_color = get_pixel_color( pdev, pdev->pen_colorref, TRUE );
1245     calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1246
1247     pdev->pen_pattern = dash_patterns[PS_SOLID];
1248
1249     pdev->defer |= DEFER_PEN;
1250
1251     style = logpen.lopnStyle & PS_STYLE_MASK;
1252
1253     switch(style)
1254     {
1255     case PS_SOLID:
1256         if(logpen.lopnStyle & PS_GEOMETRIC) break;
1257         if(pdev->pen_width <= 1)
1258             pdev->pen_lines = solid_pen_lines;
1259         else
1260             pdev->pen_lines = wide_pen_lines;
1261         pdev->defer &= ~DEFER_PEN;
1262         break;
1263
1264     case PS_DASH:
1265     case PS_DOT:
1266     case PS_DASHDOT:
1267     case PS_DASHDOTDOT:
1268         if(logpen.lopnStyle & PS_GEOMETRIC) break;
1269         if(logpen.lopnWidth.x > 1) break;
1270         pdev->pen_lines = dashed_pen_lines;
1271         pdev->pen_pattern = dash_patterns[style];
1272         pdev->defer &= ~DEFER_PEN;
1273         break;
1274
1275     case PS_NULL:
1276         pdev->pen_lines = null_pen_lines;
1277         pdev->defer &= ~DEFER_PEN;
1278         break;
1279
1280     default:
1281         break;
1282     }
1283
1284     return next->funcs->pSelectPen( next, hpen );
1285 }
1286
1287 /***********************************************************************
1288  *           dibdrv_SetDCPenColor
1289  */
1290 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1291 {
1292     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
1293     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1294
1295     if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1296     {
1297         pdev->pen_colorref = color;
1298         pdev->pen_color = get_pixel_color( pdev, pdev->pen_colorref, TRUE );
1299         calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1300     }
1301
1302     return next->funcs->pSetDCPenColor( next, color );
1303 }
1304
1305 void solid_rects( dib_info *dib, int num, const RECT *rects, const rop_mask *color, HRGN region )
1306 {
1307     int i, j;
1308     const WINEREGION *clip;
1309
1310     if (!region)
1311     {
1312         dib->funcs->solid_rects( dib, num, rects, color->and, color->xor );
1313         return;
1314     }
1315
1316     clip = get_wine_region( region );
1317
1318     for(i = 0; i < num; i++)
1319     {
1320         for(j = 0; j < clip->numRects; j++)
1321         {
1322             RECT clipped_rect;
1323
1324             if (intersect_rect( &clipped_rect, rects + i, clip->rects + j ))
1325                 dib->funcs->solid_rects( dib, 1, &clipped_rect, color->and, color->xor );
1326         }
1327     }
1328     release_wine_region( region );
1329 }
1330
1331 /**********************************************************************
1332  *             solid_brush
1333  *
1334  * Fill a number of rectangles with the solid brush
1335  */
1336 static BOOL solid_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1337 {
1338     rop_mask brush_color;
1339
1340     brush_color.and = pdev->brush_and;
1341     brush_color.xor = pdev->brush_xor;
1342
1343     solid_rects( dib, num, rects, &brush_color, region );
1344     return TRUE;
1345 }
1346
1347 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
1348 {
1349     HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
1350     HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
1351     pdev->brush_and_bits = NULL;
1352     pdev->brush_xor_bits = NULL;
1353 }
1354
1355 void free_pattern_brush( dibdrv_physdev *pdev )
1356 {
1357     free_pattern_brush_bits( pdev );
1358     free_dib_info( &pdev->brush_dib );
1359 }
1360
1361 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
1362 {
1363     DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
1364     DWORD *brush_bits = pdev->brush_dib.bits.ptr;
1365     DWORD *and_bits, *xor_bits;
1366
1367     assert(pdev->brush_and_bits == NULL);
1368     assert(pdev->brush_xor_bits == NULL);
1369
1370     assert(pdev->brush_dib.stride > 0);
1371
1372     and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1373     xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1374
1375     if(!and_bits || !xor_bits)
1376     {
1377         ERR("Failed to create pattern brush bits\n");
1378         free_pattern_brush_bits( pdev );
1379         return FALSE;
1380     }
1381
1382     while(size)
1383     {
1384         calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1385         size -= 4;
1386     }
1387
1388     return TRUE;
1389 }
1390
1391 static const DWORD hatches[6][8] =
1392 {
1393     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1394     { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL   */
1395     { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL  */
1396     { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL  */
1397     { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS      */
1398     { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }  /* HS_DIAGCROSS  */
1399 };
1400
1401 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev)
1402 {
1403     dib_info hatch;
1404     rop_mask fg_mask, bg_mask;
1405     rop_mask_bits mask_bits;
1406     DWORD size;
1407     BOOL ret;
1408
1409     assert(pdev->brush_and_bits == NULL);
1410     assert(pdev->brush_xor_bits == NULL);
1411
1412     /* Just initialise brush_dib with the color / sizing info.  We don't
1413        need the bits as we'll calculate the rop masks straight from
1414        the hatch patterns. */
1415
1416     copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1417     pdev->brush_dib.width  = 8;
1418     pdev->brush_dib.height = 8;
1419     pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1420
1421     size = pdev->brush_dib.height * pdev->brush_dib.stride;
1422
1423     mask_bits.and = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1424     mask_bits.xor = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1425
1426     if(!mask_bits.and || !mask_bits.xor)
1427     {
1428         ERR("Failed to create pattern brush bits\n");
1429         free_pattern_brush_bits( pdev );
1430         return FALSE;
1431     }
1432
1433     hatch.bit_count = 1;
1434     hatch.height = hatch.width = 8;
1435     hatch.stride = 4;
1436     hatch.bits.ptr = (void *) hatches[pdev->brush_hatch];
1437     hatch.bits.free = hatch.bits.param = NULL;
1438     hatch.bits.is_copy = FALSE;
1439
1440     fg_mask.and = pdev->brush_and;
1441     fg_mask.xor = pdev->brush_xor;
1442     get_brush_bkgnd_masks( pdev, &bg_mask.and, &bg_mask.xor );
1443
1444     ret = pdev->brush_dib.funcs->create_rop_masks( &pdev->brush_dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1445     if(!ret) free_pattern_brush_bits( pdev );
1446
1447     return ret;
1448 }
1449
1450 static BOOL matching_pattern_format( dib_info *dib, dib_info *pattern )
1451 {
1452     if (dib->bit_count != pattern->bit_count) return FALSE;
1453     if (dib->stride != pattern->stride) return FALSE;
1454
1455     switch (dib->bit_count)
1456     {
1457     case 1:
1458     case 4:
1459     case 8:
1460         if (dib->color_table_size != pattern->color_table_size) return FALSE;
1461         return !memcmp( dib->color_table, pattern->color_table, dib->color_table_size * sizeof(RGBQUAD) );
1462     case 16:
1463     case 32:
1464         return (dib->red_mask == pattern->red_mask &&
1465                 dib->green_mask == pattern->green_mask &&
1466                 dib->blue_mask == pattern->blue_mask);
1467     }
1468     return TRUE;
1469 }
1470
1471 static BOOL select_pattern_brush( dibdrv_physdev *pdev, BOOL *needs_reselect )
1472 {
1473     char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
1474     BITMAPINFO *info = (BITMAPINFO *)buffer;
1475     RGBQUAD color_table[2];
1476     RECT rect;
1477     dib_info pattern;
1478
1479     if (!pdev->brush_pattern_info)
1480     {
1481         BITMAPOBJ *bmp = GDI_GetObjPtr( pdev->brush_pattern_bitmap, OBJ_BITMAP );
1482         BOOL ret;
1483
1484         if (!bmp) return FALSE;
1485         ret = init_dib_info_from_bitmapobj( &pattern, bmp, 0 );
1486         GDI_ReleaseObj( pdev->brush_pattern_bitmap );
1487         if (!ret) return FALSE;
1488     }
1489     else if (pdev->brush_pattern_info->bmiHeader.biClrUsed && pdev->brush_pattern_usage == DIB_PAL_COLORS)
1490     {
1491         copy_bitmapinfo( info, pdev->brush_pattern_info );
1492         fill_color_table_from_pal_colors( info, pdev->dev.hdc );
1493         init_dib_info_from_bitmapinfo( &pattern, info, pdev->brush_pattern_bits, 0 );
1494         *needs_reselect = TRUE;
1495     }
1496     else
1497     {
1498         init_dib_info_from_bitmapinfo( &pattern, pdev->brush_pattern_info, pdev->brush_pattern_bits, 0 );
1499     }
1500
1501     if (pattern.bit_count == 1 && !pattern.color_table)
1502     {
1503         /* monochrome DDB pattern uses DC colors */
1504         COLORREF color = GetTextColor( pdev->dev.hdc );
1505         color_table[0].rgbRed      = GetRValue( color );
1506         color_table[0].rgbGreen    = GetGValue( color );
1507         color_table[0].rgbBlue     = GetBValue( color );
1508         color_table[0].rgbReserved = 0;
1509         color = GetBkColor( pdev->dev.hdc );
1510         color_table[1].rgbRed      = GetRValue( color );
1511         color_table[1].rgbGreen    = GetGValue( color );
1512         color_table[1].rgbBlue     = GetBValue( color );
1513         color_table[1].rgbReserved = 0;
1514         pattern.color_table = color_table;
1515         pattern.color_table_size = 2;
1516         *needs_reselect = TRUE;
1517     }
1518
1519     copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1520
1521     pdev->brush_dib.height = pattern.height;
1522     pdev->brush_dib.width  = pattern.width;
1523     pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1524
1525     if (matching_pattern_format( &pdev->brush_dib, &pattern ))
1526     {
1527         pdev->brush_dib.bits.ptr     = pattern.bits.ptr;
1528         pdev->brush_dib.bits.is_copy = FALSE;
1529         pdev->brush_dib.bits.free    = NULL;
1530     }
1531     else
1532     {
1533         pdev->brush_dib.bits.ptr     = HeapAlloc( GetProcessHeap(), 0,
1534                                                   pdev->brush_dib.height * pdev->brush_dib.stride );
1535         pdev->brush_dib.bits.is_copy = TRUE;
1536         pdev->brush_dib.bits.free    = free_heap_bits;
1537
1538         rect.left = rect.top = 0;
1539         rect.right = pattern.width;
1540         rect.bottom = pattern.height;
1541
1542         pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &pattern, &rect);
1543     }
1544     return TRUE;
1545 }
1546
1547 /**********************************************************************
1548  *             pattern_brush
1549  *
1550  * Fill a number of rectangles with the pattern brush
1551  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1552  */
1553 static BOOL pattern_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1554 {
1555     int i, j;
1556     const WINEREGION *clip;
1557     POINT origin;
1558     BOOL needs_reselect = FALSE;
1559
1560     if(pdev->brush_and_bits == NULL)
1561     {
1562         switch(pdev->brush_style)
1563         {
1564         case BS_DIBPATTERN:
1565             if (!pdev->brush_dib.bits.ptr && !select_pattern_brush( pdev, &needs_reselect ))
1566                 return FALSE;
1567             if(!create_pattern_brush_bits(pdev))
1568                 return FALSE;
1569             break;
1570
1571         case BS_HATCHED:
1572             if(!create_hatch_brush_bits(pdev))
1573                 return FALSE;
1574             break;
1575
1576         default:
1577             ERR("Unexpected brush style %d\n", pdev->brush_style);
1578             return FALSE;
1579         }
1580     }
1581
1582     GetBrushOrgEx(pdev->dev.hdc, &origin);
1583
1584     clip = get_wine_region( region );
1585
1586     if (!clip)
1587     {
1588         dib->funcs->pattern_rects( dib, num, rects, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1589         return TRUE;
1590     }
1591
1592     for(i = 0; i < num; i++)
1593     {
1594         for(j = 0; j < clip->numRects; j++)
1595         {
1596             RECT rect = rects[i];
1597
1598             /* Optimize unclipped case */
1599             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1600                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1601             {
1602                 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1603                 break;
1604             }
1605
1606             if(clip->rects[j].top >= rect.bottom) break;
1607             if(clip->rects[j].bottom <= rect.top) continue;
1608
1609             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1610             {
1611                 rect.left   = max(rect.left,   clip->rects[j].left);
1612                 rect.top    = max(rect.top,    clip->rects[j].top);
1613                 rect.right  = min(rect.right,  clip->rects[j].right);
1614                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1615
1616                 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1617             }
1618         }
1619     }
1620     release_wine_region( region );
1621
1622     if (needs_reselect) free_pattern_brush( pdev );
1623
1624     return TRUE;
1625 }
1626
1627 static BOOL null_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1628 {
1629     return TRUE;
1630 }
1631
1632 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1633 {
1634     pdev->brush_rop = rop;
1635     if(pdev->brush_style == BS_SOLID || pdev->brush_style == BS_HATCHED)
1636         calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1637     free_pattern_brush_bits( pdev );
1638 }
1639
1640 /***********************************************************************
1641  *           dibdrv_SelectBrush
1642  */
1643 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, HBITMAP bitmap,
1644                            const BITMAPINFO *info, void *bits, UINT usage )
1645 {
1646     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1647     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1648     LOGBRUSH logbrush;
1649
1650     TRACE("(%p, %p)\n", dev, hbrush);
1651
1652     if (bitmap || info)  /* pattern brush */
1653     {
1654         pdev->brush_rects = pattern_brush;
1655         pdev->brush_style = BS_DIBPATTERN;
1656         pdev->brush_pattern_info = info;
1657         pdev->brush_pattern_bits = bits;
1658         pdev->brush_pattern_usage = usage;
1659         pdev->brush_pattern_bitmap = bitmap;
1660         free_pattern_brush( pdev ); /* brush is actually selected only when it's used */
1661
1662         return next->funcs->pSelectBrush( next, hbrush, bitmap, info, bits, usage );
1663     }
1664
1665     if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1666
1667     if (hbrush == GetStockObject( DC_BRUSH ))
1668         logbrush.lbColor = GetDCBrushColor( dev->hdc );
1669
1670     pdev->brush_style = logbrush.lbStyle;
1671
1672     free_pattern_brush( pdev );
1673
1674     switch(logbrush.lbStyle)
1675     {
1676     case BS_SOLID:
1677         pdev->brush_colorref = logbrush.lbColor;
1678         pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1679         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1680         pdev->brush_rects = solid_brush;
1681         break;
1682
1683     case BS_NULL:
1684         pdev->brush_rects = null_brush;
1685         break;
1686
1687     case BS_HATCHED:
1688         if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1689         pdev->brush_hatch = logbrush.lbHatch;
1690         pdev->brush_colorref = logbrush.lbColor;
1691         pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1692         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1693         pdev->brush_rects = pattern_brush;
1694         break;
1695
1696     default:
1697         return 0;
1698     }
1699
1700     return next->funcs->pSelectBrush( next, hbrush, bitmap, info, bits, usage );
1701 }
1702
1703 /***********************************************************************
1704  *           dibdrv_SetDCBrushColor
1705  */
1706 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1707 {
1708     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1709     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1710
1711     if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1712     {
1713         pdev->brush_colorref = color;
1714         pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1715         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1716     }
1717
1718     return next->funcs->pSetDCBrushColor( next, color );
1719 }
1720
1721 BOOL brush_rects(dibdrv_physdev *pdev, int num, const RECT *rects)
1722 {
1723     return pdev->brush_rects( pdev, &pdev->dib, num, rects, pdev->clip );
1724 }