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 /******************************************************************
129 * 1 bit bitmaps map the fg/bg colors as follows:
130 * If the fg colorref exactly matches one of the color table entries then
131 * that entry is the fg color and the other is the bg.
132 * Otherwise the bg color is mapped to the closest entry in the table and
133 * the fg takes the other one.
135 DWORD get_fg_color( dibdrv_physdev *pdev, COLORREF fg )
139 if(pdev->dib.bit_count != 1)
140 return pdev->dib.funcs->colorref_to_pixel( &pdev->dib, fg );
142 fg_quad = rgbquad_from_colorref( fg );
143 if(rgbquad_equal(&fg_quad, pdev->dib.color_table))
145 if(rgbquad_equal(&fg_quad, pdev->dib.color_table + 1))
148 if(fg == GetBkColor(pdev->dev.hdc)) return pdev->bkgnd_color;
149 else return pdev->bkgnd_color ? 0 : 1;
152 /***************************************************************************
153 * get_pen_bkgnd_masks
155 * Returns the pre-calculated bkgnd color masks unless the dib is 1 bpp.
156 * In this case since there are several fg sources (pen, brush, text)
157 * this makes pdev->bkgnd_color unusable. So here we take the inverse
158 * of the relevant fg color (which is always set up correctly).
160 static inline void get_pen_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
162 if(pdev->dib.bit_count != 1 || GetBkMode(pdev->dev.hdc) == TRANSPARENT)
164 *and = pdev->bkgnd_and;
165 *xor = pdev->bkgnd_xor;
169 DWORD color = ~pdev->pen_color;
170 if(pdev->pen_colorref == GetBkColor(pdev->dev.hdc)) color = pdev->pen_color;
171 calc_and_xor_masks( GetROP2(pdev->dev.hdc), color, and, xor );
175 static inline void get_brush_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
177 if(GetBkMode(pdev->dev.hdc) == TRANSPARENT)
179 *and = pdev->bkgnd_and;
180 *xor = pdev->bkgnd_xor;
184 DWORD color = pdev->bkgnd_color;
186 if(pdev->dib.bit_count == 1)
188 if(pdev->brush_colorref == GetBkColor(pdev->dev.hdc))
189 color = pdev->brush_color;
191 color = ~pdev->brush_color;
193 calc_and_xor_masks( pdev->brush_rop, color, and, xor );
197 static inline void order_end_points(int *s, int *e)
208 static inline BOOL pt_in_rect( const RECT *rect, const POINT *pt )
210 return ((pt->x >= rect->left) && (pt->x < rect->right) &&
211 (pt->y >= rect->top) && (pt->y < rect->bottom));
214 #define Y_INCREASING_MASK 0x0f
215 #define X_INCREASING_MASK 0xc3
216 #define X_MAJOR_MASK 0x99
217 #define POS_SLOPE_MASK 0x33
219 static inline BOOL is_xmajor(DWORD octant)
221 return octant & X_MAJOR_MASK;
224 static inline BOOL is_pos_slope(DWORD octant)
226 return octant & POS_SLOPE_MASK;
229 static inline BOOL is_x_increasing(DWORD octant)
231 return octant & X_INCREASING_MASK;
234 static inline BOOL is_y_increasing(DWORD octant)
236 return octant & Y_INCREASING_MASK;
239 /**********************************************************************
242 * Return the octant number starting clockwise from the +ve x-axis.
244 static inline int get_octant_number(int dx, int dy)
248 return ( dx > dy) ? 1 : 2;
250 return (-dx > dy) ? 4 : 3;
253 return (-dx > -dy) ? 5 : 6;
255 return ( dx > -dy) ? 8 : 7;
258 static inline DWORD get_octant_mask(int dx, int dy)
260 return 1 << (get_octant_number(dx, dy) - 1);
263 static void solid_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
271 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
280 static inline DWORD calc_outcode(const POINT *pt, const RECT *clip)
283 if(pt->x < clip->left) out |= OUT_LEFT;
284 else if(pt->x >= clip->right) out |= OUT_RIGHT;
285 if(pt->y < clip->top) out |= OUT_TOP;
286 else if(pt->y >= clip->bottom) out |= OUT_BOTTOM;
291 /******************************************************************************
294 * Clips the start and end points to a rectangle.
296 * Note, this treats the end point like the start point. If the
297 * caller doesn't want it displayed, it should exclude it. If the end
298 * point is clipped out, then the likelihood is that the new end point
299 * should be displayed.
301 * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
303 * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
304 * however the Bresenham error term is defined differently so the equations
307 * For x major lines we have 2dy >= err + bias > 2dy - 2dx
308 * 0 >= err + bias - 2dy > -2dx
310 * Note dx, dy, m and n are all +ve.
312 * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
313 * err = 2dy - dx + 2mdy - 2ndx
314 * 0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
315 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
316 * which of course will give exactly one solution for n,
317 * so looking at the >= inequality
318 * n >= (2mdy + bias - dx) / 2dx
319 * n = ceiling((2mdy + bias - dx) / 2dx)
320 * = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
322 * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
323 * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
324 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
325 * 2mdy > 2ndx - bias - dx
326 * m > (2ndx - bias - dx) / 2dy
327 * m = floor((2ndx - bias - dx) / 2dy) + 1
328 * m = (2ndx - bias - dx) / 2dy + 1
330 * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
331 * err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
332 * = 2dy - dx - 2mdy + 2ndx
333 * 0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
334 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
335 * again exactly one solution.
336 * 2ndx <= 2mdy - bias + dx
337 * n = floor((2mdy - bias + dx) / 2dx)
338 * = (2mdy - bias + dx) / 2dx
340 * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
341 * mininizing m to include all of the points at y = y2 - n. As above:
342 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
343 * 2mdy >= 2ndx + bias - dx
344 * m = ceiling((2ndx + bias - dx) / 2dy)
345 * = (2ndx + bias - dx - 1) / 2dy + 1
347 * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
349 * Moving start point from y1 to y1 + n find x1 + m
350 * m = (2ndx + bias + dy - 1) / 2dy
352 * Moving start point from x1 to x1 + m find y1 + n
353 * n = (2mdy - bias - dy) / 2ndx + 1
355 * Moving end point from y2 to y2 - n find x1 - m
356 * m = (2ndx - bias + dy) / 2dy
358 * Moving end point from x2 to x2 - m find y2 - n
359 * n = (2mdy + bias - dy - 1) / 2dx + 1
361 int clip_line(const POINT *start, const POINT *end, const RECT *clip,
362 const bres_params *params, POINT *pt1, POINT *pt2)
365 BOOL clipped = FALSE;
366 DWORD start_oc, end_oc;
367 const int bias = params->bias;
368 const unsigned int dx = params->dx;
369 const unsigned int dy = params->dy;
370 const unsigned int two_dx = params->dx * 2;
371 const unsigned int two_dy = params->dy * 2;
372 const BOOL xmajor = is_xmajor(params->octant);
373 const BOOL neg_slope = !is_pos_slope(params->octant);
378 start_oc = calc_outcode(start, clip);
379 end_oc = calc_outcode(end, clip);
383 if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
384 if(start_oc & end_oc) return 0; /* trivial reject */
387 if(start_oc & OUT_LEFT)
389 m = clip->left - start->x;
391 n = (m * two_dy + bias + dx - 1) / two_dx;
393 n = (m * two_dy - bias - dy) / two_dx + 1;
396 if(neg_slope) n = -n;
397 pt1->y = start->y + n;
398 start_oc = calc_outcode(pt1, clip);
400 else if(start_oc & OUT_RIGHT)
402 m = start->x - clip->right + 1;
404 n = (m * two_dy + bias + dx - 1) / two_dx;
406 n = (m * two_dy - bias - dy) / two_dx + 1;
408 pt1->x = clip->right - 1;
409 if(neg_slope) n = -n;
410 pt1->y = start->y - n;
411 start_oc = calc_outcode(pt1, clip);
413 else if(start_oc & OUT_TOP)
415 n = clip->top - start->y;
417 m = (n * two_dx - bias - dx) / two_dy + 1;
419 m = (n * two_dx + bias + dy - 1) / two_dy;
422 if(neg_slope) m = -m;
423 pt1->x = start->x + m;
424 start_oc = calc_outcode(pt1, clip);
426 else if(start_oc & OUT_BOTTOM)
428 n = start->y - clip->bottom + 1;
430 m = (n * two_dx - bias - dx) / two_dy + 1;
432 m = (n * two_dx + bias + dy - 1) / two_dy;
434 pt1->y = clip->bottom - 1;
435 if(neg_slope) m = -m;
436 pt1->x = start->x - m;
437 start_oc = calc_outcode(pt1, clip);
439 else if(end_oc & OUT_LEFT)
441 m = clip->left - end->x;
443 n = (m * two_dy - bias + dx) / two_dx;
445 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
448 if(neg_slope) n = -n;
450 end_oc = calc_outcode(pt2, clip);
452 else if(end_oc & OUT_RIGHT)
454 m = end->x - clip->right + 1;
456 n = (m * two_dy - bias + dx) / two_dx;
458 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
460 pt2->x = clip->right - 1;
461 if(neg_slope) n = -n;
463 end_oc = calc_outcode(pt2, clip);
465 else if(end_oc & OUT_TOP)
467 n = clip->top - end->y;
469 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
471 m = (n * two_dx - bias + dy) / two_dy;
474 if(neg_slope) m = -m;
476 end_oc = calc_outcode(pt2, clip);
478 else if(end_oc & OUT_BOTTOM)
480 n = end->y - clip->bottom + 1;
482 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
484 m = (n * two_dx - bias + dy) / two_dy;
486 pt2->y = clip->bottom - 1;
487 if(neg_slope) m = -m;
489 end_oc = calc_outcode(pt2, clip);
494 static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, const bres_params *params, INT err,
495 BOOL last_pt, void (* callback)(dibdrv_physdev*,INT,INT), dibdrv_physdev *pdev)
497 const int xadd = is_x_increasing(params->octant) ? 1 : -1;
498 const int yadd = is_y_increasing(params->octant) ? 1 : -1;
501 if (is_xmajor(params->octant)) /* line is "more horizontal" */
503 erradd = 2*params->dy - 2*params->dx;
506 callback(pdev, x1, y1);
507 if (err + params->bias > 0)
512 else err += 2*params->dy;
515 if(last_pt) callback(pdev, x1, y1);
517 else /* line is "more vertical" */
519 erradd = 2*params->dx - 2*params->dy;
522 callback(pdev, x1, y1);
523 if (err + params->bias > 0)
528 else err += 2*params->dx;
531 if(last_pt) callback(pdev, x1, y1);
535 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
537 const WINEREGION *clip = get_wine_region(pdev->clip);
539 if(start->y == end->y)
544 rect.left = start->x;
547 rect.bottom = end->y + 1;
548 order_end_points(&rect.left, &rect.right);
549 for(i = 0; i < clip->numRects; i++)
551 if(clip->rects[i].top >= rect.bottom) break;
552 if(clip->rects[i].bottom <= rect.top) continue;
553 /* Optimize the unclipped case */
554 if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
556 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
559 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
562 tmp.left = max(rect.left, clip->rects[i].left);
563 tmp.right = min(rect.right, clip->rects[i].right);
564 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
568 else if(start->x == end->x)
573 rect.left = start->x;
575 rect.right = end->x + 1;
576 rect.bottom = end->y;
577 order_end_points(&rect.top, &rect.bottom);
578 for(i = 0; i < clip->numRects; i++)
580 /* Optimize unclipped case */
581 if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
582 clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
584 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
587 if(clip->rects[i].top >= rect.bottom) break;
588 if(clip->rects[i].bottom <= rect.top) continue;
589 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
592 tmp.top = max(rect.top, clip->rects[i].top);
593 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
594 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
601 INT dx = end->x - start->x;
602 INT dy = end->y - start->y;
607 params.octant = get_octant_mask(dx, dy);
608 /* Octants 3, 5, 6 and 8 take a bias */
609 params.bias = (params.octant & 0xb4) ? 1 : 0;
611 for(i = 0; i < clip->numRects; i++)
613 POINT clipped_start, clipped_end;
615 clip_status = clip_line(start, end, clip->rects + i, ¶ms, &clipped_start, &clipped_end);
619 int m = abs(clipped_start.x - start->x);
620 int n = abs(clipped_start.y - start->y);
622 BOOL last_pt = FALSE;
624 if(is_xmajor(params.octant))
625 err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
627 err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
629 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
631 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, ¶ms,
632 err, last_pt, solid_pen_line_callback, pdev);
634 if(clip_status == 2) break; /* completely unclipped, so we can finish */
639 release_wine_region(pdev->clip);
643 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
648 for (i = 0; i < num - 1; i++)
649 if (!solid_pen_line( pdev, pts + i, pts + i + 1 ))
655 void reset_dash_origin(dibdrv_physdev *pdev)
657 pdev->dash_pos.cur_dash = 0;
658 pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
659 pdev->dash_pos.mark = TRUE;
662 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
664 skip %= pdev->pen_pattern.total_len;
667 if(pdev->dash_pos.left_in_dash > skip)
669 pdev->dash_pos.left_in_dash -= skip;
672 skip -= pdev->dash_pos.left_in_dash;
673 pdev->dash_pos.cur_dash++;
674 if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
675 pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
676 pdev->dash_pos.mark = !pdev->dash_pos.mark;
680 static inline void get_dash_colors(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
682 if(pdev->dash_pos.mark)
684 *and = pdev->pen_and;
685 *xor = pdev->pen_xor;
689 get_pen_bkgnd_masks( pdev, and, xor );
693 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
698 get_dash_colors(pdev, &and, &xor);
704 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
708 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
710 const WINEREGION *clip = get_wine_region(pdev->clip);
714 const dash_pos start_pos = pdev->dash_pos;
716 if(start->y == end->y) /* hline */
719 INT left, right, cur_x;
722 rect.bottom = start->y + 1;
724 if(start->x <= end->x)
737 for(i = 0; i < clip->numRects; i++)
739 if(clip->rects[i].top > start->y) break;
740 if(clip->rects[i].bottom <= start->y) continue;
742 if(clip->rects[i].right > left && clip->rects[i].left <= right)
744 int clipped_left = max(clip->rects[i].left, left);
745 int clipped_right = min(clip->rects[i].right - 1, right);
747 pdev->dash_pos = start_pos;
751 cur_x = clipped_left;
753 skip_dash(pdev, clipped_left - left);
755 while(cur_x <= clipped_right)
757 get_dash_colors(pdev, &and, &xor);
758 dash_len = pdev->dash_pos.left_in_dash;
759 if(cur_x + dash_len > clipped_right + 1)
760 dash_len = clipped_right - cur_x + 1;
762 rect.right = cur_x + dash_len;
764 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
766 skip_dash(pdev, dash_len);
771 cur_x = clipped_right;
773 skip_dash(pdev, right - clipped_right);
775 while(cur_x >= clipped_left)
777 get_dash_colors(pdev, &and, &xor);
778 dash_len = pdev->dash_pos.left_in_dash;
779 if(cur_x - dash_len < clipped_left - 1)
780 dash_len = cur_x - clipped_left + 1;
781 rect.left = cur_x - dash_len + 1;
782 rect.right = cur_x + 1;
784 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
786 skip_dash(pdev, dash_len);
791 pdev->dash_pos = start_pos;
792 skip_dash(pdev, right - left + 1);
794 else if(start->x == end->x) /* vline */
797 INT top, bottom, cur_y;
799 rect.left = start->x;
800 rect.right = start->x + 1;
802 if(start->y <= end->y)
815 for(i = 0; i < clip->numRects; i++)
817 if(clip->rects[i].top > bottom) break;
818 if(clip->rects[i].bottom <= top) continue;
819 if(clip->rects[i].right > start->x && clip->rects[i].left <= start->x)
821 int clipped_top = max(clip->rects[i].top, top);
822 int clipped_bottom = min(clip->rects[i].bottom - 1, bottom);
824 pdev->dash_pos = start_pos;
830 skip_dash(pdev, clipped_top - top);
832 while(cur_y <= clipped_bottom)
834 get_dash_colors(pdev, &and, &xor);
835 dash_len = pdev->dash_pos.left_in_dash;
836 if(cur_y + dash_len > clipped_bottom + 1)
837 dash_len = clipped_bottom - cur_y + 1;
839 rect.bottom = cur_y + dash_len;
841 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
843 skip_dash(pdev, dash_len);
848 cur_y = clipped_bottom;
850 skip_dash(pdev, bottom - clipped_bottom);
852 while(cur_y >= clipped_top)
854 get_dash_colors(pdev, &and, &xor);
855 dash_len = pdev->dash_pos.left_in_dash;
856 if(cur_y - dash_len < clipped_top - 1)
857 dash_len = cur_y - clipped_top + 1;
858 rect.top = cur_y - dash_len + 1;
859 rect.bottom = cur_y + 1;
861 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
863 skip_dash(pdev, dash_len);
868 pdev->dash_pos = start_pos;
869 skip_dash(pdev, bottom - top + 1);
874 INT dx = end->x - start->x;
875 INT dy = end->y - start->y;
880 params.octant = get_octant_mask(dx, dy);
881 /* Octants 3, 5, 6 and 8 take a bias */
882 params.bias = (params.octant & 0xb4) ? 1 : 0;
884 for(i = 0; i < clip->numRects; i++)
886 POINT clipped_start, clipped_end;
888 clip_status = clip_line(start, end, clip->rects + i, ¶ms, &clipped_start, &clipped_end);
892 int m = abs(clipped_start.x - start->x);
893 int n = abs(clipped_start.y - start->y);
895 BOOL last_pt = FALSE;
897 pdev->dash_pos = start_pos;
899 if(is_xmajor(params.octant))
901 err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
906 err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
909 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
911 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, ¶ms,
912 err, last_pt, dashed_pen_line_callback, pdev);
914 if(clip_status == 2) break; /* completely unclipped, so we can finish */
917 pdev->dash_pos = start_pos;
918 if(is_xmajor(params.octant))
919 skip_dash(pdev, params.dx);
921 skip_dash(pdev, params.dy);
924 release_wine_region(pdev->clip);
928 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
933 for (i = 0; i < num - 1; i++)
934 if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
940 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
945 static const dash_pattern dash_patterns[5] =
947 {0, {0}, 0}, /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
948 {2, {18, 6}, 24}, /* PS_DASH */
949 {2, {3, 3}, 6}, /* PS_DOT */
950 {4, {9, 6, 3, 6}, 24}, /* PS_DASHDOT */
951 {6, {9, 3, 3, 3, 3, 3}, 24} /* PS_DASHDOTDOT */
954 /***********************************************************************
957 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
959 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
960 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
964 TRACE("(%p, %p)\n", dev, hpen);
966 if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
968 /* must be an extended pen */
970 INT size = GetObjectW( hpen, 0, NULL );
974 elp = HeapAlloc( GetProcessHeap(), 0, size );
976 GetObjectW( hpen, size, elp );
977 /* FIXME: add support for user style pens */
978 logpen.lopnStyle = elp->elpPenStyle;
979 logpen.lopnWidth.x = elp->elpWidth;
980 logpen.lopnWidth.y = 0;
981 logpen.lopnColor = elp->elpColor;
983 HeapFree( GetProcessHeap(), 0, elp );
986 if (hpen == GetStockObject( DC_PEN ))
987 logpen.lopnColor = GetDCPenColor( dev->hdc );
989 pdev->pen_colorref = logpen.lopnColor;
990 pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
991 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
993 pdev->pen_pattern = dash_patterns[PS_SOLID];
995 pdev->defer |= DEFER_PEN;
997 style = logpen.lopnStyle & PS_STYLE_MASK;
1002 if(logpen.lopnStyle & PS_GEOMETRIC) break;
1003 if(logpen.lopnWidth.x > 1) break;
1004 pdev->pen_lines = solid_pen_lines;
1005 pdev->defer &= ~DEFER_PEN;
1012 if(logpen.lopnStyle & PS_GEOMETRIC) break;
1013 if(logpen.lopnWidth.x > 1) break;
1014 pdev->pen_lines = dashed_pen_lines;
1015 pdev->pen_pattern = dash_patterns[style];
1016 pdev->defer &= ~DEFER_PEN;
1020 pdev->pen_lines = null_pen_lines;
1021 pdev->defer &= ~DEFER_PEN;
1028 return next->funcs->pSelectPen( next, hpen );
1031 /***********************************************************************
1032 * dibdrv_SetDCPenColor
1034 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1036 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
1037 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1039 if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1041 pdev->pen_colorref = color;
1042 pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
1043 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1046 return next->funcs->pSetDCPenColor( next, color );
1049 /**********************************************************************
1052 * Fill a number of rectangles with the solid brush
1053 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1055 static BOOL solid_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1058 const WINEREGION *clip = get_wine_region( region );
1062 dib->funcs->solid_rects( dib, num, rects, pdev->brush_and, pdev->brush_xor );
1066 for(i = 0; i < num; i++)
1068 for(j = 0; j < clip->numRects; j++)
1070 RECT rect = rects[i];
1072 /* Optimize unclipped case */
1073 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1074 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1076 dib->funcs->solid_rects( dib, 1, &rect, pdev->brush_and, pdev->brush_xor );
1080 if(clip->rects[j].top >= rect.bottom) break;
1081 if(clip->rects[j].bottom <= rect.top) continue;
1083 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1085 rect.left = max(rect.left, clip->rects[j].left);
1086 rect.top = max(rect.top, clip->rects[j].top);
1087 rect.right = min(rect.right, clip->rects[j].right);
1088 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1090 dib->funcs->solid_rects( dib, 1, &rect, pdev->brush_and, pdev->brush_xor );
1094 release_wine_region( region );
1098 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
1100 HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
1101 HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
1102 pdev->brush_and_bits = NULL;
1103 pdev->brush_xor_bits = NULL;
1106 void free_pattern_brush( dibdrv_physdev *pdev )
1108 free_pattern_brush_bits( pdev );
1109 free_dib_info( &pdev->brush_dib );
1112 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
1114 DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
1115 DWORD *brush_bits = pdev->brush_dib.bits.ptr;
1116 DWORD *and_bits, *xor_bits;
1118 assert(pdev->brush_and_bits == NULL);
1119 assert(pdev->brush_xor_bits == NULL);
1121 assert(pdev->brush_dib.stride > 0);
1123 and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1124 xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1126 if(!and_bits || !xor_bits)
1128 ERR("Failed to create pattern brush bits\n");
1129 free_pattern_brush_bits( pdev );
1135 calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1142 static const DWORD hatches[6][8] =
1144 { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1145 { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL */
1146 { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL */
1147 { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL */
1148 { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS */
1149 { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 } /* HS_DIAGCROSS */
1152 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev)
1155 rop_mask fg_mask, bg_mask;
1156 rop_mask_bits mask_bits;
1160 assert(pdev->brush_and_bits == NULL);
1161 assert(pdev->brush_xor_bits == NULL);
1163 /* Just initialise brush_dib with the color / sizing info. We don't
1164 need the bits as we'll calculate the rop masks straight from
1165 the hatch patterns. */
1167 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1168 pdev->brush_dib.width = 8;
1169 pdev->brush_dib.height = 8;
1170 pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1172 size = pdev->brush_dib.height * pdev->brush_dib.stride;
1174 mask_bits.and = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1175 mask_bits.xor = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1177 if(!mask_bits.and || !mask_bits.xor)
1179 ERR("Failed to create pattern brush bits\n");
1180 free_pattern_brush_bits( pdev );
1184 hatch.bit_count = 1;
1185 hatch.height = hatch.width = 8;
1187 hatch.bits.ptr = (void *) hatches[pdev->brush_hatch];
1188 hatch.bits.free = hatch.bits.param = NULL;
1189 hatch.bits.is_copy = FALSE;
1191 fg_mask.and = pdev->brush_and;
1192 fg_mask.xor = pdev->brush_xor;
1193 get_brush_bkgnd_masks( pdev, &bg_mask.and, &bg_mask.xor );
1195 ret = pdev->brush_dib.funcs->create_rop_masks( &pdev->brush_dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1196 if(!ret) free_pattern_brush_bits( pdev );
1201 /**********************************************************************
1204 * Fill a number of rectangles with the pattern brush
1205 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1207 static BOOL pattern_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1210 const WINEREGION *clip;
1213 if(pdev->brush_and_bits == NULL)
1215 switch(pdev->brush_style)
1218 if(!create_pattern_brush_bits(pdev))
1223 if(!create_hatch_brush_bits(pdev))
1228 ERR("Unexpected brush style %d\n", pdev->brush_style);
1233 GetBrushOrgEx(pdev->dev.hdc, &origin);
1235 clip = get_wine_region( region );
1239 dib->funcs->pattern_rects( dib, num, rects, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1243 for(i = 0; i < num; i++)
1245 for(j = 0; j < clip->numRects; j++)
1247 RECT rect = rects[i];
1249 /* Optimize unclipped case */
1250 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1251 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1253 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1257 if(clip->rects[j].top >= rect.bottom) break;
1258 if(clip->rects[j].bottom <= rect.top) continue;
1260 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1262 rect.left = max(rect.left, clip->rects[j].left);
1263 rect.top = max(rect.top, clip->rects[j].top);
1264 rect.right = min(rect.right, clip->rects[j].right);
1265 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1267 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1271 release_wine_region( region );
1275 static BOOL null_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1280 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1282 pdev->brush_rop = rop;
1283 if(pdev->brush_style == BS_SOLID || pdev->brush_style == BS_HATCHED)
1284 calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1285 free_pattern_brush_bits( pdev );
1288 /***********************************************************************
1289 * dibdrv_SelectBrush
1291 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
1293 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1294 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1297 TRACE("(%p, %p)\n", dev, hbrush);
1299 if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1301 if (hbrush == GetStockObject( DC_BRUSH ))
1302 logbrush.lbColor = GetDCBrushColor( dev->hdc );
1304 pdev->brush_style = logbrush.lbStyle;
1306 pdev->defer |= DEFER_BRUSH;
1308 free_pattern_brush( pdev );
1310 switch(logbrush.lbStyle)
1313 pdev->brush_colorref = logbrush.lbColor;
1314 pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1315 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1316 pdev->brush_rects = solid_brush;
1317 pdev->defer &= ~DEFER_BRUSH;
1321 pdev->brush_rects = null_brush;
1322 pdev->defer &= ~DEFER_BRUSH;
1327 BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1329 WORD usage = LOWORD(logbrush.lbColor);
1330 HPALETTE pal = (usage == DIB_PAL_COLORS) ? GetCurrentObject(dev->hdc, OBJ_PAL) : NULL;
1333 if(!bi) return NULL;
1334 if(init_dib_info_from_packed(&orig_dib, bi, usage, pal))
1336 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1338 pdev->brush_dib.height = orig_dib.height;
1339 pdev->brush_dib.width = orig_dib.width;
1340 pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1342 pdev->brush_dib.bits.param = NULL;
1343 pdev->brush_dib.bits.ptr = HeapAlloc( GetProcessHeap(), 0,
1344 pdev->brush_dib.height * pdev->brush_dib.stride );
1345 pdev->brush_dib.bits.is_copy = TRUE;
1346 pdev->brush_dib.bits.free = free_heap_bits;
1348 rect.left = rect.top = 0;
1349 rect.right = orig_dib.width;
1350 rect.bottom = orig_dib.height;
1352 if(pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &orig_dib, &rect))
1354 pdev->brush_rects = pattern_brush;
1355 pdev->defer &= ~DEFER_BRUSH;
1358 free_dib_info(&pdev->brush_dib);
1359 free_dib_info(&orig_dib);
1361 GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1367 if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1368 pdev->brush_hatch = logbrush.lbHatch;
1369 pdev->brush_colorref = logbrush.lbColor;
1370 pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1371 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1372 pdev->brush_rects = pattern_brush;
1373 pdev->defer &= ~DEFER_BRUSH;
1381 return next->funcs->pSelectBrush( next, hbrush );
1384 /***********************************************************************
1385 * dibdrv_SetDCBrushColor
1387 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1389 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1390 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1392 if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1394 pdev->brush_colorref = color;
1395 pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1396 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1399 return next->funcs->pSetDCBrushColor( next, color );
1402 BOOL brush_rects(dibdrv_physdev *pdev, int num, const RECT *rects)
1404 return pdev->brush_rects( pdev, &pdev->dib, num, rects, pdev->clip );