Release 1.4.1.
[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                                     INT bkgnd_mode, 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 (bkgnd_mode == 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
376     INT64 m, n;  /* 64-bit to avoid overflows (FIXME: find a more efficient way) */
377     BOOL clipped = FALSE;
378     DWORD start_oc, end_oc;
379     const int bias = params->bias;
380     const unsigned int dx = params->dx;
381     const unsigned int dy = params->dy;
382     const unsigned int two_dx = params->dx * 2;
383     const unsigned int two_dy = params->dy * 2;
384     const BOOL xmajor = is_xmajor(params->octant);
385     const BOOL neg_slope = !is_pos_slope(params->octant);
386
387     *pt1 = *start;
388     *pt2 = *end;
389
390     start_oc = calc_outcode(start, clip);
391     end_oc = calc_outcode(end, clip);
392
393     while(1)
394     {
395         if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
396         if(start_oc & end_oc)            return 0; /* trivial reject */
397
398         clipped = TRUE;
399         if(start_oc & OUT_LEFT)
400         {
401             m = clip->left - start->x;
402             if(xmajor)
403                 n = (m * two_dy + bias + dx - 1) / two_dx;
404             else
405                 n = (m * two_dy - bias - dy) / two_dx + 1;
406
407             pt1->x = clip->left;
408             if(neg_slope) n = -n;
409             pt1->y = start->y + n;
410             start_oc = calc_outcode(pt1, clip);
411         }
412         else if(start_oc & OUT_RIGHT)
413         {
414             m = start->x - clip->right + 1;
415             if(xmajor)
416                 n = (m * two_dy + bias + dx - 1) / two_dx;
417             else
418                 n = (m * two_dy - bias - dy) / two_dx + 1;
419
420             pt1->x = clip->right - 1;
421             if(neg_slope) n = -n;
422             pt1->y = start->y - n;
423             start_oc = calc_outcode(pt1, clip);
424         }
425         else if(start_oc & OUT_TOP)
426         {
427             n = clip->top - start->y;
428             if(xmajor)
429                 m = (n * two_dx - bias - dx) / two_dy + 1;
430             else
431                 m = (n * two_dx + bias + dy - 1) / two_dy;
432
433             pt1->y = clip->top;
434             if(neg_slope) m = -m;
435             pt1->x = start->x + m;
436             start_oc = calc_outcode(pt1, clip);
437         }
438         else if(start_oc & OUT_BOTTOM)
439         {
440             n = start->y - clip->bottom + 1;
441             if(xmajor)
442                 m = (n * two_dx - bias - dx) / two_dy + 1;
443             else
444                 m = (n * two_dx + bias + dy - 1) / two_dy;
445
446             pt1->y = clip->bottom - 1;
447             if(neg_slope) m = -m;
448             pt1->x = start->x - m;
449             start_oc = calc_outcode(pt1, clip);
450         }
451         else if(end_oc & OUT_LEFT)
452         {
453             m = clip->left - end->x;
454             if(xmajor)
455                 n = (m * two_dy - bias + dx) / two_dx;
456             else
457                 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
458
459             pt2->x = clip->left;
460             if(neg_slope) n = -n;
461             pt2->y = end->y + n;
462             end_oc = calc_outcode(pt2, clip);
463         }
464         else if(end_oc & OUT_RIGHT)
465         {
466             m = end->x - clip->right + 1;
467             if(xmajor)
468                 n = (m * two_dy - bias + dx) / two_dx;
469             else
470                 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
471
472             pt2->x = clip->right - 1;
473             if(neg_slope) n = -n;
474             pt2->y = end->y - n;
475             end_oc = calc_outcode(pt2, clip);
476         }
477         else if(end_oc & OUT_TOP)
478         {
479             n = clip->top - end->y;
480             if(xmajor)
481                 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
482             else
483                 m = (n * two_dx - bias + dy) / two_dy;
484
485             pt2->y = clip->top;
486             if(neg_slope) m = -m;
487             pt2->x = end->x + m;
488             end_oc = calc_outcode(pt2, clip);
489         }
490         else if(end_oc & OUT_BOTTOM)
491         {
492             n = end->y - clip->bottom + 1;
493             if(xmajor)
494                 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
495             else
496                 m = (n * two_dx - bias + dy) / two_dy;
497
498             pt2->y = clip->bottom - 1;
499             if(neg_slope) m = -m;
500             pt2->x = end->x - m;
501             end_oc = calc_outcode(pt2, clip);
502         }
503     }
504 }
505
506 static void bres_line_with_bias(const POINT *start, const struct line_params *params,
507                                 void (* callback)(dibdrv_physdev*,INT,INT), dibdrv_physdev *pdev)
508 {
509     POINT pt = *start;
510     int len = params->length, err = params->err_start;
511
512     if (params->x_major)
513     {
514         while(len--)
515         {
516             callback(pdev, pt.x, pt.y);
517             if (err + params->bias > 0)
518             {
519                 pt.y += params->y_inc;
520                 err += params->err_add_1;
521             }
522             else err += params->err_add_2;
523             pt.x += params->x_inc;
524         }
525     }
526     else
527     {
528         while(len--)
529         {
530             callback(pdev, pt.x, pt.y);
531             if (err + params->bias > 0)
532             {
533                 pt.x += params->x_inc;
534                 err += params->err_add_1;
535             }
536             else err += params->err_add_2;
537             pt.y += params->y_inc;
538         }
539     }
540 }
541
542 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end, DWORD and, DWORD xor)
543 {
544     struct clipped_rects clipped_rects;
545     RECT rect;
546     int i;
547
548     if(start->y == end->y)
549     {
550         rect.left   = start->x;
551         rect.top    = start->y;
552         rect.right  = end->x;
553         rect.bottom = end->y + 1;
554         order_end_points(&rect.left, &rect.right);
555         if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return TRUE;
556         pdev->dib.funcs->solid_rects(&pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor);
557     }
558     else if(start->x == end->x)
559     {
560         rect.left   = start->x;
561         rect.top    = start->y;
562         rect.right  = end->x + 1;
563         rect.bottom = end->y;
564         order_end_points(&rect.top, &rect.bottom);
565         if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return TRUE;
566         pdev->dib.funcs->solid_rects(&pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor);
567     }
568     else
569     {
570         bres_params clip_params;
571         struct line_params line_params;
572         INT dx = end->x - start->x, dy = end->y - start->y;
573         INT abs_dx = abs(dx), abs_dy = abs(dy);
574
575         clip_params.dx = abs_dx;
576         clip_params.dy = abs_dy;
577         clip_params.octant = get_octant_mask(dx, dy);
578         clip_params.bias   = get_bias( clip_params.octant );
579
580         line_params.bias    = clip_params.bias;
581         line_params.x_major = is_xmajor( clip_params.octant );
582         line_params.x_inc   = is_x_increasing( clip_params.octant ) ? 1 : -1;
583         line_params.y_inc   = is_y_increasing( clip_params.octant ) ? 1 : -1;
584
585         if (line_params.x_major)
586         {
587             line_params.err_add_1 = 2 * abs_dy - 2 * abs_dx;
588             line_params.err_add_2 = 2 * abs_dy;
589         }
590         else
591         {
592             line_params.err_add_1 = 2 * abs_dx - 2 * abs_dy;
593             line_params.err_add_2 = 2 * abs_dx;
594         }
595
596         rect.left   = min( start->x, end->x );
597         rect.top    = min( start->y, end->y );
598         rect.right  = max( start->x, end->x ) + 1;
599         rect.bottom = max( start->y, end->y ) + 1;
600         if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return TRUE;
601         for (i = 0; i < clipped_rects.count; i++)
602         {
603             POINT clipped_start, clipped_end;
604             int clip_status;
605
606             clip_status = clip_line(start, end, clipped_rects.rects + i, &clip_params, &clipped_start, &clipped_end);
607             if(clip_status)
608             {
609                 int m = abs(clipped_start.x - start->x);
610                 int n = abs(clipped_start.y - start->y);
611
612                 if (line_params.x_major)
613                 {
614                     line_params.err_start = 2 * abs_dy - abs_dx + m * 2 * abs_dy - n * 2 * abs_dx;
615                     line_params.length = abs( clipped_end.x - clipped_start.x ) + 1;
616                 }
617                 else
618                 {
619                     line_params.err_start = 2 * abs_dx - abs_dy + n * 2 * abs_dx - m * 2 * abs_dy;
620                     line_params.length = abs( clipped_end.y - clipped_start.y ) + 1;
621                 }
622
623                 if (clipped_end.x == end->x && clipped_end.y == end->y) line_params.length--;
624
625                 pdev->dib.funcs->solid_line( &pdev->dib, &clipped_start, &line_params, and, xor );
626
627                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
628             }
629         }
630     }
631     free_clipped_rects( &clipped_rects );
632     return TRUE;
633 }
634
635 static BOOL solid_pen_line_region( dibdrv_physdev *pdev, POINT *start, POINT *end, HRGN region )
636 {
637     RECT rect;
638
639     rect.left   = start->x;
640     rect.top    = start->y;
641     rect.right  = start->x + 1;
642     rect.bottom = start->y + 1;
643
644     if (start->y == end->y)
645     {
646         rect.right = end->x;
647         order_end_points(&rect.left, &rect.right);
648         add_rect_to_region( region, &rect );
649     }
650     else if(start->x == end->x)
651     {
652         rect.bottom = end->y;
653         order_end_points(&rect.top, &rect.bottom);
654         add_rect_to_region( region, &rect );
655     }
656     else
657     {
658         INT dx = end->x - start->x, dy = end->y - start->y;
659         INT abs_dx = abs(dx), abs_dy = abs(dy);
660         DWORD octant = get_octant_mask(dx, dy);
661         INT bias = get_bias( octant );
662
663         if (is_xmajor( octant ))
664         {
665             int y_inc = is_y_increasing( octant ) ? 1 : -1;
666             int err_add_1 = 2 * abs_dy - 2 * abs_dx;
667             int err_add_2 = 2 * abs_dy;
668             int err = 2 * abs_dy - abs_dx;
669
670             if (is_x_increasing( octant ))
671             {
672                 for (rect.right = start->x + 1; rect.right <= end->x; rect.right++)
673                 {
674                     if (err + bias > 0)
675                     {
676                         add_rect_to_region( region, &rect );
677                         rect.left = rect.right;
678                         rect.top += y_inc;
679                         rect.bottom += y_inc;
680                         err += err_add_1;
681                     }
682                     else err += err_add_2;
683                 }
684             }
685             else
686             {
687                 for (rect.left = start->x; rect.left > end->x; rect.left--)
688                 {
689                     if (err + bias > 0)
690                     {
691                         add_rect_to_region( region, &rect );
692                         rect.right = rect.left;
693                         rect.top += y_inc;
694                         rect.bottom += y_inc;
695                         err += err_add_1;
696                     }
697                     else err += err_add_2;
698                 }
699             }
700         }
701         else
702         {
703             int x_inc = is_x_increasing( octant ) ? 1 : -1;
704             int err_add_1 = 2 * abs_dx - 2 * abs_dy;
705             int err_add_2 = 2 * abs_dx;
706             int err = 2 * abs_dx - abs_dy;
707
708             if (is_y_increasing( octant ))
709             {
710                 for (rect.bottom = start->y + 1; rect.bottom <= end->y; rect.bottom++)
711                 {
712                     if (err + bias > 0)
713                     {
714                         add_rect_to_region( region, &rect );
715                         rect.top = rect.bottom;
716                         rect.left += x_inc;
717                         rect.right += x_inc;
718                         err += err_add_1;
719                     }
720                     else err += err_add_2;
721                 }
722             }
723             else
724             {
725                 for (rect.top = start->y; rect.top > end->y; rect.top--)
726                 {
727                     if (err + bias > 0)
728                     {
729                         add_rect_to_region( region, &rect );
730                         rect.bottom = rect.top;
731                         rect.left += x_inc;
732                         rect.right += x_inc;
733                         err += err_add_1;
734                     }
735                     else err += err_add_2;
736                 }
737             }
738         }
739         /* add final rect */
740         add_rect_to_region( region, &rect );
741     }
742     return TRUE;
743 }
744
745 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close, HRGN region)
746 {
747     int i;
748
749     assert( num >= 2 );
750
751     if (region)
752     {
753         for (i = 0; i < num - 1; i++)
754             if (!solid_pen_line_region( pdev, pts + i, pts + i + 1, region ))
755                 return FALSE;
756         if (close) return solid_pen_line_region( pdev, pts + num - 1, pts, region );
757     }
758     else
759     {
760         DWORD color, and, xor;
761
762         color = get_pixel_color( pdev, pdev->pen_brush.colorref, TRUE );
763         calc_and_xor_masks( GetROP2(pdev->dev.hdc), color, &and, &xor );
764
765         for (i = 0; i < num - 1; i++)
766             if (!solid_pen_line( pdev, pts + i, pts + i + 1, and, xor ))
767                 return FALSE;
768         if (close) return solid_pen_line( pdev, pts + num - 1, pts, and, xor );
769     }
770     return TRUE;
771 }
772
773 void reset_dash_origin(dibdrv_physdev *pdev)
774 {
775     pdev->dash_pos.cur_dash = 0;
776     pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
777     pdev->dash_pos.mark = TRUE;
778 }
779
780 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
781 {
782     skip %= pdev->pen_pattern.total_len;
783     do
784     {
785         if(pdev->dash_pos.left_in_dash > skip)
786         {
787             pdev->dash_pos.left_in_dash -= skip;
788             return;
789         }
790         skip -= pdev->dash_pos.left_in_dash;
791         pdev->dash_pos.cur_dash++;
792         if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
793         pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
794         pdev->dash_pos.mark = !pdev->dash_pos.mark;
795     }
796     while (skip);
797 }
798
799 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
800 {
801     RECT rect;
802     rop_mask mask = pdev->dash_masks[pdev->dash_pos.mark];
803
804     skip_dash(pdev, 1);
805     rect.left   = x;
806     rect.right  = x + 1;
807     rect.top    = y;
808     rect.bottom = y + 1;
809     pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, mask.and, mask.xor);
810     return;
811 }
812
813 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
814 {
815     struct clipped_rects clipped_rects;
816     int i, dash_len;
817     RECT rect;
818     const dash_pos start_pos = pdev->dash_pos;
819
820     if(start->y == end->y) /* hline */
821     {
822         BOOL l_to_r;
823         INT left, right, cur_x;
824
825         rect.top = start->y;
826         rect.bottom = start->y + 1;
827
828         if(start->x <= end->x)
829         {
830             left = start->x;
831             right = end->x - 1;
832             l_to_r = TRUE;
833         }
834         else
835         {
836             left = end->x + 1;
837             right = start->x;
838             l_to_r = FALSE;
839         }
840
841         rect.left = min( start->x, end->x );
842         rect.right = max( start->x, end->x ) + 1;
843         get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects );
844         for (i = 0; i < clipped_rects.count; i++)
845         {
846             if(clipped_rects.rects[i].right > left && clipped_rects.rects[i].left <= right)
847             {
848                 int clipped_left  = max(clipped_rects.rects[i].left, left);
849                 int clipped_right = min(clipped_rects.rects[i].right - 1, right);
850
851                 pdev->dash_pos = start_pos;
852
853                 if(l_to_r)
854                 {
855                     cur_x = clipped_left;
856                     if(cur_x != left)
857                         skip_dash(pdev, clipped_left - left);
858
859                     while(cur_x <= clipped_right)
860                     {
861                         rop_mask mask = pdev->dash_masks[pdev->dash_pos.mark];
862                         dash_len = pdev->dash_pos.left_in_dash;
863                         if(cur_x + dash_len > clipped_right + 1)
864                             dash_len = clipped_right - cur_x + 1;
865                         rect.left = cur_x;
866                         rect.right = cur_x + dash_len;
867
868                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, mask.and, mask.xor);
869                         cur_x += dash_len;
870                         skip_dash(pdev, dash_len);
871                     }
872                 }
873                 else
874                 {
875                     cur_x = clipped_right;
876                     if(cur_x != right)
877                         skip_dash(pdev, right - clipped_right);
878
879                     while(cur_x >= clipped_left)
880                     {
881                         rop_mask mask = pdev->dash_masks[pdev->dash_pos.mark];
882                         dash_len = pdev->dash_pos.left_in_dash;
883                         if(cur_x - dash_len < clipped_left - 1)
884                             dash_len = cur_x - clipped_left + 1;
885                         rect.left = cur_x - dash_len + 1;
886                         rect.right = cur_x + 1;
887
888                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, mask.and, mask.xor);
889                         cur_x -= dash_len;
890                         skip_dash(pdev, dash_len);
891                     }
892                 }
893             }
894         }
895         pdev->dash_pos = start_pos;
896         skip_dash(pdev, right - left + 1);
897     }
898     else if(start->x == end->x) /* vline */
899     {
900         BOOL t_to_b;
901         INT top, bottom, cur_y;
902
903         rect.left = start->x;
904         rect.right = start->x + 1;
905
906         if(start->y <= end->y)
907         {
908             top = start->y;
909             bottom = end->y - 1;
910             t_to_b = TRUE;
911         }
912         else
913         {
914             top = end->y + 1;
915             bottom = start->y;
916             t_to_b = FALSE;
917         }
918
919         rect.top = min( start->y, end->y );
920         rect.bottom = max( start->y, end->y ) + 1;
921         get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects );
922         for (i = 0; i < clipped_rects.count; i++)
923         {
924             if(clipped_rects.rects[i].right > start->x && clipped_rects.rects[i].left <= start->x)
925             {
926                 int clipped_top    = max(clipped_rects.rects[i].top, top);
927                 int clipped_bottom = min(clipped_rects.rects[i].bottom - 1, bottom);
928
929                 pdev->dash_pos = start_pos;
930
931                 if(t_to_b)
932                 {
933                     cur_y = clipped_top;
934                     if(cur_y != top)
935                         skip_dash(pdev, clipped_top - top);
936
937                     while(cur_y <= clipped_bottom)
938                     {
939                         rop_mask mask = pdev->dash_masks[pdev->dash_pos.mark];
940                         dash_len = pdev->dash_pos.left_in_dash;
941                         if(cur_y + dash_len > clipped_bottom + 1)
942                             dash_len = clipped_bottom - cur_y + 1;
943                         rect.top = cur_y;
944                         rect.bottom = cur_y + dash_len;
945
946                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, mask.and, mask.xor);
947                         cur_y += dash_len;
948                         skip_dash(pdev, dash_len);
949                     }
950                 }
951                 else
952                 {
953                     cur_y = clipped_bottom;
954                     if(cur_y != bottom)
955                         skip_dash(pdev, bottom - clipped_bottom);
956
957                     while(cur_y >= clipped_top)
958                     {
959                         rop_mask mask = pdev->dash_masks[pdev->dash_pos.mark];
960                         dash_len = pdev->dash_pos.left_in_dash;
961                         if(cur_y - dash_len < clipped_top - 1)
962                             dash_len = cur_y - clipped_top + 1;
963                         rect.top = cur_y - dash_len + 1;
964                         rect.bottom = cur_y + 1;
965
966                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, mask.and, mask.xor);
967                         cur_y -= dash_len;
968                         skip_dash(pdev, dash_len);
969                     }
970                 }
971             }
972         }
973         pdev->dash_pos = start_pos;
974         skip_dash(pdev, bottom - top + 1);
975     }
976     else
977     {
978         bres_params clip_params;
979         struct line_params line_params;
980         INT dx = end->x - start->x, dy = end->y - start->y;
981         INT abs_dx = abs(dx), abs_dy = abs(dy);
982
983         clip_params.dx = abs_dx;
984         clip_params.dy = abs_dy;
985         clip_params.octant = get_octant_mask(dx, dy);
986         clip_params.bias   = get_bias( clip_params.octant );
987
988         line_params.bias    = clip_params.bias;
989         line_params.x_major = is_xmajor( clip_params.octant );
990         line_params.x_inc   = is_x_increasing( clip_params.octant ) ? 1 : -1;
991         line_params.y_inc   = is_y_increasing( clip_params.octant ) ? 1 : -1;
992
993         if (line_params.x_major)
994         {
995             line_params.err_add_1 = 2 * abs_dy - 2 * abs_dx;
996             line_params.err_add_2 = 2 * abs_dy;
997         }
998         else
999         {
1000             line_params.err_add_1 = 2 * abs_dx - 2 * abs_dy;
1001             line_params.err_add_2 = 2 * abs_dx;
1002         }
1003
1004         rect.left   = min( start->x, end->x );
1005         rect.top    = min( start->y, end->y );
1006         rect.right  = max( start->x, end->x ) + 1;
1007         rect.bottom = max( start->y, end->y ) + 1;
1008         get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects );
1009         for (i = 0; i < clipped_rects.count; i++)
1010         {
1011             POINT clipped_start, clipped_end;
1012             int clip_status;
1013             clip_status = clip_line(start, end, clipped_rects.rects + i, &clip_params, &clipped_start, &clipped_end);
1014
1015             if(clip_status)
1016             {
1017                 int m = abs(clipped_start.x - start->x);
1018                 int n = abs(clipped_start.y - start->y);
1019
1020                 pdev->dash_pos = start_pos;
1021
1022                 if (line_params.x_major)
1023                 {
1024                     line_params.err_start = 2 * abs_dy - abs_dx + m * 2 * abs_dy - n * 2 * abs_dx;
1025                     line_params.length = abs( clipped_end.x - clipped_start.x ) + 1;
1026                     skip_dash(pdev, m);
1027                 }
1028                 else
1029                 {
1030                     line_params.err_start = 2 * abs_dx - abs_dy + n * 2 * abs_dx - m * 2 * abs_dy;
1031                     line_params.length = abs( clipped_end.y - clipped_start.y ) + 1;
1032                     skip_dash(pdev, n);
1033                 }
1034                 if (clipped_end.x == end->x && clipped_end.y == end->y) line_params.length--;
1035
1036                 bres_line_with_bias( &clipped_start, &line_params, dashed_pen_line_callback, pdev );
1037
1038                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
1039             }
1040         }
1041         pdev->dash_pos = start_pos;
1042         if(line_params.x_major)
1043             skip_dash(pdev, abs_dx);
1044         else
1045             skip_dash(pdev, abs_dy);
1046     }
1047
1048     free_clipped_rects( &clipped_rects );
1049     return TRUE;
1050 }
1051
1052 static BOOL dashed_pen_line_region(dibdrv_physdev *pdev, POINT *start, POINT *end, HRGN region)
1053 {
1054     int i, dash_len;
1055     RECT rect;
1056
1057     rect.left   = start->x;
1058     rect.top    = start->y;
1059     rect.right  = start->x + 1;
1060     rect.bottom = start->y + 1;
1061
1062     if (start->y == end->y) /* hline */
1063     {
1064         if (start->x <= end->x)
1065         {
1066             for (i = start->x; i < end->x; i += dash_len)
1067             {
1068                 dash_len = min( pdev->dash_pos.left_in_dash, end->x - i );
1069                 if (pdev->dash_pos.mark)
1070                 {
1071                     rect.left = i;
1072                     rect.right = i + dash_len;
1073                     add_rect_to_region( region, &rect );
1074                 }
1075                 skip_dash(pdev, dash_len);
1076             }
1077         }
1078         else
1079         {
1080             for (i = start->x; i > end->x; i -= dash_len)
1081             {
1082                 dash_len = min( pdev->dash_pos.left_in_dash, i - end->x );
1083                 if (pdev->dash_pos.mark)
1084                 {
1085                     rect.left = i - dash_len + 1;
1086                     rect.right = i + 1;
1087                     add_rect_to_region( region, &rect );
1088                 }
1089                 skip_dash(pdev, dash_len);
1090             }
1091         }
1092     }
1093     else if (start->x == end->x) /* vline */
1094     {
1095         if (start->y <= end->y)
1096         {
1097             for (i = start->y; i < end->y; i += dash_len)
1098             {
1099                 dash_len = min( pdev->dash_pos.left_in_dash, end->y - i );
1100                 if (pdev->dash_pos.mark)
1101                 {
1102                     rect.top = i;
1103                     rect.bottom = i + dash_len;
1104                     add_rect_to_region( region, &rect );
1105                 }
1106                 skip_dash(pdev, dash_len);
1107             }
1108         }
1109         else
1110         {
1111             for (i = start->y; i > end->y; i -= dash_len)
1112             {
1113                 dash_len = min( pdev->dash_pos.left_in_dash, i - end->y );
1114                 if (pdev->dash_pos.mark)
1115                 {
1116                     rect.top = i - dash_len + 1;
1117                     rect.bottom = i + 1;
1118                     add_rect_to_region( region, &rect );
1119                 }
1120                 skip_dash(pdev, dash_len);
1121             }
1122         }
1123     }
1124     else
1125     {
1126         INT dx = end->x - start->x, dy = end->y - start->y;
1127         INT abs_dx = abs(dx), abs_dy = abs(dy);
1128         DWORD octant = get_octant_mask(dx, dy);
1129         INT bias = get_bias( octant );
1130         int x_inc = is_x_increasing( octant ) ? 1 : -1;
1131         int y_inc = is_y_increasing( octant ) ? 1 : -1;
1132
1133         if (is_xmajor( octant ))
1134         {
1135             int err_add_1 = 2 * abs_dy - 2 * abs_dx;
1136             int err_add_2 = 2 * abs_dy;
1137             int err = 2 * abs_dy - abs_dx;
1138
1139             while (abs_dx--)
1140             {
1141                 if (pdev->dash_pos.mark) add_rect_to_region( region, &rect );
1142                 skip_dash(pdev, 1);
1143                 rect.left += x_inc;
1144                 rect.right += x_inc;
1145                 if (err + bias > 0)
1146                 {
1147                     rect.top += y_inc;
1148                     rect.bottom += y_inc;
1149                     err += err_add_1;
1150                 }
1151                 else err += err_add_2;
1152             }
1153
1154         }
1155         else
1156         {
1157             int err_add_1 = 2 * abs_dx - 2 * abs_dy;
1158             int err_add_2 = 2 * abs_dx;
1159             int err = 2 * abs_dx - abs_dy;
1160
1161             while (abs_dy--)
1162             {
1163                 if (pdev->dash_pos.mark) add_rect_to_region( region, &rect );
1164                 skip_dash(pdev, 1);
1165                 rect.top += y_inc;
1166                 rect.bottom += y_inc;
1167                 if (err + bias > 0)
1168                 {
1169                     rect.left += x_inc;
1170                     rect.right += x_inc;
1171                     err += err_add_1;
1172                 }
1173                 else err += err_add_2;
1174             }
1175         }
1176     }
1177     return TRUE;
1178 }
1179
1180 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close, HRGN region)
1181 {
1182     int i;
1183
1184     assert( num >= 2 );
1185
1186     if (region)
1187     {
1188         for (i = 0; i < num - 1; i++)
1189             if (!dashed_pen_line_region( pdev, pts + i, pts + i + 1, region ))
1190                 return FALSE;
1191         if (close) return dashed_pen_line_region( pdev, pts + num - 1, pts, region );
1192     }
1193     else
1194     {
1195         get_color_masks( pdev, GetROP2(pdev->dev.hdc), pdev->pen_brush.colorref,
1196                          pdev->pen_is_ext ? TRANSPARENT : GetBkMode(pdev->dev.hdc),
1197                          &pdev->dash_masks[1], &pdev->dash_masks[0] );
1198
1199         for (i = 0; i < num - 1; i++)
1200             if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
1201                 return FALSE;
1202         if (close) return dashed_pen_line( pdev, pts + num - 1, pts );
1203     }
1204     return TRUE;
1205 }
1206
1207 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close, HRGN region)
1208 {
1209     return TRUE;
1210 }
1211
1212 struct face
1213 {
1214     POINT start, end;
1215     int dx, dy;
1216 };
1217
1218 static void add_cap( dibdrv_physdev *pdev, HRGN region, HRGN round_cap, const POINT *pt )
1219 {
1220     switch (pdev->pen_endcap)
1221     {
1222     default: FIXME( "Unknown end cap %x\n", pdev->pen_endcap );
1223         /* fall through */
1224     case PS_ENDCAP_ROUND:
1225         OffsetRgn( round_cap, pt->x, pt->y );
1226         CombineRgn( region, region, round_cap, RGN_OR );
1227         OffsetRgn( round_cap, -pt->x, -pt->y );
1228         return;
1229
1230     case PS_ENDCAP_SQUARE: /* already been handled */
1231     case PS_ENDCAP_FLAT:
1232         return;
1233     }
1234 }
1235
1236 #define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5)
1237
1238 /*******************************************************************************
1239  *                 create_miter_region
1240  *
1241  * We need to calculate the intersection of two lines.  We know a point
1242  * on each line (a face start and the other face end point) and
1243  * the direction vector of each line eg. (dx_1, dy_1).
1244  *
1245  * (x, y) = (x_1, y_1) + u * (dx_1, dy_1) = (x_2, y_2) + v * (dx_2, dy_2)
1246  * solving (eg using Cramer's rule) gives:
1247  * u = ((x_2 - x_1) dy_2 - (y_2 - y_1) dx_2) / det
1248  * with det = dx_1 dy_2 - dx_2 dy_1
1249  * substituting back in and simplifying gives
1250  * (x, y) = a (dx_1, dy_1) - b (dx_2, dy_2)
1251  * with a = (x_2 dy_2 - y_2 dx_2) / det
1252  * and  b = (x_1 dy_1 - y_1 dx_1) / det
1253  */
1254 static HRGN create_miter_region( dibdrv_physdev *pdev, const POINT *pt,
1255                                  const struct face *face_1, const struct face *face_2 )
1256 {
1257     int det = face_1->dx * face_2->dy - face_1->dy * face_2->dx;
1258     POINT pt_1, pt_2, pts[5];
1259     double a, b, x, y;
1260     FLOAT limit;
1261
1262     if (det == 0) return 0;
1263
1264     if (det < 0)
1265     {
1266         const struct face *tmp = face_1;
1267         face_1 = face_2;
1268         face_2 = tmp;
1269         det = -det;
1270     }
1271
1272     pt_1 = face_1->start;
1273     pt_2 = face_2->end;
1274
1275     a = (double)((pt_2.x * face_2->dy - pt_2.y * face_2->dx)) / det;
1276     b = (double)((pt_1.x * face_1->dy - pt_1.y * face_1->dx)) / det;
1277
1278     x = a * face_1->dx - b * face_2->dx;
1279     y = a * face_1->dy - b * face_2->dy;
1280
1281     GetMiterLimit( pdev->dev.hdc, &limit );
1282
1283     if (((x - pt->x) * (x - pt->x) + (y - pt->y) * (y - pt->y)) * 4 > limit * limit * pdev->pen_width * pdev->pen_width)
1284         return 0;
1285
1286     pts[0] = face_2->start;
1287     pts[1] = face_1->start;
1288     pts[2].x = round( x );
1289     pts[2].y = round( y );
1290     pts[3] = face_2->end;
1291     pts[4] = face_1->end;
1292
1293     return CreatePolygonRgn( pts, 5, ALTERNATE );
1294 }
1295
1296 static void add_join( dibdrv_physdev *pdev, HRGN region, HRGN round_cap, const POINT *pt,
1297                       const struct face *face_1, const struct face *face_2 )
1298 {
1299     HRGN join;
1300     POINT pts[4];
1301
1302     switch (pdev->pen_join)
1303     {
1304     default: FIXME( "Unknown line join %x\n", pdev->pen_join );
1305         /* fall through */
1306     case PS_JOIN_ROUND:
1307         OffsetRgn( round_cap, pt->x, pt->y );
1308         CombineRgn( region, region, round_cap, RGN_OR );
1309         OffsetRgn( round_cap, -pt->x, -pt->y );
1310         return;
1311
1312     case PS_JOIN_MITER:
1313         join = create_miter_region( pdev, pt, face_1, face_2 );
1314         if (join) break;
1315         /* fall through */
1316     case PS_JOIN_BEVEL:
1317         pts[0] = face_1->start;
1318         pts[1] = face_2->end;
1319         pts[2] = face_1->end;
1320         pts[3] = face_2->start;
1321         join = CreatePolygonRgn( pts, 4, ALTERNATE );
1322         break;
1323     }
1324
1325     CombineRgn( region, region, join, RGN_OR );
1326     DeleteObject( join );
1327     return;
1328 }
1329
1330 static int wide_line_segment( dibdrv_physdev *pdev, HRGN total,
1331                                const POINT *pt_1, const POINT *pt_2, int dx, int dy,
1332                                BOOL need_cap_1, BOOL need_cap_2, struct face *face_1, struct face *face_2 )
1333 {
1334     RECT rect;
1335     BOOL sq_cap_1 = need_cap_1 && (pdev->pen_endcap == PS_ENDCAP_SQUARE);
1336     BOOL sq_cap_2 = need_cap_2 && (pdev->pen_endcap == PS_ENDCAP_SQUARE);
1337
1338     if (dx == 0 && dy == 0) return 0;
1339
1340     if (dy == 0)
1341     {
1342         rect.left = min( pt_1->x, pt_2->x );
1343         rect.right = max( pt_1->x, pt_2->x );
1344         rect.top = pt_1->y - pdev->pen_width / 2;
1345         rect.bottom = rect.top + pdev->pen_width;
1346         if ((sq_cap_1 && dx > 0) || (sq_cap_2 && dx < 0)) rect.left  -= pdev->pen_width / 2;
1347         if ((sq_cap_2 && dx > 0) || (sq_cap_1 && dx < 0)) rect.right += pdev->pen_width / 2;
1348         add_rect_to_region( total, &rect );
1349         if (dx > 0)
1350         {
1351             face_1->start.x = face_1->end.x   = rect.left;
1352             face_1->start.y = face_2->end.y   = rect.bottom;
1353             face_1->end.y   = face_2->start.y = rect.top;
1354             face_2->start.x = face_2->end.x   = rect.right - 1;
1355         }
1356         else
1357         {
1358             face_1->start.x = face_1->end.x   = rect.right;
1359             face_1->start.y = face_2->end.y   = rect.top;
1360             face_1->end.y   = face_2->start.y = rect.bottom;
1361             face_2->start.x = face_2->end.x   = rect.left + 1;
1362         }
1363     }
1364     else if (dx == 0)
1365     {
1366         rect.top = min( pt_1->y, pt_2->y );
1367         rect.bottom = max( pt_1->y, pt_2->y );
1368         rect.left = pt_1->x - pdev->pen_width / 2;
1369         rect.right = rect.left + pdev->pen_width;
1370         if ((sq_cap_1 && dy > 0) || (sq_cap_2 && dy < 0)) rect.top    -= pdev->pen_width / 2;
1371         if ((sq_cap_2 && dy > 0) || (sq_cap_1 && dy < 0)) rect.bottom += pdev->pen_width / 2;
1372         add_rect_to_region( total, &rect );
1373         if (dy > 0)
1374         {
1375             face_1->start.x = face_2->end.x   = rect.left;
1376             face_1->start.y = face_1->end.y   = rect.top;
1377             face_1->end.x   = face_2->start.x = rect.right;
1378             face_2->start.y = face_2->end.y   = rect.bottom - 1;
1379         }
1380         else
1381         {
1382             face_1->start.x = face_2->end.x   = rect.right;
1383             face_1->start.y = face_1->end.y   = rect.bottom;
1384             face_1->end.x   = face_2->start.x = rect.left;
1385             face_2->start.y = face_2->end.y   = rect.top + 1;
1386         }
1387     }
1388     else
1389     {
1390         double len = hypot( dx, dy );
1391         double width_x, width_y;
1392         POINT seg_pts[4];
1393         POINT wide_half, narrow_half;
1394         HRGN segment;
1395
1396         width_x = pdev->pen_width * abs( dy ) / len;
1397         width_y = pdev->pen_width * abs( dx ) / len;
1398
1399         narrow_half.x = round( width_x / 2 );
1400         narrow_half.y = round( width_y / 2 );
1401         wide_half.x   = round( (width_x + 1) / 2 );
1402         wide_half.y   = round( (width_y + 1) / 2 );
1403
1404         if (dx < 0)
1405         {
1406             wide_half.y   = -wide_half.y;
1407             narrow_half.y = -narrow_half.y;
1408         }
1409
1410         if (dy < 0)
1411         {
1412             POINT tmp = narrow_half; narrow_half = wide_half; wide_half = tmp;
1413             wide_half.x   = -wide_half.x;
1414             narrow_half.x = -narrow_half.x;
1415         }
1416
1417         seg_pts[0].x = pt_1->x - narrow_half.x;
1418         seg_pts[0].y = pt_1->y + narrow_half.y;
1419         seg_pts[1].x = pt_1->x + wide_half.x;
1420         seg_pts[1].y = pt_1->y - wide_half.y;
1421         seg_pts[2].x = pt_2->x + wide_half.x;
1422         seg_pts[2].y = pt_2->y - wide_half.y;
1423         seg_pts[3].x = pt_2->x - narrow_half.x;
1424         seg_pts[3].y = pt_2->y + narrow_half.y;
1425
1426         if (sq_cap_1)
1427         {
1428             seg_pts[0].x -= narrow_half.y;
1429             seg_pts[1].x -= narrow_half.y;
1430             seg_pts[0].y -= narrow_half.x;
1431             seg_pts[1].y -= narrow_half.x;
1432         }
1433
1434         if (sq_cap_2)
1435         {
1436             seg_pts[2].x += wide_half.y;
1437             seg_pts[3].x += wide_half.y;
1438             seg_pts[2].y += wide_half.x;
1439             seg_pts[3].y += wide_half.x;
1440         }
1441
1442         segment = CreatePolygonRgn( seg_pts, 4, ALTERNATE );
1443         CombineRgn( total, total, segment, RGN_OR );
1444         DeleteObject( segment );
1445
1446         face_1->start = seg_pts[0];
1447         face_1->end   = seg_pts[1];
1448         face_2->start = seg_pts[2];
1449         face_2->end   = seg_pts[3];
1450     }
1451
1452     face_1->dx = face_2->dx = dx;
1453     face_1->dy = face_2->dy = dy;
1454
1455     return 1;
1456 }
1457
1458 static void wide_line_segments( dibdrv_physdev *pdev, int num, const POINT *pts, BOOL close,
1459                                 int start, int count, const POINT *first_pt, const POINT *last_pt,
1460                                 HRGN round_cap, HRGN total )
1461 {
1462     int i;
1463     struct face face_1, face_2, prev_face, first_face;
1464     const POINT *pt_1, *pt_2;
1465
1466     if (!close)
1467     {
1468         add_cap( pdev, total, round_cap, first_pt );
1469         add_cap( pdev, total, round_cap, last_pt );
1470     }
1471
1472     if (count == 1)
1473     {
1474         pt_1 = &pts[start];
1475         pt_2 = &pts[(start + 1) % num];
1476         wide_line_segment( pdev, total, first_pt, last_pt, pt_2->x - pt_1->x, pt_2->y - pt_1->y,
1477                            TRUE, TRUE, &face_1, &face_2 );
1478         return;
1479     }
1480
1481     pt_1 = &pts[start];
1482     pt_2 = &pts[(start + 1) % num];
1483     wide_line_segment( pdev, total, first_pt, pt_2, pt_2->x - pt_1->x, pt_2->y - pt_1->y,
1484                        !close, FALSE, &first_face, &prev_face );
1485
1486
1487     for (i = 1; i < count - 1; i++)
1488     {
1489         pt_1 = &pts[(start + i) % num];
1490         pt_2 = &pts[(start + i + 1) % num];
1491         if (wide_line_segment( pdev, total, pt_1, pt_2, pt_2->x - pt_1->x, pt_2->y - pt_1->y,
1492                                FALSE, FALSE, &face_1, &face_2 ))
1493         {
1494             add_join( pdev, total, round_cap, pt_1, &prev_face, &face_1 );
1495             prev_face = face_2;
1496         }
1497     }
1498
1499     pt_1 = &pts[(start + count - 1) % num];
1500     pt_2 = &pts[(start + count) % num];
1501     wide_line_segment( pdev, total, pt_1, last_pt, pt_2->x - pt_1->x, pt_2->y - pt_1->y,
1502                        FALSE, !close, &face_1, &face_2 );
1503     add_join( pdev, total, round_cap, pt_1, &prev_face, &face_1 );
1504     if (close) add_join( pdev, total, round_cap, last_pt, &face_2, &first_face );
1505 }
1506
1507 static BOOL wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close, HRGN total)
1508 {
1509     HRGN round_cap = 0;
1510
1511     assert( total != 0 );  /* wide pens should always be drawn through a region */
1512     assert( num >= 2 );
1513
1514     /* skip empty segments */
1515     while (num > 2 && pts[0].x == pts[1].x && pts[0].y == pts[1].y) { pts++; num--; }
1516     while (num > 2 && pts[num - 1].x == pts[num - 2].x && pts[num - 1].y == pts[num - 2].y) num--;
1517
1518     if (pdev->pen_join == PS_JOIN_ROUND || pdev->pen_endcap == PS_ENDCAP_ROUND)
1519         round_cap = CreateEllipticRgn( -(pdev->pen_width / 2), -(pdev->pen_width / 2),
1520                                        (pdev->pen_width + 1) / 2 + 1, (pdev->pen_width + 1) / 2 + 1 );
1521
1522     if (close)
1523         wide_line_segments( pdev, num, pts, TRUE, 0, num, &pts[0], &pts[0], round_cap, total );
1524     else
1525         wide_line_segments( pdev, num, pts, FALSE, 0, num - 1, &pts[0], &pts[num - 1], round_cap, total );
1526
1527     if (round_cap) DeleteObject( round_cap );
1528     return TRUE;
1529 }
1530
1531 static BOOL dashed_wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close, HRGN total)
1532 {
1533     int i, start, cur_len, initial_num = 0;
1534     POINT initial_point, start_point, end_point;
1535     HRGN round_cap = 0;
1536
1537     assert( total != 0 );  /* wide pens should always be drawn through a region */
1538     assert( num >= 2 );
1539
1540     /* skip empty segments */
1541     while (num > 2 && pts[0].x == pts[1].x && pts[0].y == pts[1].y) { pts++; num--; }
1542     while (num > 2 && pts[num - 1].x == pts[num - 2].x && pts[num - 1].y == pts[num - 2].y) num--;
1543
1544     if (pdev->pen_join == PS_JOIN_ROUND || pdev->pen_endcap == PS_ENDCAP_ROUND)
1545         round_cap = CreateEllipticRgn( -(pdev->pen_width / 2), -(pdev->pen_width / 2),
1546                                        (pdev->pen_width + 1) / 2 + 1, (pdev->pen_width + 1) / 2 + 1);
1547
1548     start = 0;
1549     cur_len = 0;
1550     start_point = pts[0];
1551
1552     for (i = 0; i < (close ? num : num - 1); i++)
1553     {
1554         const POINT *pt_1 = pts + i;
1555         const POINT *pt_2 = pts + ((close && i == num - 1) ? 0 : i + 1);
1556         int dx = pt_2->x - pt_1->x;
1557         int dy = pt_2->y - pt_1->y;
1558
1559         if (dx == 0 && dy == 0) continue;
1560
1561         if (dy == 0)
1562         {
1563             if (abs( dx ) - cur_len < pdev->dash_pos.left_in_dash)
1564             {
1565                 skip_dash( pdev, abs( dx ) - cur_len );
1566                 cur_len = 0;
1567                 continue;
1568             }
1569             cur_len += pdev->dash_pos.left_in_dash;
1570             dx = (dx > 0) ? cur_len : -cur_len;
1571         }
1572         else if (dx == 0)
1573         {
1574             if (abs( dy ) - cur_len < pdev->dash_pos.left_in_dash)
1575             {
1576                 skip_dash( pdev, abs( dy ) - cur_len );
1577                 cur_len = 0;
1578                 continue;
1579             }
1580             cur_len += pdev->dash_pos.left_in_dash;
1581             dy = (dy > 0) ? cur_len : -cur_len;
1582         }
1583         else
1584         {
1585             double len = hypot( dx, dy );
1586
1587             if (len - cur_len < pdev->dash_pos.left_in_dash)
1588             {
1589                 skip_dash( pdev, len - cur_len );
1590                 cur_len = 0;
1591                 continue;
1592             }
1593             cur_len += pdev->dash_pos.left_in_dash;
1594             dx = dx * cur_len / len;
1595             dy = dy * cur_len / len;
1596         }
1597         end_point.x = pt_1->x + dx;
1598         end_point.y = pt_1->y + dy;
1599
1600         if (pdev->dash_pos.mark)
1601         {
1602             if (!initial_num && close)  /* this is the first dash, save it for later */
1603             {
1604                 initial_num = i - start + 1;
1605                 initial_point = end_point;
1606             }
1607             else wide_line_segments( pdev, num, pts, FALSE, start, i - start + 1,
1608                                      &start_point, &end_point, round_cap, total );
1609         }
1610         if (!initial_num) initial_num = -1;  /* no need to close it */
1611
1612         skip_dash( pdev, pdev->dash_pos.left_in_dash );
1613         start_point = end_point;
1614         start = i;
1615         i--;  /* go on with the same segment */
1616     }
1617
1618     if (pdev->dash_pos.mark)  /* we have a final dash */
1619     {
1620         int count;
1621
1622         if (initial_num > 0)
1623         {
1624             count = num - start + initial_num;
1625             end_point = initial_point;
1626         }
1627         else if (close)
1628         {
1629             count = num - start;
1630             end_point = pts[0];
1631         }
1632         else
1633         {
1634             count = num - start - 1;
1635             end_point = pts[num - 1];
1636         }
1637         wide_line_segments( pdev, num, pts, FALSE, start, count,
1638                             &start_point, &end_point, round_cap, total );
1639     }
1640     else if (initial_num > 0)  /* initial dash only */
1641     {
1642         wide_line_segments( pdev, num, pts, FALSE, 0, initial_num,
1643                             &pts[0], &initial_point, round_cap, total );
1644     }
1645
1646     if (round_cap) DeleteObject( round_cap );
1647     return TRUE;
1648 }
1649
1650 static const dash_pattern dash_patterns_cosmetic[4] =
1651 {
1652     {2, {18, 6}, 24},             /* PS_DASH */
1653     {2, {3,  3}, 6},              /* PS_DOT */
1654     {4, {9, 6, 3, 6}, 24},        /* PS_DASHDOT */
1655     {6, {9, 3, 3, 3, 3, 3}, 24}   /* PS_DASHDOTDOT */
1656 };
1657
1658 static const dash_pattern dash_patterns_geometric[4] =
1659 {
1660     {2, {3, 1}, 4},               /* PS_DASH */
1661     {2, {1, 1}, 2},               /* PS_DOT */
1662     {4, {3, 1, 1, 1}, 6},         /* PS_DASHDOT */
1663     {6, {3, 1, 1, 1, 1, 1}, 8}    /* PS_DASHDOTDOT */
1664 };
1665
1666 static inline void set_dash_pattern( dash_pattern *pattern, DWORD count, DWORD *dashes )
1667 {
1668     DWORD i;
1669
1670     pattern->count = count;
1671     pattern->total_len = 0;
1672     memcpy( pattern->dashes, dashes, count * sizeof(DWORD) );
1673     for (i = 0; i < count; i++) pattern->total_len += dashes[i];
1674     if (pattern->count % 2) pattern->total_len *= 2;
1675 }
1676
1677 static inline void scale_dash_pattern( dash_pattern *pattern, DWORD scale, DWORD endcap )
1678 {
1679     DWORD i;
1680
1681     for (i = 0; i < pattern->count; i++) pattern->dashes[i] *= scale;
1682     pattern->total_len *= scale;
1683
1684     if (endcap != PS_ENDCAP_FLAT)  /* shrink the dashes to leave room for the caps */
1685     {
1686         for (i = 0; i < pattern->count; i += 2)
1687         {
1688             pattern->dashes[i] -= scale;
1689             pattern->dashes[i + 1] += scale;
1690         }
1691     }
1692 }
1693
1694 static inline int get_pen_device_width( dibdrv_physdev *pdev, int width )
1695 {
1696     POINT pts[2];
1697
1698     if (!width) return 1;
1699     pts[0].x = pts[0].y = pts[1].y = 0;
1700     pts[1].x = width;
1701     LPtoDP( pdev->dev.hdc, pts, 2 );
1702     width = abs( pts[1].x - pts[0].x );
1703     return max( width, 1 );
1704 }
1705
1706 /***********************************************************************
1707  *           dibdrv_SetDCPenColor
1708  */
1709 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1710 {
1711     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1712
1713     if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1714         pdev->pen_brush.colorref = color;
1715
1716     return color;
1717 }
1718
1719 /**********************************************************************
1720  *             solid_brush
1721  *
1722  * Fill a number of rectangles with the solid brush
1723  */
1724 static BOOL solid_brush(dibdrv_physdev *pdev, dib_brush *brush, dib_info *dib,
1725                         int num, const RECT *rects, INT rop)
1726 {
1727     rop_mask brush_color;
1728     DWORD color = get_pixel_color( pdev, brush->colorref, TRUE );
1729
1730     calc_rop_masks( rop, color, &brush_color );
1731     dib->funcs->solid_rects( dib, num, rects, brush_color.and, brush_color.xor );
1732     return TRUE;
1733 }
1734
1735 static void free_pattern_brush_bits( dib_brush *brush )
1736 {
1737     HeapFree(GetProcessHeap(), 0, brush->and_bits);
1738     HeapFree(GetProcessHeap(), 0, brush->xor_bits);
1739     brush->and_bits = NULL;
1740     brush->xor_bits = NULL;
1741 }
1742
1743 void free_pattern_brush( dib_brush *brush )
1744 {
1745     free_pattern_brush_bits( brush );
1746     free_dib_info( &brush->dib );
1747 }
1748
1749 static BOOL create_pattern_brush_bits( dib_brush *brush )
1750 {
1751     DWORD size = brush->dib.height * abs(brush->dib.stride);
1752     DWORD *brush_bits = brush->dib.bits.ptr;
1753     DWORD *and_bits, *xor_bits;
1754
1755     assert(brush->and_bits == NULL);
1756     assert(brush->xor_bits == NULL);
1757
1758     assert(brush->dib.stride > 0);
1759
1760     and_bits = brush->and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1761     xor_bits = brush->xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1762
1763     if(!and_bits || !xor_bits)
1764     {
1765         ERR("Failed to create pattern brush bits\n");
1766         free_pattern_brush_bits( brush );
1767         return FALSE;
1768     }
1769
1770     while(size)
1771     {
1772         calc_and_xor_masks(brush->rop, *brush_bits++, and_bits++, xor_bits++);
1773         size -= 4;
1774     }
1775
1776     return TRUE;
1777 }
1778
1779 static const DWORD hatches[6][8] =
1780 {
1781     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1782     { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL   */
1783     { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL  */
1784     { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL  */
1785     { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS      */
1786     { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }  /* HS_DIAGCROSS  */
1787 };
1788
1789 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev, dib_brush *brush, BOOL *needs_reselect)
1790 {
1791     dib_info hatch;
1792     rop_mask fg_mask, bg_mask;
1793     rop_mask_bits mask_bits;
1794     DWORD size;
1795     BOOL ret;
1796
1797     assert(brush->and_bits == NULL);
1798     assert(brush->xor_bits == NULL);
1799
1800     /* Just initialise brush dib with the color / sizing info.  We don't
1801        need the bits as we'll calculate the rop masks straight from
1802        the hatch patterns. */
1803
1804     copy_dib_color_info(&brush->dib, &pdev->dib);
1805     brush->dib.width  = 8;
1806     brush->dib.height = 8;
1807     brush->dib.stride = get_dib_stride( brush->dib.width, brush->dib.bit_count );
1808
1809     size = brush->dib.height * brush->dib.stride;
1810
1811     mask_bits.and = brush->and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1812     mask_bits.xor = brush->xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1813
1814     if(!mask_bits.and || !mask_bits.xor)
1815     {
1816         ERR("Failed to create pattern brush bits\n");
1817         free_pattern_brush_bits( brush );
1818         return FALSE;
1819     }
1820
1821     hatch.bit_count = 1;
1822     hatch.height = hatch.width = 8;
1823     hatch.stride = 4;
1824     hatch.bits.ptr = (void *) hatches[brush->hatch];
1825     hatch.bits.free = hatch.bits.param = NULL;
1826     hatch.bits.is_copy = FALSE;
1827
1828     get_color_masks( pdev, brush->rop, brush->colorref, GetBkMode(pdev->dev.hdc),
1829                      &fg_mask, &bg_mask );
1830
1831     if (brush->colorref & (1 << 24))  /* PALETTEINDEX */
1832         *needs_reselect = TRUE;
1833     if (GetBkMode(pdev->dev.hdc) != TRANSPARENT && (GetBkColor(pdev->dev.hdc) & (1 << 24)))
1834         *needs_reselect = TRUE;
1835
1836     ret = brush->dib.funcs->create_rop_masks( &brush->dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1837     if(!ret) free_pattern_brush_bits( brush );
1838
1839     return ret;
1840 }
1841
1842 static BOOL matching_pattern_format( dib_info *dib, dib_info *pattern )
1843 {
1844     if (dib->bit_count != pattern->bit_count) return FALSE;
1845     if (dib->stride != pattern->stride) return FALSE;
1846
1847     switch (dib->bit_count)
1848     {
1849     case 1:
1850     case 4:
1851     case 8:
1852         if (dib->color_table_size != pattern->color_table_size) return FALSE;
1853         return !memcmp( dib->color_table, pattern->color_table, dib->color_table_size * sizeof(RGBQUAD) );
1854     case 16:
1855     case 32:
1856         return (dib->red_mask == pattern->red_mask &&
1857                 dib->green_mask == pattern->green_mask &&
1858                 dib->blue_mask == pattern->blue_mask);
1859     }
1860     return TRUE;
1861 }
1862
1863 static BOOL select_pattern_brush( dibdrv_physdev *pdev, dib_brush *brush, BOOL *needs_reselect )
1864 {
1865     char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
1866     BITMAPINFO *info = (BITMAPINFO *)buffer;
1867     RGBQUAD color_table[2];
1868     RECT rect;
1869     dib_info pattern;
1870
1871     if (!brush->pattern.info)
1872     {
1873         BITMAPOBJ *bmp = GDI_GetObjPtr( brush->pattern.bitmap, OBJ_BITMAP );
1874         BOOL ret;
1875
1876         if (!bmp) return FALSE;
1877         ret = init_dib_info_from_bitmapobj( &pattern, bmp, 0 );
1878         GDI_ReleaseObj( brush->pattern.bitmap );
1879         if (!ret) return FALSE;
1880     }
1881     else if (brush->pattern.info->bmiHeader.biClrUsed && brush->pattern.usage == DIB_PAL_COLORS)
1882     {
1883         copy_bitmapinfo( info, brush->pattern.info );
1884         fill_color_table_from_pal_colors( info, pdev->dev.hdc );
1885         init_dib_info_from_bitmapinfo( &pattern, info, brush->pattern.bits.ptr, 0 );
1886         *needs_reselect = TRUE;
1887     }
1888     else
1889     {
1890         init_dib_info_from_bitmapinfo( &pattern, brush->pattern.info, brush->pattern.bits.ptr, 0 );
1891     }
1892
1893     if (pattern.bit_count == 1 && !pattern.color_table)
1894     {
1895         /* monochrome DDB pattern uses DC colors */
1896         DWORD pixel;
1897         BOOL got_pixel;
1898         COLORREF color;
1899
1900         color = make_rgb_colorref( pdev->dev.hdc, &pdev->dib, GetTextColor( pdev->dev.hdc ),
1901                                    &got_pixel, &pixel );
1902         color_table[0].rgbRed      = GetRValue( color );
1903         color_table[0].rgbGreen    = GetGValue( color );
1904         color_table[0].rgbBlue     = GetBValue( color );
1905         color_table[0].rgbReserved = 0;
1906
1907         color = make_rgb_colorref( pdev->dev.hdc, &pdev->dib, GetBkColor( pdev->dev.hdc ),
1908                                    &got_pixel, &pixel );
1909         color_table[1].rgbRed      = GetRValue( color );
1910         color_table[1].rgbGreen    = GetGValue( color );
1911         color_table[1].rgbBlue     = GetBValue( color );
1912         color_table[1].rgbReserved = 0;
1913
1914         pattern.color_table = color_table;
1915         pattern.color_table_size = 2;
1916         *needs_reselect = TRUE;
1917     }
1918
1919     copy_dib_color_info(&brush->dib, &pdev->dib);
1920
1921     brush->dib.height = pattern.height;
1922     brush->dib.width  = pattern.width;
1923     brush->dib.stride = get_dib_stride( brush->dib.width, brush->dib.bit_count );
1924
1925     if (matching_pattern_format( &brush->dib, &pattern ))
1926     {
1927         brush->dib.bits.ptr     = pattern.bits.ptr;
1928         brush->dib.bits.is_copy = FALSE;
1929         brush->dib.bits.free    = NULL;
1930     }
1931     else
1932     {
1933         brush->dib.bits.ptr     = HeapAlloc( GetProcessHeap(), 0, brush->dib.height * brush->dib.stride );
1934         brush->dib.bits.is_copy = TRUE;
1935         brush->dib.bits.free    = free_heap_bits;
1936
1937         rect.left = rect.top = 0;
1938         rect.right = pattern.width;
1939         rect.bottom = pattern.height;
1940
1941         brush->dib.funcs->convert_to(&brush->dib, &pattern, &rect);
1942     }
1943     return TRUE;
1944 }
1945
1946 /**********************************************************************
1947  *             pattern_brush
1948  *
1949  * Fill a number of rectangles with the pattern brush
1950  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1951  */
1952 static BOOL pattern_brush(dibdrv_physdev *pdev, dib_brush *brush, dib_info *dib,
1953                           int num, const RECT *rects, INT rop)
1954 {
1955     POINT origin;
1956     BOOL needs_reselect = FALSE;
1957
1958     if (rop != brush->rop)
1959     {
1960         free_pattern_brush_bits( brush );
1961         brush->rop = rop;
1962     }
1963
1964     if(brush->and_bits == NULL)
1965     {
1966         switch(brush->style)
1967         {
1968         case BS_DIBPATTERN:
1969             if (!brush->dib.bits.ptr && !select_pattern_brush( pdev, brush, &needs_reselect ))
1970                 return FALSE;
1971             if(!create_pattern_brush_bits( brush ))
1972                 return FALSE;
1973             break;
1974
1975         case BS_HATCHED:
1976             if(!create_hatch_brush_bits(pdev, brush, &needs_reselect))
1977                 return FALSE;
1978             break;
1979
1980         default:
1981             ERR("Unexpected brush style %d\n", brush->style);
1982             return FALSE;
1983         }
1984     }
1985
1986     GetBrushOrgEx(pdev->dev.hdc, &origin);
1987
1988     dib->funcs->pattern_rects( dib, num, rects, &origin, &brush->dib, brush->and_bits, brush->xor_bits );
1989
1990     if (needs_reselect) free_pattern_brush( brush );
1991     return TRUE;
1992 }
1993
1994 static BOOL null_brush(dibdrv_physdev *pdev, dib_brush *brush, dib_info *dib,
1995                        int num, const RECT *rects, INT rop)
1996 {
1997     return TRUE;
1998 }
1999
2000 static void select_brush( dib_brush *brush, const LOGBRUSH *logbrush, const struct brush_pattern *pattern )
2001 {
2002     free_pattern_brush( brush );
2003
2004     if (pattern)
2005     {
2006         brush->style   = BS_DIBPATTERN;
2007         brush->pattern = *pattern;  /* brush is actually selected only when it's used */
2008         brush->rects   = pattern_brush;
2009     }
2010     else
2011     {
2012         brush->style    = logbrush->lbStyle;
2013         brush->colorref = logbrush->lbColor;
2014         brush->hatch    = logbrush->lbHatch;
2015
2016         switch (logbrush->lbStyle)
2017         {
2018         case BS_SOLID:   brush->rects = solid_brush; break;
2019         case BS_NULL:    brush->rects = null_brush; break;
2020         case BS_HATCHED: brush->rects = pattern_brush; break;
2021         }
2022     }
2023 }
2024
2025 /***********************************************************************
2026  *           dibdrv_SelectBrush
2027  */
2028 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
2029 {
2030     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
2031     LOGBRUSH logbrush;
2032
2033     TRACE("(%p, %p)\n", dev, hbrush);
2034
2035     GetObjectW( hbrush, sizeof(logbrush), &logbrush );
2036
2037     if (hbrush == GetStockObject( DC_BRUSH ))
2038         logbrush.lbColor = GetDCBrushColor( dev->hdc );
2039
2040     select_brush( &pdev->brush, &logbrush, pattern );
2041     return hbrush;
2042 }
2043
2044 /***********************************************************************
2045  *           dibdrv_SelectPen
2046  */
2047 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen, const struct brush_pattern *pattern )
2048 {
2049     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
2050     LOGPEN logpen;
2051     LOGBRUSH logbrush;
2052     EXTLOGPEN *elp = NULL;
2053
2054     TRACE("(%p, %p)\n", dev, hpen);
2055
2056     if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
2057     {
2058         /* must be an extended pen */
2059         INT size = GetObjectW( hpen, 0, NULL );
2060
2061         if (!size) return 0;
2062
2063         elp = HeapAlloc( GetProcessHeap(), 0, size );
2064
2065         GetObjectW( hpen, size, elp );
2066         logpen.lopnStyle = elp->elpPenStyle;
2067         logpen.lopnWidth.x = elp->elpWidth;
2068         /* cosmetic ext pens are always 1-pixel wide */
2069         if (!(logpen.lopnStyle & PS_GEOMETRIC)) logpen.lopnWidth.x = 0;
2070
2071         logbrush.lbStyle = elp->elpBrushStyle;
2072         logbrush.lbColor = elp->elpColor;
2073         logbrush.lbHatch = elp->elpHatch;
2074     }
2075     else
2076     {
2077         logbrush.lbStyle = BS_SOLID;
2078         logbrush.lbColor = logpen.lopnColor;
2079         logbrush.lbHatch = 0;
2080     }
2081
2082     pdev->pen_join   = logpen.lopnStyle & PS_JOIN_MASK;
2083     pdev->pen_endcap = logpen.lopnStyle & PS_ENDCAP_MASK;
2084     pdev->pen_width  = get_pen_device_width( pdev, logpen.lopnWidth.x );
2085
2086     if (hpen == GetStockObject( DC_PEN ))
2087         logbrush.lbColor = GetDCPenColor( dev->hdc );
2088
2089     set_dash_pattern( &pdev->pen_pattern, 0, NULL );
2090     select_brush( &pdev->pen_brush, &logbrush, pattern );
2091
2092     pdev->pen_style = logpen.lopnStyle & PS_STYLE_MASK;
2093
2094     switch (pdev->pen_style)
2095     {
2096     case PS_DASH:
2097     case PS_DOT:
2098     case PS_DASHDOT:
2099     case PS_DASHDOTDOT:
2100         if (logpen.lopnStyle & PS_GEOMETRIC)
2101         {
2102             pdev->pen_pattern = dash_patterns_geometric[pdev->pen_style - 1];
2103             if (pdev->pen_width > 1)
2104             {
2105                 scale_dash_pattern( &pdev->pen_pattern, pdev->pen_width, pdev->pen_endcap );
2106                 pdev->pen_lines = dashed_wide_pen_lines;
2107             }
2108             else pdev->pen_lines = dashed_pen_lines;
2109             break;
2110         }
2111         if (pdev->pen_width == 1)  /* wide cosmetic pens are not dashed */
2112         {
2113             pdev->pen_lines = dashed_pen_lines;
2114             pdev->pen_pattern = dash_patterns_cosmetic[pdev->pen_style - 1];
2115             break;
2116         }
2117         /* fall through */
2118     case PS_SOLID:
2119     case PS_INSIDEFRAME:
2120         pdev->pen_lines = (pdev->pen_width == 1) ? solid_pen_lines : wide_pen_lines;
2121         break;
2122
2123     case PS_NULL:
2124         pdev->pen_width = 0;
2125         pdev->pen_lines = null_pen_lines;
2126         break;
2127
2128     case PS_ALTERNATE:
2129         pdev->pen_lines = dashed_pen_lines;
2130         pdev->pen_pattern = dash_patterns_geometric[PS_DOT - 1];
2131         break;
2132
2133     case PS_USERSTYLE:
2134         pdev->pen_lines = (pdev->pen_width == 1) ? dashed_pen_lines : dashed_wide_pen_lines;
2135         set_dash_pattern( &pdev->pen_pattern, elp->elpNumEntries, elp->elpStyleEntry );
2136         if (!(logpen.lopnStyle & PS_GEOMETRIC)) scale_dash_pattern( &pdev->pen_pattern, 3, PS_ENDCAP_FLAT );
2137         break;
2138     }
2139
2140     pdev->pen_uses_region = (logpen.lopnStyle & PS_GEOMETRIC || pdev->pen_width > 1);
2141     pdev->pen_is_ext = (elp != NULL);
2142     HeapFree( GetProcessHeap(), 0, elp );
2143     return hpen;
2144 }
2145
2146 /***********************************************************************
2147  *           dibdrv_SetDCBrushColor
2148  */
2149 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
2150 {
2151     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
2152
2153     if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
2154         pdev->brush.colorref = color;
2155
2156     return color;
2157 }
2158
2159 BOOL brush_rect(dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip, INT rop)
2160 {
2161     struct clipped_rects clipped_rects;
2162     BOOL ret;
2163
2164     if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE;
2165     ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop );
2166     free_clipped_rects( &clipped_rects );
2167     return ret;
2168 }
2169
2170 /* paint a region with the brush (note: the region can be modified) */
2171 BOOL brush_region( dibdrv_physdev *pdev, HRGN region )
2172 {
2173     if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
2174     return brush_rect( pdev, &pdev->brush, NULL, region, GetROP2( pdev->dev.hdc ));
2175 }
2176
2177 /* paint a region with the pen (note: the region can be modified) */
2178 BOOL pen_region( dibdrv_physdev *pdev, HRGN region )
2179 {
2180     if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
2181     return brush_rect( pdev, &pdev->pen_brush, NULL, region, GetROP2( pdev->dev.hdc ));
2182 }