gdi32: Move to using a multi-line pen object-level function.
[wine] / dlls / gdi32 / dibdrv / objects.c
1 /*
2  * DIB driver GDI objects.
3  *
4  * Copyright 2011 Huw Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <assert.h>
22 #include <stdlib.h>
23
24 #include "gdi_private.h"
25 #include "dibdrv.h"
26
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(dib);
30
31 /*
32  *
33  * Decompose the 16 ROP2s into an expression of the form
34  *
35  * D = (D & A) ^ X
36  *
37  * Where A and X depend only on P (and so can be precomputed).
38  *
39  *                                       A    X
40  *
41  * R2_BLACK         0                    0    0
42  * R2_NOTMERGEPEN   ~(D | P)            ~P   ~P
43  * R2_MASKNOTPEN    ~P & D              ~P    0
44  * R2_NOTCOPYPEN    ~P                   0   ~P
45  * R2_MASKPENNOT    P & ~D               P    P
46  * R2_NOT           ~D                   1    1
47  * R2_XORPEN        P ^ D                1    P
48  * R2_NOTMASKPEN    ~(P & D)             P    1
49  * R2_MASKPEN       P & D                P    0
50  * R2_NOTXORPEN     ~(P ^ D)             1   ~P
51  * R2_NOP           D                    1    0
52  * R2_MERGENOTPEN   ~P | D               P   ~P
53  * R2_COPYPEN       P                    0    P
54  * R2_MERGEPENNOT   P | ~D              ~P    1
55  * R2_MERGEPEN      P | D               ~P    P
56  * R2_WHITE         1                    0    1
57  *
58  */
59
60 /* A = (P & A1) ^ A2 */
61 #define ZERO  { 0u,  0u}
62 #define ONE   { 0u, ~0u}
63 #define P     {~0u,  0u}
64 #define NOT_P {~0u, ~0u}
65
66 static const DWORD rop2_and_array[16][2] =
67 {
68     ZERO, NOT_P, NOT_P, ZERO,
69     P,    ONE,   ONE,   P,
70     P,    ONE,   ONE,   P,
71     ZERO, NOT_P, NOT_P, ZERO
72 };
73
74 /* X = (P & X1) ^ X2 */
75 static const DWORD rop2_xor_array[16][2] =
76 {
77     ZERO, NOT_P, ZERO, NOT_P,
78     P,    ONE,   P,    ONE,
79     ZERO, NOT_P, ZERO, NOT_P,
80     P,    ONE,   P,    ONE
81 };
82
83 #undef NOT_P
84 #undef P
85 #undef ONE
86 #undef ZERO
87
88 void get_rop_codes(INT rop, struct rop_codes *codes)
89 {
90     /* NB The ROP2 codes start at one and the arrays are zero-based */
91     codes->a1 = rop2_and_array[rop-1][0];
92     codes->a2 = rop2_and_array[rop-1][1];
93     codes->x1 = rop2_xor_array[rop-1][0];
94     codes->x2 = rop2_xor_array[rop-1][1];
95 }
96
97 void calc_and_xor_masks(INT rop, DWORD color, DWORD *and, DWORD *xor)
98 {
99     struct rop_codes codes;
100     get_rop_codes( rop, &codes );
101
102     *and = (color & codes.a1) ^ codes.a2;
103     *xor = (color & codes.x1) ^ codes.x2;
104 }
105
106 static inline RGBQUAD rgbquad_from_colorref(COLORREF c)
107 {
108     RGBQUAD ret;
109
110     ret.rgbRed      = GetRValue(c);
111     ret.rgbGreen    = GetGValue(c);
112     ret.rgbBlue     = GetBValue(c);
113     ret.rgbReserved = 0;
114     return ret;
115 }
116
117 static inline BOOL rgbquad_equal(const RGBQUAD *a, const RGBQUAD *b)
118 {
119     if(a->rgbRed   == b->rgbRed   &&
120        a->rgbGreen == b->rgbGreen &&
121        a->rgbBlue  == b->rgbBlue)
122         return TRUE;
123     return FALSE;
124 }
125
126 /******************************************************************
127  *                   get_fg_color
128  *
129  * 1 bit bitmaps map the fg/bg colors as follows:
130  * If the fg colorref exactly matches one of the color table entries then
131  * that entry is the fg color and the other is the bg.
132  * Otherwise the bg color is mapped to the closest entry in the table and
133  * the fg takes the other one.
134  */
135 DWORD get_fg_color( dibdrv_physdev *pdev, COLORREF fg )
136 {
137     RGBQUAD fg_quad;
138
139     if(pdev->dib.bit_count != 1)
140         return pdev->dib.funcs->colorref_to_pixel( &pdev->dib, fg );
141
142     fg_quad = rgbquad_from_colorref( fg );
143     if(rgbquad_equal(&fg_quad, pdev->dib.color_table))
144         return 0;
145     if(rgbquad_equal(&fg_quad, pdev->dib.color_table + 1))
146         return 1;
147
148     if(fg == GetBkColor(pdev->dev.hdc)) return pdev->bkgnd_color;
149     else return pdev->bkgnd_color ? 0 : 1;
150 }
151
152 /***************************************************************************
153  *                get_pen_bkgnd_masks
154  *
155  * Returns the pre-calculated bkgnd color masks unless the dib is 1 bpp.
156  * In this case since there are several fg sources (pen, brush, text)
157  * this makes pdev->bkgnd_color unusable.  So here we take the inverse
158  * of the relevant fg color (which is always set up correctly).
159  */
160 static inline void get_pen_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
161 {
162     if(pdev->dib.bit_count != 1 || GetBkMode(pdev->dev.hdc) == TRANSPARENT)
163     {
164         *and = pdev->bkgnd_and;
165         *xor = pdev->bkgnd_xor;
166     }
167     else
168     {
169         DWORD color = ~pdev->pen_color;
170         if(pdev->pen_colorref == GetBkColor(pdev->dev.hdc)) color = pdev->pen_color;
171         calc_and_xor_masks( GetROP2(pdev->dev.hdc), color, and, xor );
172     }
173 }
174
175 static inline void get_brush_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
176 {
177     if(GetBkMode(pdev->dev.hdc) == TRANSPARENT)
178     {
179         *and = pdev->bkgnd_and;
180         *xor = pdev->bkgnd_xor;
181     }
182     else
183     {
184         DWORD color = pdev->bkgnd_color;
185
186         if(pdev->dib.bit_count == 1)
187         {
188             if(pdev->brush_colorref == GetBkColor(pdev->dev.hdc))
189                 color = pdev->brush_color;
190             else
191                 color = ~pdev->brush_color;
192         }
193         calc_and_xor_masks( pdev->brush_rop, color, and, xor );
194     }
195 }
196
197 static inline void order_end_points(int *s, int *e)
198 {
199     if(*s > *e)
200     {
201         int tmp;
202         tmp = *s + 1;
203         *s = *e + 1;
204         *e = tmp;
205     }
206 }
207
208 static inline BOOL pt_in_rect( const RECT *rect, const POINT *pt )
209 {
210     return ((pt->x >= rect->left) && (pt->x < rect->right) &&
211             (pt->y >= rect->top) && (pt->y < rect->bottom));
212 }
213
214 #define Y_INCREASING_MASK 0x0f
215 #define X_INCREASING_MASK 0xc3
216 #define X_MAJOR_MASK      0x99
217 #define POS_SLOPE_MASK    0x33
218
219 static inline BOOL is_xmajor(DWORD octant)
220 {
221     return octant & X_MAJOR_MASK;
222 }
223
224 static inline BOOL is_pos_slope(DWORD octant)
225 {
226     return octant & POS_SLOPE_MASK;
227 }
228
229 static inline BOOL is_x_increasing(DWORD octant)
230 {
231     return octant & X_INCREASING_MASK;
232 }
233
234 static inline BOOL is_y_increasing(DWORD octant)
235 {
236     return octant & Y_INCREASING_MASK;
237 }
238
239 /**********************************************************************
240  *                  get_octant_number
241  *
242  * Return the octant number starting clockwise from the +ve x-axis.
243  */
244 static inline int get_octant_number(int dx, int dy)
245 {
246     if(dy > 0)
247         if(dx > 0)
248             return ( dx >  dy) ? 1 : 2;
249         else
250             return (-dx >  dy) ? 4 : 3;
251     else
252         if(dx < 0)
253             return (-dx > -dy) ? 5 : 6;
254         else
255             return ( dx > -dy) ? 8 : 7;
256 }
257
258 static inline DWORD get_octant_mask(int dx, int dy)
259 {
260     return 1 << (get_octant_number(dx, dy) - 1);
261 }
262
263 static void solid_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
264 {
265     RECT rect;
266
267     rect.left   = x;
268     rect.right  = x + 1;
269     rect.top    = y;
270     rect.bottom = y + 1;
271     pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
272     return;
273 }
274
275 #define OUT_LEFT    1
276 #define OUT_RIGHT   2
277 #define OUT_TOP     4
278 #define OUT_BOTTOM  8
279
280 static inline DWORD calc_outcode(const POINT *pt, const RECT *clip)
281 {
282     DWORD out = 0;
283     if(pt->x < clip->left)         out |= OUT_LEFT;
284     else if(pt->x >= clip->right)  out |= OUT_RIGHT;
285     if(pt->y < clip->top)          out |= OUT_TOP;
286     else if(pt->y >= clip->bottom) out |= OUT_BOTTOM;
287
288     return out;
289 }
290
291 typedef struct
292 {
293     unsigned int dx, dy;
294     int bias;
295     DWORD octant;
296 } bres_params;
297
298 /******************************************************************************
299  *                clip_line
300  *
301  * Clips the start and end points to a rectangle.
302  *
303  * Note, this treats the end point like the start point.  If the
304  * caller doesn't want it displayed, it should exclude it.  If the end
305  * point is clipped out, then the likelihood is that the new end point
306  * should be displayed.
307  *
308  * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
309  *
310  * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
311  * however the Bresenham error term is defined differently so the equations
312  * will also differ.
313  *
314  * For x major lines we have 2dy >= err + bias > 2dy - 2dx
315  *                           0   >= err + bias - 2dy > -2dx
316  *
317  * Note dx, dy, m and n are all +ve.
318  *
319  * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
320  *                     err = 2dy - dx + 2mdy - 2ndx
321  *                      0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
322  *                      0 >= 2mdy - 2ndx + bias - dx > -2dx
323  *                      which of course will give exactly one solution for n,
324  *                      so looking at the >= inequality
325  *                      n >= (2mdy + bias - dx) / 2dx
326  *                      n = ceiling((2mdy + bias - dx) / 2dx)
327  *                        = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
328  *
329  * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
330  * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
331  *                     0 >= 2mdy - 2ndx + bias - dx > -2dx
332  *                  2mdy > 2ndx - bias - dx
333  *                     m > (2ndx - bias - dx) / 2dy
334  *                     m = floor((2ndx - bias - dx) / 2dy) + 1
335  *                     m = (2ndx - bias - dx) / 2dy + 1
336  *
337  * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
338  *                  err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
339  *                      = 2dy - dx - 2mdy + 2ndx
340  *                   0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
341  *                   0 >= 2ndx - 2mdy + bias - dx > -2dx
342  *                   again exactly one solution.
343  *                   2ndx <= 2mdy - bias + dx
344  *                   n = floor((2mdy - bias + dx) / 2dx)
345  *                     = (2mdy - bias + dx) / 2dx
346  *
347  * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
348  * mininizing m to include all of the points at y = y2 - n.  As above:
349  *                  0 >= 2ndx - 2mdy + bias - dx > -2dx
350  *               2mdy >= 2ndx + bias - dx
351  *                   m = ceiling((2ndx + bias - dx) / 2dy)
352  *                     = (2ndx + bias - dx - 1) / 2dy + 1
353  *
354  * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
355  *
356  * Moving start point from y1 to y1 + n find x1 + m
357  *                     m = (2ndx + bias + dy - 1) / 2dy
358  *
359  * Moving start point from x1 to x1 + m find y1 + n
360  *                     n = (2mdy - bias - dy) / 2ndx + 1
361  *
362  * Moving end point from y2 to y2 - n find x1 - m
363  *                     m = (2ndx - bias + dy) / 2dy
364  *
365  * Moving end point from x2 to x2 - m find y2 - n
366  *                     n = (2mdy + bias - dy - 1) / 2dx + 1
367  */
368 static int clip_line(const POINT *start, const POINT *end, const RECT *clip,
369                      const bres_params *params, POINT *pt1, POINT *pt2)
370 {
371     unsigned int n, m;
372     BOOL clipped = FALSE;
373     DWORD start_oc, end_oc;
374     const int bias = params->bias;
375     const unsigned int dx = params->dx;
376     const unsigned int dy = params->dy;
377     const unsigned int two_dx = params->dx * 2;
378     const unsigned int two_dy = params->dy * 2;
379     const BOOL xmajor = is_xmajor(params->octant);
380     const BOOL neg_slope = !is_pos_slope(params->octant);
381
382     *pt1 = *start;
383     *pt2 = *end;
384
385     start_oc = calc_outcode(start, clip);
386     end_oc = calc_outcode(end, clip);
387
388     while(1)
389     {
390         if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
391         if(start_oc & end_oc)            return 0; /* trivial reject */
392
393         clipped = TRUE;
394         if(start_oc & OUT_LEFT)
395         {
396             m = clip->left - start->x;
397             if(xmajor)
398                 n = (m * two_dy + bias + dx - 1) / two_dx;
399             else
400                 n = (m * two_dy - bias - dy) / two_dx + 1;
401
402             pt1->x = clip->left;
403             if(neg_slope) n = -n;
404             pt1->y = start->y + n;
405             start_oc = calc_outcode(pt1, clip);
406         }
407         else if(start_oc & OUT_RIGHT)
408         {
409             m = start->x - clip->right + 1;
410             if(xmajor)
411                 n = (m * two_dy + bias + dx - 1) / two_dx;
412             else
413                 n = (m * two_dy - bias - dy) / two_dx + 1;
414
415             pt1->x = clip->right - 1;
416             if(neg_slope) n = -n;
417             pt1->y = start->y - n;
418             start_oc = calc_outcode(pt1, clip);
419         }
420         else if(start_oc & OUT_TOP)
421         {
422             n = clip->top - start->y;
423             if(xmajor)
424                 m = (n * two_dx - bias - dx) / two_dy + 1;
425             else
426                 m = (n * two_dx + bias + dy - 1) / two_dy;
427
428             pt1->y = clip->top;
429             if(neg_slope) m = -m;
430             pt1->x = start->x + m;
431             start_oc = calc_outcode(pt1, clip);
432         }
433         else if(start_oc & OUT_BOTTOM)
434         {
435             n = start->y - clip->bottom + 1;
436             if(xmajor)
437                 m = (n * two_dx - bias - dx) / two_dy + 1;
438             else
439                 m = (n * two_dx + bias + dy - 1) / two_dy;
440
441             pt1->y = clip->bottom - 1;
442             if(neg_slope) m = -m;
443             pt1->x = start->x - m;
444             start_oc = calc_outcode(pt1, clip);
445         }
446         else if(end_oc & OUT_LEFT)
447         {
448             m = clip->left - end->x;
449             if(xmajor)
450                 n = (m * two_dy - bias + dx) / two_dx;
451             else
452                 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
453
454             pt2->x = clip->left;
455             if(neg_slope) n = -n;
456             pt2->y = end->y + n;
457             end_oc = calc_outcode(pt2, clip);
458         }
459         else if(end_oc & OUT_RIGHT)
460         {
461             m = end->x - clip->right + 1;
462             if(xmajor)
463                 n = (m * two_dy - bias + dx) / two_dx;
464             else
465                 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
466
467             pt2->x = clip->right - 1;
468             if(neg_slope) n = -n;
469             pt2->y = end->y - n;
470             end_oc = calc_outcode(pt2, clip);
471         }
472         else if(end_oc & OUT_TOP)
473         {
474             n = clip->top - end->y;
475             if(xmajor)
476                 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
477             else
478                 m = (n * two_dx - bias + dy) / two_dy;
479
480             pt2->y = clip->top;
481             if(neg_slope) m = -m;
482             pt2->x = end->x + m;
483             end_oc = calc_outcode(pt2, clip);
484         }
485         else if(end_oc & OUT_BOTTOM)
486         {
487             n = end->y - clip->bottom + 1;
488             if(xmajor)
489                 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
490             else
491                 m = (n * two_dx - bias + dy) / two_dy;
492
493             pt2->y = clip->bottom - 1;
494             if(neg_slope) m = -m;
495             pt2->x = end->x - m;
496             end_oc = calc_outcode(pt2, clip);
497         }
498     }
499 }
500
501 static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, const bres_params *params, INT err,
502                                 BOOL last_pt, void (* callback)(dibdrv_physdev*,INT,INT), dibdrv_physdev *pdev)
503 {
504     const int xadd = is_x_increasing(params->octant) ? 1 : -1;
505     const int yadd = is_y_increasing(params->octant) ? 1 : -1;
506     INT erradd;
507
508     if (is_xmajor(params->octant))  /* line is "more horizontal" */
509     {
510         erradd = 2*params->dy - 2*params->dx;
511         while(x1 != x2)
512         {
513             callback(pdev, x1, y1);
514             if (err + params->bias > 0)
515             {
516                 y1 += yadd;
517                 err += erradd;
518             }
519             else err += 2*params->dy;
520             x1 += xadd;
521         }
522         if(last_pt) callback(pdev, x1, y1);
523     }
524     else   /* line is "more vertical" */
525     {
526         erradd = 2*params->dx - 2*params->dy;
527         while(y1 != y2)
528         {
529             callback(pdev, x1, y1);
530             if (err + params->bias > 0)
531             {
532                 x1 += xadd;
533                 err += erradd;
534             }
535             else err += 2*params->dx;
536             y1 += yadd;
537         }
538         if(last_pt) callback(pdev, x1, y1);
539     }
540 }
541
542 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
543 {
544     const WINEREGION *clip = get_wine_region(pdev->clip);
545
546     if(start->y == end->y)
547     {
548         RECT rect;
549         int i;
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         for(i = 0; i < clip->numRects; i++)
557         {
558             if(clip->rects[i].top >= rect.bottom) break;
559             if(clip->rects[i].bottom <= rect.top) continue;
560             /* Optimize the unclipped case */
561             if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
562             {
563                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
564                 break;
565             }
566             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
567             {
568                 RECT tmp = rect;
569                 tmp.left = max(rect.left, clip->rects[i].left);
570                 tmp.right = min(rect.right, clip->rects[i].right);
571                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
572             }
573         }
574     }
575     else if(start->x == end->x)
576     {
577         RECT rect;
578         int i;
579
580         rect.left   = start->x;
581         rect.top    = start->y;
582         rect.right  = end->x + 1;
583         rect.bottom = end->y;
584         order_end_points(&rect.top, &rect.bottom);
585         for(i = 0; i < clip->numRects; i++)
586         {
587             /* Optimize unclipped case */
588             if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
589                clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
590             {
591                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
592                 break;
593             }
594             if(clip->rects[i].top >= rect.bottom) break;
595             if(clip->rects[i].bottom <= rect.top) continue;
596             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
597             {
598                 RECT tmp = rect;
599                 tmp.top = max(rect.top, clip->rects[i].top);
600                 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
601                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
602             }
603         }
604     }
605     else
606     {
607         bres_params params;
608         INT dx = end->x - start->x;
609         INT dy = end->y - start->y;
610         INT i;
611
612         params.dx = abs(dx);
613         params.dy = abs(dy);
614         params.octant = get_octant_mask(dx, dy);
615         /* Octants 3, 5, 6 and 8 take a bias */
616         params.bias = (params.octant & 0xb4) ? 1 : 0;
617
618         for(i = 0; i < clip->numRects; i++)
619         {
620             POINT clipped_start, clipped_end;
621             int clip_status;
622             clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
623
624             if(clip_status)
625             {
626                 int m = abs(clipped_start.x - start->x);
627                 int n = abs(clipped_start.y - start->y);
628                 int err;
629                 BOOL last_pt = FALSE;
630
631                 if(is_xmajor(params.octant))
632                     err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
633                 else
634                     err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
635
636                 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
637
638                 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, &params,
639                                     err, last_pt, solid_pen_line_callback, pdev);
640
641                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
642             }
643         }
644
645     }
646     release_wine_region(pdev->clip);
647     return TRUE;
648 }
649
650 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
651 {
652     int i;
653
654     assert( num >= 2 );
655     for (i = 0; i < num - 1; i++)
656         if (!solid_pen_line( pdev, pts + i, pts + i + 1 ))
657             return FALSE;
658
659     return TRUE;
660 }
661
662 void reset_dash_origin(dibdrv_physdev *pdev)
663 {
664     pdev->dash_pos.cur_dash = 0;
665     pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
666     pdev->dash_pos.mark = TRUE;
667 }
668
669 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
670 {
671     skip %= pdev->pen_pattern.total_len;
672     while(skip)
673     {
674         if(pdev->dash_pos.left_in_dash > skip)
675         {
676             pdev->dash_pos.left_in_dash -= skip;
677             return;
678         }
679         skip -= pdev->dash_pos.left_in_dash;
680         pdev->dash_pos.cur_dash++;
681         if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
682         pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
683         pdev->dash_pos.mark = !pdev->dash_pos.mark;
684     }
685 }
686
687 static inline void get_dash_colors(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
688 {
689     if(pdev->dash_pos.mark)
690     {
691         *and = pdev->pen_and;
692         *xor = pdev->pen_xor;
693     }
694     else /* space */
695     {
696         get_pen_bkgnd_masks( pdev, and, xor );
697     }
698 }
699
700 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
701 {
702     RECT rect;
703     DWORD and, xor;
704
705     get_dash_colors(pdev, &and, &xor);
706     skip_dash(pdev, 1);
707     rect.left   = x;
708     rect.right  = x + 1;
709     rect.top    = y;
710     rect.bottom = y + 1;
711     pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
712     return;
713 }
714
715 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
716 {
717     const WINEREGION *clip = get_wine_region(pdev->clip);
718     DWORD and, xor;
719     int i, dash_len;
720     RECT rect;
721     const dash_pos start_pos = pdev->dash_pos;
722
723     if(start->y == end->y) /* hline */
724     {
725         BOOL l_to_r;
726         INT left, right, cur_x;
727
728         rect.top = start->y;
729         rect.bottom = start->y + 1;
730
731         if(start->x <= end->x)
732         {
733             left = start->x;
734             right = end->x - 1;
735             l_to_r = TRUE;
736         }
737         else
738         {
739             left = end->x + 1;
740             right = start->x;
741             l_to_r = FALSE;
742         }
743
744         for(i = 0; i < clip->numRects; i++)
745         {
746             if(clip->rects[i].top > start->y) break;
747             if(clip->rects[i].bottom <= start->y) continue;
748
749             if(clip->rects[i].right > left && clip->rects[i].left <= right)
750             {
751                 int clipped_left  = max(clip->rects[i].left, left);
752                 int clipped_right = min(clip->rects[i].right - 1, right);
753
754                 pdev->dash_pos = start_pos;
755
756                 if(l_to_r)
757                 {
758                     cur_x = clipped_left;
759                     if(cur_x != left)
760                         skip_dash(pdev, clipped_left - left);
761
762                     while(cur_x <= clipped_right)
763                     {
764                         get_dash_colors(pdev, &and, &xor);
765                         dash_len = pdev->dash_pos.left_in_dash;
766                         if(cur_x + dash_len > clipped_right + 1)
767                             dash_len = clipped_right - cur_x + 1;
768                         rect.left = cur_x;
769                         rect.right = cur_x + dash_len;
770
771                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
772                         cur_x += dash_len;
773                         skip_dash(pdev, dash_len);
774                     }
775                 }
776                 else
777                 {
778                     cur_x = clipped_right;
779                     if(cur_x != right)
780                         skip_dash(pdev, right - clipped_right);
781
782                     while(cur_x >= clipped_left)
783                     {
784                         get_dash_colors(pdev, &and, &xor);
785                         dash_len = pdev->dash_pos.left_in_dash;
786                         if(cur_x - dash_len < clipped_left - 1)
787                             dash_len = cur_x - clipped_left + 1;
788                         rect.left = cur_x - dash_len + 1;
789                         rect.right = cur_x + 1;
790
791                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
792                         cur_x -= dash_len;
793                         skip_dash(pdev, dash_len);
794                     }
795                 }
796             }
797         }
798         pdev->dash_pos = start_pos;
799         skip_dash(pdev, right - left + 1);
800     }
801     else if(start->x == end->x) /* vline */
802     {
803         BOOL t_to_b;
804         INT top, bottom, cur_y;
805
806         rect.left = start->x;
807         rect.right = start->x + 1;
808
809         if(start->y <= end->y)
810         {
811             top = start->y;
812             bottom = end->y - 1;
813             t_to_b = TRUE;
814         }
815         else
816         {
817             top = end->y + 1;
818             bottom = start->y;
819             t_to_b = FALSE;
820         }
821
822         for(i = 0; i < clip->numRects; i++)
823         {
824             if(clip->rects[i].top > bottom) break;
825             if(clip->rects[i].bottom <= top) continue;
826             if(clip->rects[i].right > start->x && clip->rects[i].left <= start->x)
827             {
828                 int clipped_top    = max(clip->rects[i].top, top);
829                 int clipped_bottom = min(clip->rects[i].bottom - 1, bottom);
830
831                 pdev->dash_pos = start_pos;
832
833                 if(t_to_b)
834                 {
835                     cur_y = clipped_top;
836                     if(cur_y != top)
837                         skip_dash(pdev, clipped_top - top);
838
839                     while(cur_y <= clipped_bottom)
840                     {
841                         get_dash_colors(pdev, &and, &xor);
842                         dash_len = pdev->dash_pos.left_in_dash;
843                         if(cur_y + dash_len > clipped_bottom + 1)
844                             dash_len = clipped_bottom - cur_y + 1;
845                         rect.top = cur_y;
846                         rect.bottom = cur_y + dash_len;
847
848                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
849                         cur_y += dash_len;
850                         skip_dash(pdev, dash_len);
851                     }
852                 }
853                 else
854                 {
855                     cur_y = clipped_bottom;
856                     if(cur_y != bottom)
857                         skip_dash(pdev, bottom - clipped_bottom);
858
859                     while(cur_y >= clipped_top)
860                     {
861                         get_dash_colors(pdev, &and, &xor);
862                         dash_len = pdev->dash_pos.left_in_dash;
863                         if(cur_y - dash_len < clipped_top - 1)
864                             dash_len = cur_y - clipped_top + 1;
865                         rect.top = cur_y - dash_len + 1;
866                         rect.bottom = cur_y + 1;
867
868                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
869                         cur_y -= dash_len;
870                         skip_dash(pdev, dash_len);
871                     }
872                 }
873             }
874         }
875         pdev->dash_pos = start_pos;
876         skip_dash(pdev, bottom - top + 1);
877     }
878     else
879     {
880         bres_params params;
881         INT dx = end->x - start->x;
882         INT dy = end->y - start->y;
883         INT i;
884
885         params.dx = abs(dx);
886         params.dy = abs(dy);
887         params.octant = get_octant_mask(dx, dy);
888         /* Octants 3, 5, 6 and 8 take a bias */
889         params.bias = (params.octant & 0xb4) ? 1 : 0;
890
891         for(i = 0; i < clip->numRects; i++)
892         {
893             POINT clipped_start, clipped_end;
894             int clip_status;
895             clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
896
897             if(clip_status)
898             {
899                 int m = abs(clipped_start.x - start->x);
900                 int n = abs(clipped_start.y - start->y);
901                 int err;
902                 BOOL last_pt = FALSE;
903
904                 pdev->dash_pos = start_pos;
905
906                 if(is_xmajor(params.octant))
907                 {
908                     err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
909                     skip_dash(pdev, m);
910                 }
911                 else
912                 {
913                     err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
914                     skip_dash(pdev, n);
915                 }
916                 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
917
918                 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, &params,
919                                     err, last_pt, dashed_pen_line_callback, pdev);
920
921                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
922             }
923         }
924         pdev->dash_pos = start_pos;
925         if(is_xmajor(params.octant))
926             skip_dash(pdev, params.dx);
927         else
928             skip_dash(pdev, params.dy);
929     }
930
931     release_wine_region(pdev->clip);
932     return TRUE;
933 }
934
935 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
936 {
937     int i;
938
939     assert( num >= 2 );
940     for (i = 0; i < num - 1; i++)
941         if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
942             return FALSE;
943
944     return TRUE;
945 }
946
947 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
948 {
949     return TRUE;
950 }
951
952 static const dash_pattern dash_patterns[5] =
953 {
954     {0, {0}, 0},                  /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
955     {2, {18, 6}, 24},             /* PS_DASH */
956     {2, {3,  3}, 6},              /* PS_DOT */
957     {4, {9, 6, 3, 6}, 24},        /* PS_DASHDOT */
958     {6, {9, 3, 3, 3, 3, 3}, 24}   /* PS_DASHDOTDOT */
959 };
960
961 /***********************************************************************
962  *           dibdrv_SelectPen
963  */
964 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
965 {
966     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
967     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
968     LOGPEN logpen;
969     DWORD style;
970
971     TRACE("(%p, %p)\n", dev, hpen);
972
973     if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
974     {
975         /* must be an extended pen */
976         EXTLOGPEN *elp;
977         INT size = GetObjectW( hpen, 0, NULL );
978
979         if (!size) return 0;
980
981         elp = HeapAlloc( GetProcessHeap(), 0, size );
982
983         GetObjectW( hpen, size, elp );
984         /* FIXME: add support for user style pens */
985         logpen.lopnStyle = elp->elpPenStyle;
986         logpen.lopnWidth.x = elp->elpWidth;
987         logpen.lopnWidth.y = 0;
988         logpen.lopnColor = elp->elpColor;
989
990         HeapFree( GetProcessHeap(), 0, elp );
991     }
992
993     if (hpen == GetStockObject( DC_PEN ))
994         logpen.lopnColor = GetDCPenColor( dev->hdc );
995
996     pdev->pen_colorref = logpen.lopnColor;
997     pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
998     calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
999
1000     pdev->pen_pattern = dash_patterns[PS_SOLID];
1001
1002     pdev->defer |= DEFER_PEN;
1003
1004     style = logpen.lopnStyle & PS_STYLE_MASK;
1005
1006     switch(style)
1007     {
1008     case PS_SOLID:
1009         if(logpen.lopnStyle & PS_GEOMETRIC) break;
1010         if(logpen.lopnWidth.x > 1) break;
1011         pdev->pen_lines = solid_pen_lines;
1012         pdev->defer &= ~DEFER_PEN;
1013         break;
1014
1015     case PS_DASH:
1016     case PS_DOT:
1017     case PS_DASHDOT:
1018     case PS_DASHDOTDOT:
1019         if(logpen.lopnStyle & PS_GEOMETRIC) break;
1020         if(logpen.lopnWidth.x > 1) break;
1021         pdev->pen_lines = dashed_pen_lines;
1022         pdev->pen_pattern = dash_patterns[style];
1023         pdev->defer &= ~DEFER_PEN;
1024         break;
1025
1026     case PS_NULL:
1027         pdev->pen_lines = null_pen_lines;
1028         pdev->defer &= ~DEFER_PEN;
1029         break;
1030
1031     default:
1032         break;
1033     }
1034
1035     return next->funcs->pSelectPen( next, hpen );
1036 }
1037
1038 /***********************************************************************
1039  *           dibdrv_SetDCPenColor
1040  */
1041 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1042 {
1043     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
1044     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1045
1046     if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1047     {
1048         pdev->pen_colorref = color;
1049         pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
1050         calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1051     }
1052
1053     return next->funcs->pSetDCPenColor( next, color );
1054 }
1055
1056 /**********************************************************************
1057  *             solid_brush
1058  *
1059  * Fill a number of rectangles with the solid brush
1060  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1061  */
1062 static BOOL solid_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1063 {
1064     int i, j;
1065     const WINEREGION *clip = get_wine_region(pdev->clip);
1066
1067     for(i = 0; i < num; i++)
1068     {
1069         for(j = 0; j < clip->numRects; j++)
1070         {
1071             RECT rect = rects[i];
1072
1073             /* Optimize unclipped case */
1074             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1075                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1076             {
1077                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
1078                 break;
1079             }
1080
1081             if(clip->rects[j].top >= rect.bottom) break;
1082             if(clip->rects[j].bottom <= rect.top) continue;
1083
1084             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1085             {
1086                 rect.left   = max(rect.left,   clip->rects[j].left);
1087                 rect.top    = max(rect.top,    clip->rects[j].top);
1088                 rect.right  = min(rect.right,  clip->rects[j].right);
1089                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1090
1091                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
1092             }
1093         }
1094     }
1095     release_wine_region(pdev->clip);
1096     return TRUE;
1097 }
1098
1099 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
1100 {
1101     HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
1102     HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
1103     pdev->brush_and_bits = NULL;
1104     pdev->brush_xor_bits = NULL;
1105 }
1106
1107 void free_pattern_brush( dibdrv_physdev *pdev )
1108 {
1109     free_pattern_brush_bits( pdev );
1110     free_dib_info( &pdev->brush_dib );
1111 }
1112
1113 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
1114 {
1115     DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
1116     DWORD *brush_bits = pdev->brush_dib.bits.ptr;
1117     DWORD *and_bits, *xor_bits;
1118
1119     assert(pdev->brush_and_bits == NULL);
1120     assert(pdev->brush_xor_bits == NULL);
1121
1122     assert(pdev->brush_dib.stride > 0);
1123
1124     and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1125     xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1126
1127     if(!and_bits || !xor_bits)
1128     {
1129         ERR("Failed to create pattern brush bits\n");
1130         free_pattern_brush_bits( pdev );
1131         return FALSE;
1132     }
1133
1134     while(size)
1135     {
1136         calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1137         size -= 4;
1138     }
1139
1140     return TRUE;
1141 }
1142
1143 static const DWORD hatches[6][8] =
1144 {
1145     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1146     { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL   */
1147     { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL  */
1148     { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL  */
1149     { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS      */
1150     { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }  /* HS_DIAGCROSS  */
1151 };
1152
1153 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev)
1154 {
1155     dib_info hatch;
1156     rop_mask fg_mask, bg_mask;
1157     rop_mask_bits mask_bits;
1158     DWORD size;
1159     BOOL ret;
1160
1161     assert(pdev->brush_and_bits == NULL);
1162     assert(pdev->brush_xor_bits == NULL);
1163
1164     /* Just initialise brush_dib with the color / sizing info.  We don't
1165        need the bits as we'll calculate the rop masks straight from
1166        the hatch patterns. */
1167
1168     copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1169     pdev->brush_dib.width  = 8;
1170     pdev->brush_dib.height = 8;
1171     pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1172
1173     size = pdev->brush_dib.height * pdev->brush_dib.stride;
1174
1175     mask_bits.and = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1176     mask_bits.xor = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1177
1178     if(!mask_bits.and || !mask_bits.xor)
1179     {
1180         ERR("Failed to create pattern brush bits\n");
1181         free_pattern_brush_bits( pdev );
1182         return FALSE;
1183     }
1184
1185     hatch.bit_count = 1;
1186     hatch.height = hatch.width = 8;
1187     hatch.stride = 4;
1188     hatch.bits.ptr = (void *) hatches[pdev->brush_hatch];
1189     hatch.bits.free = hatch.bits.param = NULL;
1190     hatch.bits.is_copy = FALSE;
1191
1192     fg_mask.and = pdev->brush_and;
1193     fg_mask.xor = pdev->brush_xor;
1194     get_brush_bkgnd_masks( pdev, &bg_mask.and, &bg_mask.xor );
1195
1196     ret = pdev->brush_dib.funcs->create_rop_masks( &pdev->brush_dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1197     if(!ret) free_pattern_brush_bits( pdev );
1198
1199     return ret;
1200 }
1201
1202 /**********************************************************************
1203  *             pattern_brush
1204  *
1205  * Fill a number of rectangles with the pattern brush
1206  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1207  */
1208 static BOOL pattern_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1209 {
1210     int i, j;
1211     const WINEREGION *clip;
1212     POINT origin;
1213
1214     if(pdev->brush_and_bits == NULL)
1215     {
1216         switch(pdev->brush_style)
1217         {
1218         case BS_DIBPATTERN:
1219             if(!create_pattern_brush_bits(pdev))
1220                 return FALSE;
1221             break;
1222
1223         case BS_HATCHED:
1224             if(!create_hatch_brush_bits(pdev))
1225                 return FALSE;
1226             break;
1227
1228         default:
1229             ERR("Unexpected brush style %d\n", pdev->brush_style);
1230             return FALSE;
1231         }
1232     }
1233
1234     GetBrushOrgEx(pdev->dev.hdc, &origin);
1235
1236     clip = get_wine_region(pdev->clip);
1237     for(i = 0; i < num; i++)
1238     {
1239         for(j = 0; j < clip->numRects; j++)
1240         {
1241             RECT rect = rects[i];
1242
1243             /* Optimize unclipped case */
1244             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1245                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1246             {
1247                 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1248                 break;
1249             }
1250
1251             if(clip->rects[j].top >= rect.bottom) break;
1252             if(clip->rects[j].bottom <= rect.top) continue;
1253
1254             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1255             {
1256                 rect.left   = max(rect.left,   clip->rects[j].left);
1257                 rect.top    = max(rect.top,    clip->rects[j].top);
1258                 rect.right  = min(rect.right,  clip->rects[j].right);
1259                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1260
1261                 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1262             }
1263         }
1264     }
1265     release_wine_region(pdev->clip);
1266     return TRUE;
1267 }
1268
1269 static BOOL null_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1270 {
1271     return TRUE;
1272 }
1273
1274 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1275 {
1276     pdev->brush_rop = rop;
1277     if(pdev->brush_style == BS_SOLID || pdev->brush_style == BS_HATCHED)
1278         calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1279     free_pattern_brush_bits( pdev );
1280 }
1281
1282 /***********************************************************************
1283  *           dibdrv_SelectBrush
1284  */
1285 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
1286 {
1287     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1288     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1289     LOGBRUSH logbrush;
1290
1291     TRACE("(%p, %p)\n", dev, hbrush);
1292
1293     if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1294
1295     if (hbrush == GetStockObject( DC_BRUSH ))
1296         logbrush.lbColor = GetDCBrushColor( dev->hdc );
1297
1298     pdev->brush_style = logbrush.lbStyle;
1299
1300     pdev->defer |= DEFER_BRUSH;
1301
1302     free_pattern_brush( pdev );
1303
1304     switch(logbrush.lbStyle)
1305     {
1306     case BS_SOLID:
1307         pdev->brush_colorref = logbrush.lbColor;
1308         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1309         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1310         pdev->brush_rects = solid_brush;
1311         pdev->defer &= ~DEFER_BRUSH;
1312         break;
1313
1314     case BS_NULL:
1315         pdev->brush_rects = null_brush;
1316         pdev->defer &= ~DEFER_BRUSH;
1317         break;
1318
1319     case BS_DIBPATTERN:
1320     {
1321         BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1322         dib_info orig_dib;
1323         WORD usage = LOWORD(logbrush.lbColor);
1324         HPALETTE pal = (usage == DIB_PAL_COLORS) ? GetCurrentObject(dev->hdc, OBJ_PAL) : NULL;
1325         RECT rect;
1326
1327         if(!bi) return NULL;
1328         if(init_dib_info_from_packed(&orig_dib, bi, usage, pal))
1329         {
1330             copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1331
1332             pdev->brush_dib.height = orig_dib.height;
1333             pdev->brush_dib.width  = orig_dib.width;
1334             pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1335
1336             pdev->brush_dib.bits.param   = NULL;
1337             pdev->brush_dib.bits.ptr     = HeapAlloc( GetProcessHeap(), 0,
1338                                                       pdev->brush_dib.height * pdev->brush_dib.stride );
1339             pdev->brush_dib.bits.is_copy = TRUE;
1340             pdev->brush_dib.bits.free    = free_heap_bits;
1341
1342             rect.left = rect.top = 0;
1343             rect.right = orig_dib.width;
1344             rect.bottom = orig_dib.height;
1345
1346             if(pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &orig_dib, &rect))
1347             {
1348                 pdev->brush_rects = pattern_brush;
1349                 pdev->defer &= ~DEFER_BRUSH;
1350             }
1351             else
1352                 free_dib_info(&pdev->brush_dib);
1353             free_dib_info(&orig_dib);
1354         }
1355         GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1356         break;
1357     }
1358
1359     case BS_HATCHED:
1360     {
1361         if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1362         pdev->brush_hatch = logbrush.lbHatch;
1363         pdev->brush_colorref = logbrush.lbColor;
1364         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1365         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1366         pdev->brush_rects = pattern_brush;
1367         pdev->defer &= ~DEFER_BRUSH;
1368         break;
1369     }
1370
1371     default:
1372         break;
1373     }
1374
1375     return next->funcs->pSelectBrush( next, hbrush );
1376 }
1377
1378 /***********************************************************************
1379  *           dibdrv_SetDCBrushColor
1380  */
1381 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1382 {
1383     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1384     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1385
1386     if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1387     {
1388         pdev->brush_colorref = color;
1389         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1390         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1391     }
1392
1393     return next->funcs->pSetDCBrushColor( next, color );
1394 }