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) | (~P & A2) */
62 #define ONE {0xffffffff, 0xffffffff}
63 #define P {0xffffffff, 0}
64 #define NOT_P {0, 0xffffffff}
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) | (~P & 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 calc_and_xor_masks(INT rop
, DWORD color
, DWORD
*and, DWORD
*xor)
90 /* NB The ROP2 codes start at one and the arrays are zero-based */
91 *and = (color
& rop2_and_array
[rop
-1][0]) | ((~color
) & rop2_and_array
[rop
-1][1]);
92 *xor = (color
& rop2_xor_array
[rop
-1][0]) | ((~color
) & rop2_xor_array
[rop
-1][1]);
95 static inline void order_end_points(int *s
, int *e
)
106 static inline BOOL
pt_in_rect( const RECT
*rect
, const POINT
*pt
)
108 return ((pt
->x
>= rect
->left
) && (pt
->x
< rect
->right
) &&
109 (pt
->y
>= rect
->top
) && (pt
->y
< rect
->bottom
));
112 #define Y_INCREASING_MASK 0x0f
113 #define X_INCREASING_MASK 0xc3
114 #define X_MAJOR_MASK 0x99
115 #define POS_SLOPE_MASK 0x33
117 static inline BOOL
is_xmajor(DWORD octant
)
119 return octant
& X_MAJOR_MASK
;
122 static inline BOOL
is_pos_slope(DWORD octant
)
124 return octant
& POS_SLOPE_MASK
;
127 static inline BOOL
is_x_increasing(DWORD octant
)
129 return octant
& X_INCREASING_MASK
;
132 static inline BOOL
is_y_increasing(DWORD octant
)
134 return octant
& Y_INCREASING_MASK
;
137 /**********************************************************************
140 * Return the octant number starting clockwise from the +ve x-axis.
142 static inline int get_octant_number(int dx
, int dy
)
146 return ( dx
> dy
) ? 1 : 2;
148 return (-dx
> dy
) ? 4 : 3;
151 return (-dx
> -dy
) ? 5 : 6;
153 return ( dx
> -dy
) ? 8 : 7;
156 static inline DWORD
get_octant_mask(int dx
, int dy
)
158 return 1 << (get_octant_number(dx
, dy
) - 1);
161 static void solid_pen_line_callback(dibdrv_physdev
*pdev
, INT x
, INT y
)
169 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, pdev
->pen_and
, pdev
->pen_xor
);
178 static inline DWORD
calc_outcode(const POINT
*pt
, const RECT
*clip
)
181 if(pt
->x
< clip
->left
) out
|= OUT_LEFT
;
182 else if(pt
->x
>= clip
->right
) out
|= OUT_RIGHT
;
183 if(pt
->y
< clip
->top
) out
|= OUT_TOP
;
184 else if(pt
->y
>= clip
->bottom
) out
|= OUT_BOTTOM
;
196 /******************************************************************************
199 * Clips the start and end points to a rectangle.
201 * Note, this treats the end point like the start point. If the
202 * caller doesn't want it displayed, it should exclude it. If the end
203 * point is clipped out, then the likelihood is that the new end point
204 * should be displayed.
206 * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
208 * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
209 * however the Bresenham error term is defined differently so the equations
212 * For x major lines we have 2dy >= err + bias > 2dy - 2dx
213 * 0 >= err + bias - 2dy > -2dx
215 * Note dx, dy, m and n are all +ve.
217 * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
218 * err = 2dy - dx + 2mdy - 2ndx
219 * 0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
220 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
221 * which of course will give exactly one solution for n,
222 * so looking at the >= inequality
223 * n >= (2mdy + bias - dx) / 2dx
224 * n = ceiling((2mdy + bias - dx) / 2dx)
225 * = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
227 * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
228 * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
229 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
230 * 2mdy > 2ndx - bias - dx
231 * m > (2ndx - bias - dx) / 2dy
232 * m = floor((2ndx - bias - dx) / 2dy) + 1
233 * m = (2ndx - bias - dx) / 2dy + 1
235 * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
236 * err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
237 * = 2dy - dx - 2mdy + 2ndx
238 * 0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
239 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
240 * again exactly one solution.
241 * 2ndx <= 2mdy - bias + dx
242 * n = floor((2mdy - bias + dx) / 2dx)
243 * = (2mdy - bias + dx) / 2dx
245 * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
246 * mininizing m to include all of the points at y = y2 - n. As above:
247 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
248 * 2mdy >= 2ndx + bias - dx
249 * m = ceiling((2ndx + bias - dx) / 2dy)
250 * = (2ndx + bias - dx - 1) / 2dy + 1
252 * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
254 * Moving start point from y1 to y1 + n find x1 + m
255 * m = (2ndx + bias + dy - 1) / 2dy
257 * Moving start point from x1 to x1 + m find y1 + n
258 * n = (2mdy - bias - dy) / 2ndx + 1
260 * Moving end point from y2 to y2 - n find x1 - m
261 * m = (2ndx - bias + dy) / 2dy
263 * Moving end point from x2 to x2 - m find y2 - n
264 * n = (2mdy + bias - dy - 1) / 2dx + 1
266 static int clip_line(const POINT
*start
, const POINT
*end
, const RECT
*clip
,
267 const bres_params
*params
, POINT
*pt1
, POINT
*pt2
)
270 BOOL clipped
= FALSE
;
271 DWORD start_oc
, end_oc
;
272 const int bias
= params
->bias
;
273 const unsigned int dx
= params
->dx
;
274 const unsigned int dy
= params
->dy
;
275 const unsigned int two_dx
= params
->dx
* 2;
276 const unsigned int two_dy
= params
->dy
* 2;
277 const BOOL xmajor
= is_xmajor(params
->octant
);
278 const BOOL neg_slope
= !is_pos_slope(params
->octant
);
283 start_oc
= calc_outcode(start
, clip
);
284 end_oc
= calc_outcode(end
, clip
);
288 if(start_oc
== 0 && end_oc
== 0) return clipped
? 1 : 2; /* trivial accept */
289 if(start_oc
& end_oc
) return 0; /* trivial reject */
292 if(start_oc
& OUT_LEFT
)
294 m
= clip
->left
- start
->x
;
296 n
= (m
* two_dy
+ bias
+ dx
- 1) / two_dx
;
298 n
= (m
* two_dy
- bias
- dy
) / two_dx
+ 1;
301 if(neg_slope
) n
= -n
;
302 pt1
->y
= start
->y
+ n
;
303 start_oc
= calc_outcode(pt1
, clip
);
305 else if(start_oc
& OUT_RIGHT
)
307 m
= start
->x
- clip
->right
+ 1;
309 n
= (m
* two_dy
+ bias
+ dx
- 1) / two_dx
;
311 n
= (m
* two_dy
- bias
- dy
) / two_dx
+ 1;
313 pt1
->x
= clip
->right
- 1;
314 if(neg_slope
) n
= -n
;
315 pt1
->y
= start
->y
- n
;
316 start_oc
= calc_outcode(pt1
, clip
);
318 else if(start_oc
& OUT_TOP
)
320 n
= clip
->top
- start
->y
;
322 m
= (n
* two_dx
- bias
- dx
) / two_dy
+ 1;
324 m
= (n
* two_dx
+ bias
+ dy
- 1) / two_dy
;
327 if(neg_slope
) m
= -m
;
328 pt1
->x
= start
->x
+ m
;
329 start_oc
= calc_outcode(pt1
, clip
);
331 else if(start_oc
& OUT_BOTTOM
)
333 n
= start
->y
- clip
->bottom
+ 1;
335 m
= (n
* two_dx
- bias
- dx
) / two_dy
+ 1;
337 m
= (n
* two_dx
+ bias
+ dy
- 1) / two_dy
;
339 pt1
->y
= clip
->bottom
- 1;
340 if(neg_slope
) m
= -m
;
341 pt1
->x
= start
->x
- m
;
342 start_oc
= calc_outcode(pt1
, clip
);
344 else if(end_oc
& OUT_LEFT
)
346 m
= clip
->left
- end
->x
;
348 n
= (m
* two_dy
- bias
+ dx
) / two_dx
;
350 n
= (m
* two_dy
+ bias
- dy
- 1) / two_dx
+ 1;
353 if(neg_slope
) n
= -n
;
355 end_oc
= calc_outcode(pt2
, clip
);
357 else if(end_oc
& OUT_RIGHT
)
359 m
= end
->x
- clip
->right
+ 1;
361 n
= (m
* two_dy
- bias
+ dx
) / two_dx
;
363 n
= (m
* two_dy
+ bias
- dy
- 1) / two_dx
+ 1;
365 pt2
->x
= clip
->right
- 1;
366 if(neg_slope
) n
= -n
;
368 end_oc
= calc_outcode(pt2
, clip
);
370 else if(end_oc
& OUT_TOP
)
372 n
= clip
->top
- end
->y
;
374 m
= (n
* two_dx
+ bias
- dx
- 1) / two_dy
+ 1;
376 m
= (n
* two_dx
- bias
+ dy
) / two_dy
;
379 if(neg_slope
) m
= -m
;
381 end_oc
= calc_outcode(pt2
, clip
);
383 else if(end_oc
& OUT_BOTTOM
)
385 n
= end
->y
- clip
->bottom
+ 1;
387 m
= (n
* two_dx
+ bias
- dx
- 1) / two_dy
+ 1;
389 m
= (n
* two_dx
- bias
+ dy
) / two_dy
;
391 pt2
->y
= clip
->bottom
- 1;
392 if(neg_slope
) m
= -m
;
394 end_oc
= calc_outcode(pt2
, clip
);
399 static void bres_line_with_bias(INT x1
, INT y1
, INT x2
, INT y2
, const bres_params
*params
, INT err
,
400 BOOL last_pt
, void (* callback
)(dibdrv_physdev
*,INT
,INT
), dibdrv_physdev
*pdev
)
402 const int xadd
= is_x_increasing(params
->octant
) ? 1 : -1;
403 const int yadd
= is_y_increasing(params
->octant
) ? 1 : -1;
406 if (is_xmajor(params
->octant
)) /* line is "more horizontal" */
408 erradd
= 2*params
->dy
- 2*params
->dx
;
411 callback(pdev
, x1
, y1
);
412 if (err
+ params
->bias
> 0)
417 else err
+= 2*params
->dy
;
420 if(last_pt
) callback(pdev
, x1
, y1
);
422 else /* line is "more vertical" */
424 erradd
= 2*params
->dx
- 2*params
->dy
;
427 callback(pdev
, x1
, y1
);
428 if (err
+ params
->bias
> 0)
433 else err
+= 2*params
->dx
;
436 if(last_pt
) callback(pdev
, x1
, y1
);
440 static BOOL
solid_pen_line(dibdrv_physdev
*pdev
, POINT
*start
, POINT
*end
)
442 const WINEREGION
*clip
= get_wine_region(pdev
->clip
);
444 if(start
->y
== end
->y
)
449 rect
.left
= start
->x
;
452 rect
.bottom
= end
->y
+ 1;
453 order_end_points(&rect
.left
, &rect
.right
);
454 for(i
= 0; i
< clip
->numRects
; i
++)
456 if(clip
->rects
[i
].top
>= rect
.bottom
) break;
457 if(clip
->rects
[i
].bottom
<= rect
.top
) continue;
458 /* Optimize the unclipped case */
459 if(clip
->rects
[i
].left
<= rect
.left
&& clip
->rects
[i
].right
>= rect
.right
)
461 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, pdev
->pen_and
, pdev
->pen_xor
);
464 if(clip
->rects
[i
].right
> rect
.left
&& clip
->rects
[i
].left
< rect
.right
)
467 tmp
.left
= max(rect
.left
, clip
->rects
[i
].left
);
468 tmp
.right
= min(rect
.right
, clip
->rects
[i
].right
);
469 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &tmp
, pdev
->pen_and
, pdev
->pen_xor
);
473 else if(start
->x
== end
->x
)
478 rect
.left
= start
->x
;
480 rect
.right
= end
->x
+ 1;
481 rect
.bottom
= end
->y
;
482 order_end_points(&rect
.top
, &rect
.bottom
);
483 for(i
= 0; i
< clip
->numRects
; i
++)
485 /* Optimize unclipped case */
486 if(clip
->rects
[i
].top
<= rect
.top
&& clip
->rects
[i
].bottom
>= rect
.bottom
&&
487 clip
->rects
[i
].left
<= rect
.left
&& clip
->rects
[i
].right
>= rect
.right
)
489 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, pdev
->pen_and
, pdev
->pen_xor
);
492 if(clip
->rects
[i
].top
>= rect
.bottom
) break;
493 if(clip
->rects
[i
].bottom
<= rect
.top
) continue;
494 if(clip
->rects
[i
].right
> rect
.left
&& clip
->rects
[i
].left
< rect
.right
)
497 tmp
.top
= max(rect
.top
, clip
->rects
[i
].top
);
498 tmp
.bottom
= min(rect
.bottom
, clip
->rects
[i
].bottom
);
499 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &tmp
, pdev
->pen_and
, pdev
->pen_xor
);
506 INT dx
= end
->x
- start
->x
;
507 INT dy
= end
->y
- start
->y
;
512 params
.octant
= get_octant_mask(dx
, dy
);
513 /* Octants 3, 5, 6 and 8 take a bias */
514 params
.bias
= (params
.octant
& 0xb4) ? 1 : 0;
516 for(i
= 0; i
< clip
->numRects
; i
++)
518 POINT clipped_start
, clipped_end
;
520 clip_status
= clip_line(start
, end
, clip
->rects
+ i
, ¶ms
, &clipped_start
, &clipped_end
);
524 int m
= abs(clipped_start
.x
- start
->x
);
525 int n
= abs(clipped_start
.y
- start
->y
);
527 BOOL last_pt
= FALSE
;
529 if(is_xmajor(params
.octant
))
530 err
= 2 * params
.dy
- params
.dx
+ m
* 2 * params
.dy
- n
* 2 * params
.dx
;
532 err
= 2 * params
.dx
- params
.dy
+ n
* 2 * params
.dx
- m
* 2 * params
.dy
;
534 if(clip_status
== 1 && (end
->x
!= clipped_end
.x
|| end
->y
!= clipped_end
.y
)) last_pt
= TRUE
;
536 bres_line_with_bias(clipped_start
.x
, clipped_start
.y
, clipped_end
.x
, clipped_end
.y
, ¶ms
,
537 err
, last_pt
, solid_pen_line_callback
, pdev
);
539 if(clip_status
== 2) break; /* completely unclipped, so we can finish */
544 release_wine_region(pdev
->clip
);
548 void reset_dash_origin(dibdrv_physdev
*pdev
)
550 pdev
->dash_pos
.cur_dash
= 0;
551 pdev
->dash_pos
.left_in_dash
= pdev
->pen_pattern
.dashes
[0];
552 pdev
->dash_pos
.mark
= TRUE
;
555 static inline void skip_dash(dibdrv_physdev
*pdev
, unsigned int skip
)
557 skip
%= pdev
->pen_pattern
.total_len
;
560 if(pdev
->dash_pos
.left_in_dash
> skip
)
562 pdev
->dash_pos
.left_in_dash
-= skip
;
565 skip
-= pdev
->dash_pos
.left_in_dash
;
566 pdev
->dash_pos
.cur_dash
++;
567 if(pdev
->dash_pos
.cur_dash
== pdev
->pen_pattern
.count
) pdev
->dash_pos
.cur_dash
= 0;
568 pdev
->dash_pos
.left_in_dash
= pdev
->pen_pattern
.dashes
[pdev
->dash_pos
.cur_dash
];
569 pdev
->dash_pos
.mark
= !pdev
->dash_pos
.mark
;
573 static inline void get_dash_colors(const dibdrv_physdev
*pdev
, DWORD
*and, DWORD
*xor)
575 if(pdev
->dash_pos
.mark
)
577 *and = pdev
->pen_and
;
578 *xor = pdev
->pen_xor
;
582 *and = pdev
->bkgnd_and
;
583 *xor = pdev
->bkgnd_xor
;
587 static void dashed_pen_line_callback(dibdrv_physdev
*pdev
, INT x
, INT y
)
592 get_dash_colors(pdev
, &and, &xor);
598 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
602 static BOOL
dashed_pen_line(dibdrv_physdev
*pdev
, POINT
*start
, POINT
*end
)
604 const WINEREGION
*clip
= get_wine_region(pdev
->clip
);
608 const dash_pos start_pos
= pdev
->dash_pos
;
610 if(start
->y
== end
->y
) /* hline */
613 INT left
, right
, cur_x
;
616 rect
.bottom
= start
->y
+ 1;
618 if(start
->x
<= end
->x
)
631 for(i
= 0; i
< clip
->numRects
; i
++)
633 if(clip
->rects
[i
].top
> start
->y
) break;
634 if(clip
->rects
[i
].bottom
<= start
->y
) continue;
636 if(clip
->rects
[i
].right
> left
&& clip
->rects
[i
].left
<= right
)
638 int clipped_left
= max(clip
->rects
[i
].left
, left
);
639 int clipped_right
= min(clip
->rects
[i
].right
- 1, right
);
641 pdev
->dash_pos
= start_pos
;
645 cur_x
= clipped_left
;
647 skip_dash(pdev
, clipped_left
- left
);
649 while(cur_x
<= clipped_right
)
651 get_dash_colors(pdev
, &and, &xor);
652 dash_len
= pdev
->dash_pos
.left_in_dash
;
653 if(cur_x
+ dash_len
> clipped_right
+ 1)
654 dash_len
= clipped_right
- cur_x
+ 1;
656 rect
.right
= cur_x
+ dash_len
;
658 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
660 skip_dash(pdev
, dash_len
);
665 cur_x
= clipped_right
;
667 skip_dash(pdev
, right
- clipped_right
);
669 while(cur_x
>= clipped_left
)
671 get_dash_colors(pdev
, &and, &xor);
672 dash_len
= pdev
->dash_pos
.left_in_dash
;
673 if(cur_x
- dash_len
< clipped_left
- 1)
674 dash_len
= cur_x
- clipped_left
+ 1;
675 rect
.left
= cur_x
- dash_len
+ 1;
676 rect
.right
= cur_x
+ 1;
678 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
680 skip_dash(pdev
, dash_len
);
685 pdev
->dash_pos
= start_pos
;
686 skip_dash(pdev
, right
- left
+ 1);
688 else if(start
->x
== end
->x
) /* vline */
691 INT top
, bottom
, cur_y
;
693 rect
.left
= start
->x
;
694 rect
.right
= start
->x
+ 1;
696 if(start
->y
<= end
->y
)
709 for(i
= 0; i
< clip
->numRects
; i
++)
711 if(clip
->rects
[i
].top
> bottom
) break;
712 if(clip
->rects
[i
].bottom
<= top
) continue;
713 if(clip
->rects
[i
].right
> start
->x
&& clip
->rects
[i
].left
<= start
->x
)
715 int clipped_top
= max(clip
->rects
[i
].top
, top
);
716 int clipped_bottom
= min(clip
->rects
[i
].bottom
- 1, bottom
);
718 pdev
->dash_pos
= start_pos
;
724 skip_dash(pdev
, clipped_top
- top
);
726 while(cur_y
<= clipped_bottom
)
728 get_dash_colors(pdev
, &and, &xor);
729 dash_len
= pdev
->dash_pos
.left_in_dash
;
730 if(cur_y
+ dash_len
> clipped_bottom
+ 1)
731 dash_len
= clipped_bottom
- cur_y
+ 1;
733 rect
.bottom
= cur_y
+ dash_len
;
735 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
737 skip_dash(pdev
, dash_len
);
742 cur_y
= clipped_bottom
;
744 skip_dash(pdev
, bottom
- clipped_bottom
);
746 while(cur_y
>= clipped_top
)
748 get_dash_colors(pdev
, &and, &xor);
749 dash_len
= pdev
->dash_pos
.left_in_dash
;
750 if(cur_y
- dash_len
< clipped_top
- 1)
751 dash_len
= cur_y
- clipped_top
+ 1;
752 rect
.top
= cur_y
- dash_len
+ 1;
753 rect
.bottom
= cur_y
+ 1;
755 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, and, xor);
757 skip_dash(pdev
, dash_len
);
762 pdev
->dash_pos
= start_pos
;
763 skip_dash(pdev
, bottom
- top
+ 1);
768 INT dx
= end
->x
- start
->x
;
769 INT dy
= end
->y
- start
->y
;
774 params
.octant
= get_octant_mask(dx
, dy
);
775 /* Octants 3, 5, 6 and 8 take a bias */
776 params
.bias
= (params
.octant
& 0xb4) ? 1 : 0;
778 for(i
= 0; i
< clip
->numRects
; i
++)
780 POINT clipped_start
, clipped_end
;
782 clip_status
= clip_line(start
, end
, clip
->rects
+ i
, ¶ms
, &clipped_start
, &clipped_end
);
786 int m
= abs(clipped_start
.x
- start
->x
);
787 int n
= abs(clipped_start
.y
- start
->y
);
789 BOOL last_pt
= FALSE
;
791 pdev
->dash_pos
= start_pos
;
793 if(is_xmajor(params
.octant
))
795 err
= 2 * params
.dy
- params
.dx
+ m
* 2 * params
.dy
- n
* 2 * params
.dx
;
800 err
= 2 * params
.dx
- params
.dy
+ n
* 2 * params
.dx
- m
* 2 * params
.dy
;
803 if(clip_status
== 1 && (end
->x
!= clipped_end
.x
|| end
->y
!= clipped_end
.y
)) last_pt
= TRUE
;
805 bres_line_with_bias(clipped_start
.x
, clipped_start
.y
, clipped_end
.x
, clipped_end
.y
, ¶ms
,
806 err
, last_pt
, dashed_pen_line_callback
, pdev
);
808 if(clip_status
== 2) break; /* completely unclipped, so we can finish */
811 pdev
->dash_pos
= start_pos
;
812 if(is_xmajor(params
.octant
))
813 skip_dash(pdev
, params
.dx
);
815 skip_dash(pdev
, params
.dy
);
818 release_wine_region(pdev
->clip
);
822 static BOOL
null_pen_line(dibdrv_physdev
*pdev
, POINT
*start
, POINT
*end
)
827 static const dash_pattern dash_patterns
[5] =
829 {0, {0}, 0}, /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
830 {2, {18, 6}, 24}, /* PS_DASH */
831 {2, {3, 3}, 6}, /* PS_DOT */
832 {4, {9, 6, 3, 6}, 24}, /* PS_DASHDOT */
833 {6, {9, 3, 3, 3, 3, 3}, 24} /* PS_DASHDOTDOT */
836 /***********************************************************************
839 HPEN CDECL
dibdrv_SelectPen( PHYSDEV dev
, HPEN hpen
)
841 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSelectPen
);
842 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
846 TRACE("(%p, %p)\n", dev
, hpen
);
848 if (!GetObjectW( hpen
, sizeof(logpen
), &logpen
))
850 /* must be an extended pen */
852 INT size
= GetObjectW( hpen
, 0, NULL
);
856 elp
= HeapAlloc( GetProcessHeap(), 0, size
);
858 GetObjectW( hpen
, size
, elp
);
859 /* FIXME: add support for user style pens */
860 logpen
.lopnStyle
= elp
->elpPenStyle
;
861 logpen
.lopnWidth
.x
= elp
->elpWidth
;
862 logpen
.lopnWidth
.y
= 0;
863 logpen
.lopnColor
= elp
->elpColor
;
865 HeapFree( GetProcessHeap(), 0, elp
);
868 if (hpen
== GetStockObject( DC_PEN
))
869 logpen
.lopnColor
= GetDCPenColor( dev
->hdc
);
871 pdev
->pen_colorref
= logpen
.lopnColor
;
872 pdev
->pen_color
= pdev
->dib
.funcs
->colorref_to_pixel(&pdev
->dib
, logpen
.lopnColor
);
873 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->pen_color
, &pdev
->pen_and
, &pdev
->pen_xor
);
875 pdev
->pen_pattern
= dash_patterns
[PS_SOLID
];
877 pdev
->defer
|= DEFER_PEN
;
879 style
= logpen
.lopnStyle
& PS_STYLE_MASK
;
884 if(logpen
.lopnStyle
& PS_GEOMETRIC
) break;
885 if(logpen
.lopnWidth
.x
> 1) break;
886 pdev
->pen_line
= solid_pen_line
;
887 pdev
->defer
&= ~DEFER_PEN
;
894 if(logpen
.lopnStyle
& PS_GEOMETRIC
) break;
895 if(logpen
.lopnWidth
.x
> 1) break;
896 pdev
->pen_line
= dashed_pen_line
;
897 pdev
->pen_pattern
= dash_patterns
[style
];
898 pdev
->defer
&= ~DEFER_PEN
;
902 pdev
->pen_line
= null_pen_line
;
903 pdev
->defer
&= ~DEFER_PEN
;
910 return next
->funcs
->pSelectPen( next
, hpen
);
913 /***********************************************************************
914 * dibdrv_SetDCPenColor
916 COLORREF CDECL
dibdrv_SetDCPenColor( PHYSDEV dev
, COLORREF color
)
918 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSetDCPenColor
);
919 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
921 if (GetCurrentObject(dev
->hdc
, OBJ_PEN
) == GetStockObject( DC_PEN
))
923 pdev
->pen_colorref
= color
;
924 pdev
->pen_color
= pdev
->dib
.funcs
->colorref_to_pixel(&pdev
->dib
, color
);
925 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->pen_color
, &pdev
->pen_and
, &pdev
->pen_xor
);
928 return next
->funcs
->pSetDCPenColor( next
, color
);
931 /**********************************************************************
934 * Fill a number of rectangles with the solid brush
935 * FIXME: Should we insist l < r && t < b? Currently we assume this.
937 static BOOL
solid_brush(dibdrv_physdev
*pdev
, int num
, RECT
*rects
)
940 const WINEREGION
*clip
= get_wine_region(pdev
->clip
);
942 for(i
= 0; i
< num
; i
++)
944 for(j
= 0; j
< clip
->numRects
; j
++)
946 RECT rect
= rects
[i
];
948 /* Optimize unclipped case */
949 if(clip
->rects
[j
].top
<= rect
.top
&& clip
->rects
[j
].bottom
>= rect
.bottom
&&
950 clip
->rects
[j
].left
<= rect
.left
&& clip
->rects
[j
].right
>= rect
.right
)
952 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, pdev
->brush_and
, pdev
->brush_xor
);
956 if(clip
->rects
[j
].top
>= rect
.bottom
) break;
957 if(clip
->rects
[j
].bottom
<= rect
.top
) continue;
959 if(clip
->rects
[j
].right
> rect
.left
&& clip
->rects
[j
].left
< rect
.right
)
961 rect
.left
= max(rect
.left
, clip
->rects
[j
].left
);
962 rect
.top
= max(rect
.top
, clip
->rects
[j
].top
);
963 rect
.right
= min(rect
.right
, clip
->rects
[j
].right
);
964 rect
.bottom
= min(rect
.bottom
, clip
->rects
[j
].bottom
);
966 pdev
->dib
.funcs
->solid_rects(&pdev
->dib
, 1, &rect
, pdev
->brush_and
, pdev
->brush_xor
);
970 release_wine_region(pdev
->clip
);
974 static void free_pattern_brush_bits( dibdrv_physdev
*pdev
)
976 HeapFree(GetProcessHeap(), 0, pdev
->brush_and_bits
);
977 HeapFree(GetProcessHeap(), 0, pdev
->brush_xor_bits
);
978 pdev
->brush_and_bits
= NULL
;
979 pdev
->brush_xor_bits
= NULL
;
982 void free_pattern_brush( dibdrv_physdev
*pdev
)
984 free_pattern_brush_bits( pdev
);
985 free_dib_info( &pdev
->brush_dib
, TRUE
);
988 static BOOL
create_pattern_brush_bits(dibdrv_physdev
*pdev
)
990 DWORD size
= pdev
->brush_dib
.height
* abs(pdev
->brush_dib
.stride
);
991 DWORD
*brush_bits
= pdev
->brush_dib
.bits
;
992 DWORD
*and_bits
, *xor_bits
;
994 assert(pdev
->brush_and_bits
== NULL
);
995 assert(pdev
->brush_xor_bits
== NULL
);
997 assert(pdev
->brush_dib
.stride
> 0);
999 and_bits
= pdev
->brush_and_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1000 xor_bits
= pdev
->brush_xor_bits
= HeapAlloc(GetProcessHeap(), 0, size
);
1002 if(!and_bits
|| !xor_bits
)
1004 ERR("Failed to create pattern brush bits\n");
1005 free_pattern_brush_bits( pdev
);
1011 calc_and_xor_masks(pdev
->brush_rop
, *brush_bits
++, and_bits
++, xor_bits
++);
1018 /**********************************************************************
1021 * Fill a number of rectangles with the pattern brush
1022 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1024 static BOOL
pattern_brush(dibdrv_physdev
*pdev
, int num
, RECT
*rects
)
1027 const WINEREGION
*clip
;
1030 if(pdev
->brush_and_bits
== NULL
)
1031 if(!create_pattern_brush_bits(pdev
))
1034 GetBrushOrgEx(pdev
->dev
.hdc
, &origin
);
1036 clip
= get_wine_region(pdev
->clip
);
1037 for(i
= 0; i
< num
; i
++)
1039 for(j
= 0; j
< clip
->numRects
; j
++)
1041 RECT rect
= rects
[i
];
1043 /* Optimize unclipped case */
1044 if(clip
->rects
[j
].top
<= rect
.top
&& clip
->rects
[j
].bottom
>= rect
.bottom
&&
1045 clip
->rects
[j
].left
<= rect
.left
&& clip
->rects
[j
].right
>= rect
.right
)
1047 pdev
->dib
.funcs
->pattern_rects(&pdev
->dib
, 1, &rect
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1051 if(clip
->rects
[j
].top
>= rect
.bottom
) break;
1052 if(clip
->rects
[j
].bottom
<= rect
.top
) continue;
1054 if(clip
->rects
[j
].right
> rect
.left
&& clip
->rects
[j
].left
< rect
.right
)
1056 rect
.left
= max(rect
.left
, clip
->rects
[j
].left
);
1057 rect
.top
= max(rect
.top
, clip
->rects
[j
].top
);
1058 rect
.right
= min(rect
.right
, clip
->rects
[j
].right
);
1059 rect
.bottom
= min(rect
.bottom
, clip
->rects
[j
].bottom
);
1061 pdev
->dib
.funcs
->pattern_rects(&pdev
->dib
, 1, &rect
, &origin
, &pdev
->brush_dib
, pdev
->brush_and_bits
, pdev
->brush_xor_bits
);
1065 release_wine_region(pdev
->clip
);
1069 static BOOL
null_brush(dibdrv_physdev
*pdev
, int num
, RECT
*rects
)
1074 void update_brush_rop( dibdrv_physdev
*pdev
, INT rop
)
1076 pdev
->brush_rop
= rop
;
1077 if(pdev
->brush_style
== BS_SOLID
)
1078 calc_and_xor_masks(rop
, pdev
->brush_color
, &pdev
->brush_and
, &pdev
->brush_xor
);
1079 free_pattern_brush_bits( pdev
);
1082 /***********************************************************************
1083 * dibdrv_SelectBrush
1085 HBRUSH CDECL
dibdrv_SelectBrush( PHYSDEV dev
, HBRUSH hbrush
)
1087 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSelectBrush
);
1088 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1091 TRACE("(%p, %p)\n", dev
, hbrush
);
1093 if (!GetObjectW( hbrush
, sizeof(logbrush
), &logbrush
)) return 0;
1095 if (hbrush
== GetStockObject( DC_BRUSH
))
1096 logbrush
.lbColor
= GetDCBrushColor( dev
->hdc
);
1098 pdev
->brush_style
= logbrush
.lbStyle
;
1100 pdev
->defer
|= DEFER_BRUSH
;
1102 free_pattern_brush( pdev
);
1104 switch(logbrush
.lbStyle
)
1107 pdev
->brush_colorref
= logbrush
.lbColor
;
1108 pdev
->brush_color
= pdev
->dib
.funcs
->colorref_to_pixel(&pdev
->dib
, logbrush
.lbColor
);
1109 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->brush_color
, &pdev
->brush_and
, &pdev
->brush_xor
);
1110 pdev
->brush_rects
= solid_brush
;
1111 pdev
->defer
&= ~DEFER_BRUSH
;
1115 pdev
->brush_rects
= null_brush
;
1116 pdev
->defer
&= ~DEFER_BRUSH
;
1121 BITMAPINFOHEADER
*bi
= GlobalLock((HGLOBAL
)logbrush
.lbHatch
);
1123 WORD usage
= LOWORD(logbrush
.lbColor
);
1124 HPALETTE pal
= (usage
== DIB_PAL_COLORS
) ? GetCurrentObject(dev
->hdc
, OBJ_PAL
) : NULL
;
1126 if(!bi
) return NULL
;
1127 if(init_dib_info_from_packed(&orig_dib
, bi
, usage
, pal
))
1129 copy_dib_color_info(&pdev
->brush_dib
, &pdev
->dib
);
1130 if(convert_dib(&pdev
->brush_dib
, &orig_dib
))
1132 pdev
->brush_rects
= pattern_brush
;
1133 pdev
->defer
&= ~DEFER_BRUSH
;
1135 free_dib_info(&orig_dib
, FALSE
);
1137 GlobalUnlock((HGLOBAL
)logbrush
.lbHatch
);
1145 return next
->funcs
->pSelectBrush( next
, hbrush
);
1148 /***********************************************************************
1149 * dibdrv_SetDCBrushColor
1151 COLORREF CDECL
dibdrv_SetDCBrushColor( PHYSDEV dev
, COLORREF color
)
1153 PHYSDEV next
= GET_NEXT_PHYSDEV( dev
, pSetDCBrushColor
);
1154 dibdrv_physdev
*pdev
= get_dibdrv_pdev(dev
);
1156 if (GetCurrentObject(dev
->hdc
, OBJ_BRUSH
) == GetStockObject( DC_BRUSH
))
1158 pdev
->brush_colorref
= color
;
1159 pdev
->brush_color
= pdev
->dib
.funcs
->colorref_to_pixel(&pdev
->dib
, color
);
1160 calc_and_xor_masks(GetROP2(dev
->hdc
), pdev
->brush_color
, &pdev
->brush_and
, &pdev
->brush_xor
);
1163 return next
->funcs
->pSetDCBrushColor( next
, color
);