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