2 * DIB driver GDI objects.
4 * Copyright 2011 Huw Davies
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.
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.
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
24 #include "gdi_private.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dib
);
33 * Decompose the 16 ROP2s into an expression of the form
37 * Where A and X depend only on P (and so can be precomputed).
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
48 * R2_NOTMASKPEN ~(P & D) P 1
49 * R2_MASKPEN P & D P 0
50 * R2_NOTXORPEN ~(P ^ D) 1 ~P
52 * R2_MERGENOTPEN ~P | D P ~P
54 * R2_MERGEPENNOT P | ~D ~P 1
55 * R2_MERGEPEN P | D ~P P
60 /* A = (P & A1) ^ A2 */
61 #define ZERO { 0u, 0u}
62 #define ONE { 0u, ~0u}
64 #define NOT_P {~0u, ~0u}
66 static const DWORD rop2_and_array
[16][2] =
68 ZERO
, NOT_P
, NOT_P
, ZERO
,
71 ZERO
, NOT_P
, NOT_P
, ZERO
74 /* X = (P & X1) ^ X2 */
75 static const DWORD rop2_xor_array
[16][2] =
77 ZERO
, NOT_P
, ZERO
, NOT_P
,
79 ZERO
, NOT_P
, ZERO
, NOT_P
,
88 void get_rop_codes(INT rop
, struct rop_codes
*codes
)
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];
97 void calc_and_xor_masks(INT rop
, DWORD color
, DWORD
*and, DWORD
*xor)
99 struct rop_codes codes
;
100 get_rop_codes( rop
, &codes
);
102 *and = (color
& codes
.a1
) ^ codes
.a2
;
103 *xor = (color
& codes
.x1
) ^ codes
.x2
;
106 static inline RGBQUAD
rgbquad_from_colorref(COLORREF c
)
110 ret
.rgbRed
= GetRValue(c
);
111 ret
.rgbGreen
= GetGValue(c
);
112 ret
.rgbBlue
= GetBValue(c
);
117 static inline BOOL
rgbquad_equal(const RGBQUAD
*a
, const RGBQUAD
*b
)
119 if(a
->rgbRed
== b
->rgbRed
&&
120 a
->rgbGreen
== b
->rgbGreen
&&
121 a
->rgbBlue
== b
->rgbBlue
)
126 COLORREF
make_rgb_colorref( HDC hdc
, dib_info
*dib
, COLORREF color
, BOOL
*got_pixel
, DWORD
*pixel
)
128 BYTE type
= color
>> 24;
129 WORD index
= LOWORD( color
);
130 HPALETTE pal
= GetCurrentObject( hdc
, OBJ_PAL
);
131 PALETTEENTRY pal_ent
;
140 case 0x10: /* DIBINDEX */
143 color
= RGB(0, 0, 0);
145 if (dib
->bit_count
<= 8 && index
< (1 << dib
->bit_count
))
148 if (index
< dib
->color_table_size
)
149 color
= RGB( dib
->color_table
[index
].rgbRed
,
150 dib
->color_table
[index
].rgbGreen
,
151 dib
->color_table
[index
].rgbBlue
);
155 case 2: /* PALETTERGB */
159 case 1: /* PALETTEINDEX */
160 if (!GetPaletteEntries( pal
, index
, 1, &pal_ent
))
161 GetPaletteEntries( pal
, 0, 1, &pal_ent
);
162 color
= RGB( pal_ent
.peRed
, pal_ent
.peGreen
, pal_ent
.peBlue
);
166 FIXME("Unhandled color type %08x\n", color
);
173 /******************************************************************
176 * 1 bit bitmaps map the fg/bg colors as follows:
177 * If the fg colorref exactly matches one of the color table entries then
178 * that entry is the fg color and the other is the bg.
179 * Otherwise the bg color is mapped to the closest entry in the table and
180 * the fg takes the other one.
182 DWORD
get_pixel_color( dibdrv_physdev
*pdev
, COLORREF color
, BOOL mono_fixup
)
189 rgb_ref
= make_rgb_colorref( pdev
->dev
.hdc
, &pdev
->dib
, color
, &got_pixel
, &pixel
);
190 if (got_pixel
) return pixel
;
192 if (pdev
->dib
.bit_count
!= 1 || !mono_fixup
)
193 return pdev
->dib
.funcs
->colorref_to_pixel( &pdev
->dib
, rgb_ref
);
195 fg_quad
= rgbquad_from_colorref( rgb_ref
);
196 if(rgbquad_equal(&fg_quad
, pdev
->dib
.color_table
))
198 if(rgbquad_equal(&fg_quad
, pdev
->dib
.color_table
+ 1))
201 if(color
== GetBkColor(pdev
->dev
.hdc
)) return pdev
->bkgnd_color
;
202 else return pdev
->bkgnd_color
? 0 : 1;
205 /***************************************************************************
206 * get_pen_bkgnd_masks
208 * Returns the pre-calculated bkgnd color masks unless the dib is 1 bpp.
209 * In this case since there are several fg sources (pen, brush, text)
210 * this makes pdev->bkgnd_color unusable. So here we take the inverse
211 * of the relevant fg color (which is always set up correctly).
213 static inline void get_pen_bkgnd_masks(const dibdrv_physdev
*pdev
, DWORD
*and, DWORD
*xor)
215 if(pdev
->dib
.bit_count
!= 1 || GetBkMode(pdev
->dev
.hdc
) == TRANSPARENT
)
217 *and = pdev
->bkgnd_and
;
218 *xor = pdev
->bkgnd_xor
;
222 DWORD color
= ~pdev
->pen_color
;
223 if(pdev
->pen_colorref
== GetBkColor(pdev
->dev
.hdc
)) color
= pdev
->pen_color
;
224 calc_and_xor_masks( GetROP2(pdev
->dev
.hdc
), color
, and, xor );
228 static inline void get_brush_bkgnd_masks(const dibdrv_physdev
*pdev
, DWORD
*and, DWORD
*xor)
230 if(GetBkMode(pdev
->dev
.hdc
) == TRANSPARENT
)
232 *and = pdev
->bkgnd_and
;
233 *xor = pdev
->bkgnd_xor
;
237 DWORD color
= pdev
->bkgnd_color
;
239 if(pdev
->dib
.bit_count
== 1)
241 if(pdev
->brush_colorref
== GetBkColor(pdev
->dev
.hdc
))
242 color
= pdev
->brush_color
;
244 color
= ~pdev
->brush_color
;
246 calc_and_xor_masks( pdev
->brush_rop
, color
, and, xor );
250 static inline void order_end_points(int *s
, int *e
)
261 #define Y_INCREASING_MASK 0x0f
262 #define X_INCREASING_MASK 0xc3
263 #define X_MAJOR_MASK 0x99
264 #define POS_SLOPE_MASK 0x33
266 static inline BOOL
is_xmajor(DWORD octant
)
268 return octant
& X_MAJOR_MASK
;
271 static inline BOOL
is_pos_slope(DWORD octant
)
273 return octant
& POS_SLOPE_MASK
;
276 static inline BOOL
is_x_increasing(DWORD octant
)
278 return octant
& X_INCREASING_MASK
;
281 static inline BOOL
is_y_increasing(DWORD octant
)
283 return octant
& Y_INCREASING_MASK
;
286 /**********************************************************************
289 * Return the octant number starting clockwise from the +ve x-axis.
291 static inline int get_octant_number(int dx
, int dy
)
295 return ( dx
> dy
) ? 1 : 2;
297 return (-dx
> dy
) ? 4 : 3;
300 return (-dx
> -dy
) ? 5 : 6;
302 return ( dx
> -dy
) ? 8 : 7;
305 static inline DWORD
get_octant_mask(int dx
, int dy
)
307 return 1 << (get_octant_number(dx
, dy
) - 1);
310 static inline int get_bias( DWORD mask
)
312 /* Octants 3, 5, 6 and 8 take a bias */
313 return (mask
& 0xb4) ? 1 : 0;
321 static inline DWORD
calc_outcode(const POINT
*pt
, const RECT
*clip
)
324 if(pt
->x
< clip
->left
) out
|= OUT_LEFT
;
325 else if(pt
->x
>= clip
->right
) out
|= OUT_RIGHT
;
326 if(pt
->y
< clip
->top
) out
|= OUT_TOP
;
327 else if(pt
->y
>= clip
->bottom
) out
|= OUT_BOTTOM
;
332 /******************************************************************************
335 * Clips the start and end points to a rectangle.
337 * Note, this treats the end point like the start point. If the
338 * caller doesn't want it displayed, it should exclude it. If the end
339 * point is clipped out, then the likelihood is that the new end point
340 * should be displayed.
342 * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
344 * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
345 * however the Bresenham error term is defined differently so the equations
348 * For x major lines we have 2dy >= err + bias > 2dy - 2dx
349 * 0 >= err + bias - 2dy > -2dx
351 * Note dx, dy, m and n are all +ve.
353 * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
354 * err = 2dy - dx + 2mdy - 2ndx
355 * 0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
356 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
357 * which of course will give exactly one solution for n,
358 * so looking at the >= inequality
359 * n >= (2mdy + bias - dx) / 2dx
360 * n = ceiling((2mdy + bias - dx) / 2dx)
361 * = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
363 * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
364 * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
365 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
366 * 2mdy > 2ndx - bias - dx
367 * m > (2ndx - bias - dx) / 2dy
368 * m = floor((2ndx - bias - dx) / 2dy) + 1
369 * m = (2ndx - bias - dx) / 2dy + 1
371 * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
372 * err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
373 * = 2dy - dx - 2mdy + 2ndx
374 * 0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
375 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
376 * again exactly one solution.
377 * 2ndx <= 2mdy - bias + dx
378 * n = floor((2mdy - bias + dx) / 2dx)
379 * = (2mdy - bias + dx) / 2dx
381 * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
382 * mininizing m to include all of the points at y = y2 - n. As above:
383 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
384 * 2mdy >= 2ndx + bias - dx
385 * m = ceiling((2ndx + bias - dx) / 2dy)
386 * = (2ndx + bias - dx - 1) / 2dy + 1
388 * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
390 * Moving start point from y1 to y1 + n find x1 + m
391 * m = (2ndx + bias + dy - 1) / 2dy
393 * Moving start point from x1 to x1 + m find y1 + n
394 * n = (2mdy - bias - dy) / 2ndx + 1
396 * Moving end point from y2 to y2 - n find x1 - m
397 * m = (2ndx - bias + dy) / 2dy
399 * Moving end point from x2 to x2 - m find y2 - n
400 * n = (2mdy + bias - dy - 1) / 2dx + 1
402 int clip_line(const POINT
*start
, const POINT
*end
, const RECT
*clip
,
403 const bres_params
*params
, POINT
*pt1
, POINT
*pt2
)
406 BOOL clipped
= FALSE
;
407 DWORD start_oc
, end_oc
;
408 const int bias
= params
->bias
;
409 const unsigned int dx
= params
->dx
;
410 const unsigned int dy
= params
->dy
;
411 const unsigned int two_dx
= params
->dx
* 2;
412 const unsigned int two_dy
= params
->dy
* 2;
413 const BOOL xmajor
= is_xmajor(params
->octant
);
414 const BOOL neg_slope
= !is_pos_slope(params
->octant
);
419 start_oc
= calc_outcode(start
, clip
);
420 end_oc
= calc_outcode(end
, clip
);
424 if(start_oc
== 0 && end_oc
== 0) return clipped
? 1 : 2; /* trivial accept */
425 if(start_oc
& end_oc
) return 0; /* trivial reject */
428 if(start_oc
& OUT_LEFT
)
430 m
= clip
->left
- start
->x
;
432 n
= (m
* two_dy
+ bias
+ dx
- 1) / two_dx
;
434 n
= (m
* two_dy
- bias
- dy
) / two_dx
+ 1;
437 if(neg_slope
) n
= -n
;
438 pt1
->y
= start
->y
+ n
;
439 start_oc
= calc_outcode(pt1
, clip
);
441 else if(start_oc
& OUT_RIGHT
)
443 m
= start
->x
- clip
->right
+ 1;
445 n
= (m
* two_dy
+ bias
+ dx
- 1) / two_dx
;
447 n
= (m
* two_dy
- bias
- dy
) / two_dx
+ 1;
449 pt1
->x
= clip
->right
- 1;
450 if(neg_slope
) n
= -n
;
451 pt1
->y
= start
->y
- n
;
452 start_oc
= calc_outcode(pt1
, clip
);
454 else if(start_oc
& OUT_TOP
)
456 n
= clip
->top
- start
->y
;
458 m
= (n
* two_dx
- bias
- dx
) / two_dy
+ 1;
460 m
= (n
* two_dx
+ bias
+ dy
- 1) / two_dy
;
463 if(neg_slope
) m
= -m
;
464 pt1
->x
= start
->x
+ m
;
465 start_oc
= calc_outcode(pt1
, clip
);
467 else if(start_oc
& OUT_BOTTOM
)
469 n
= start
->y
- clip
->bottom
+ 1;
471 m
= (n
* two_dx
- bias
- dx
) / two_dy
+ 1;
473 m
= (n
* two_dx
+ bias
+ dy
- 1) / two_dy
;
475 pt1
->y
= clip
->bottom
- 1;
476 if(neg_slope
) m
= -m
;
477 pt1
->x
= start
->x
- m
;
478 start_oc
= calc_outcode(pt1
, clip
);
480 else if(end_oc
& OUT_LEFT
)
482 m
= clip
->left
- end
->x
;
484 n
= (m
* two_dy
- bias
+ dx
) / two_dx
;
486 n
= (m
* two_dy
+ bias
- dy
- 1) / two_dx
+ 1;
489 if(neg_slope
) n
= -n
;
491 end_oc
= calc_outcode(pt2
, clip
);
493 else if(end_oc
& OUT_RIGHT
)
495 m
= end
->x
- clip
->right
+ 1;
497 n
= (m
* two_dy
- bias
+ dx
) / two_dx
;
499 n
= (m
* two_dy
+ bias
- dy
- 1) / two_dx
+ 1;
501 pt2
->x
= clip
->right
- 1;
502 if(neg_slope
) n
= -n
;
504 end_oc
= calc_outcode(pt2
, clip
);
506 else if(end_oc
& OUT_TOP
)
508 n
= clip
->top
- end
->y
;
510 m
= (n
* two_dx
+ bias
- dx
- 1) / two_dy
+ 1;
512 m
= (n
* two_dx
- bias
+ dy
) / two_dy
;
515 if(neg_slope
) m
= -m
;
517 end_oc
= calc_outcode(pt2
, clip
);
519 else if(end_oc
& OUT_BOTTOM
)
521 n
= end
->y
- clip
->bottom
+ 1;
523 m
= (n
* two_dx
+ bias
- dx
- 1) / two_dy
+ 1;
525 m
= (n
* two_dx
- bias
+ dy
) / two_dy
;
527 pt2
->y
= clip
->bottom
- 1;
528 if(neg_slope
) m
= -m
;
530 end_oc
= calc_outcode(pt2
, clip
);
535 static void bres_line_with_bias(const POINT
*start
, const struct line_params
*params
,
536 void (* callback
)(dibdrv_physdev
*,INT
,INT
), dibdrv_physdev
*pdev
)
539 int len
= params
->length
, err
= params
->err_start
;
545 callback(pdev
, pt
.x
, pt
.y
);
546 if (err
+ params
->bias
> 0)
548 pt
.y
+= params
->y_inc
;
549 err
+= params
->err_add_1
;
551 else err
+= params
->err_add_2
;
552 pt
.x
+= params
->x_inc
;
559 callback(pdev
, pt
.x
, pt
.y
);
560 if (err
+ params
->bias
> 0)
562 pt
.x
+= params
->x_inc
;
563 err
+= params
->err_add_1
;
565 else err
+= params
->err_add_2
;
566 pt
.y
+= params
->y_inc
;
571 static BOOL
solid_pen_line(dibdrv_physdev
*pdev
, POINT
*start
, POINT
*end
)
573 const WINEREGION
*clip
= get_wine_region(pdev
->clip
);
575 if(start
->y
== end
->y
)
580 rect
.left
= start
->x
;
583 rect
.bottom
= end
->y
+ 1;
584 order_end_points(&rect
.left
, &rect
.right
);
585 for(i
= 0; i
< clip
->numRects
; i
++)
587 if(clip
->rects
[i
].top
>= rect
.bottom
) break;
588 if(clip
->rects
[i
].bottom
<= rect
.top
) continue;
589 /* Optimize the unclipped case */
590 if(clip
->rects
[i
].left
<= rect
.left
&& clip
->rects
[i
].right
>= rect
.right
)
592 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, pdev
->pen_and
, pdev
->pen_xor
);
595 if(clip
->rects
[i
].right
> rect
.left
&& clip
->rects
[i
].left
< rect
.right
)
598 tmp
.left
= max(rect
.left
, clip
->rects
[i
].left
);
599 tmp
.right
= min(rect
.right
, clip
->rects
[i
].right
);
600 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &tmp
, pdev
->pen_and
, pdev
->pen_xor
);
604 else if(start
->x
== end
->x
)
609 rect
.left
= start
->x
;
611 rect
.right
= end
->x
+ 1;
612 rect
.bottom
= end
->y
;
613 order_end_points(&rect
.top
, &rect
.bottom
);
614 for(i
= 0; i
< clip
->numRects
; i
++)
616 /* Optimize unclipped case */
617 if(clip
->rects
[i
].top
<= rect
.top
&& clip
->rects
[i
].bottom
>= rect
.bottom
&&
618 clip
->rects
[i
].left
<= rect
.left
&& clip
->rects
[i
].right
>= rect
.right
)
620 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, pdev
->pen_and
, pdev
->pen_xor
);
623 if(clip
->rects
[i
].top
>= rect
.bottom
) break;
624 if(clip
->rects
[i
].bottom
<= rect
.top
) continue;
625 if(clip
->rects
[i
].right
> rect
.left
&& clip
->rects
[i
].left
< rect
.right
)
628 tmp
.top
= max(rect
.top
, clip
->rects
[i
].top
);
629 tmp
.bottom
= min(rect
.bottom
, clip
->rects
[i
].bottom
);
630 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &tmp
, pdev
->pen_and
, pdev
->pen_xor
);
636 bres_params clip_params
;
637 struct line_params line_params
;
638 INT dx
= end
->x
- start
->x
, dy
= end
->y
- start
->y
;
639 INT abs_dx
= abs(dx
), abs_dy
= abs(dy
);
642 clip_params
.dx
= abs_dx
;
643 clip_params
.dy
= abs_dy
;
644 clip_params
.octant
= get_octant_mask(dx
, dy
);
645 clip_params
.bias
= get_bias( clip_params
.octant
);
647 line_params
.bias
= clip_params
.bias
;
648 line_params
.x_major
= is_xmajor( clip_params
.octant
);
649 line_params
.x_inc
= is_x_increasing( clip_params
.octant
) ? 1 : -1;
650 line_params
.y_inc
= is_y_increasing( clip_params
.octant
) ? 1 : -1;
652 if (line_params
.x_major
)
654 line_params
.err_add_1
= 2 * abs_dy
- 2 * abs_dx
;
655 line_params
.err_add_2
= 2 * abs_dy
;
659 line_params
.err_add_1
= 2 * abs_dx
- 2 * abs_dy
;
660 line_params
.err_add_2
= 2 * abs_dx
;
663 for(i
= 0; i
< clip
->numRects
; i
++)
665 POINT clipped_start
, clipped_end
;
667 clip_status
= clip_line(start
, end
, clip
->rects
+ i
, &clip_params
, &clipped_start
, &clipped_end
);
671 int m
= abs(clipped_start
.x
- start
->x
);
672 int n
= abs(clipped_start
.y
- start
->y
);
674 if (line_params
.x_major
)
676 line_params
.err_start
= 2 * abs_dy
- abs_dx
+ m
* 2 * abs_dy
- n
* 2 * abs_dx
;
677 line_params
.length
= abs( clipped_end
.x
- clipped_start
.x
) + 1;
681 line_params
.err_start
= 2 * abs_dx
- abs_dy
+ n
* 2 * abs_dx
- m
* 2 * abs_dy
;
682 line_params
.length
= abs( clipped_end
.y
- clipped_start
.y
) + 1;
685 if (clipped_end
.x
== end
->x
&& clipped_end
.y
== end
->y
) line_params
.length
--;
687 pdev
->dib
.funcs
->solid_line( &pdev
->dib
, &clipped_start
, &line_params
,
688 pdev
->pen_and
, pdev
->pen_xor
);
690 if(clip_status
== 2) break; /* completely unclipped, so we can finish */
695 release_wine_region(pdev
->clip
);
699 static BOOL
solid_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
704 for (i
= 0; i
< num
- 1; i
++)
705 if (!solid_pen_line( pdev
, pts
+ i
, pts
+ i
+ 1 ))
708 if (close
) return solid_pen_line( pdev
, pts
+ num
- 1, pts
);
713 void reset_dash_origin(dibdrv_physdev
*pdev
)
715 pdev
->dash_pos
.cur_dash
= 0;
716 pdev
->dash_pos
.left_in_dash
= pdev
->pen_pattern
.dashes
[0];
717 pdev
->dash_pos
.mark
= TRUE
;
720 static inline void skip_dash(dibdrv_physdev
*pdev
, unsigned int skip
)
722 skip
%= pdev
->pen_pattern
.total_len
;
725 if(pdev
->dash_pos
.left_in_dash
> skip
)
727 pdev
->dash_pos
.left_in_dash
-= skip
;
730 skip
-= pdev
->dash_pos
.left_in_dash
;
731 pdev
->dash_pos
.cur_dash
++;
732 if(pdev
->dash_pos
.cur_dash
== pdev
->pen_pattern
.count
) pdev
->dash_pos
.cur_dash
= 0;
733 pdev
->dash_pos
.left_in_dash
= pdev
->pen_pattern
.dashes
[pdev
->dash_pos
.cur_dash
];
734 pdev
->dash_pos
.mark
= !pdev
->dash_pos
.mark
;
738 static inline void get_dash_colors(const dibdrv_physdev
*pdev
, DWORD
*and, DWORD
*xor)
740 if(pdev
->dash_pos
.mark
)
742 *and = pdev
->pen_and
;
743 *xor = pdev
->pen_xor
;
747 get_pen_bkgnd_masks( pdev
, and, xor );
751 static void dashed_pen_line_callback(dibdrv_physdev
*pdev
, INT x
, INT y
)
756 get_dash_colors(pdev
, &and, &xor);
762 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
766 static BOOL
dashed_pen_line(dibdrv_physdev
*pdev
, POINT
*start
, POINT
*end
)
768 const WINEREGION
*clip
= get_wine_region(pdev
->clip
);
772 const dash_pos start_pos
= pdev
->dash_pos
;
774 if(start
->y
== end
->y
) /* hline */
777 INT left
, right
, cur_x
;
780 rect
.bottom
= start
->y
+ 1;
782 if(start
->x
<= end
->x
)
795 for(i
= 0; i
< clip
->numRects
; i
++)
797 if(clip
->rects
[i
].top
> start
->y
) break;
798 if(clip
->rects
[i
].bottom
<= start
->y
) continue;
800 if(clip
->rects
[i
].right
> left
&& clip
->rects
[i
].left
<= right
)
802 int clipped_left
= max(clip
->rects
[i
].left
, left
);
803 int clipped_right
= min(clip
->rects
[i
].right
- 1, right
);
805 pdev
->dash_pos
= start_pos
;
809 cur_x
= clipped_left
;
811 skip_dash(pdev
, clipped_left
- left
);
813 while(cur_x
<= clipped_right
)
815 get_dash_colors(pdev
, &and, &xor);
816 dash_len
= pdev
->dash_pos
.left_in_dash
;
817 if(cur_x
+ dash_len
> clipped_right
+ 1)
818 dash_len
= clipped_right
- cur_x
+ 1;
820 rect
.right
= cur_x
+ dash_len
;
822 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
824 skip_dash(pdev
, dash_len
);
829 cur_x
= clipped_right
;
831 skip_dash(pdev
, right
- clipped_right
);
833 while(cur_x
>= clipped_left
)
835 get_dash_colors(pdev
, &and, &xor);
836 dash_len
= pdev
->dash_pos
.left_in_dash
;
837 if(cur_x
- dash_len
< clipped_left
- 1)
838 dash_len
= cur_x
- clipped_left
+ 1;
839 rect
.left
= cur_x
- dash_len
+ 1;
840 rect
.right
= cur_x
+ 1;
842 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
844 skip_dash(pdev
, dash_len
);
849 pdev
->dash_pos
= start_pos
;
850 skip_dash(pdev
, right
- left
+ 1);
852 else if(start
->x
== end
->x
) /* vline */
855 INT top
, bottom
, cur_y
;
857 rect
.left
= start
->x
;
858 rect
.right
= start
->x
+ 1;
860 if(start
->y
<= end
->y
)
873 for(i
= 0; i
< clip
->numRects
; i
++)
875 if(clip
->rects
[i
].top
> bottom
) break;
876 if(clip
->rects
[i
].bottom
<= top
) continue;
877 if(clip
->rects
[i
].right
> start
->x
&& clip
->rects
[i
].left
<= start
->x
)
879 int clipped_top
= max(clip
->rects
[i
].top
, top
);
880 int clipped_bottom
= min(clip
->rects
[i
].bottom
- 1, bottom
);
882 pdev
->dash_pos
= start_pos
;
888 skip_dash(pdev
, clipped_top
- top
);
890 while(cur_y
<= clipped_bottom
)
892 get_dash_colors(pdev
, &and, &xor);
893 dash_len
= pdev
->dash_pos
.left_in_dash
;
894 if(cur_y
+ dash_len
> clipped_bottom
+ 1)
895 dash_len
= clipped_bottom
- cur_y
+ 1;
897 rect
.bottom
= cur_y
+ dash_len
;
899 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
901 skip_dash(pdev
, dash_len
);
906 cur_y
= clipped_bottom
;
908 skip_dash(pdev
, bottom
- clipped_bottom
);
910 while(cur_y
>= clipped_top
)
912 get_dash_colors(pdev
, &and, &xor);
913 dash_len
= pdev
->dash_pos
.left_in_dash
;
914 if(cur_y
- dash_len
< clipped_top
- 1)
915 dash_len
= cur_y
- clipped_top
+ 1;
916 rect
.top
= cur_y
- dash_len
+ 1;
917 rect
.bottom
= cur_y
+ 1;
919 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
921 skip_dash(pdev
, dash_len
);
926 pdev
->dash_pos
= start_pos
;
927 skip_dash(pdev
, bottom
- top
+ 1);
931 bres_params clip_params
;
932 struct line_params line_params
;
933 INT dx
= end
->x
- start
->x
, dy
= end
->y
- start
->y
;
934 INT abs_dx
= abs(dx
), abs_dy
= abs(dy
);
937 clip_params
.dx
= abs_dx
;
938 clip_params
.dy
= abs_dy
;
939 clip_params
.octant
= get_octant_mask(dx
, dy
);
940 clip_params
.bias
= get_bias( clip_params
.octant
);
942 line_params
.bias
= clip_params
.bias
;
943 line_params
.x_major
= is_xmajor( clip_params
.octant
);
944 line_params
.x_inc
= is_x_increasing( clip_params
.octant
) ? 1 : -1;
945 line_params
.y_inc
= is_y_increasing( clip_params
.octant
) ? 1 : -1;
947 if (line_params
.x_major
)
949 line_params
.err_add_1
= 2 * abs_dy
- 2 * abs_dx
;
950 line_params
.err_add_2
= 2 * abs_dy
;
954 line_params
.err_add_1
= 2 * abs_dx
- 2 * abs_dy
;
955 line_params
.err_add_2
= 2 * abs_dx
;
958 for(i
= 0; i
< clip
->numRects
; i
++)
960 POINT clipped_start
, clipped_end
;
962 clip_status
= clip_line(start
, end
, clip
->rects
+ i
, &clip_params
, &clipped_start
, &clipped_end
);
966 int m
= abs(clipped_start
.x
- start
->x
);
967 int n
= abs(clipped_start
.y
- start
->y
);
969 pdev
->dash_pos
= start_pos
;
971 if (line_params
.x_major
)
973 line_params
.err_start
= 2 * abs_dy
- abs_dx
+ m
* 2 * abs_dy
- n
* 2 * abs_dx
;
974 line_params
.length
= abs( clipped_end
.x
- clipped_start
.x
) + 1;
979 line_params
.err_start
= 2 * abs_dx
- abs_dy
+ n
* 2 * abs_dx
- m
* 2 * abs_dy
;
980 line_params
.length
= abs( clipped_end
.y
- clipped_start
.y
) + 1;
983 if (clipped_end
.x
== end
->x
&& clipped_end
.y
== end
->y
) line_params
.length
--;
985 bres_line_with_bias( &clipped_start
, &line_params
, dashed_pen_line_callback
, pdev
);
987 if(clip_status
== 2) break; /* completely unclipped, so we can finish */
990 pdev
->dash_pos
= start_pos
;
991 if(line_params
.x_major
)
992 skip_dash(pdev
, abs_dx
);
994 skip_dash(pdev
, abs_dy
);
997 release_wine_region(pdev
->clip
);
1001 static BOOL
dashed_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
1006 for (i
= 0; i
< num
- 1; i
++)
1007 if (!dashed_pen_line( pdev
, pts
+ i
, pts
+ i
+ 1 ))
1010 if (close
) return dashed_pen_line( pdev
, pts
+ num
- 1, pts
);
1015 static BOOL
null_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
1020 static void add_cap( dibdrv_physdev
*pdev
, HRGN region
, const POINT
*pt
)
1024 switch (pdev
->pen_endcap
)
1026 case PS_ENDCAP_ROUND
:
1027 cap
= CreateEllipticRgn( pt
->x
- pdev
->pen_width
/ 2, pt
->y
- pdev
->pen_width
/ 2,
1028 pt
->x
+ (pdev
->pen_width
+ 1) / 2, pt
->y
+ (pdev
->pen_width
+ 1) / 2 );
1031 default: /* only supporting cosmetic pens so far, so always PS_ENDCAP_ROUND */
1035 CombineRgn( region
, region
, cap
, RGN_OR
);
1036 DeleteObject( cap
);
1040 static void add_join( dibdrv_physdev
*pdev
, HRGN region
, const POINT
*pt
)
1044 switch (pdev
->pen_join
)
1047 join
= CreateEllipticRgn( pt
->x
- pdev
->pen_width
/ 2, pt
->y
- pdev
->pen_width
/ 2,
1048 pt
->x
+ (pdev
->pen_width
+ 1) / 2, pt
->y
+ (pdev
->pen_width
+ 1) / 2 );
1051 default: /* only supporting cosmetic pens so far, so always PS_JOIN_ROUND */
1055 CombineRgn( region
, region
, join
, RGN_OR
);
1056 DeleteObject( join
);
1060 #define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5)
1062 static HRGN
get_wide_lines_region( dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
1065 HRGN total
, segment
;
1069 total
= CreateRectRgn( 0, 0, 0, 0 );
1072 for (i
= 0; i
< num
; i
++)
1074 const POINT
*pt_1
= pts
+ i
;
1075 const POINT
*pt_2
= pts
+ ((close
&& i
== num
- 1) ? 0 : i
+ 1);
1076 int dx
= pt_2
->x
- pt_1
->x
;
1077 int dy
= pt_2
->y
- pt_1
->y
;
1080 if (dx
== 0 && dy
== 0) continue;
1084 rect
.left
= min( pt_1
->x
, pt_2
->x
);
1085 rect
.right
= rect
.left
+ abs( dx
);
1086 rect
.top
= pt_1
->y
- pdev
->pen_width
/ 2;
1087 rect
.bottom
= rect
.top
+ pdev
->pen_width
;
1088 segment
= CreateRectRgnIndirect( &rect
);
1092 rect
.top
= min( pt_1
->y
, pt_2
->y
);
1093 rect
.bottom
= rect
.top
+ abs( dy
);
1094 rect
.left
= pt_1
->x
- pdev
->pen_width
/ 2;
1095 rect
.right
= rect
.left
+ pdev
->pen_width
;
1096 segment
= CreateRectRgnIndirect( &rect
);
1100 double len
= hypot( dx
, dy
);
1101 double width_x
, width_y
;
1103 POINT wide_half
, narrow_half
;
1105 width_x
= pdev
->pen_width
* abs( dy
) / len
;
1106 width_y
= pdev
->pen_width
* abs( dx
) / len
;
1108 narrow_half
.x
= round( width_x
/ 2 );
1109 narrow_half
.y
= round( width_y
/ 2 );
1110 wide_half
.x
= round( (width_x
+ 1) / 2 );
1111 wide_half
.y
= round( (width_y
+ 1) / 2 );
1115 wide_half
.y
= -wide_half
.y
;
1116 narrow_half
.y
= -narrow_half
.y
;
1121 POINT tmp
= narrow_half
; narrow_half
= wide_half
; wide_half
= tmp
;
1122 wide_half
.x
= -wide_half
.x
;
1123 narrow_half
.x
= -narrow_half
.x
;
1126 seg_pts
[0].x
= pt_1
->x
- narrow_half
.x
;
1127 seg_pts
[0].y
= pt_1
->y
+ narrow_half
.y
;
1128 seg_pts
[1].x
= pt_1
->x
+ wide_half
.x
;
1129 seg_pts
[1].y
= pt_1
->y
- wide_half
.y
;
1130 seg_pts
[2].x
= pt_2
->x
+ wide_half
.x
;
1131 seg_pts
[2].y
= pt_2
->y
- wide_half
.y
;
1132 seg_pts
[3].x
= pt_2
->x
- narrow_half
.x
;
1133 seg_pts
[3].y
= pt_2
->y
+ narrow_half
.y
;
1135 segment
= CreatePolygonRgn( seg_pts
, 4, ALTERNATE
);
1138 CombineRgn( total
, total
, segment
, RGN_OR
);
1139 DeleteObject( segment
);
1143 if (!close
) add_cap( pdev
, total
, pt_1
);
1146 add_join( pdev
, total
, pt_1
);
1150 if (close
) add_join( pdev
, total
, pt_2
);
1151 else add_cap( pdev
, total
, pt_2
);
1158 static BOOL
wide_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
1160 const WINEREGION
*data
;
1164 color
.and = pdev
->pen_and
;
1165 color
.xor = pdev
->pen_xor
;
1167 region
= get_wide_lines_region( pdev
, num
, pts
, close
);
1169 if (CombineRgn( region
, region
, pdev
->clip
, RGN_AND
) != ERROR
)
1171 data
= get_wine_region( region
);
1172 solid_rects( &pdev
->dib
, data
->numRects
, data
->rects
, &color
, NULL
);
1173 release_wine_region( region
);
1176 DeleteObject( region
);
1180 static const dash_pattern dash_patterns
[5] =
1182 {0, {0}, 0}, /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
1183 {2, {18, 6}, 24}, /* PS_DASH */
1184 {2, {3, 3}, 6}, /* PS_DOT */
1185 {4, {9, 6, 3, 6}, 24}, /* PS_DASHDOT */
1186 {6, {9, 3, 3, 3, 3, 3}, 24} /* PS_DASHDOTDOT */
1189 static inline int get_pen_device_width( dibdrv_physdev
*pdev
, LOGPEN
*pen
)
1191 int width
= pen
->lopnWidth
.x
;
1193 if (pen
->lopnStyle
& PS_GEOMETRIC
&& width
> 1)
1196 pts
[0].x
= pts
[0].y
= pts
[1].y
= 0;
1198 LPtoDP( pdev
->dev
.hdc
, pts
, 2 );
1199 width
= max( abs( pts
[1].x
- pts
[0].x
), 1 );
1204 /***********************************************************************
1207 HPEN
dibdrv_SelectPen( PHYSDEV dev
, HPEN hpen
)
1209 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSelectPen
);
1210 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1214 TRACE("(%p, %p)\n", dev
, hpen
);
1216 if (!GetObjectW( hpen
, sizeof(logpen
), &logpen
))
1218 /* must be an extended pen */
1220 INT size
= GetObjectW( hpen
, 0, NULL
);
1222 if (!size
) return 0;
1224 elp
= HeapAlloc( GetProcessHeap(), 0, size
);
1226 GetObjectW( hpen
, size
, elp
);
1227 /* FIXME: add support for user style pens */
1228 logpen
.lopnStyle
= elp
->elpPenStyle
;
1229 logpen
.lopnWidth
.x
= elp
->elpWidth
;
1230 logpen
.lopnWidth
.y
= 0;
1231 logpen
.lopnColor
= elp
->elpColor
;
1233 HeapFree( GetProcessHeap(), 0, elp
);
1236 pdev
->pen_join
= logpen
.lopnStyle
& PS_JOIN_MASK
;
1237 pdev
->pen_endcap
= logpen
.lopnStyle
& PS_ENDCAP_MASK
;
1238 pdev
->pen_width
= get_pen_device_width( pdev
, &logpen
);
1240 if (hpen
== GetStockObject( DC_PEN
))
1241 logpen
.lopnColor
= GetDCPenColor( dev
->hdc
);
1243 pdev
->pen_colorref
= logpen
.lopnColor
;
1244 pdev
->pen_color
= get_pixel_color( pdev
, pdev
->pen_colorref
, TRUE
);
1245 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->pen_color
, &pdev
->pen_and
, &pdev
->pen_xor
);
1247 pdev
->pen_pattern
= dash_patterns
[PS_SOLID
];
1249 pdev
->defer
|= DEFER_PEN
;
1251 style
= logpen
.lopnStyle
& PS_STYLE_MASK
;
1256 if(logpen
.lopnStyle
& PS_GEOMETRIC
) break;
1257 if(pdev
->pen_width
<= 1)
1258 pdev
->pen_lines
= solid_pen_lines
;
1260 pdev
->pen_lines
= wide_pen_lines
;
1261 pdev
->defer
&= ~DEFER_PEN
;
1268 if(logpen
.lopnStyle
& PS_GEOMETRIC
) break;
1269 if(logpen
.lopnWidth
.x
> 1) break;
1270 pdev
->pen_lines
= dashed_pen_lines
;
1271 pdev
->pen_pattern
= dash_patterns
[style
];
1272 pdev
->defer
&= ~DEFER_PEN
;
1276 pdev
->pen_lines
= null_pen_lines
;
1277 pdev
->defer
&= ~DEFER_PEN
;
1284 return next
->funcs
->pSelectPen( next
, hpen
);
1287 /***********************************************************************
1288 * dibdrv_SetDCPenColor
1290 COLORREF
dibdrv_SetDCPenColor( PHYSDEV dev
, COLORREF color
)
1292 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSetDCPenColor
);
1293 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1295 if (GetCurrentObject(dev
->hdc
, OBJ_PEN
) == GetStockObject( DC_PEN
))
1297 pdev
->pen_colorref
= color
;
1298 pdev
->pen_color
= get_pixel_color( pdev
, pdev
->pen_colorref
, TRUE
);
1299 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->pen_color
, &pdev
->pen_and
, &pdev
->pen_xor
);
1302 return next
->funcs
->pSetDCPenColor( next
, color
);
1305 void solid_rects( dib_info
*dib
, int num
, const RECT
*rects
, const rop_mask
*color
, HRGN region
)
1308 const WINEREGION
*clip
;
1312 dib
->funcs
->solid_rects( dib
, num
, rects
, color
->and, color
->xor );
1316 clip
= get_wine_region( region
);
1318 for(i
= 0; i
< num
; i
++)
1320 for(j
= 0; j
< clip
->numRects
; j
++)
1324 if (intersect_rect( &clipped_rect
, rects
+ i
, clip
->rects
+ j
))
1325 dib
->funcs
->solid_rects( dib
, 1, &clipped_rect
, color
->and, color
->xor );
1328 release_wine_region( region
);
1331 /**********************************************************************
1334 * Fill a number of rectangles with the solid brush
1336 static BOOL
solid_brush(dibdrv_physdev
*pdev
, dib_info
*dib
, int num
, const RECT
*rects
, HRGN region
)
1338 rop_mask brush_color
;
1340 brush_color
.and = pdev
->brush_and
;
1341 brush_color
.xor = pdev
->brush_xor
;
1343 solid_rects( dib
, num
, rects
, &brush_color
, region
);
1347 static void free_pattern_brush_bits( dibdrv_physdev
*pdev
)
1349 HeapFree(GetProcessHeap(), 0, pdev
->brush_and_bits
);
1350 HeapFree(GetProcessHeap(), 0, pdev
->brush_xor_bits
);
1351 pdev
->brush_and_bits
= NULL
;
1352 pdev
->brush_xor_bits
= NULL
;
1355 void free_pattern_brush( dibdrv_physdev
*pdev
)
1357 free_pattern_brush_bits( pdev
);
1358 free_dib_info( &pdev
->brush_dib
);
1361 static BOOL
create_pattern_brush_bits(dibdrv_physdev
*pdev
)
1363 DWORD size
= pdev
->brush_dib
.height
* abs(pdev
->brush_dib
.stride
);
1364 DWORD
*brush_bits
= pdev
->brush_dib
.bits
.ptr
;
1365 DWORD
*and_bits
, *xor_bits
;
1367 assert(pdev
->brush_and_bits
== NULL
);
1368 assert(pdev
->brush_xor_bits
== NULL
);
1370 assert(pdev
->brush_dib
.stride
> 0);
1372 and_bits
= pdev
->brush_and_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1373 xor_bits
= pdev
->brush_xor_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1375 if(!and_bits
|| !xor_bits
)
1377 ERR("Failed to create pattern brush bits\n");
1378 free_pattern_brush_bits( pdev
);
1384 calc_and_xor_masks(pdev
->brush_rop
, *brush_bits
++, and_bits
++, xor_bits
++);
1391 static const DWORD hatches
[6][8] =
1393 { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1394 { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL */
1395 { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL */
1396 { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL */
1397 { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS */
1398 { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 } /* HS_DIAGCROSS */
1401 static BOOL
create_hatch_brush_bits(dibdrv_physdev
*pdev
)
1404 rop_mask fg_mask
, bg_mask
;
1405 rop_mask_bits mask_bits
;
1409 assert(pdev
->brush_and_bits
== NULL
);
1410 assert(pdev
->brush_xor_bits
== NULL
);
1412 /* Just initialise brush_dib with the color / sizing info. We don't
1413 need the bits as we'll calculate the rop masks straight from
1414 the hatch patterns. */
1416 copy_dib_color_info(&pdev
->brush_dib
, &pdev
->dib
);
1417 pdev
->brush_dib
.width
= 8;
1418 pdev
->brush_dib
.height
= 8;
1419 pdev
->brush_dib
.stride
= get_dib_stride( pdev
->brush_dib
.width
, pdev
->brush_dib
.bit_count
);
1421 size
= pdev
->brush_dib
.height
* pdev
->brush_dib
.stride
;
1423 mask_bits
.and = pdev
->brush_and_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1424 mask_bits
.xor = pdev
->brush_xor_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1426 if(!mask_bits
.and || !mask_bits
.xor)
1428 ERR("Failed to create pattern brush bits\n");
1429 free_pattern_brush_bits( pdev
);
1433 hatch
.bit_count
= 1;
1434 hatch
.height
= hatch
.width
= 8;
1436 hatch
.bits
.ptr
= (void *) hatches
[pdev
->brush_hatch
];
1437 hatch
.bits
.free
= hatch
.bits
.param
= NULL
;
1438 hatch
.bits
.is_copy
= FALSE
;
1440 fg_mask
.and = pdev
->brush_and
;
1441 fg_mask
.xor = pdev
->brush_xor
;
1442 get_brush_bkgnd_masks( pdev
, &bg_mask
.and, &bg_mask
.xor );
1444 ret
= pdev
->brush_dib
.funcs
->create_rop_masks( &pdev
->brush_dib
, &hatch
, &fg_mask
, &bg_mask
, &mask_bits
);
1445 if(!ret
) free_pattern_brush_bits( pdev
);
1450 static BOOL
matching_pattern_format( dib_info
*dib
, dib_info
*pattern
)
1452 if (dib
->bit_count
!= pattern
->bit_count
) return FALSE
;
1453 if (dib
->stride
!= pattern
->stride
) return FALSE
;
1455 switch (dib
->bit_count
)
1460 if (dib
->color_table_size
!= pattern
->color_table_size
) return FALSE
;
1461 return !memcmp( dib
->color_table
, pattern
->color_table
, dib
->color_table_size
* sizeof(RGBQUAD
) );
1464 return (dib
->red_mask
== pattern
->red_mask
&&
1465 dib
->green_mask
== pattern
->green_mask
&&
1466 dib
->blue_mask
== pattern
->blue_mask
);
1471 static BOOL
select_pattern_brush( dibdrv_physdev
*pdev
, BOOL
*needs_reselect
)
1473 char buffer
[FIELD_OFFSET( BITMAPINFO
, bmiColors
[256] )];
1474 BITMAPINFO
*info
= (BITMAPINFO
*)buffer
;
1475 RGBQUAD color_table
[2];
1479 if (!pdev
->brush_pattern_info
)
1481 BITMAPOBJ
*bmp
= GDI_GetObjPtr( pdev
->brush_pattern_bitmap
, OBJ_BITMAP
);
1484 if (!bmp
) return FALSE
;
1485 ret
= init_dib_info_from_bitmapobj( &pattern
, bmp
, 0 );
1486 GDI_ReleaseObj( pdev
->brush_pattern_bitmap
);
1487 if (!ret
) return FALSE
;
1489 else if (pdev
->brush_pattern_info
->bmiHeader
.biClrUsed
&& pdev
->brush_pattern_usage
== DIB_PAL_COLORS
)
1491 copy_bitmapinfo( info
, pdev
->brush_pattern_info
);
1492 fill_color_table_from_pal_colors( info
, pdev
->dev
.hdc
);
1493 init_dib_info_from_bitmapinfo( &pattern
, info
, pdev
->brush_pattern_bits
, 0 );
1494 *needs_reselect
= TRUE
;
1498 init_dib_info_from_bitmapinfo( &pattern
, pdev
->brush_pattern_info
, pdev
->brush_pattern_bits
, 0 );
1501 if (pattern
.bit_count
== 1 && !pattern
.color_table
)
1503 /* monochrome DDB pattern uses DC colors */
1504 COLORREF color
= GetTextColor( pdev
->dev
.hdc
);
1505 color_table
[0].rgbRed
= GetRValue( color
);
1506 color_table
[0].rgbGreen
= GetGValue( color
);
1507 color_table
[0].rgbBlue
= GetBValue( color
);
1508 color_table
[0].rgbReserved
= 0;
1509 color
= GetBkColor( pdev
->dev
.hdc
);
1510 color_table
[1].rgbRed
= GetRValue( color
);
1511 color_table
[1].rgbGreen
= GetGValue( color
);
1512 color_table
[1].rgbBlue
= GetBValue( color
);
1513 color_table
[1].rgbReserved
= 0;
1514 pattern
.color_table
= color_table
;
1515 pattern
.color_table_size
= 2;
1516 *needs_reselect
= TRUE
;
1519 copy_dib_color_info(&pdev
->brush_dib
, &pdev
->dib
);
1521 pdev
->brush_dib
.height
= pattern
.height
;
1522 pdev
->brush_dib
.width
= pattern
.width
;
1523 pdev
->brush_dib
.stride
= get_dib_stride( pdev
->brush_dib
.width
, pdev
->brush_dib
.bit_count
);
1525 if (matching_pattern_format( &pdev
->brush_dib
, &pattern
))
1527 pdev
->brush_dib
.bits
.ptr
= pattern
.bits
.ptr
;
1528 pdev
->brush_dib
.bits
.is_copy
= FALSE
;
1529 pdev
->brush_dib
.bits
.free
= NULL
;
1533 pdev
->brush_dib
.bits
.ptr
= HeapAlloc( GetProcessHeap(), 0,
1534 pdev
->brush_dib
.height
* pdev
->brush_dib
.stride
);
1535 pdev
->brush_dib
.bits
.is_copy
= TRUE
;
1536 pdev
->brush_dib
.bits
.free
= free_heap_bits
;
1538 rect
.left
= rect
.top
= 0;
1539 rect
.right
= pattern
.width
;
1540 rect
.bottom
= pattern
.height
;
1542 pdev
->brush_dib
.funcs
->convert_to(&pdev
->brush_dib
, &pattern
, &rect
);
1547 /**********************************************************************
1550 * Fill a number of rectangles with the pattern brush
1551 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1553 static BOOL
pattern_brush(dibdrv_physdev
*pdev
, dib_info
*dib
, int num
, const RECT
*rects
, HRGN region
)
1556 const WINEREGION
*clip
;
1558 BOOL needs_reselect
= FALSE
;
1560 if(pdev
->brush_and_bits
== NULL
)
1562 switch(pdev
->brush_style
)
1565 if (!pdev
->brush_dib
.bits
.ptr
&& !select_pattern_brush( pdev
, &needs_reselect
))
1567 if(!create_pattern_brush_bits(pdev
))
1572 if(!create_hatch_brush_bits(pdev
))
1577 ERR("Unexpected brush style %d\n", pdev
->brush_style
);
1582 GetBrushOrgEx(pdev
->dev
.hdc
, &origin
);
1584 clip
= get_wine_region( region
);
1588 dib
->funcs
->pattern_rects( dib
, num
, rects
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1592 for(i
= 0; i
< num
; i
++)
1594 for(j
= 0; j
< clip
->numRects
; j
++)
1596 RECT rect
= rects
[i
];
1598 /* Optimize unclipped case */
1599 if(clip
->rects
[j
].top
<= rect
.top
&& clip
->rects
[j
].bottom
>= rect
.bottom
&&
1600 clip
->rects
[j
].left
<= rect
.left
&& clip
->rects
[j
].right
>= rect
.right
)
1602 dib
->funcs
->pattern_rects( dib
, 1, &rect
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1606 if(clip
->rects
[j
].top
>= rect
.bottom
) break;
1607 if(clip
->rects
[j
].bottom
<= rect
.top
) continue;
1609 if(clip
->rects
[j
].right
> rect
.left
&& clip
->rects
[j
].left
< rect
.right
)
1611 rect
.left
= max(rect
.left
, clip
->rects
[j
].left
);
1612 rect
.top
= max(rect
.top
, clip
->rects
[j
].top
);
1613 rect
.right
= min(rect
.right
, clip
->rects
[j
].right
);
1614 rect
.bottom
= min(rect
.bottom
, clip
->rects
[j
].bottom
);
1616 dib
->funcs
->pattern_rects( dib
, 1, &rect
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1620 release_wine_region( region
);
1622 if (needs_reselect
) free_pattern_brush( pdev
);
1627 static BOOL
null_brush(dibdrv_physdev
*pdev
, dib_info
*dib
, int num
, const RECT
*rects
, HRGN region
)
1632 void update_brush_rop( dibdrv_physdev
*pdev
, INT rop
)
1634 pdev
->brush_rop
= rop
;
1635 if(pdev
->brush_style
== BS_SOLID
|| pdev
->brush_style
== BS_HATCHED
)
1636 calc_and_xor_masks(rop
, pdev
->brush_color
, &pdev
->brush_and
, &pdev
->brush_xor
);
1637 free_pattern_brush_bits( pdev
);
1640 /***********************************************************************
1641 * dibdrv_SelectBrush
1643 HBRUSH
dibdrv_SelectBrush( PHYSDEV dev
, HBRUSH hbrush
, HBITMAP bitmap
,
1644 const BITMAPINFO
*info
, void *bits
, UINT usage
)
1646 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSelectBrush
);
1647 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1650 TRACE("(%p, %p)\n", dev
, hbrush
);
1652 if (bitmap
|| info
) /* pattern brush */
1654 pdev
->brush_rects
= pattern_brush
;
1655 pdev
->brush_style
= BS_DIBPATTERN
;
1656 pdev
->brush_pattern_info
= info
;
1657 pdev
->brush_pattern_bits
= bits
;
1658 pdev
->brush_pattern_usage
= usage
;
1659 pdev
->brush_pattern_bitmap
= bitmap
;
1660 pdev
->defer
&= ~DEFER_BRUSH
;
1661 free_pattern_brush( pdev
); /* brush is actually selected only when it's used */
1663 return next
->funcs
->pSelectBrush( next
, hbrush
, bitmap
, info
, bits
, usage
);
1666 if (!GetObjectW( hbrush
, sizeof(logbrush
), &logbrush
)) return 0;
1668 if (hbrush
== GetStockObject( DC_BRUSH
))
1669 logbrush
.lbColor
= GetDCBrushColor( dev
->hdc
);
1671 pdev
->brush_style
= logbrush
.lbStyle
;
1673 pdev
->defer
|= DEFER_BRUSH
;
1675 free_pattern_brush( pdev
);
1677 switch(logbrush
.lbStyle
)
1680 pdev
->brush_colorref
= logbrush
.lbColor
;
1681 pdev
->brush_color
= get_pixel_color( pdev
, pdev
->brush_colorref
, TRUE
);
1682 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->brush_color
, &pdev
->brush_and
, &pdev
->brush_xor
);
1683 pdev
->brush_rects
= solid_brush
;
1684 pdev
->defer
&= ~DEFER_BRUSH
;
1688 pdev
->brush_rects
= null_brush
;
1689 pdev
->defer
&= ~DEFER_BRUSH
;
1693 if(logbrush
.lbHatch
> HS_DIAGCROSS
) return 0;
1694 pdev
->brush_hatch
= logbrush
.lbHatch
;
1695 pdev
->brush_colorref
= logbrush
.lbColor
;
1696 pdev
->brush_color
= get_pixel_color( pdev
, pdev
->brush_colorref
, TRUE
);
1697 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->brush_color
, &pdev
->brush_and
, &pdev
->brush_xor
);
1698 pdev
->brush_rects
= pattern_brush
;
1699 pdev
->defer
&= ~DEFER_BRUSH
;
1706 return next
->funcs
->pSelectBrush( next
, hbrush
, bitmap
, info
, bits
, usage
);
1709 /***********************************************************************
1710 * dibdrv_SetDCBrushColor
1712 COLORREF
dibdrv_SetDCBrushColor( PHYSDEV dev
, COLORREF color
)
1714 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSetDCBrushColor
);
1715 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1717 if (GetCurrentObject(dev
->hdc
, OBJ_BRUSH
) == GetStockObject( DC_BRUSH
))
1719 pdev
->brush_colorref
= color
;
1720 pdev
->brush_color
= get_pixel_color( pdev
, pdev
->brush_colorref
, TRUE
);
1721 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->brush_color
, &pdev
->brush_and
, &pdev
->brush_xor
);
1724 return next
->funcs
->pSetDCBrushColor( next
, color
);
1727 BOOL
brush_rects(dibdrv_physdev
*pdev
, int num
, const RECT
*rects
)
1729 return pdev
->brush_rects( pdev
, &pdev
->dib
, num
, rects
, pdev
->clip
);