gdi32: Add a PutImage implementation to the dib driver.
[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 void reset_dash_origin(dibdrv_physdev *pdev)
651 {
652     pdev->dash_pos.cur_dash = 0;
653     pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
654     pdev->dash_pos.mark = TRUE;
655 }
656
657 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
658 {
659     skip %= pdev->pen_pattern.total_len;
660     while(skip)
661     {
662         if(pdev->dash_pos.left_in_dash > skip)
663         {
664             pdev->dash_pos.left_in_dash -= skip;
665             return;
666         }
667         skip -= pdev->dash_pos.left_in_dash;
668         pdev->dash_pos.cur_dash++;
669         if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
670         pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
671         pdev->dash_pos.mark = !pdev->dash_pos.mark;
672     }
673 }
674
675 static inline void get_dash_colors(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
676 {
677     if(pdev->dash_pos.mark)
678     {
679         *and = pdev->pen_and;
680         *xor = pdev->pen_xor;
681     }
682     else /* space */
683     {
684         get_pen_bkgnd_masks( pdev, and, xor );
685     }
686 }
687
688 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
689 {
690     RECT rect;
691     DWORD and, xor;
692
693     get_dash_colors(pdev, &and, &xor);
694     skip_dash(pdev, 1);
695     rect.left   = x;
696     rect.right  = x + 1;
697     rect.top    = y;
698     rect.bottom = y + 1;
699     pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
700     return;
701 }
702
703 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
704 {
705     const WINEREGION *clip = get_wine_region(pdev->clip);
706     DWORD and, xor;
707     int i, dash_len;
708     RECT rect;
709     const dash_pos start_pos = pdev->dash_pos;
710
711     if(start->y == end->y) /* hline */
712     {
713         BOOL l_to_r;
714         INT left, right, cur_x;
715
716         rect.top = start->y;
717         rect.bottom = start->y + 1;
718
719         if(start->x <= end->x)
720         {
721             left = start->x;
722             right = end->x - 1;
723             l_to_r = TRUE;
724         }
725         else
726         {
727             left = end->x + 1;
728             right = start->x;
729             l_to_r = FALSE;
730         }
731
732         for(i = 0; i < clip->numRects; i++)
733         {
734             if(clip->rects[i].top > start->y) break;
735             if(clip->rects[i].bottom <= start->y) continue;
736
737             if(clip->rects[i].right > left && clip->rects[i].left <= right)
738             {
739                 int clipped_left  = max(clip->rects[i].left, left);
740                 int clipped_right = min(clip->rects[i].right - 1, right);
741
742                 pdev->dash_pos = start_pos;
743
744                 if(l_to_r)
745                 {
746                     cur_x = clipped_left;
747                     if(cur_x != left)
748                         skip_dash(pdev, clipped_left - left);
749
750                     while(cur_x <= clipped_right)
751                     {
752                         get_dash_colors(pdev, &and, &xor);
753                         dash_len = pdev->dash_pos.left_in_dash;
754                         if(cur_x + dash_len > clipped_right + 1)
755                             dash_len = clipped_right - cur_x + 1;
756                         rect.left = cur_x;
757                         rect.right = cur_x + dash_len;
758
759                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
760                         cur_x += dash_len;
761                         skip_dash(pdev, dash_len);
762                     }
763                 }
764                 else
765                 {
766                     cur_x = clipped_right;
767                     if(cur_x != right)
768                         skip_dash(pdev, right - clipped_right);
769
770                     while(cur_x >= clipped_left)
771                     {
772                         get_dash_colors(pdev, &and, &xor);
773                         dash_len = pdev->dash_pos.left_in_dash;
774                         if(cur_x - dash_len < clipped_left - 1)
775                             dash_len = cur_x - clipped_left + 1;
776                         rect.left = cur_x - dash_len + 1;
777                         rect.right = cur_x + 1;
778
779                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
780                         cur_x -= dash_len;
781                         skip_dash(pdev, dash_len);
782                     }
783                 }
784             }
785         }
786         pdev->dash_pos = start_pos;
787         skip_dash(pdev, right - left + 1);
788     }
789     else if(start->x == end->x) /* vline */
790     {
791         BOOL t_to_b;
792         INT top, bottom, cur_y;
793
794         rect.left = start->x;
795         rect.right = start->x + 1;
796
797         if(start->y <= end->y)
798         {
799             top = start->y;
800             bottom = end->y - 1;
801             t_to_b = TRUE;
802         }
803         else
804         {
805             top = end->y + 1;
806             bottom = start->y;
807             t_to_b = FALSE;
808         }
809
810         for(i = 0; i < clip->numRects; i++)
811         {
812             if(clip->rects[i].top > bottom) break;
813             if(clip->rects[i].bottom <= top) continue;
814             if(clip->rects[i].right > start->x && clip->rects[i].left <= start->x)
815             {
816                 int clipped_top    = max(clip->rects[i].top, top);
817                 int clipped_bottom = min(clip->rects[i].bottom - 1, bottom);
818
819                 pdev->dash_pos = start_pos;
820
821                 if(t_to_b)
822                 {
823                     cur_y = clipped_top;
824                     if(cur_y != top)
825                         skip_dash(pdev, clipped_top - top);
826
827                     while(cur_y <= clipped_bottom)
828                     {
829                         get_dash_colors(pdev, &and, &xor);
830                         dash_len = pdev->dash_pos.left_in_dash;
831                         if(cur_y + dash_len > clipped_bottom + 1)
832                             dash_len = clipped_bottom - cur_y + 1;
833                         rect.top = cur_y;
834                         rect.bottom = cur_y + dash_len;
835
836                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
837                         cur_y += dash_len;
838                         skip_dash(pdev, dash_len);
839                     }
840                 }
841                 else
842                 {
843                     cur_y = clipped_bottom;
844                     if(cur_y != bottom)
845                         skip_dash(pdev, bottom - clipped_bottom);
846
847                     while(cur_y >= clipped_top)
848                     {
849                         get_dash_colors(pdev, &and, &xor);
850                         dash_len = pdev->dash_pos.left_in_dash;
851                         if(cur_y - dash_len < clipped_top - 1)
852                             dash_len = cur_y - clipped_top + 1;
853                         rect.top = cur_y - dash_len + 1;
854                         rect.bottom = cur_y + 1;
855
856                         pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
857                         cur_y -= dash_len;
858                         skip_dash(pdev, dash_len);
859                     }
860                 }
861             }
862         }
863         pdev->dash_pos = start_pos;
864         skip_dash(pdev, bottom - top + 1);
865     }
866     else
867     {
868         bres_params params;
869         INT dx = end->x - start->x;
870         INT dy = end->y - start->y;
871         INT i;
872
873         params.dx = abs(dx);
874         params.dy = abs(dy);
875         params.octant = get_octant_mask(dx, dy);
876         /* Octants 3, 5, 6 and 8 take a bias */
877         params.bias = (params.octant & 0xb4) ? 1 : 0;
878
879         for(i = 0; i < clip->numRects; i++)
880         {
881             POINT clipped_start, clipped_end;
882             int clip_status;
883             clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
884
885             if(clip_status)
886             {
887                 int m = abs(clipped_start.x - start->x);
888                 int n = abs(clipped_start.y - start->y);
889                 int err;
890                 BOOL last_pt = FALSE;
891
892                 pdev->dash_pos = start_pos;
893
894                 if(is_xmajor(params.octant))
895                 {
896                     err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
897                     skip_dash(pdev, m);
898                 }
899                 else
900                 {
901                     err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
902                     skip_dash(pdev, n);
903                 }
904                 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
905
906                 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, &params,
907                                     err, last_pt, dashed_pen_line_callback, pdev);
908
909                 if(clip_status == 2) break; /* completely unclipped, so we can finish */
910             }
911         }
912         pdev->dash_pos = start_pos;
913         if(is_xmajor(params.octant))
914             skip_dash(pdev, params.dx);
915         else
916             skip_dash(pdev, params.dy);
917     }
918
919     release_wine_region(pdev->clip);
920     return TRUE;
921 }
922
923 static BOOL null_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
924 {
925     return TRUE;
926 }
927
928 static const dash_pattern dash_patterns[5] =
929 {
930     {0, {0}, 0},                  /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
931     {2, {18, 6}, 24},             /* PS_DASH */
932     {2, {3,  3}, 6},              /* PS_DOT */
933     {4, {9, 6, 3, 6}, 24},        /* PS_DASHDOT */
934     {6, {9, 3, 3, 3, 3, 3}, 24}   /* PS_DASHDOTDOT */
935 };
936
937 /***********************************************************************
938  *           dibdrv_SelectPen
939  */
940 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
941 {
942     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
943     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
944     LOGPEN logpen;
945     DWORD style;
946
947     TRACE("(%p, %p)\n", dev, hpen);
948
949     if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
950     {
951         /* must be an extended pen */
952         EXTLOGPEN *elp;
953         INT size = GetObjectW( hpen, 0, NULL );
954
955         if (!size) return 0;
956
957         elp = HeapAlloc( GetProcessHeap(), 0, size );
958
959         GetObjectW( hpen, size, elp );
960         /* FIXME: add support for user style pens */
961         logpen.lopnStyle = elp->elpPenStyle;
962         logpen.lopnWidth.x = elp->elpWidth;
963         logpen.lopnWidth.y = 0;
964         logpen.lopnColor = elp->elpColor;
965
966         HeapFree( GetProcessHeap(), 0, elp );
967     }
968
969     if (hpen == GetStockObject( DC_PEN ))
970         logpen.lopnColor = GetDCPenColor( dev->hdc );
971
972     pdev->pen_colorref = logpen.lopnColor;
973     pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
974     calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
975
976     pdev->pen_pattern = dash_patterns[PS_SOLID];
977
978     pdev->defer |= DEFER_PEN;
979
980     style = logpen.lopnStyle & PS_STYLE_MASK;
981
982     switch(style)
983     {
984     case PS_SOLID:
985         if(logpen.lopnStyle & PS_GEOMETRIC) break;
986         if(logpen.lopnWidth.x > 1) break;
987         pdev->pen_line = solid_pen_line;
988         pdev->defer &= ~DEFER_PEN;
989         break;
990
991     case PS_DASH:
992     case PS_DOT:
993     case PS_DASHDOT:
994     case PS_DASHDOTDOT:
995         if(logpen.lopnStyle & PS_GEOMETRIC) break;
996         if(logpen.lopnWidth.x > 1) break;
997         pdev->pen_line = dashed_pen_line;
998         pdev->pen_pattern = dash_patterns[style];
999         pdev->defer &= ~DEFER_PEN;
1000         break;
1001
1002     case PS_NULL:
1003         pdev->pen_line = null_pen_line;
1004         pdev->defer &= ~DEFER_PEN;
1005         break;
1006
1007     default:
1008         break;
1009     }
1010
1011     return next->funcs->pSelectPen( next, hpen );
1012 }
1013
1014 /***********************************************************************
1015  *           dibdrv_SetDCPenColor
1016  */
1017 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1018 {
1019     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
1020     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1021
1022     if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1023     {
1024         pdev->pen_colorref = color;
1025         pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
1026         calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1027     }
1028
1029     return next->funcs->pSetDCPenColor( next, color );
1030 }
1031
1032 /**********************************************************************
1033  *             solid_brush
1034  *
1035  * Fill a number of rectangles with the solid brush
1036  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1037  */
1038 static BOOL solid_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1039 {
1040     int i, j;
1041     const WINEREGION *clip = get_wine_region(pdev->clip);
1042
1043     for(i = 0; i < num; i++)
1044     {
1045         for(j = 0; j < clip->numRects; j++)
1046         {
1047             RECT rect = rects[i];
1048
1049             /* Optimize unclipped case */
1050             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1051                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1052             {
1053                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
1054                 break;
1055             }
1056
1057             if(clip->rects[j].top >= rect.bottom) break;
1058             if(clip->rects[j].bottom <= rect.top) continue;
1059
1060             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1061             {
1062                 rect.left   = max(rect.left,   clip->rects[j].left);
1063                 rect.top    = max(rect.top,    clip->rects[j].top);
1064                 rect.right  = min(rect.right,  clip->rects[j].right);
1065                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1066
1067                 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
1068             }
1069         }
1070     }
1071     release_wine_region(pdev->clip);
1072     return TRUE;
1073 }
1074
1075 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
1076 {
1077     HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
1078     HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
1079     pdev->brush_and_bits = NULL;
1080     pdev->brush_xor_bits = NULL;
1081 }
1082
1083 void free_pattern_brush( dibdrv_physdev *pdev )
1084 {
1085     free_pattern_brush_bits( pdev );
1086     free_dib_info( &pdev->brush_dib );
1087 }
1088
1089 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
1090 {
1091     DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
1092     DWORD *brush_bits = pdev->brush_dib.bits;
1093     DWORD *and_bits, *xor_bits;
1094
1095     assert(pdev->brush_and_bits == NULL);
1096     assert(pdev->brush_xor_bits == NULL);
1097
1098     assert(pdev->brush_dib.stride > 0);
1099
1100     and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1101     xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1102
1103     if(!and_bits || !xor_bits)
1104     {
1105         ERR("Failed to create pattern brush bits\n");
1106         free_pattern_brush_bits( pdev );
1107         return FALSE;
1108     }
1109
1110     while(size)
1111     {
1112         calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1113         size -= 4;
1114     }
1115
1116     return TRUE;
1117 }
1118
1119 static const DWORD hatches[6][8] =
1120 {
1121     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1122     { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL   */
1123     { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL  */
1124     { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL  */
1125     { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS      */
1126     { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }  /* HS_DIAGCROSS  */
1127 };
1128
1129 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev)
1130 {
1131     dib_info hatch;
1132     rop_mask fg_mask, bg_mask;
1133     rop_mask_bits mask_bits;
1134     DWORD size;
1135     BOOL ret;
1136
1137     assert(pdev->brush_and_bits == NULL);
1138     assert(pdev->brush_xor_bits == NULL);
1139
1140     /* Just initialise brush_dib with the color / sizing info.  We don't
1141        need the bits as we'll calculate the rop masks straight from
1142        the hatch patterns. */
1143
1144     copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1145     pdev->brush_dib.width  = 8;
1146     pdev->brush_dib.height = 8;
1147     pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1148
1149     size = pdev->brush_dib.height * pdev->brush_dib.stride;
1150
1151     mask_bits.and = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1152     mask_bits.xor = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1153
1154     if(!mask_bits.and || !mask_bits.xor)
1155     {
1156         ERR("Failed to create pattern brush bits\n");
1157         free_pattern_brush_bits( pdev );
1158         return FALSE;
1159     }
1160
1161     hatch.bit_count = 1;
1162     hatch.height = hatch.width = 8;
1163     hatch.stride = 4;
1164     hatch.bits = (void *) hatches[pdev->brush_hatch];
1165
1166     fg_mask.and = pdev->brush_and;
1167     fg_mask.xor = pdev->brush_xor;
1168     get_brush_bkgnd_masks( pdev, &bg_mask.and, &bg_mask.xor );
1169
1170     ret = pdev->brush_dib.funcs->create_rop_masks( &pdev->brush_dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1171     if(!ret) free_pattern_brush_bits( pdev );
1172
1173     return ret;
1174 }
1175
1176 /**********************************************************************
1177  *             pattern_brush
1178  *
1179  * Fill a number of rectangles with the pattern brush
1180  * FIXME: Should we insist l < r && t < b?  Currently we assume this.
1181  */
1182 static BOOL pattern_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1183 {
1184     int i, j;
1185     const WINEREGION *clip;
1186     POINT origin;
1187
1188     if(pdev->brush_and_bits == NULL)
1189     {
1190         switch(pdev->brush_style)
1191         {
1192         case BS_DIBPATTERN:
1193             if(!create_pattern_brush_bits(pdev))
1194                 return FALSE;
1195             break;
1196
1197         case BS_HATCHED:
1198             if(!create_hatch_brush_bits(pdev))
1199                 return FALSE;
1200             break;
1201
1202         default:
1203             ERR("Unexpected brush style %d\n", pdev->brush_style);
1204             return FALSE;
1205         }
1206     }
1207
1208     GetBrushOrgEx(pdev->dev.hdc, &origin);
1209
1210     clip = get_wine_region(pdev->clip);
1211     for(i = 0; i < num; i++)
1212     {
1213         for(j = 0; j < clip->numRects; j++)
1214         {
1215             RECT rect = rects[i];
1216
1217             /* Optimize unclipped case */
1218             if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1219                clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1220             {
1221                 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1222                 break;
1223             }
1224
1225             if(clip->rects[j].top >= rect.bottom) break;
1226             if(clip->rects[j].bottom <= rect.top) continue;
1227
1228             if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1229             {
1230                 rect.left   = max(rect.left,   clip->rects[j].left);
1231                 rect.top    = max(rect.top,    clip->rects[j].top);
1232                 rect.right  = min(rect.right,  clip->rects[j].right);
1233                 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1234
1235                 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1236             }
1237         }
1238     }
1239     release_wine_region(pdev->clip);
1240     return TRUE;
1241 }
1242
1243 static BOOL null_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1244 {
1245     return TRUE;
1246 }
1247
1248 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1249 {
1250     pdev->brush_rop = rop;
1251     if(pdev->brush_style == BS_SOLID || pdev->brush_style == BS_HATCHED)
1252         calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1253     free_pattern_brush_bits( pdev );
1254 }
1255
1256 /***********************************************************************
1257  *           dibdrv_SelectBrush
1258  */
1259 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
1260 {
1261     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1262     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1263     LOGBRUSH logbrush;
1264
1265     TRACE("(%p, %p)\n", dev, hbrush);
1266
1267     if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1268
1269     if (hbrush == GetStockObject( DC_BRUSH ))
1270         logbrush.lbColor = GetDCBrushColor( dev->hdc );
1271
1272     pdev->brush_style = logbrush.lbStyle;
1273
1274     pdev->defer |= DEFER_BRUSH;
1275
1276     free_pattern_brush( pdev );
1277
1278     switch(logbrush.lbStyle)
1279     {
1280     case BS_SOLID:
1281         pdev->brush_colorref = logbrush.lbColor;
1282         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1283         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1284         pdev->brush_rects = solid_brush;
1285         pdev->defer &= ~DEFER_BRUSH;
1286         break;
1287
1288     case BS_NULL:
1289         pdev->brush_rects = null_brush;
1290         pdev->defer &= ~DEFER_BRUSH;
1291         break;
1292
1293     case BS_DIBPATTERN:
1294     {
1295         BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1296         dib_info orig_dib;
1297         WORD usage = LOWORD(logbrush.lbColor);
1298         HPALETTE pal = (usage == DIB_PAL_COLORS) ? GetCurrentObject(dev->hdc, OBJ_PAL) : NULL;
1299         RECT rect;
1300
1301         if(!bi) return NULL;
1302         if(init_dib_info_from_packed(&orig_dib, bi, usage, pal))
1303         {
1304             copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1305
1306             pdev->brush_dib.height = orig_dib.height;
1307             pdev->brush_dib.width  = orig_dib.width;
1308             pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1309             pdev->brush_dib.ptr_to_free = HeapAlloc( GetProcessHeap(), 0, pdev->brush_dib.height * pdev->brush_dib.stride );
1310             pdev->brush_dib.bits = pdev->brush_dib.ptr_to_free;
1311
1312             rect.left = rect.top = 0;
1313             rect.right = orig_dib.width;
1314             rect.bottom = orig_dib.height;
1315
1316             if(pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &orig_dib, &rect))
1317             {
1318                 pdev->brush_rects = pattern_brush;
1319                 pdev->defer &= ~DEFER_BRUSH;
1320             }
1321             else
1322                 free_dib_info(&pdev->brush_dib);
1323             free_dib_info(&orig_dib);
1324         }
1325         GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1326         break;
1327     }
1328
1329     case BS_HATCHED:
1330     {
1331         if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1332         pdev->brush_hatch = logbrush.lbHatch;
1333         pdev->brush_colorref = logbrush.lbColor;
1334         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1335         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1336         pdev->brush_rects = pattern_brush;
1337         pdev->defer &= ~DEFER_BRUSH;
1338         break;
1339     }
1340
1341     default:
1342         break;
1343     }
1344
1345     return next->funcs->pSelectBrush( next, hbrush );
1346 }
1347
1348 /***********************************************************************
1349  *           dibdrv_SetDCBrushColor
1350  */
1351 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1352 {
1353     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1354     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1355
1356     if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1357     {
1358         pdev->brush_colorref = color;
1359         pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1360         calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1361     }
1362
1363     return next->funcs->pSetDCBrushColor( next, color );
1364 }