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
)
131 if (color
& (1 << 24)) /* PALETTEINDEX */
133 HPALETTE pal
= GetCurrentObject( hdc
, OBJ_PAL
);
134 PALETTEENTRY pal_ent
;
136 if (!GetPaletteEntries( pal
, LOWORD(color
), 1, &pal_ent
))
137 GetPaletteEntries( pal
, 0, 1, &pal_ent
);
138 return RGB( pal_ent
.peRed
, pal_ent
.peGreen
, pal_ent
.peBlue
);
141 if (color
>> 16 == 0x10ff) /* DIBINDEX */
143 WORD index
= LOWORD( color
);
145 if (!dib
->color_table
|| index
>= (1 << dib
->bit_count
)) return 0;
147 return RGB( dib
->color_table
[index
].rgbRed
,
148 dib
->color_table
[index
].rgbGreen
,
149 dib
->color_table
[index
].rgbBlue
);
152 return color
& 0xffffff;
155 /******************************************************************
158 * 1 bit bitmaps map the fg/bg colors as follows:
159 * If the fg colorref exactly matches one of the color table entries then
160 * that entry is the fg color and the other is the bg.
161 * Otherwise the bg color is mapped to the closest entry in the table and
162 * the fg takes the other one.
164 DWORD
get_pixel_color( dibdrv_physdev
*pdev
, COLORREF color
, BOOL mono_fixup
)
171 rgb_ref
= make_rgb_colorref( pdev
->dev
.hdc
, &pdev
->dib
, color
, &got_pixel
, &pixel
);
172 if (got_pixel
) return pixel
;
174 if (pdev
->dib
.bit_count
!= 1 || !mono_fixup
)
175 return pdev
->dib
.funcs
->colorref_to_pixel( &pdev
->dib
, rgb_ref
);
177 fg_quad
= rgbquad_from_colorref( rgb_ref
);
178 if(rgbquad_equal(&fg_quad
, pdev
->dib
.color_table
))
180 if(rgbquad_equal(&fg_quad
, pdev
->dib
.color_table
+ 1))
183 pixel
= get_pixel_color( pdev
, GetBkColor(pdev
->dev
.hdc
), FALSE
);
184 if (color
== GetBkColor(pdev
->dev
.hdc
)) return pixel
;
188 /***************************************************************************
189 * get_pen_bkgnd_masks
191 * Returns the pre-calculated bkgnd color masks unless the dib is 1 bpp.
192 * In this case since there are several fg sources (pen, brush, text)
193 * this makes pdev->bkgnd_color unusable. So here we take the inverse
194 * of the relevant fg color (which is always set up correctly).
196 static inline void get_pen_bkgnd_masks(dibdrv_physdev
*pdev
, DWORD
*and, DWORD
*xor)
200 if (GetBkMode(pdev
->dev
.hdc
) == TRANSPARENT
)
207 if (pdev
->dib
.bit_count
!= 1)
209 color
= get_pixel_color( pdev
, GetBkColor(pdev
->dev
.hdc
), FALSE
);
213 color
= get_pixel_color( pdev
, pdev
->pen_colorref
, TRUE
);
214 if(pdev
->pen_colorref
!= GetBkColor(pdev
->dev
.hdc
)) color
= !color
;
216 calc_and_xor_masks( GetROP2(pdev
->dev
.hdc
), color
, and, xor );
219 static inline void get_brush_bkgnd_masks(dibdrv_physdev
*pdev
, DWORD
*and, DWORD
*xor)
223 if(GetBkMode(pdev
->dev
.hdc
) == TRANSPARENT
)
230 if(pdev
->dib
.bit_count
== 1)
232 if(pdev
->brush_colorref
== GetBkColor(pdev
->dev
.hdc
))
233 color
= get_pixel_color( pdev
, pdev
->brush_colorref
, TRUE
);
235 color
= ~get_pixel_color( pdev
, pdev
->brush_colorref
, TRUE
);
237 else color
= get_pixel_color( pdev
, GetBkColor( pdev
->dev
.hdc
), FALSE
);
239 calc_and_xor_masks( pdev
->brush_rop
, color
, and, xor );
243 static inline void order_end_points(int *s
, int *e
)
254 #define Y_INCREASING_MASK 0x0f
255 #define X_INCREASING_MASK 0xc3
256 #define X_MAJOR_MASK 0x99
257 #define POS_SLOPE_MASK 0x33
259 static inline BOOL
is_xmajor(DWORD octant
)
261 return octant
& X_MAJOR_MASK
;
264 static inline BOOL
is_pos_slope(DWORD octant
)
266 return octant
& POS_SLOPE_MASK
;
269 static inline BOOL
is_x_increasing(DWORD octant
)
271 return octant
& X_INCREASING_MASK
;
274 static inline BOOL
is_y_increasing(DWORD octant
)
276 return octant
& Y_INCREASING_MASK
;
279 /**********************************************************************
282 * Return the octant number starting clockwise from the +ve x-axis.
284 static inline int get_octant_number(int dx
, int dy
)
288 return ( dx
> dy
) ? 1 : 2;
290 return (-dx
> dy
) ? 4 : 3;
293 return (-dx
> -dy
) ? 5 : 6;
295 return ( dx
> -dy
) ? 8 : 7;
298 static inline DWORD
get_octant_mask(int dx
, int dy
)
300 return 1 << (get_octant_number(dx
, dy
) - 1);
303 static inline int get_bias( DWORD mask
)
305 /* Octants 3, 5, 6 and 8 take a bias */
306 return (mask
& 0xb4) ? 1 : 0;
314 static inline DWORD
calc_outcode(const POINT
*pt
, const RECT
*clip
)
317 if(pt
->x
< clip
->left
) out
|= OUT_LEFT
;
318 else if(pt
->x
>= clip
->right
) out
|= OUT_RIGHT
;
319 if(pt
->y
< clip
->top
) out
|= OUT_TOP
;
320 else if(pt
->y
>= clip
->bottom
) out
|= OUT_BOTTOM
;
325 /******************************************************************************
328 * Clips the start and end points to a rectangle.
330 * Note, this treats the end point like the start point. If the
331 * caller doesn't want it displayed, it should exclude it. If the end
332 * point is clipped out, then the likelihood is that the new end point
333 * should be displayed.
335 * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
337 * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
338 * however the Bresenham error term is defined differently so the equations
341 * For x major lines we have 2dy >= err + bias > 2dy - 2dx
342 * 0 >= err + bias - 2dy > -2dx
344 * Note dx, dy, m and n are all +ve.
346 * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
347 * err = 2dy - dx + 2mdy - 2ndx
348 * 0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
349 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
350 * which of course will give exactly one solution for n,
351 * so looking at the >= inequality
352 * n >= (2mdy + bias - dx) / 2dx
353 * n = ceiling((2mdy + bias - dx) / 2dx)
354 * = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
356 * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
357 * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
358 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
359 * 2mdy > 2ndx - bias - dx
360 * m > (2ndx - bias - dx) / 2dy
361 * m = floor((2ndx - bias - dx) / 2dy) + 1
362 * m = (2ndx - bias - dx) / 2dy + 1
364 * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
365 * err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
366 * = 2dy - dx - 2mdy + 2ndx
367 * 0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
368 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
369 * again exactly one solution.
370 * 2ndx <= 2mdy - bias + dx
371 * n = floor((2mdy - bias + dx) / 2dx)
372 * = (2mdy - bias + dx) / 2dx
374 * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
375 * mininizing m to include all of the points at y = y2 - n. As above:
376 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
377 * 2mdy >= 2ndx + bias - dx
378 * m = ceiling((2ndx + bias - dx) / 2dy)
379 * = (2ndx + bias - dx - 1) / 2dy + 1
381 * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
383 * Moving start point from y1 to y1 + n find x1 + m
384 * m = (2ndx + bias + dy - 1) / 2dy
386 * Moving start point from x1 to x1 + m find y1 + n
387 * n = (2mdy - bias - dy) / 2ndx + 1
389 * Moving end point from y2 to y2 - n find x1 - m
390 * m = (2ndx - bias + dy) / 2dy
392 * Moving end point from x2 to x2 - m find y2 - n
393 * n = (2mdy + bias - dy - 1) / 2dx + 1
395 int clip_line(const POINT
*start
, const POINT
*end
, const RECT
*clip
,
396 const bres_params
*params
, POINT
*pt1
, POINT
*pt2
)
399 BOOL clipped
= FALSE
;
400 DWORD start_oc
, end_oc
;
401 const int bias
= params
->bias
;
402 const unsigned int dx
= params
->dx
;
403 const unsigned int dy
= params
->dy
;
404 const unsigned int two_dx
= params
->dx
* 2;
405 const unsigned int two_dy
= params
->dy
* 2;
406 const BOOL xmajor
= is_xmajor(params
->octant
);
407 const BOOL neg_slope
= !is_pos_slope(params
->octant
);
412 start_oc
= calc_outcode(start
, clip
);
413 end_oc
= calc_outcode(end
, clip
);
417 if(start_oc
== 0 && end_oc
== 0) return clipped
? 1 : 2; /* trivial accept */
418 if(start_oc
& end_oc
) return 0; /* trivial reject */
421 if(start_oc
& OUT_LEFT
)
423 m
= clip
->left
- start
->x
;
425 n
= (m
* two_dy
+ bias
+ dx
- 1) / two_dx
;
427 n
= (m
* two_dy
- bias
- dy
) / two_dx
+ 1;
430 if(neg_slope
) n
= -n
;
431 pt1
->y
= start
->y
+ n
;
432 start_oc
= calc_outcode(pt1
, clip
);
434 else if(start_oc
& OUT_RIGHT
)
436 m
= start
->x
- clip
->right
+ 1;
438 n
= (m
* two_dy
+ bias
+ dx
- 1) / two_dx
;
440 n
= (m
* two_dy
- bias
- dy
) / two_dx
+ 1;
442 pt1
->x
= clip
->right
- 1;
443 if(neg_slope
) n
= -n
;
444 pt1
->y
= start
->y
- n
;
445 start_oc
= calc_outcode(pt1
, clip
);
447 else if(start_oc
& OUT_TOP
)
449 n
= clip
->top
- start
->y
;
451 m
= (n
* two_dx
- bias
- dx
) / two_dy
+ 1;
453 m
= (n
* two_dx
+ bias
+ dy
- 1) / two_dy
;
456 if(neg_slope
) m
= -m
;
457 pt1
->x
= start
->x
+ m
;
458 start_oc
= calc_outcode(pt1
, clip
);
460 else if(start_oc
& OUT_BOTTOM
)
462 n
= start
->y
- clip
->bottom
+ 1;
464 m
= (n
* two_dx
- bias
- dx
) / two_dy
+ 1;
466 m
= (n
* two_dx
+ bias
+ dy
- 1) / two_dy
;
468 pt1
->y
= clip
->bottom
- 1;
469 if(neg_slope
) m
= -m
;
470 pt1
->x
= start
->x
- m
;
471 start_oc
= calc_outcode(pt1
, clip
);
473 else if(end_oc
& OUT_LEFT
)
475 m
= clip
->left
- end
->x
;
477 n
= (m
* two_dy
- bias
+ dx
) / two_dx
;
479 n
= (m
* two_dy
+ bias
- dy
- 1) / two_dx
+ 1;
482 if(neg_slope
) n
= -n
;
484 end_oc
= calc_outcode(pt2
, clip
);
486 else if(end_oc
& OUT_RIGHT
)
488 m
= end
->x
- clip
->right
+ 1;
490 n
= (m
* two_dy
- bias
+ dx
) / two_dx
;
492 n
= (m
* two_dy
+ bias
- dy
- 1) / two_dx
+ 1;
494 pt2
->x
= clip
->right
- 1;
495 if(neg_slope
) n
= -n
;
497 end_oc
= calc_outcode(pt2
, clip
);
499 else if(end_oc
& OUT_TOP
)
501 n
= clip
->top
- end
->y
;
503 m
= (n
* two_dx
+ bias
- dx
- 1) / two_dy
+ 1;
505 m
= (n
* two_dx
- bias
+ dy
) / two_dy
;
508 if(neg_slope
) m
= -m
;
510 end_oc
= calc_outcode(pt2
, clip
);
512 else if(end_oc
& OUT_BOTTOM
)
514 n
= end
->y
- clip
->bottom
+ 1;
516 m
= (n
* two_dx
+ bias
- dx
- 1) / two_dy
+ 1;
518 m
= (n
* two_dx
- bias
+ dy
) / two_dy
;
520 pt2
->y
= clip
->bottom
- 1;
521 if(neg_slope
) m
= -m
;
523 end_oc
= calc_outcode(pt2
, clip
);
528 static void bres_line_with_bias(const POINT
*start
, const struct line_params
*params
,
529 void (* callback
)(dibdrv_physdev
*,INT
,INT
), dibdrv_physdev
*pdev
)
532 int len
= params
->length
, err
= params
->err_start
;
538 callback(pdev
, pt
.x
, pt
.y
);
539 if (err
+ params
->bias
> 0)
541 pt
.y
+= params
->y_inc
;
542 err
+= params
->err_add_1
;
544 else err
+= params
->err_add_2
;
545 pt
.x
+= params
->x_inc
;
552 callback(pdev
, pt
.x
, pt
.y
);
553 if (err
+ params
->bias
> 0)
555 pt
.x
+= params
->x_inc
;
556 err
+= params
->err_add_1
;
558 else err
+= params
->err_add_2
;
559 pt
.y
+= params
->y_inc
;
564 static BOOL
solid_pen_line(dibdrv_physdev
*pdev
, POINT
*start
, POINT
*end
, DWORD
and, DWORD
xor)
566 const WINEREGION
*clip
= get_wine_region(pdev
->clip
);
568 if(start
->y
== end
->y
)
573 rect
.left
= start
->x
;
576 rect
.bottom
= end
->y
+ 1;
577 order_end_points(&rect
.left
, &rect
.right
);
578 for(i
= 0; i
< clip
->numRects
; i
++)
580 if(clip
->rects
[i
].top
>= rect
.bottom
) break;
581 if(clip
->rects
[i
].bottom
<= rect
.top
) continue;
582 /* Optimize the unclipped case */
583 if(clip
->rects
[i
].left
<= rect
.left
&& clip
->rects
[i
].right
>= rect
.right
)
585 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
588 if(clip
->rects
[i
].right
> rect
.left
&& clip
->rects
[i
].left
< rect
.right
)
591 tmp
.left
= max(rect
.left
, clip
->rects
[i
].left
);
592 tmp
.right
= min(rect
.right
, clip
->rects
[i
].right
);
593 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &tmp
, and, xor);
597 else if(start
->x
== end
->x
)
602 rect
.left
= start
->x
;
604 rect
.right
= end
->x
+ 1;
605 rect
.bottom
= end
->y
;
606 order_end_points(&rect
.top
, &rect
.bottom
);
607 for(i
= 0; i
< clip
->numRects
; i
++)
609 /* Optimize unclipped case */
610 if(clip
->rects
[i
].top
<= rect
.top
&& clip
->rects
[i
].bottom
>= rect
.bottom
&&
611 clip
->rects
[i
].left
<= rect
.left
&& clip
->rects
[i
].right
>= rect
.right
)
613 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
616 if(clip
->rects
[i
].top
>= rect
.bottom
) break;
617 if(clip
->rects
[i
].bottom
<= rect
.top
) continue;
618 if(clip
->rects
[i
].right
> rect
.left
&& clip
->rects
[i
].left
< rect
.right
)
621 tmp
.top
= max(rect
.top
, clip
->rects
[i
].top
);
622 tmp
.bottom
= min(rect
.bottom
, clip
->rects
[i
].bottom
);
623 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &tmp
, and, xor);
629 bres_params clip_params
;
630 struct line_params line_params
;
631 INT dx
= end
->x
- start
->x
, dy
= end
->y
- start
->y
;
632 INT abs_dx
= abs(dx
), abs_dy
= abs(dy
);
635 clip_params
.dx
= abs_dx
;
636 clip_params
.dy
= abs_dy
;
637 clip_params
.octant
= get_octant_mask(dx
, dy
);
638 clip_params
.bias
= get_bias( clip_params
.octant
);
640 line_params
.bias
= clip_params
.bias
;
641 line_params
.x_major
= is_xmajor( clip_params
.octant
);
642 line_params
.x_inc
= is_x_increasing( clip_params
.octant
) ? 1 : -1;
643 line_params
.y_inc
= is_y_increasing( clip_params
.octant
) ? 1 : -1;
645 if (line_params
.x_major
)
647 line_params
.err_add_1
= 2 * abs_dy
- 2 * abs_dx
;
648 line_params
.err_add_2
= 2 * abs_dy
;
652 line_params
.err_add_1
= 2 * abs_dx
- 2 * abs_dy
;
653 line_params
.err_add_2
= 2 * abs_dx
;
656 for(i
= 0; i
< clip
->numRects
; i
++)
658 POINT clipped_start
, clipped_end
;
660 clip_status
= clip_line(start
, end
, clip
->rects
+ i
, &clip_params
, &clipped_start
, &clipped_end
);
664 int m
= abs(clipped_start
.x
- start
->x
);
665 int n
= abs(clipped_start
.y
- start
->y
);
667 if (line_params
.x_major
)
669 line_params
.err_start
= 2 * abs_dy
- abs_dx
+ m
* 2 * abs_dy
- n
* 2 * abs_dx
;
670 line_params
.length
= abs( clipped_end
.x
- clipped_start
.x
) + 1;
674 line_params
.err_start
= 2 * abs_dx
- abs_dy
+ n
* 2 * abs_dx
- m
* 2 * abs_dy
;
675 line_params
.length
= abs( clipped_end
.y
- clipped_start
.y
) + 1;
678 if (clipped_end
.x
== end
->x
&& clipped_end
.y
== end
->y
) line_params
.length
--;
680 pdev
->dib
.funcs
->solid_line( &pdev
->dib
, &clipped_start
, &line_params
, and, xor );
682 if(clip_status
== 2) break; /* completely unclipped, so we can finish */
687 release_wine_region(pdev
->clip
);
691 static BOOL
solid_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
694 DWORD color
, and, xor;
696 color
= get_pixel_color( pdev
, pdev
->pen_colorref
, TRUE
);
697 calc_and_xor_masks( GetROP2(pdev
->dev
.hdc
), color
, &and, &xor );
700 for (i
= 0; i
< num
- 1; i
++)
701 if (!solid_pen_line( pdev
, pts
+ i
, pts
+ i
+ 1, and, xor ))
704 if (close
) return solid_pen_line( pdev
, pts
+ num
- 1, pts
, and, xor );
709 void reset_dash_origin(dibdrv_physdev
*pdev
)
711 pdev
->dash_pos
.cur_dash
= 0;
712 pdev
->dash_pos
.left_in_dash
= pdev
->pen_pattern
.dashes
[0];
713 pdev
->dash_pos
.mark
= TRUE
;
716 static inline void skip_dash(dibdrv_physdev
*pdev
, unsigned int skip
)
718 skip
%= pdev
->pen_pattern
.total_len
;
721 if(pdev
->dash_pos
.left_in_dash
> skip
)
723 pdev
->dash_pos
.left_in_dash
-= skip
;
726 skip
-= pdev
->dash_pos
.left_in_dash
;
727 pdev
->dash_pos
.cur_dash
++;
728 if(pdev
->dash_pos
.cur_dash
== pdev
->pen_pattern
.count
) pdev
->dash_pos
.cur_dash
= 0;
729 pdev
->dash_pos
.left_in_dash
= pdev
->pen_pattern
.dashes
[pdev
->dash_pos
.cur_dash
];
730 pdev
->dash_pos
.mark
= !pdev
->dash_pos
.mark
;
734 static void dashed_pen_line_callback(dibdrv_physdev
*pdev
, INT x
, INT y
)
737 rop_mask mask
= pdev
->dash_masks
[pdev
->dash_pos
.mark
];
744 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, mask
.and, mask
.xor);
748 static BOOL
dashed_pen_line(dibdrv_physdev
*pdev
, POINT
*start
, POINT
*end
)
750 const WINEREGION
*clip
= get_wine_region(pdev
->clip
);
753 const dash_pos start_pos
= pdev
->dash_pos
;
755 if(start
->y
== end
->y
) /* hline */
758 INT left
, right
, cur_x
;
761 rect
.bottom
= start
->y
+ 1;
763 if(start
->x
<= end
->x
)
776 for(i
= 0; i
< clip
->numRects
; i
++)
778 if(clip
->rects
[i
].top
> start
->y
) break;
779 if(clip
->rects
[i
].bottom
<= start
->y
) continue;
781 if(clip
->rects
[i
].right
> left
&& clip
->rects
[i
].left
<= right
)
783 int clipped_left
= max(clip
->rects
[i
].left
, left
);
784 int clipped_right
= min(clip
->rects
[i
].right
- 1, right
);
786 pdev
->dash_pos
= start_pos
;
790 cur_x
= clipped_left
;
792 skip_dash(pdev
, clipped_left
- left
);
794 while(cur_x
<= clipped_right
)
796 rop_mask mask
= pdev
->dash_masks
[pdev
->dash_pos
.mark
];
797 dash_len
= pdev
->dash_pos
.left_in_dash
;
798 if(cur_x
+ dash_len
> clipped_right
+ 1)
799 dash_len
= clipped_right
- cur_x
+ 1;
801 rect
.right
= cur_x
+ dash_len
;
803 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, mask
.and, mask
.xor);
805 skip_dash(pdev
, dash_len
);
810 cur_x
= clipped_right
;
812 skip_dash(pdev
, right
- clipped_right
);
814 while(cur_x
>= clipped_left
)
816 rop_mask mask
= pdev
->dash_masks
[pdev
->dash_pos
.mark
];
817 dash_len
= pdev
->dash_pos
.left_in_dash
;
818 if(cur_x
- dash_len
< clipped_left
- 1)
819 dash_len
= cur_x
- clipped_left
+ 1;
820 rect
.left
= cur_x
- dash_len
+ 1;
821 rect
.right
= cur_x
+ 1;
823 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, mask
.and, mask
.xor);
825 skip_dash(pdev
, dash_len
);
830 pdev
->dash_pos
= start_pos
;
831 skip_dash(pdev
, right
- left
+ 1);
833 else if(start
->x
== end
->x
) /* vline */
836 INT top
, bottom
, cur_y
;
838 rect
.left
= start
->x
;
839 rect
.right
= start
->x
+ 1;
841 if(start
->y
<= end
->y
)
854 for(i
= 0; i
< clip
->numRects
; i
++)
856 if(clip
->rects
[i
].top
> bottom
) break;
857 if(clip
->rects
[i
].bottom
<= top
) continue;
858 if(clip
->rects
[i
].right
> start
->x
&& clip
->rects
[i
].left
<= start
->x
)
860 int clipped_top
= max(clip
->rects
[i
].top
, top
);
861 int clipped_bottom
= min(clip
->rects
[i
].bottom
- 1, bottom
);
863 pdev
->dash_pos
= start_pos
;
869 skip_dash(pdev
, clipped_top
- top
);
871 while(cur_y
<= clipped_bottom
)
873 rop_mask mask
= pdev
->dash_masks
[pdev
->dash_pos
.mark
];
874 dash_len
= pdev
->dash_pos
.left_in_dash
;
875 if(cur_y
+ dash_len
> clipped_bottom
+ 1)
876 dash_len
= clipped_bottom
- cur_y
+ 1;
878 rect
.bottom
= cur_y
+ dash_len
;
880 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, mask
.and, mask
.xor);
882 skip_dash(pdev
, dash_len
);
887 cur_y
= clipped_bottom
;
889 skip_dash(pdev
, bottom
- clipped_bottom
);
891 while(cur_y
>= clipped_top
)
893 rop_mask mask
= pdev
->dash_masks
[pdev
->dash_pos
.mark
];
894 dash_len
= pdev
->dash_pos
.left_in_dash
;
895 if(cur_y
- dash_len
< clipped_top
- 1)
896 dash_len
= cur_y
- clipped_top
+ 1;
897 rect
.top
= cur_y
- dash_len
+ 1;
898 rect
.bottom
= cur_y
+ 1;
900 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, mask
.and, mask
.xor);
902 skip_dash(pdev
, dash_len
);
907 pdev
->dash_pos
= start_pos
;
908 skip_dash(pdev
, bottom
- top
+ 1);
912 bres_params clip_params
;
913 struct line_params line_params
;
914 INT dx
= end
->x
- start
->x
, dy
= end
->y
- start
->y
;
915 INT abs_dx
= abs(dx
), abs_dy
= abs(dy
);
918 clip_params
.dx
= abs_dx
;
919 clip_params
.dy
= abs_dy
;
920 clip_params
.octant
= get_octant_mask(dx
, dy
);
921 clip_params
.bias
= get_bias( clip_params
.octant
);
923 line_params
.bias
= clip_params
.bias
;
924 line_params
.x_major
= is_xmajor( clip_params
.octant
);
925 line_params
.x_inc
= is_x_increasing( clip_params
.octant
) ? 1 : -1;
926 line_params
.y_inc
= is_y_increasing( clip_params
.octant
) ? 1 : -1;
928 if (line_params
.x_major
)
930 line_params
.err_add_1
= 2 * abs_dy
- 2 * abs_dx
;
931 line_params
.err_add_2
= 2 * abs_dy
;
935 line_params
.err_add_1
= 2 * abs_dx
- 2 * abs_dy
;
936 line_params
.err_add_2
= 2 * abs_dx
;
939 for(i
= 0; i
< clip
->numRects
; i
++)
941 POINT clipped_start
, clipped_end
;
943 clip_status
= clip_line(start
, end
, clip
->rects
+ i
, &clip_params
, &clipped_start
, &clipped_end
);
947 int m
= abs(clipped_start
.x
- start
->x
);
948 int n
= abs(clipped_start
.y
- start
->y
);
950 pdev
->dash_pos
= start_pos
;
952 if (line_params
.x_major
)
954 line_params
.err_start
= 2 * abs_dy
- abs_dx
+ m
* 2 * abs_dy
- n
* 2 * abs_dx
;
955 line_params
.length
= abs( clipped_end
.x
- clipped_start
.x
) + 1;
960 line_params
.err_start
= 2 * abs_dx
- abs_dy
+ n
* 2 * abs_dx
- m
* 2 * abs_dy
;
961 line_params
.length
= abs( clipped_end
.y
- clipped_start
.y
) + 1;
964 if (clipped_end
.x
== end
->x
&& clipped_end
.y
== end
->y
) line_params
.length
--;
966 bres_line_with_bias( &clipped_start
, &line_params
, dashed_pen_line_callback
, pdev
);
968 if(clip_status
== 2) break; /* completely unclipped, so we can finish */
971 pdev
->dash_pos
= start_pos
;
972 if(line_params
.x_major
)
973 skip_dash(pdev
, abs_dx
);
975 skip_dash(pdev
, abs_dy
);
978 release_wine_region(pdev
->clip
);
982 static BOOL
dashed_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
987 color
= get_pixel_color( pdev
, pdev
->pen_colorref
, TRUE
);
988 get_pen_bkgnd_masks( pdev
, &pdev
->dash_masks
[0].and, &pdev
->dash_masks
[0].xor );
989 calc_and_xor_masks( GetROP2(pdev
->dev
.hdc
), color
,
990 &pdev
->dash_masks
[1].and, &pdev
->dash_masks
[1].xor );
993 for (i
= 0; i
< num
- 1; i
++)
994 if (!dashed_pen_line( pdev
, pts
+ i
, pts
+ i
+ 1 ))
997 if (close
) return dashed_pen_line( pdev
, pts
+ num
- 1, pts
);
1002 static BOOL
null_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
1013 static void add_cap( dibdrv_physdev
*pdev
, HRGN region
, const POINT
*pt
)
1017 switch (pdev
->pen_endcap
)
1019 default: FIXME( "Unknown end cap %x\n", pdev
->pen_endcap
);
1021 case PS_ENDCAP_ROUND
:
1022 cap
= CreateEllipticRgn( pt
->x
- pdev
->pen_width
/ 2, pt
->y
- pdev
->pen_width
/ 2,
1023 pt
->x
+ (pdev
->pen_width
+ 1) / 2, pt
->y
+ (pdev
->pen_width
+ 1) / 2 );
1026 case PS_ENDCAP_SQUARE
: /* already been handled */
1027 case PS_ENDCAP_FLAT
:
1031 CombineRgn( region
, region
, cap
, RGN_OR
);
1032 DeleteObject( cap
);
1036 #define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5)
1038 /*******************************************************************************
1039 * create_miter_region
1041 * We need to calculate the intersection of two lines. We know a point
1042 * on each line (a face start and the other face end point) and
1043 * the direction vector of each line eg. (dx_1, dy_1).
1045 * (x, y) = (x_1, y_1) + u * (dx_1, dy_1) = (x_2, y_2) + v * (dx_2, dy_2)
1046 * solving (eg using Cramer's rule) gives:
1047 * u = ((x_2 - x_1) dy_2 - (y_2 - y_1) dx_2) / det
1048 * with det = dx_1 dy_2 - dx_2 dy_1
1049 * substituting back in and simplifying gives
1050 * (x, y) = a (dx_1, dy_1) - b (dx_2, dy_2)
1051 * with a = (x_2 dy_2 - y_2 dx_2) / det
1052 * and b = (x_1 dy_1 - y_1 dx_1) / det
1054 static HRGN
create_miter_region( dibdrv_physdev
*pdev
, const POINT
*pt
,
1055 const struct face
*face_1
, const struct face
*face_2
)
1057 int det
= face_1
->dx
* face_2
->dy
- face_1
->dy
* face_2
->dx
;
1058 POINT pt_1
, pt_2
, pts
[5];
1062 if (det
== 0) return 0;
1066 const struct face
*tmp
= face_1
;
1072 pt_1
= face_1
->start
;
1075 a
= (double)((pt_2
.x
* face_2
->dy
- pt_2
.y
* face_2
->dx
)) / det
;
1076 b
= (double)((pt_1
.x
* face_1
->dy
- pt_1
.y
* face_1
->dx
)) / det
;
1078 x
= a
* face_1
->dx
- b
* face_2
->dx
;
1079 y
= a
* face_1
->dy
- b
* face_2
->dy
;
1081 GetMiterLimit( pdev
->dev
.hdc
, &limit
);
1083 if (((x
- pt
->x
) * (x
- pt
->x
) + (y
- pt
->y
) * (y
- pt
->y
)) * 4 > limit
* limit
* pdev
->pen_width
* pdev
->pen_width
)
1086 pts
[0] = face_2
->start
;
1087 pts
[1] = face_1
->start
;
1088 pts
[2].x
= round( x
);
1089 pts
[2].y
= round( y
);
1090 pts
[3] = face_2
->end
;
1091 pts
[4] = face_1
->end
;
1093 return CreatePolygonRgn( pts
, 5, ALTERNATE
);
1096 static void add_join( dibdrv_physdev
*pdev
, HRGN region
, const POINT
*pt
,
1097 const struct face
*face_1
, const struct face
*face_2
)
1102 switch (pdev
->pen_join
)
1104 default: FIXME( "Unknown line join %x\n", pdev
->pen_join
);
1107 join
= CreateEllipticRgn( pt
->x
- pdev
->pen_width
/ 2, pt
->y
- pdev
->pen_width
/ 2,
1108 pt
->x
+ (pdev
->pen_width
+ 1) / 2, pt
->y
+ (pdev
->pen_width
+ 1) / 2 );
1112 join
= create_miter_region( pdev
, pt
, face_1
, face_2
);
1116 pts
[0] = face_1
->start
;
1117 pts
[1] = face_2
->end
;
1118 pts
[2] = face_1
->end
;
1119 pts
[3] = face_2
->start
;
1120 join
= CreatePolygonRgn( pts
, 4, ALTERNATE
);
1124 CombineRgn( region
, region
, join
, RGN_OR
);
1125 DeleteObject( join
);
1129 static HRGN
get_wide_lines_region( dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
1132 HRGN total
, segment
;
1136 total
= CreateRectRgn( 0, 0, 0, 0 );
1139 for (i
= 0; i
< num
; i
++)
1141 const POINT
*pt_1
= pts
+ i
;
1142 const POINT
*pt_2
= pts
+ ((close
&& i
== num
- 1) ? 0 : i
+ 1);
1143 int dx
= pt_2
->x
- pt_1
->x
;
1144 int dy
= pt_2
->y
- pt_1
->y
;
1146 struct face face_1
, face_2
, prev_face
, first_face
;
1147 BOOL need_cap_1
= !close
&& (i
== 0);
1148 BOOL need_cap_2
= !close
&& (i
== num
- 1);
1149 BOOL sq_cap_1
= need_cap_1
&& (pdev
->pen_endcap
== PS_ENDCAP_SQUARE
);
1150 BOOL sq_cap_2
= need_cap_2
&& (pdev
->pen_endcap
== PS_ENDCAP_SQUARE
);
1152 if (dx
== 0 && dy
== 0) continue;
1156 rect
.left
= min( pt_1
->x
, pt_2
->x
);
1157 rect
.right
= rect
.left
+ abs( dx
);
1158 rect
.top
= pt_1
->y
- pdev
->pen_width
/ 2;
1159 rect
.bottom
= rect
.top
+ pdev
->pen_width
;
1160 if ((sq_cap_1
&& dx
> 0) || (sq_cap_2
&& dx
< 0)) rect
.left
-= pdev
->pen_width
/ 2;
1161 if ((sq_cap_2
&& dx
> 0) || (sq_cap_1
&& dx
< 0)) rect
.right
+= pdev
->pen_width
/ 2;
1162 segment
= CreateRectRgnIndirect( &rect
);
1165 face_1
.start
.x
= face_1
.end
.x
= rect
.left
;
1166 face_1
.start
.y
= face_2
.end
.y
= rect
.bottom
;
1167 face_1
.end
.y
= face_2
.start
.y
= rect
.top
;
1168 face_2
.start
.x
= face_2
.end
.x
= rect
.right
- 1;
1172 face_1
.start
.x
= face_1
.end
.x
= rect
.right
;
1173 face_1
.start
.y
= face_2
.end
.y
= rect
.top
;
1174 face_1
.end
.y
= face_2
.start
.y
= rect
.bottom
;
1175 face_2
.start
.x
= face_2
.end
.x
= rect
.left
+ 1;
1180 rect
.top
= min( pt_1
->y
, pt_2
->y
);
1181 rect
.bottom
= rect
.top
+ abs( dy
);
1182 rect
.left
= pt_1
->x
- pdev
->pen_width
/ 2;
1183 rect
.right
= rect
.left
+ pdev
->pen_width
;
1184 if ((sq_cap_1
&& dy
> 0) || (sq_cap_2
&& dy
< 0)) rect
.top
-= pdev
->pen_width
/ 2;
1185 if ((sq_cap_2
&& dy
> 0) || (sq_cap_1
&& dy
< 0)) rect
.bottom
+= pdev
->pen_width
/ 2;
1186 segment
= CreateRectRgnIndirect( &rect
);
1189 face_1
.start
.x
= face_2
.end
.x
= rect
.left
;
1190 face_1
.start
.y
= face_1
.end
.y
= rect
.top
;
1191 face_1
.end
.x
= face_2
.start
.x
= rect
.right
;
1192 face_2
.start
.y
= face_2
.end
.y
= rect
.bottom
- 1;
1196 face_1
.start
.x
= face_2
.end
.x
= rect
.right
;
1197 face_1
.start
.y
= face_1
.end
.y
= rect
.bottom
;
1198 face_1
.end
.x
= face_2
.start
.x
= rect
.left
;
1199 face_2
.start
.y
= face_2
.end
.y
= rect
.top
+ 1;
1204 double len
= hypot( dx
, dy
);
1205 double width_x
, width_y
;
1207 POINT wide_half
, narrow_half
;
1209 width_x
= pdev
->pen_width
* abs( dy
) / len
;
1210 width_y
= pdev
->pen_width
* abs( dx
) / len
;
1212 narrow_half
.x
= round( width_x
/ 2 );
1213 narrow_half
.y
= round( width_y
/ 2 );
1214 wide_half
.x
= round( (width_x
+ 1) / 2 );
1215 wide_half
.y
= round( (width_y
+ 1) / 2 );
1219 wide_half
.y
= -wide_half
.y
;
1220 narrow_half
.y
= -narrow_half
.y
;
1225 POINT tmp
= narrow_half
; narrow_half
= wide_half
; wide_half
= tmp
;
1226 wide_half
.x
= -wide_half
.x
;
1227 narrow_half
.x
= -narrow_half
.x
;
1230 seg_pts
[0].x
= pt_1
->x
- narrow_half
.x
;
1231 seg_pts
[0].y
= pt_1
->y
+ narrow_half
.y
;
1232 seg_pts
[1].x
= pt_1
->x
+ wide_half
.x
;
1233 seg_pts
[1].y
= pt_1
->y
- wide_half
.y
;
1234 seg_pts
[2].x
= pt_2
->x
+ wide_half
.x
;
1235 seg_pts
[2].y
= pt_2
->y
- wide_half
.y
;
1236 seg_pts
[3].x
= pt_2
->x
- narrow_half
.x
;
1237 seg_pts
[3].y
= pt_2
->y
+ narrow_half
.y
;
1241 seg_pts
[0].x
-= narrow_half
.y
;
1242 seg_pts
[1].x
-= narrow_half
.y
;
1243 seg_pts
[0].y
-= narrow_half
.x
;
1244 seg_pts
[1].y
-= narrow_half
.x
;
1249 seg_pts
[2].x
+= wide_half
.y
;
1250 seg_pts
[3].x
+= wide_half
.y
;
1251 seg_pts
[2].y
+= wide_half
.x
;
1252 seg_pts
[3].y
+= wide_half
.x
;
1255 segment
= CreatePolygonRgn( seg_pts
, 4, ALTERNATE
);
1257 face_1
.start
= seg_pts
[0];
1258 face_1
.end
= seg_pts
[1];
1259 face_2
.start
= seg_pts
[2];
1260 face_2
.end
= seg_pts
[3];
1263 CombineRgn( total
, total
, segment
, RGN_OR
);
1264 DeleteObject( segment
);
1266 if (need_cap_1
) add_cap( pdev
, total
, pt_1
);
1267 if (need_cap_2
) add_cap( pdev
, total
, pt_2
);
1269 face_1
.dx
= face_2
.dx
= dx
;
1270 face_1
.dy
= face_2
.dy
= dy
;
1272 if (i
== 0) first_face
= face_1
;
1273 else add_join( pdev
, total
, pt_1
, &prev_face
, &face_1
);
1275 if (i
== num
- 1 && close
)
1276 add_join( pdev
, total
, pt_2
, &face_2
, &first_face
);
1283 static BOOL
wide_pen_lines(dibdrv_physdev
*pdev
, int num
, POINT
*pts
, BOOL close
)
1285 const WINEREGION
*data
;
1288 DWORD pen_color
= get_pixel_color( pdev
, pdev
->pen_colorref
, TRUE
);
1290 calc_and_xor_masks( GetROP2(pdev
->dev
.hdc
), pen_color
, &color
.and, &color
.xor );
1292 region
= get_wide_lines_region( pdev
, num
, pts
, close
);
1294 if (CombineRgn( region
, region
, pdev
->clip
, RGN_AND
) != ERROR
)
1296 data
= get_wine_region( region
);
1297 solid_rects( &pdev
->dib
, data
->numRects
, data
->rects
, &color
, NULL
);
1298 release_wine_region( region
);
1301 DeleteObject( region
);
1305 static const dash_pattern dash_patterns
[5] =
1307 {0, {0}, 0}, /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
1308 {2, {18, 6}, 24}, /* PS_DASH */
1309 {2, {3, 3}, 6}, /* PS_DOT */
1310 {4, {9, 6, 3, 6}, 24}, /* PS_DASHDOT */
1311 {6, {9, 3, 3, 3, 3, 3}, 24} /* PS_DASHDOTDOT */
1314 static inline int get_pen_device_width( dibdrv_physdev
*pdev
, LOGPEN
*pen
)
1316 int width
= pen
->lopnWidth
.x
;
1318 if (pen
->lopnStyle
& PS_GEOMETRIC
&& width
> 1)
1321 pts
[0].x
= pts
[0].y
= pts
[1].y
= 0;
1323 LPtoDP( pdev
->dev
.hdc
, pts
, 2 );
1324 width
= max( abs( pts
[1].x
- pts
[0].x
), 1 );
1329 /***********************************************************************
1332 HPEN
dibdrv_SelectPen( PHYSDEV dev
, HPEN hpen
)
1334 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSelectPen
);
1335 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1339 TRACE("(%p, %p)\n", dev
, hpen
);
1341 if (!GetObjectW( hpen
, sizeof(logpen
), &logpen
))
1343 /* must be an extended pen */
1345 INT size
= GetObjectW( hpen
, 0, NULL
);
1347 if (!size
) return 0;
1349 elp
= HeapAlloc( GetProcessHeap(), 0, size
);
1351 GetObjectW( hpen
, size
, elp
);
1352 /* FIXME: add support for user style pens */
1353 logpen
.lopnStyle
= elp
->elpPenStyle
;
1354 logpen
.lopnWidth
.x
= elp
->elpWidth
;
1355 logpen
.lopnWidth
.y
= 0;
1356 logpen
.lopnColor
= elp
->elpColor
;
1358 HeapFree( GetProcessHeap(), 0, elp
);
1361 pdev
->pen_join
= logpen
.lopnStyle
& PS_JOIN_MASK
;
1362 pdev
->pen_endcap
= logpen
.lopnStyle
& PS_ENDCAP_MASK
;
1363 pdev
->pen_width
= get_pen_device_width( pdev
, &logpen
);
1365 if (hpen
== GetStockObject( DC_PEN
))
1366 logpen
.lopnColor
= GetDCPenColor( dev
->hdc
);
1368 pdev
->pen_colorref
= logpen
.lopnColor
;
1369 pdev
->pen_pattern
= dash_patterns
[PS_SOLID
];
1371 pdev
->defer
|= DEFER_PEN
;
1373 style
= logpen
.lopnStyle
& PS_STYLE_MASK
;
1378 if(pdev
->pen_width
<= 1)
1379 pdev
->pen_lines
= solid_pen_lines
;
1381 pdev
->pen_lines
= wide_pen_lines
;
1382 pdev
->defer
&= ~DEFER_PEN
;
1389 if(logpen
.lopnStyle
& PS_GEOMETRIC
) break;
1390 if(logpen
.lopnWidth
.x
> 1) break;
1391 pdev
->pen_lines
= dashed_pen_lines
;
1392 pdev
->pen_pattern
= dash_patterns
[style
];
1393 pdev
->defer
&= ~DEFER_PEN
;
1397 pdev
->pen_lines
= null_pen_lines
;
1398 pdev
->defer
&= ~DEFER_PEN
;
1405 return next
->funcs
->pSelectPen( next
, hpen
);
1408 /***********************************************************************
1409 * dibdrv_SetDCPenColor
1411 COLORREF
dibdrv_SetDCPenColor( PHYSDEV dev
, COLORREF color
)
1413 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSetDCPenColor
);
1414 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1416 if (GetCurrentObject(dev
->hdc
, OBJ_PEN
) == GetStockObject( DC_PEN
))
1417 pdev
->pen_colorref
= color
;
1419 return next
->funcs
->pSetDCPenColor( next
, color
);
1422 void solid_rects( dib_info
*dib
, int num
, const RECT
*rects
, const rop_mask
*color
, HRGN region
)
1425 const WINEREGION
*clip
;
1429 dib
->funcs
->solid_rects( dib
, num
, rects
, color
->and, color
->xor );
1433 clip
= get_wine_region( region
);
1435 for(i
= 0; i
< num
; i
++)
1437 for(j
= 0; j
< clip
->numRects
; j
++)
1441 if (intersect_rect( &clipped_rect
, rects
+ i
, clip
->rects
+ j
))
1442 dib
->funcs
->solid_rects( dib
, 1, &clipped_rect
, color
->and, color
->xor );
1445 release_wine_region( region
);
1448 /**********************************************************************
1451 * Fill a number of rectangles with the solid brush
1453 static BOOL
solid_brush(dibdrv_physdev
*pdev
, dib_info
*dib
, int num
, const RECT
*rects
, HRGN region
)
1455 rop_mask brush_color
;
1456 DWORD color
= get_pixel_color( pdev
, pdev
->brush_colorref
, TRUE
);
1458 calc_and_xor_masks( pdev
->brush_rop
, color
, &brush_color
.and, &brush_color
.xor );
1459 solid_rects( dib
, num
, rects
, &brush_color
, region
);
1463 static void free_pattern_brush_bits( dibdrv_physdev
*pdev
)
1465 HeapFree(GetProcessHeap(), 0, pdev
->brush_and_bits
);
1466 HeapFree(GetProcessHeap(), 0, pdev
->brush_xor_bits
);
1467 pdev
->brush_and_bits
= NULL
;
1468 pdev
->brush_xor_bits
= NULL
;
1471 void free_pattern_brush( dibdrv_physdev
*pdev
)
1473 free_pattern_brush_bits( pdev
);
1474 free_dib_info( &pdev
->brush_dib
);
1477 static BOOL
create_pattern_brush_bits(dibdrv_physdev
*pdev
)
1479 DWORD size
= pdev
->brush_dib
.height
* abs(pdev
->brush_dib
.stride
);
1480 DWORD
*brush_bits
= pdev
->brush_dib
.bits
.ptr
;
1481 DWORD
*and_bits
, *xor_bits
;
1483 assert(pdev
->brush_and_bits
== NULL
);
1484 assert(pdev
->brush_xor_bits
== NULL
);
1486 assert(pdev
->brush_dib
.stride
> 0);
1488 and_bits
= pdev
->brush_and_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1489 xor_bits
= pdev
->brush_xor_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1491 if(!and_bits
|| !xor_bits
)
1493 ERR("Failed to create pattern brush bits\n");
1494 free_pattern_brush_bits( pdev
);
1500 calc_and_xor_masks(pdev
->brush_rop
, *brush_bits
++, and_bits
++, xor_bits
++);
1507 static const DWORD hatches
[6][8] =
1509 { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1510 { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL */
1511 { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL */
1512 { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL */
1513 { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS */
1514 { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 } /* HS_DIAGCROSS */
1517 static BOOL
create_hatch_brush_bits(dibdrv_physdev
*pdev
)
1520 rop_mask fg_mask
, bg_mask
;
1521 rop_mask_bits mask_bits
;
1525 assert(pdev
->brush_and_bits
== NULL
);
1526 assert(pdev
->brush_xor_bits
== NULL
);
1528 /* Just initialise brush_dib with the color / sizing info. We don't
1529 need the bits as we'll calculate the rop masks straight from
1530 the hatch patterns. */
1532 copy_dib_color_info(&pdev
->brush_dib
, &pdev
->dib
);
1533 pdev
->brush_dib
.width
= 8;
1534 pdev
->brush_dib
.height
= 8;
1535 pdev
->brush_dib
.stride
= get_dib_stride( pdev
->brush_dib
.width
, pdev
->brush_dib
.bit_count
);
1537 size
= pdev
->brush_dib
.height
* pdev
->brush_dib
.stride
;
1539 mask_bits
.and = pdev
->brush_and_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1540 mask_bits
.xor = pdev
->brush_xor_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1542 if(!mask_bits
.and || !mask_bits
.xor)
1544 ERR("Failed to create pattern brush bits\n");
1545 free_pattern_brush_bits( pdev
);
1549 hatch
.bit_count
= 1;
1550 hatch
.height
= hatch
.width
= 8;
1552 hatch
.bits
.ptr
= (void *) hatches
[pdev
->brush_hatch
];
1553 hatch
.bits
.free
= hatch
.bits
.param
= NULL
;
1554 hatch
.bits
.is_copy
= FALSE
;
1556 color
= get_pixel_color( pdev
, pdev
->brush_colorref
, TRUE
);
1557 calc_and_xor_masks( pdev
->brush_rop
, color
, &fg_mask
.and, &fg_mask
.xor );
1559 get_brush_bkgnd_masks( pdev
, &bg_mask
.and, &bg_mask
.xor );
1561 ret
= pdev
->brush_dib
.funcs
->create_rop_masks( &pdev
->brush_dib
, &hatch
, &fg_mask
, &bg_mask
, &mask_bits
);
1562 if(!ret
) free_pattern_brush_bits( pdev
);
1567 static BOOL
matching_pattern_format( dib_info
*dib
, dib_info
*pattern
)
1569 if (dib
->bit_count
!= pattern
->bit_count
) return FALSE
;
1570 if (dib
->stride
!= pattern
->stride
) return FALSE
;
1572 switch (dib
->bit_count
)
1577 if (dib
->color_table_size
!= pattern
->color_table_size
) return FALSE
;
1578 return !memcmp( dib
->color_table
, pattern
->color_table
, dib
->color_table_size
* sizeof(RGBQUAD
) );
1581 return (dib
->red_mask
== pattern
->red_mask
&&
1582 dib
->green_mask
== pattern
->green_mask
&&
1583 dib
->blue_mask
== pattern
->blue_mask
);
1588 static BOOL
select_pattern_brush( dibdrv_physdev
*pdev
, BOOL
*needs_reselect
)
1590 char buffer
[FIELD_OFFSET( BITMAPINFO
, bmiColors
[256] )];
1591 BITMAPINFO
*info
= (BITMAPINFO
*)buffer
;
1592 RGBQUAD color_table
[2];
1596 if (!pdev
->brush_pattern_info
)
1598 BITMAPOBJ
*bmp
= GDI_GetObjPtr( pdev
->brush_pattern_bitmap
, OBJ_BITMAP
);
1601 if (!bmp
) return FALSE
;
1602 ret
= init_dib_info_from_bitmapobj( &pattern
, bmp
, 0 );
1603 GDI_ReleaseObj( pdev
->brush_pattern_bitmap
);
1604 if (!ret
) return FALSE
;
1606 else if (pdev
->brush_pattern_info
->bmiHeader
.biClrUsed
&& pdev
->brush_pattern_usage
== DIB_PAL_COLORS
)
1608 copy_bitmapinfo( info
, pdev
->brush_pattern_info
);
1609 fill_color_table_from_pal_colors( info
, pdev
->dev
.hdc
);
1610 init_dib_info_from_bitmapinfo( &pattern
, info
, pdev
->brush_pattern_bits
, 0 );
1611 *needs_reselect
= TRUE
;
1615 init_dib_info_from_bitmapinfo( &pattern
, pdev
->brush_pattern_info
, pdev
->brush_pattern_bits
, 0 );
1618 if (pattern
.bit_count
== 1 && !pattern
.color_table
)
1620 /* monochrome DDB pattern uses DC colors */
1621 COLORREF color
= GetTextColor( pdev
->dev
.hdc
);
1622 color_table
[0].rgbRed
= GetRValue( color
);
1623 color_table
[0].rgbGreen
= GetGValue( color
);
1624 color_table
[0].rgbBlue
= GetBValue( color
);
1625 color_table
[0].rgbReserved
= 0;
1626 color
= GetBkColor( pdev
->dev
.hdc
);
1627 color_table
[1].rgbRed
= GetRValue( color
);
1628 color_table
[1].rgbGreen
= GetGValue( color
);
1629 color_table
[1].rgbBlue
= GetBValue( color
);
1630 color_table
[1].rgbReserved
= 0;
1631 pattern
.color_table
= color_table
;
1632 pattern
.color_table_size
= 2;
1633 *needs_reselect
= TRUE
;
1636 copy_dib_color_info(&pdev
->brush_dib
, &pdev
->dib
);
1638 pdev
->brush_dib
.height
= pattern
.height
;
1639 pdev
->brush_dib
.width
= pattern
.width
;
1640 pdev
->brush_dib
.stride
= get_dib_stride( pdev
->brush_dib
.width
, pdev
->brush_dib
.bit_count
);
1642 if (matching_pattern_format( &pdev
->brush_dib
, &pattern
))
1644 pdev
->brush_dib
.bits
.ptr
= pattern
.bits
.ptr
;
1645 pdev
->brush_dib
.bits
.is_copy
= FALSE
;
1646 pdev
->brush_dib
.bits
.free
= NULL
;
1650 pdev
->brush_dib
.bits
.ptr
= HeapAlloc( GetProcessHeap(), 0,
1651 pdev
->brush_dib
.height
* pdev
->brush_dib
.stride
);
1652 pdev
->brush_dib
.bits
.is_copy
= TRUE
;
1653 pdev
->brush_dib
.bits
.free
= free_heap_bits
;
1655 rect
.left
= rect
.top
= 0;
1656 rect
.right
= pattern
.width
;
1657 rect
.bottom
= pattern
.height
;
1659 pdev
->brush_dib
.funcs
->convert_to(&pdev
->brush_dib
, &pattern
, &rect
);
1664 /**********************************************************************
1667 * Fill a number of rectangles with the pattern brush
1668 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1670 static BOOL
pattern_brush(dibdrv_physdev
*pdev
, dib_info
*dib
, int num
, const RECT
*rects
, HRGN region
)
1673 const WINEREGION
*clip
;
1675 BOOL needs_reselect
= FALSE
;
1677 if(pdev
->brush_and_bits
== NULL
)
1679 switch(pdev
->brush_style
)
1682 if (!pdev
->brush_dib
.bits
.ptr
&& !select_pattern_brush( pdev
, &needs_reselect
))
1684 if(!create_pattern_brush_bits(pdev
))
1689 if(!create_hatch_brush_bits(pdev
))
1694 ERR("Unexpected brush style %d\n", pdev
->brush_style
);
1699 GetBrushOrgEx(pdev
->dev
.hdc
, &origin
);
1701 clip
= get_wine_region( region
);
1705 dib
->funcs
->pattern_rects( dib
, num
, rects
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1709 for(i
= 0; i
< num
; i
++)
1711 for(j
= 0; j
< clip
->numRects
; j
++)
1713 RECT rect
= rects
[i
];
1715 /* Optimize unclipped case */
1716 if(clip
->rects
[j
].top
<= rect
.top
&& clip
->rects
[j
].bottom
>= rect
.bottom
&&
1717 clip
->rects
[j
].left
<= rect
.left
&& clip
->rects
[j
].right
>= rect
.right
)
1719 dib
->funcs
->pattern_rects( dib
, 1, &rect
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1723 if(clip
->rects
[j
].top
>= rect
.bottom
) break;
1724 if(clip
->rects
[j
].bottom
<= rect
.top
) continue;
1726 if(clip
->rects
[j
].right
> rect
.left
&& clip
->rects
[j
].left
< rect
.right
)
1728 rect
.left
= max(rect
.left
, clip
->rects
[j
].left
);
1729 rect
.top
= max(rect
.top
, clip
->rects
[j
].top
);
1730 rect
.right
= min(rect
.right
, clip
->rects
[j
].right
);
1731 rect
.bottom
= min(rect
.bottom
, clip
->rects
[j
].bottom
);
1733 dib
->funcs
->pattern_rects( dib
, 1, &rect
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1737 release_wine_region( region
);
1740 if (needs_reselect
) free_pattern_brush( pdev
);
1744 static BOOL
null_brush(dibdrv_physdev
*pdev
, dib_info
*dib
, int num
, const RECT
*rects
, HRGN region
)
1749 void update_brush_rop( dibdrv_physdev
*pdev
, INT rop
)
1751 pdev
->brush_rop
= rop
;
1752 free_pattern_brush_bits( pdev
);
1755 /***********************************************************************
1756 * dibdrv_SelectBrush
1758 HBRUSH
dibdrv_SelectBrush( PHYSDEV dev
, HBRUSH hbrush
, HBITMAP bitmap
,
1759 const BITMAPINFO
*info
, void *bits
, UINT usage
)
1761 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSelectBrush
);
1762 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1765 TRACE("(%p, %p)\n", dev
, hbrush
);
1767 free_pattern_brush( pdev
);
1769 if (bitmap
|| info
) /* pattern brush */
1771 pdev
->brush_rects
= pattern_brush
;
1772 pdev
->brush_style
= BS_DIBPATTERN
;
1773 pdev
->brush_pattern_info
= info
;
1774 pdev
->brush_pattern_bits
= bits
;
1775 pdev
->brush_pattern_usage
= usage
;
1776 pdev
->brush_pattern_bitmap
= bitmap
;
1777 /* brush is actually selected only when it's used */
1779 return next
->funcs
->pSelectBrush( next
, hbrush
, bitmap
, info
, bits
, usage
);
1782 GetObjectW( hbrush
, sizeof(logbrush
), &logbrush
);
1784 if (hbrush
== GetStockObject( DC_BRUSH
))
1785 logbrush
.lbColor
= GetDCBrushColor( dev
->hdc
);
1787 pdev
->brush_style
= logbrush
.lbStyle
;
1789 switch(logbrush
.lbStyle
)
1792 pdev
->brush_colorref
= logbrush
.lbColor
;
1793 pdev
->brush_rop
= GetROP2( dev
->hdc
);
1794 pdev
->brush_rects
= solid_brush
;
1798 pdev
->brush_rects
= null_brush
;
1802 if(logbrush
.lbHatch
> HS_DIAGCROSS
) return 0;
1803 pdev
->brush_hatch
= logbrush
.lbHatch
;
1804 pdev
->brush_colorref
= logbrush
.lbColor
;
1805 pdev
->brush_rop
= GetROP2( dev
->hdc
);
1806 pdev
->brush_rects
= pattern_brush
;
1813 return next
->funcs
->pSelectBrush( next
, hbrush
, bitmap
, info
, bits
, usage
);
1816 /***********************************************************************
1817 * dibdrv_SetDCBrushColor
1819 COLORREF
dibdrv_SetDCBrushColor( PHYSDEV dev
, COLORREF color
)
1821 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSetDCBrushColor
);
1822 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1824 if (GetCurrentObject(dev
->hdc
, OBJ_BRUSH
) == GetStockObject( DC_BRUSH
))
1825 pdev
->brush_colorref
= color
;
1827 return next
->funcs
->pSetDCBrushColor( next
, color
);
1830 BOOL
brush_rects(dibdrv_physdev
*pdev
, int num
, const RECT
*rects
)
1832 return pdev
->brush_rects( pdev
, &pdev
->dib
, num
, rects
, pdev
->clip
);