kernel32/tests: Better check the NT path returned by QueryFullProcessImageName().
[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 /******************************************************************************
292  *                clip_line
293  *
294  * Clips the start and end points to a rectangle.
295  *
296  * Note, this treats the end point like the start point.  If the
297  * caller doesn't want it displayed, it should exclude it.  If the end
298  * point is clipped out, then the likelihood is that the new end point
299  * should be displayed.
300  *
301  * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
302  *
303  * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
304  * however the Bresenham error term is defined differently so the equations
305  * will also differ.
306  *
307  * For x major lines we have 2dy >= err + bias > 2dy - 2dx
308  *                           0   >= err + bias - 2dy > -2dx
309  *
310  * Note dx, dy, m and n are all +ve.
311  *
312  * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
313  *                     err = 2dy - dx + 2mdy - 2ndx
314  *                      0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
315  *                      0 >= 2mdy - 2ndx + bias - dx > -2dx
316  *                      which of course will give exactly one solution for n,
317  *                      so looking at the >= inequality
318  *                      n >= (2mdy + bias - dx) / 2dx
319  *                      n = ceiling((2mdy + bias - dx) / 2dx)
320  *                        = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
321  *
322  * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
323  * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
324  *                     0 >= 2mdy - 2ndx + bias - dx > -2dx
325  *                  2mdy > 2ndx - bias - dx
326  *                     m > (2ndx - bias - dx) / 2dy
327  *                     m = floor((2ndx - bias - dx) / 2dy) + 1
328  *                     m = (2ndx - bias - dx) / 2dy + 1
329  *
330  * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
331  *                  err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
332  *                      = 2dy - dx - 2mdy + 2ndx
333  *                   0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
334  *                   0 >= 2ndx - 2mdy + bias - dx > -2dx
335  *                   again exactly one solution.
336  *                   2ndx <= 2mdy - bias + dx
337  *                   n = floor((2mdy - bias + dx) / 2dx)
338  *                     = (2mdy - bias + dx) / 2dx
339  *
340  * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
341  * mininizing m to include all of the points at y = y2 - n.  As above:
342  *                  0 >= 2ndx - 2mdy + bias - dx > -2dx
343  *               2mdy >= 2ndx + bias - dx
344  *                   m = ceiling((2ndx + bias - dx) / 2dy)
345  *                     = (2ndx + bias - dx - 1) / 2dy + 1
346  *
347  * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
348  *
349  * Moving start point from y1 to y1 + n find x1 + m
350  *                     m = (2ndx + bias + dy - 1) / 2dy
351  *
352  * Moving start point from x1 to x1 + m find y1 + n
353  *                     n = (2mdy - bias - dy) / 2ndx + 1
354  *
355  * Moving end point from y2 to y2 - n find x1 - m
356  *                     m = (2ndx - bias + dy) / 2dy
357  *
358  * Moving end point from x2 to x2 - m find y2 - n
359  *                     n = (2mdy + bias - dy - 1) / 2dx + 1
360  */
361 int clip_line(const POINT *start, const POINT *end, const RECT *clip,
362               const bres_params *params, POINT *pt1, POINT *pt2)
363 {
364     int m, n;
365     BOOL clipped = FALSE;
366     DWORD start_oc, end_oc;
367     const int bias = params->bias;
368     const unsigned int dx = params->dx;
369     const unsigned int dy = params->dy;
370     const unsigned int two_dx = params->dx * 2;
371     const unsigned int two_dy = params->dy * 2;
372     const BOOL xmajor = is_xmajor(params->octant);
373     const BOOL neg_slope = !is_pos_slope(params->octant);
374
375     *pt1 = *start;
376     *pt2 = *end;
377
378     start_oc = calc_outcode(start, clip);
379     end_oc = calc_outcode(end, clip);
380
381     while(1)
382     {
383         if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
384         if(start_oc & end_oc)            return 0; /* trivial reject */
385
386         clipped = TRUE;
387         if(start_oc & OUT_LEFT)
388         {
389             m = clip->left - start->x;
390             if(xmajor)
391                 n = (m * two_dy + bias + dx - 1) / two_dx;
392             else
393                 n = (m * two_dy - bias - dy) / two_dx + 1;
394
395             pt1->x = clip->left;
396             if(neg_slope) n = -n;
397             pt1->y = start->y + n;
398             start_oc = calc_outcode(pt1, clip);
399         }
400         else if(start_oc & OUT_RIGHT)
401         {
402             m = start->x - clip->right + 1;
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->right - 1;
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_TOP)
414         {
415             n = clip->top - start->y;
416             if(xmajor)
417                 m = (n * two_dx - bias - dx) / two_dy + 1;
418             else
419                 m = (n * two_dx + bias + dy - 1) / two_dy;
420
421             pt1->y = clip->top;
422             if(neg_slope) m = -m;
423             pt1->x = start->x + m;
424             start_oc = calc_outcode(pt1, clip);
425         }
426         else if(start_oc & OUT_BOTTOM)
427         {
428             n = start->y - clip->bottom + 1;
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->bottom - 1;
435             if(neg_slope) m = -m;
436             pt1->x = start->x - m;
437             start_oc = calc_outcode(pt1, clip);
438         }
439         else if(end_oc & OUT_LEFT)
440         {
441             m = clip->left - end->x;
442             if(xmajor)
443                 n = (m * two_dy - bias + dx) / two_dx;
444             else
445                 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
446
447             pt2->x = clip->left;
448             if(neg_slope) n = -n;
449             pt2->y = end->y + n;
450             end_oc = calc_outcode(pt2, clip);
451         }
452         else if(end_oc & OUT_RIGHT)
453         {
454             m = end->x - clip->right + 1;
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->right - 1;
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_TOP)
466         {
467             n = clip->top - end->y;
468             if(xmajor)
469                 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
470             else
471                 m = (n * two_dx - bias + dy) / two_dy;
472
473             pt2->y = clip->top;
474             if(neg_slope) m = -m;
475             pt2->x = end->x + m;
476             end_oc = calc_outcode(pt2, clip);
477         }
478         else if(end_oc & OUT_BOTTOM)
479         {
480             n = end->y - clip->bottom + 1;
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->bottom - 1;
487             if(neg_slope) m = -m;
488             pt2->x = end->x - m;
489             end_oc = calc_outcode(pt2, clip);
490         }
491     }
492 }
493
494 static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, const bres_params *params, INT err,
495                                 BOOL last_pt, void (* callback)(dibdrv_physdev*,INT,INT), dibdrv_physdev *pdev)
496 {
497     const int xadd = is_x_increasing(params->octant) ? 1 : -1;
498     const int yadd = is_y_increasing(params->octant) ? 1 : -1;
499     INT erradd;
500
501     if (is_xmajor(params->octant))  /* line is "more horizontal" */
502     {
503         erradd = 2*params->dy - 2*params->dx;
504         while(x1 != x2)
505         {
506             callback(pdev, x1, y1);
507             if (err + params->bias > 0)
508             {
509                 y1 += yadd;
510                 err += erradd;
511             }
512             else err += 2*params->dy;
513             x1 += xadd;
514         }
515         if(last_pt) callback(pdev, x1, y1);
516     }
517     else   /* line is "more vertical" */
518     {
519         erradd = 2*params->dx - 2*params->dy;
520         while(y1 != y2)
521         {
522             callback(pdev, x1, y1);
523             if (err + params->bias > 0)
524             {
525                 x1 += xadd;
526                 err += erradd;
527             }
528             else err += 2*params->dx;
529             y1 += yadd;
530         }
531         if(last_pt) callback(pdev, x1, y1);
532     }
533 }
534
535 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
536 {
537     const WINEREGION *clip = get_wine_region(pdev->clip);
538
539     if(start->y == end->y)
540     {
541         RECT rect;
542         int i;
543
544         rect.left   = start->x;
545         rect.top    = start->y;
546         rect.right  = end->x;
547         rect.bottom = end->y + 1;
548         order_end_points(&rect.left, &rect.right);
549         for(i = 0; i < clip->numRects; i++)
550         {
551             if(clip->rects[i].top >= rect.bottom) break;
552             if(clip->rects[i].bottom <= rect.top) continue;
553             /* Optimize the unclipped case */
554             if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
555             {
556                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
557                 break;
558             }
559             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
560             {
561                 RECT tmp = rect;
562                 tmp.left = max(rect.left, clip->rects[i].left);
563                 tmp.right = min(rect.right, clip->rects[i].right);
564                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
565             }
566         }
567     }
568     else if(start->x == end->x)
569     {
570         RECT rect;
571         int i;
572
573         rect.left   = start->x;
574         rect.top    = start->y;
575         rect.right  = end->x + 1;
576         rect.bottom = end->y;
577         order_end_points(&rect.top, &rect.bottom);
578         for(i = 0; i < clip->numRects; i++)
579         {
580             /* Optimize unclipped case */
581             if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
582                clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
583             {
584                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
585                 break;
586             }
587             if(clip->rects[i].top >= rect.bottom) break;
588             if(clip->rects[i].bottom <= rect.top) continue;
589             if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
590             {
591                 RECT tmp = rect;
592                 tmp.top = max(rect.top, clip->rects[i].top);
593                 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
594                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
595             }
596         }
597     }
598     else
599     {
600         bres_params params;
601         INT dx = end->x - start->x;
602         INT dy = end->y - start->y;
603         INT i;
604
605         params.dx = abs(dx);
606         params.dy = abs(dy);
607         params.octant = get_octant_mask(dx, dy);
608         /* Octants 3, 5, 6 and 8 take a bias */
609         params.bias = (params.octant & 0xb4) ? 1 : 0;
610
611         for(i = 0; i < clip->numRects; i++)
612         {
613             POINT clipped_start, clipped_end;
614             int clip_status;
615             clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
616
617             if(clip_status)
618             {
619                 int m = abs(clipped_start.x - start->x);
620                 int n = abs(clipped_start.y - start->y);
621                 int err;
622                 BOOL last_pt = FALSE;
623
624                 if(is_xmajor(params.octant))
625                     err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
626                 else
627                     err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
628
629                 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
630
631                 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, &params,
632                                     err, last_pt, solid_pen_line_callback, pdev);
633
634                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
635             }
636         }
637
638     }
639     release_wine_region(pdev->clip);
640     return TRUE;
641 }
642
643 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
644 {
645     int i;
646
647     assert( num >= 2 );
648     for (i = 0; i < num - 1; i++)
649         if (!solid_pen_line( pdev, pts + i, pts + i + 1 ))
650             return FALSE;
651
652     return TRUE;
653 }
654
655 void reset_dash_origin(dibdrv_physdev *pdev)
656 {
657     pdev->dash_pos.cur_dash = 0;
658     pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
659     pdev->dash_pos.mark = TRUE;
660 }
661
662 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
663 {
664     skip %= pdev->pen_pattern.total_len;
665     while(skip)
666     {
667         if(pdev->dash_pos.left_in_dash > skip)
668         {
669             pdev->dash_pos.left_in_dash -= skip;
670             return;
671         }
672         skip -= pdev->dash_pos.left_in_dash;
673         pdev->dash_pos.cur_dash++;
674         if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
675         pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
676         pdev->dash_pos.mark = !pdev->dash_pos.mark;
677     }
678 }
679
680 static inline void get_dash_colors(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
681 {
682     if(pdev->dash_pos.mark)
683     {
684         *and = pdev->pen_and;
685         *xor = pdev->pen_xor;
686     }
687     else /* space */
688     {
689         get_pen_bkgnd_masks( pdev, and, xor );
690     }
691 }
692
693 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
694 {
695     RECT rect;
696     DWORD and, xor;
697
698     get_dash_colors(pdev, &and, &xor);
699     skip_dash(pdev, 1);
700     rect.left   = x;
701     rect.right  = x + 1;
702     rect.top    = y;
703     rect.bottom = y + 1;
704     pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
705     return;
706 }
707
708 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
709 {
710     const WINEREGION *clip = get_wine_region(pdev->clip);
711     DWORD and, xor;
712     int i, dash_len;
713     RECT rect;
714     const dash_pos start_pos = pdev->dash_pos;
715
716     if(start->y == end->y) /* hline */
717     {
718         BOOL l_to_r;
719         INT left, right, cur_x;
720
721         rect.top = start->y;
722         rect.bottom = start->y + 1;
723
724         if(start->x <= end->x)
725         {
726             left = start->x;
727             right = end->x - 1;
728             l_to_r = TRUE;
729         }
730         else
731         {
732             left = end->x + 1;
733             right = start->x;
734             l_to_r = FALSE;
735         }
736
737         for(i = 0; i < clip->numRects; i++)
738         {
739             if(clip->rects[i].top > start->y) break;
740             if(clip->rects[i].bottom <= start->y) continue;
741
742             if(clip->rects[i].right > left && clip->rects[i].left <= right)
743             {
744                 int clipped_left  = max(clip->rects[i].left, left);
745                 int clipped_right = min(clip->rects[i].right - 1, right);
746
747                 pdev->dash_pos = start_pos;
748
749                 if(l_to_r)
750                 {
751                     cur_x = clipped_left;
752                     if(cur_x != left)
753                         skip_dash(pdev, clipped_left - left);
754
755                     while(cur_x <= clipped_right)
756                     {
757                         get_dash_colors(pdev, &and, &xor);
758                         dash_len = pdev->dash_pos.left_in_dash;
759                         if(cur_x + dash_len > clipped_right + 1)
760                             dash_len = clipped_right - cur_x + 1;
761                         rect.left = cur_x;
762                         rect.right = cur_x + dash_len;
763
764                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
765                         cur_x += dash_len;
766                         skip_dash(pdev, dash_len);
767                     }
768                 }
769                 else
770                 {
771                     cur_x = clipped_right;
772                     if(cur_x != right)
773                         skip_dash(pdev, right - clipped_right);
774
775                     while(cur_x >= clipped_left)
776                     {
777                         get_dash_colors(pdev, &and, &xor);
778                         dash_len = pdev->dash_pos.left_in_dash;
779                         if(cur_x - dash_len < clipped_left - 1)
780                             dash_len = cur_x - clipped_left + 1;
781                         rect.left = cur_x - dash_len + 1;
782                         rect.right = cur_x + 1;
783
784                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
785                         cur_x -= dash_len;
786                         skip_dash(pdev, dash_len);
787                     }
788                 }
789             }
790         }
791         pdev->dash_pos = start_pos;
792         skip_dash(pdev, right - left + 1);
793     }
794     else if(start->x == end->x) /* vline */
795     {
796         BOOL t_to_b;
797         INT top, bottom, cur_y;
798
799         rect.left = start->x;
800         rect.right = start->x + 1;
801
802         if(start->y <= end->y)
803         {
804             top = start->y;
805             bottom = end->y - 1;
806             t_to_b = TRUE;
807         }
808         else
809         {
810             top = end->y + 1;
811             bottom = start->y;
812             t_to_b = FALSE;
813         }
814
815         for(i = 0; i < clip->numRects; i++)
816         {
817             if(clip->rects[i].top > bottom) break;
818             if(clip->rects[i].bottom <= top) continue;
819             if(clip->rects[i].right > start->x && clip->rects[i].left <= start->x)
820             {
821                 int clipped_top    = max(clip->rects[i].top, top);
822                 int clipped_bottom = min(clip->rects[i].bottom - 1, bottom);
823
824                 pdev->dash_pos = start_pos;
825
826                 if(t_to_b)
827                 {
828                     cur_y = clipped_top;
829                     if(cur_y != top)
830                         skip_dash(pdev, clipped_top - top);
831
832                     while(cur_y <= clipped_bottom)
833                     {
834                         get_dash_colors(pdev, &and, &xor);
835                         dash_len = pdev->dash_pos.left_in_dash;
836                         if(cur_y + dash_len > clipped_bottom + 1)
837                             dash_len = clipped_bottom - cur_y + 1;
838                         rect.top = cur_y;
839                         rect.bottom = cur_y + dash_len;
840
841                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
842                         cur_y += dash_len;
843                         skip_dash(pdev, dash_len);
844                     }
845                 }
846                 else
847                 {
848                     cur_y = clipped_bottom;
849                     if(cur_y != bottom)
850                         skip_dash(pdev, bottom - clipped_bottom);
851
852                     while(cur_y >= clipped_top)
853                     {
854                         get_dash_colors(pdev, &and, &xor);
855                         dash_len = pdev->dash_pos.left_in_dash;
856                         if(cur_y - dash_len < clipped_top - 1)
857                             dash_len = cur_y - clipped_top + 1;
858                         rect.top = cur_y - dash_len + 1;
859                         rect.bottom = cur_y + 1;
860
861                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
862                         cur_y -= dash_len;
863                         skip_dash(pdev, dash_len);
864                     }
865                 }
866             }
867         }
868         pdev->dash_pos = start_pos;
869         skip_dash(pdev, bottom - top + 1);
870     }
871     else
872     {
873         bres_params params;
874         INT dx = end->x - start->x;
875         INT dy = end->y - start->y;
876         INT i;
877
878         params.dx = abs(dx);
879         params.dy = abs(dy);
880         params.octant = get_octant_mask(dx, dy);
881         /* Octants 3, 5, 6 and 8 take a bias */
882         params.bias = (params.octant & 0xb4) ? 1 : 0;
883
884         for(i = 0; i < clip->numRects; i++)
885         {
886             POINT clipped_start, clipped_end;
887             int clip_status;
888             clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
889
890             if(clip_status)
891             {
892                 int m = abs(clipped_start.x - start->x);
893                 int n = abs(clipped_start.y - start->y);
894                 int err;
895                 BOOL last_pt = FALSE;
896
897                 pdev->dash_pos = start_pos;
898
899                 if(is_xmajor(params.octant))
900                 {
901                     err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
902                     skip_dash(pdev, m);
903                 }
904                 else
905                 {
906                     err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
907                     skip_dash(pdev, n);
908                 }
909                 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
910
911                 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, &params,
912                                     err, last_pt, dashed_pen_line_callback, pdev);
913
914                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
915             }
916         }
917         pdev->dash_pos = start_pos;
918         if(is_xmajor(params.octant))
919             skip_dash(pdev, params.dx);
920         else
921             skip_dash(pdev, params.dy);
922     }
923
924     release_wine_region(pdev->clip);
925     return TRUE;
926 }
927
928 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
929 {
930     int i;
931
932     assert( num >= 2 );
933     for (i = 0; i < num - 1; i++)
934         if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
935             return FALSE;
936
937     return TRUE;
938 }
939
940 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
941 {
942     return TRUE;
943 }
944
945 static const dash_pattern dash_patterns[5] =
946 {
947     {0, {0}, 0},                  /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
948     {2, {18, 6}, 24},             /* PS_DASH */
949     {2, {3,  3}, 6},              /* PS_DOT */
950     {4, {9, 6, 3, 6}, 24},        /* PS_DASHDOT */
951     {6, {9, 3, 3, 3, 3, 3}, 24}   /* PS_DASHDOTDOT */
952 };
953
954 /***********************************************************************
955  *           dibdrv_SelectPen
956  */
957 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
958 {
959     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
960     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
961     LOGPEN logpen;
962     DWORD style;
963
964     TRACE("(%p, %p)\n", dev, hpen);
965
966     if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
967     {
968         /* must be an extended pen */
969         EXTLOGPEN *elp;
970         INT size = GetObjectW( hpen, 0, NULL );
971
972         if (!size) return 0;
973
974         elp = HeapAlloc( GetProcessHeap(), 0, size );
975
976         GetObjectW( hpen, size, elp );
977         /* FIXME: add support for user style pens */
978         logpen.lopnStyle = elp->elpPenStyle;
979         logpen.lopnWidth.x = elp->elpWidth;
980         logpen.lopnWidth.y = 0;
981         logpen.lopnColor = elp->elpColor;
982
983         HeapFree( GetProcessHeap(), 0, elp );
984     }
985
986     if (hpen == GetStockObject( DC_PEN ))
987         logpen.lopnColor = GetDCPenColor( dev->hdc );
988
989     pdev->pen_colorref = logpen.lopnColor;
990     pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
991     calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
992
993     pdev->pen_pattern = dash_patterns[PS_SOLID];
994
995     pdev->defer |= DEFER_PEN;
996
997     style = logpen.lopnStyle & PS_STYLE_MASK;
998
999     switch(style)
1000     {
1001     case PS_SOLID:
1002         if(logpen.lopnStyle & PS_GEOMETRIC) break;
1003         if(logpen.lopnWidth.x > 1) break;
1004         pdev->pen_lines = solid_pen_lines;
1005         pdev->defer &= ~DEFER_PEN;
1006         break;
1007
1008     case PS_DASH:
1009     case PS_DOT:
1010     case PS_DASHDOT:
1011     case PS_DASHDOTDOT:
1012         if(logpen.lopnStyle & PS_GEOMETRIC) break;
1013         if(logpen.lopnWidth.x > 1) break;
1014         pdev->pen_lines = dashed_pen_lines;
1015         pdev->pen_pattern = dash_patterns[style];
1016         pdev->defer &= ~DEFER_PEN;
1017         break;
1018
1019     case PS_NULL:
1020         pdev->pen_lines = null_pen_lines;
1021         pdev->defer &= ~DEFER_PEN;
1022         break;
1023
1024     default:
1025         break;
1026     }
1027
1028     return next->funcs->pSelectPen( next, hpen );
1029 }
1030
1031 /***********************************************************************
1032  *           dibdrv_SetDCPenColor
1033  */
1034 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1035 {
1036     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
1037     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1038
1039     if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1040     {
1041         pdev->pen_colorref = color;
1042         pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
1043         calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1044     }
1045
1046     return next->funcs->pSetDCPenColor( next, color );
1047 }
1048
1049 /**********************************************************************
1050  *             solid_brush
1051  *
1052  * Fill a number of rectangles with the solid brush
1053  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1054  */
1055 static BOOL solid_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1056 {
1057     int i, j;
1058     const WINEREGION *clip = get_wine_region( region );
1059
1060     if (!clip)
1061     {
1062         dib->funcs->solid_rects( dib, num, rects, pdev->brush_and, pdev->brush_xor );
1063         return TRUE;
1064     }
1065
1066     for(i = 0; i < num; i++)
1067     {
1068         for(j = 0; j < clip->numRects; j++)
1069         {
1070             RECT rect = rects[i];
1071
1072             /* Optimize unclipped case */
1073             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1074                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1075             {
1076                 dib->funcs->solid_rects( dib, 1, &rect, pdev->brush_and, pdev->brush_xor );
1077                 break;
1078             }
1079
1080             if(clip->rects[j].top >= rect.bottom) break;
1081             if(clip->rects[j].bottom <= rect.top) continue;
1082
1083             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1084             {
1085                 rect.left   = max(rect.left,   clip->rects[j].left);
1086                 rect.top    = max(rect.top,    clip->rects[j].top);
1087                 rect.right  = min(rect.right,  clip->rects[j].right);
1088                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1089
1090                 dib->funcs->solid_rects( dib, 1, &rect, pdev->brush_and, pdev->brush_xor );
1091             }
1092         }
1093     }
1094     release_wine_region( region );
1095     return TRUE;
1096 }
1097
1098 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
1099 {
1100     HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
1101     HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
1102     pdev->brush_and_bits = NULL;
1103     pdev->brush_xor_bits = NULL;
1104 }
1105
1106 void free_pattern_brush( dibdrv_physdev *pdev )
1107 {
1108     free_pattern_brush_bits( pdev );
1109     free_dib_info( &pdev->brush_dib );
1110 }
1111
1112 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
1113 {
1114     DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
1115     DWORD *brush_bits = pdev->brush_dib.bits.ptr;
1116     DWORD *and_bits, *xor_bits;
1117
1118     assert(pdev->brush_and_bits == NULL);
1119     assert(pdev->brush_xor_bits == NULL);
1120
1121     assert(pdev->brush_dib.stride > 0);
1122
1123     and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1124     xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1125
1126     if(!and_bits || !xor_bits)
1127     {
1128         ERR("Failed to create pattern brush bits\n");
1129         free_pattern_brush_bits( pdev );
1130         return FALSE;
1131     }
1132
1133     while(size)
1134     {
1135         calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1136         size -= 4;
1137     }
1138
1139     return TRUE;
1140 }
1141
1142 static const DWORD hatches[6][8] =
1143 {
1144     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1145     { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL   */
1146     { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL  */
1147     { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL  */
1148     { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS      */
1149     { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }  /* HS_DIAGCROSS  */
1150 };
1151
1152 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev)
1153 {
1154     dib_info hatch;
1155     rop_mask fg_mask, bg_mask;
1156     rop_mask_bits mask_bits;
1157     DWORD size;
1158     BOOL ret;
1159
1160     assert(pdev->brush_and_bits == NULL);
1161     assert(pdev->brush_xor_bits == NULL);
1162
1163     /* Just initialise brush_dib with the color / sizing info.  We don't
1164        need the bits as we'll calculate the rop masks straight from
1165        the hatch patterns. */
1166
1167     copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1168     pdev->brush_dib.width  = 8;
1169     pdev->brush_dib.height = 8;
1170     pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1171
1172     size = pdev->brush_dib.height * pdev->brush_dib.stride;
1173
1174     mask_bits.and = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1175     mask_bits.xor = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1176
1177     if(!mask_bits.and || !mask_bits.xor)
1178     {
1179         ERR("Failed to create pattern brush bits\n");
1180         free_pattern_brush_bits( pdev );
1181         return FALSE;
1182     }
1183
1184     hatch.bit_count = 1;
1185     hatch.height = hatch.width = 8;
1186     hatch.stride = 4;
1187     hatch.bits.ptr = (void *) hatches[pdev->brush_hatch];
1188     hatch.bits.free = hatch.bits.param = NULL;
1189     hatch.bits.is_copy = FALSE;
1190
1191     fg_mask.and = pdev->brush_and;
1192     fg_mask.xor = pdev->brush_xor;
1193     get_brush_bkgnd_masks( pdev, &bg_mask.and, &bg_mask.xor );
1194
1195     ret = pdev->brush_dib.funcs->create_rop_masks( &pdev->brush_dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1196     if(!ret) free_pattern_brush_bits( pdev );
1197
1198     return ret;
1199 }
1200
1201 /**********************************************************************
1202  *             pattern_brush
1203  *
1204  * Fill a number of rectangles with the pattern brush
1205  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1206  */
1207 static BOOL pattern_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1208 {
1209     int i, j;
1210     const WINEREGION *clip;
1211     POINT origin;
1212
1213     if(pdev->brush_and_bits == NULL)
1214     {
1215         switch(pdev->brush_style)
1216         {
1217         case BS_DIBPATTERN:
1218             if(!create_pattern_brush_bits(pdev))
1219                 return FALSE;
1220             break;
1221
1222         case BS_HATCHED:
1223             if(!create_hatch_brush_bits(pdev))
1224                 return FALSE;
1225             break;
1226
1227         default:
1228             ERR("Unexpected brush style %d\n", pdev->brush_style);
1229             return FALSE;
1230         }
1231     }
1232
1233     GetBrushOrgEx(pdev->dev.hdc, &origin);
1234
1235     clip = get_wine_region( region );
1236
1237     if (!clip)
1238     {
1239         dib->funcs->pattern_rects( dib, num, rects, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1240         return TRUE;
1241     }
1242
1243     for(i = 0; i < num; i++)
1244     {
1245         for(j = 0; j < clip->numRects; j++)
1246         {
1247             RECT rect = rects[i];
1248
1249             /* Optimize unclipped case */
1250             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1251                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1252             {
1253                 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1254                 break;
1255             }
1256
1257             if(clip->rects[j].top >= rect.bottom) break;
1258             if(clip->rects[j].bottom <= rect.top) continue;
1259
1260             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1261             {
1262                 rect.left   = max(rect.left,   clip->rects[j].left);
1263                 rect.top    = max(rect.top,    clip->rects[j].top);
1264                 rect.right  = min(rect.right,  clip->rects[j].right);
1265                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1266
1267                 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1268             }
1269         }
1270     }
1271     release_wine_region( region );
1272     return TRUE;
1273 }
1274
1275 static BOOL null_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1276 {
1277     return TRUE;
1278 }
1279
1280 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1281 {
1282     pdev->brush_rop = rop;
1283     if(pdev->brush_style == BS_SOLID || pdev->brush_style == BS_HATCHED)
1284         calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1285     free_pattern_brush_bits( pdev );
1286 }
1287
1288 /***********************************************************************
1289  *           dibdrv_SelectBrush
1290  */
1291 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
1292 {
1293     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1294     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1295     LOGBRUSH logbrush;
1296
1297     TRACE("(%p, %p)\n", dev, hbrush);
1298
1299     if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1300
1301     if (hbrush == GetStockObject( DC_BRUSH ))
1302         logbrush.lbColor = GetDCBrushColor( dev->hdc );
1303
1304     pdev->brush_style = logbrush.lbStyle;
1305
1306     pdev->defer |= DEFER_BRUSH;
1307
1308     free_pattern_brush( pdev );
1309
1310     switch(logbrush.lbStyle)
1311     {
1312     case BS_SOLID:
1313         pdev->brush_colorref = logbrush.lbColor;
1314         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1315         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1316         pdev->brush_rects = solid_brush;
1317         pdev->defer &= ~DEFER_BRUSH;
1318         break;
1319
1320     case BS_NULL:
1321         pdev->brush_rects = null_brush;
1322         pdev->defer &= ~DEFER_BRUSH;
1323         break;
1324
1325     case BS_DIBPATTERN:
1326     {
1327         BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1328         dib_info orig_dib;
1329         WORD usage = LOWORD(logbrush.lbColor);
1330         HPALETTE pal = (usage == DIB_PAL_COLORS) ? GetCurrentObject(dev->hdc, OBJ_PAL) : NULL;
1331         RECT rect;
1332
1333         if(!bi) return NULL;
1334         if(init_dib_info_from_packed(&orig_dib, bi, usage, pal))
1335         {
1336             copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1337
1338             pdev->brush_dib.height = orig_dib.height;
1339             pdev->brush_dib.width  = orig_dib.width;
1340             pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1341
1342             pdev->brush_dib.bits.param   = NULL;
1343             pdev->brush_dib.bits.ptr     = HeapAlloc( GetProcessHeap(), 0,
1344                                                       pdev->brush_dib.height * pdev->brush_dib.stride );
1345             pdev->brush_dib.bits.is_copy = TRUE;
1346             pdev->brush_dib.bits.free    = free_heap_bits;
1347
1348             rect.left = rect.top = 0;
1349             rect.right = orig_dib.width;
1350             rect.bottom = orig_dib.height;
1351
1352             if(pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &orig_dib, &rect))
1353             {
1354                 pdev->brush_rects = pattern_brush;
1355                 pdev->defer &= ~DEFER_BRUSH;
1356             }
1357             else
1358                 free_dib_info(&pdev->brush_dib);
1359             free_dib_info(&orig_dib);
1360         }
1361         GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1362         break;
1363     }
1364
1365     case BS_HATCHED:
1366     {
1367         if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1368         pdev->brush_hatch = logbrush.lbHatch;
1369         pdev->brush_colorref = logbrush.lbColor;
1370         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1371         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1372         pdev->brush_rects = pattern_brush;
1373         pdev->defer &= ~DEFER_BRUSH;
1374         break;
1375     }
1376
1377     default:
1378         break;
1379     }
1380
1381     return next->funcs->pSelectBrush( next, hbrush );
1382 }
1383
1384 /***********************************************************************
1385  *           dibdrv_SetDCBrushColor
1386  */
1387 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1388 {
1389     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1390     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1391
1392     if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1393     {
1394         pdev->brush_colorref = color;
1395         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1396         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1397     }
1398
1399     return next->funcs->pSetDCBrushColor( next, color );
1400 }
1401
1402 BOOL brush_rects(dibdrv_physdev *pdev, int num, const RECT *rects)
1403 {
1404     return pdev->brush_rects( pdev, &pdev->dib, num, rects, pdev->clip );
1405 }