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