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_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, logpen.lopnColor);
872 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
874 pdev->pen_pattern = dash_patterns[PS_SOLID];
876 pdev->defer |= DEFER_PEN;
878 style = logpen.lopnStyle & PS_STYLE_MASK;
883 if(logpen.lopnStyle & PS_GEOMETRIC) break;
884 if(logpen.lopnWidth.x > 1) break;
885 pdev->pen_line = solid_pen_line;
886 pdev->defer &= ~DEFER_PEN;
893 if(logpen.lopnStyle & PS_GEOMETRIC) break;
894 if(logpen.lopnWidth.x > 1) break;
895 pdev->pen_line = dashed_pen_line;
896 pdev->pen_pattern = dash_patterns[style];
897 pdev->defer &= ~DEFER_PEN;
901 pdev->pen_line = null_pen_line;
902 pdev->defer &= ~DEFER_PEN;
909 return next->funcs->pSelectPen( next, hpen );
912 /***********************************************************************
913 * dibdrv_SetDCPenColor
915 COLORREF CDECL dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
917 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
918 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
920 if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
922 pdev->pen_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, color);
923 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
926 return next->funcs->pSetDCPenColor( next, color );
929 /**********************************************************************
932 * Fill a number of rectangles with the solid brush
933 * FIXME: Should we insist l < r && t < b? Currently we assume this.
935 static BOOL solid_brush(dibdrv_physdev *pdev, int num, RECT *rects)
938 const WINEREGION *clip = get_wine_region(pdev->clip);
940 for(i = 0; i < num; i++)
942 for(j = 0; j < clip->numRects; j++)
944 RECT rect = rects[i];
946 /* Optimize unclipped case */
947 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
948 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
950 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
954 if(clip->rects[j].top >= rect.bottom) break;
955 if(clip->rects[j].bottom <= rect.top) continue;
957 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
959 rect.left = max(rect.left, clip->rects[j].left);
960 rect.top = max(rect.top, clip->rects[j].top);
961 rect.right = min(rect.right, clip->rects[j].right);
962 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
964 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
968 release_wine_region(pdev->clip);
972 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
974 HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
975 HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
976 pdev->brush_and_bits = NULL;
977 pdev->brush_xor_bits = NULL;
980 void free_pattern_brush( dibdrv_physdev *pdev )
982 free_pattern_brush_bits( pdev );
983 free_dib_info( &pdev->brush_dib, TRUE );
986 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
988 DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
989 DWORD *brush_bits = pdev->brush_dib.bits;
990 DWORD *and_bits, *xor_bits;
992 assert(pdev->brush_and_bits == NULL);
993 assert(pdev->brush_xor_bits == NULL);
995 assert(pdev->brush_dib.stride > 0);
997 and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
998 xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1000 if(!and_bits || !xor_bits)
1002 ERR("Failed to create pattern brush bits\n");
1003 free_pattern_brush_bits( pdev );
1009 calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1016 /**********************************************************************
1019 * Fill a number of rectangles with the pattern brush
1020 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1022 static BOOL pattern_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1025 const WINEREGION *clip;
1028 if(pdev->brush_and_bits == NULL)
1029 if(!create_pattern_brush_bits(pdev))
1032 GetBrushOrgEx(pdev->dev.hdc, &origin);
1034 clip = get_wine_region(pdev->clip);
1035 for(i = 0; i < num; i++)
1037 for(j = 0; j < clip->numRects; j++)
1039 RECT rect = rects[i];
1041 /* Optimize unclipped case */
1042 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1043 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1045 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1049 if(clip->rects[j].top >= rect.bottom) break;
1050 if(clip->rects[j].bottom <= rect.top) continue;
1052 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1054 rect.left = max(rect.left, clip->rects[j].left);
1055 rect.top = max(rect.top, clip->rects[j].top);
1056 rect.right = min(rect.right, clip->rects[j].right);
1057 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1059 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1063 release_wine_region(pdev->clip);
1067 static BOOL null_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1072 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1074 pdev->brush_rop = rop;
1075 if(pdev->brush_style == BS_SOLID)
1076 calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1077 free_pattern_brush_bits( pdev );
1080 /***********************************************************************
1081 * dibdrv_SelectBrush
1083 HBRUSH CDECL dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
1085 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1086 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1089 TRACE("(%p, %p)\n", dev, hbrush);
1091 if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1093 if (hbrush == GetStockObject( DC_BRUSH ))
1094 logbrush.lbColor = GetDCBrushColor( dev->hdc );
1096 pdev->brush_style = logbrush.lbStyle;
1098 pdev->defer |= DEFER_BRUSH;
1100 free_pattern_brush( pdev );
1102 switch(logbrush.lbStyle)
1105 pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, logbrush.lbColor);
1106 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1107 pdev->brush_rects = solid_brush;
1108 pdev->defer &= ~DEFER_BRUSH;
1112 pdev->brush_rects = null_brush;
1113 pdev->defer &= ~DEFER_BRUSH;
1118 BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1120 WORD usage = LOWORD(logbrush.lbColor);
1121 HPALETTE pal = (usage == DIB_PAL_COLORS) ? GetCurrentObject(dev->hdc, OBJ_PAL) : NULL;
1123 if(!bi) return NULL;
1124 if(init_dib_info_from_packed(&orig_dib, bi, usage, pal))
1126 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1127 if(convert_dib(&pdev->brush_dib, &orig_dib))
1129 pdev->brush_rects = pattern_brush;
1130 pdev->defer &= ~DEFER_BRUSH;
1132 free_dib_info(&orig_dib, FALSE);
1134 GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1142 return next->funcs->pSelectBrush( next, hbrush );
1145 /***********************************************************************
1146 * dibdrv_SetDCBrushColor
1148 COLORREF CDECL dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1150 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1151 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1153 if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1155 pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, color);
1156 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1159 return next->funcs->pSetDCBrushColor( next, color );