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;
298 /******************************************************************************
301 * Clips the start and end points to a rectangle.
303 * Note, this treats the end point like the start point. If the
304 * caller doesn't want it displayed, it should exclude it. If the end
305 * point is clipped out, then the likelihood is that the new end point
306 * should be displayed.
308 * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
310 * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
311 * however the Bresenham error term is defined differently so the equations
314 * For x major lines we have 2dy >= err + bias > 2dy - 2dx
315 * 0 >= err + bias - 2dy > -2dx
317 * Note dx, dy, m and n are all +ve.
319 * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
320 * err = 2dy - dx + 2mdy - 2ndx
321 * 0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
322 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
323 * which of course will give exactly one solution for n,
324 * so looking at the >= inequality
325 * n >= (2mdy + bias - dx) / 2dx
326 * n = ceiling((2mdy + bias - dx) / 2dx)
327 * = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
329 * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
330 * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
331 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
332 * 2mdy > 2ndx - bias - dx
333 * m > (2ndx - bias - dx) / 2dy
334 * m = floor((2ndx - bias - dx) / 2dy) + 1
335 * m = (2ndx - bias - dx) / 2dy + 1
337 * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
338 * err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
339 * = 2dy - dx - 2mdy + 2ndx
340 * 0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
341 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
342 * again exactly one solution.
343 * 2ndx <= 2mdy - bias + dx
344 * n = floor((2mdy - bias + dx) / 2dx)
345 * = (2mdy - bias + dx) / 2dx
347 * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
348 * mininizing m to include all of the points at y = y2 - n. As above:
349 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
350 * 2mdy >= 2ndx + bias - dx
351 * m = ceiling((2ndx + bias - dx) / 2dy)
352 * = (2ndx + bias - dx - 1) / 2dy + 1
354 * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
356 * Moving start point from y1 to y1 + n find x1 + m
357 * m = (2ndx + bias + dy - 1) / 2dy
359 * Moving start point from x1 to x1 + m find y1 + n
360 * n = (2mdy - bias - dy) / 2ndx + 1
362 * Moving end point from y2 to y2 - n find x1 - m
363 * m = (2ndx - bias + dy) / 2dy
365 * Moving end point from x2 to x2 - m find y2 - n
366 * n = (2mdy + bias - dy - 1) / 2dx + 1
368 static int clip_line(const POINT *start, const POINT *end, const RECT *clip,
369 const bres_params *params, POINT *pt1, POINT *pt2)
372 BOOL clipped = FALSE;
373 DWORD start_oc, end_oc;
374 const int bias = params->bias;
375 const unsigned int dx = params->dx;
376 const unsigned int dy = params->dy;
377 const unsigned int two_dx = params->dx * 2;
378 const unsigned int two_dy = params->dy * 2;
379 const BOOL xmajor = is_xmajor(params->octant);
380 const BOOL neg_slope = !is_pos_slope(params->octant);
385 start_oc = calc_outcode(start, clip);
386 end_oc = calc_outcode(end, clip);
390 if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
391 if(start_oc & end_oc) return 0; /* trivial reject */
394 if(start_oc & OUT_LEFT)
396 m = clip->left - start->x;
398 n = (m * two_dy + bias + dx - 1) / two_dx;
400 n = (m * two_dy - bias - dy) / two_dx + 1;
403 if(neg_slope) n = -n;
404 pt1->y = start->y + n;
405 start_oc = calc_outcode(pt1, clip);
407 else if(start_oc & OUT_RIGHT)
409 m = start->x - clip->right + 1;
411 n = (m * two_dy + bias + dx - 1) / two_dx;
413 n = (m * two_dy - bias - dy) / two_dx + 1;
415 pt1->x = clip->right - 1;
416 if(neg_slope) n = -n;
417 pt1->y = start->y - n;
418 start_oc = calc_outcode(pt1, clip);
420 else if(start_oc & OUT_TOP)
422 n = clip->top - start->y;
424 m = (n * two_dx - bias - dx) / two_dy + 1;
426 m = (n * two_dx + bias + dy - 1) / two_dy;
429 if(neg_slope) m = -m;
430 pt1->x = start->x + m;
431 start_oc = calc_outcode(pt1, clip);
433 else if(start_oc & OUT_BOTTOM)
435 n = start->y - clip->bottom + 1;
437 m = (n * two_dx - bias - dx) / two_dy + 1;
439 m = (n * two_dx + bias + dy - 1) / two_dy;
441 pt1->y = clip->bottom - 1;
442 if(neg_slope) m = -m;
443 pt1->x = start->x - m;
444 start_oc = calc_outcode(pt1, clip);
446 else if(end_oc & OUT_LEFT)
448 m = clip->left - end->x;
450 n = (m * two_dy - bias + dx) / two_dx;
452 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
455 if(neg_slope) n = -n;
457 end_oc = calc_outcode(pt2, clip);
459 else if(end_oc & OUT_RIGHT)
461 m = end->x - clip->right + 1;
463 n = (m * two_dy - bias + dx) / two_dx;
465 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
467 pt2->x = clip->right - 1;
468 if(neg_slope) n = -n;
470 end_oc = calc_outcode(pt2, clip);
472 else if(end_oc & OUT_TOP)
474 n = clip->top - end->y;
476 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
478 m = (n * two_dx - bias + dy) / two_dy;
481 if(neg_slope) m = -m;
483 end_oc = calc_outcode(pt2, clip);
485 else if(end_oc & OUT_BOTTOM)
487 n = end->y - clip->bottom + 1;
489 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
491 m = (n * two_dx - bias + dy) / two_dy;
493 pt2->y = clip->bottom - 1;
494 if(neg_slope) m = -m;
496 end_oc = calc_outcode(pt2, clip);
501 static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, const bres_params *params, INT err,
502 BOOL last_pt, void (* callback)(dibdrv_physdev*,INT,INT), dibdrv_physdev *pdev)
504 const int xadd = is_x_increasing(params->octant) ? 1 : -1;
505 const int yadd = is_y_increasing(params->octant) ? 1 : -1;
508 if (is_xmajor(params->octant)) /* line is "more horizontal" */
510 erradd = 2*params->dy - 2*params->dx;
513 callback(pdev, x1, y1);
514 if (err + params->bias > 0)
519 else err += 2*params->dy;
522 if(last_pt) callback(pdev, x1, y1);
524 else /* line is "more vertical" */
526 erradd = 2*params->dx - 2*params->dy;
529 callback(pdev, x1, y1);
530 if (err + params->bias > 0)
535 else err += 2*params->dx;
538 if(last_pt) callback(pdev, x1, y1);
542 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
544 const WINEREGION *clip = get_wine_region(pdev->clip);
546 if(start->y == end->y)
551 rect.left = start->x;
554 rect.bottom = end->y + 1;
555 order_end_points(&rect.left, &rect.right);
556 for(i = 0; i < clip->numRects; i++)
558 if(clip->rects[i].top >= rect.bottom) break;
559 if(clip->rects[i].bottom <= rect.top) continue;
560 /* Optimize the unclipped case */
561 if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
563 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
566 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
569 tmp.left = max(rect.left, clip->rects[i].left);
570 tmp.right = min(rect.right, clip->rects[i].right);
571 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
575 else if(start->x == end->x)
580 rect.left = start->x;
582 rect.right = end->x + 1;
583 rect.bottom = end->y;
584 order_end_points(&rect.top, &rect.bottom);
585 for(i = 0; i < clip->numRects; i++)
587 /* Optimize unclipped case */
588 if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
589 clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
591 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
594 if(clip->rects[i].top >= rect.bottom) break;
595 if(clip->rects[i].bottom <= rect.top) continue;
596 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
599 tmp.top = max(rect.top, clip->rects[i].top);
600 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
601 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
608 INT dx = end->x - start->x;
609 INT dy = end->y - start->y;
614 params.octant = get_octant_mask(dx, dy);
615 /* Octants 3, 5, 6 and 8 take a bias */
616 params.bias = (params.octant & 0xb4) ? 1 : 0;
618 for(i = 0; i < clip->numRects; i++)
620 POINT clipped_start, clipped_end;
622 clip_status = clip_line(start, end, clip->rects + i, ¶ms, &clipped_start, &clipped_end);
626 int m = abs(clipped_start.x - start->x);
627 int n = abs(clipped_start.y - start->y);
629 BOOL last_pt = FALSE;
631 if(is_xmajor(params.octant))
632 err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
634 err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
636 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
638 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, ¶ms,
639 err, last_pt, solid_pen_line_callback, pdev);
641 if(clip_status == 2) break; /* completely unclipped, so we can finish */
646 release_wine_region(pdev->clip);
650 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
655 for (i = 0; i < num - 1; i++)
656 if (!solid_pen_line( pdev, pts + i, pts + i + 1 ))
662 void reset_dash_origin(dibdrv_physdev *pdev)
664 pdev->dash_pos.cur_dash = 0;
665 pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
666 pdev->dash_pos.mark = TRUE;
669 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
671 skip %= pdev->pen_pattern.total_len;
674 if(pdev->dash_pos.left_in_dash > skip)
676 pdev->dash_pos.left_in_dash -= skip;
679 skip -= pdev->dash_pos.left_in_dash;
680 pdev->dash_pos.cur_dash++;
681 if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
682 pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
683 pdev->dash_pos.mark = !pdev->dash_pos.mark;
687 static inline void get_dash_colors(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
689 if(pdev->dash_pos.mark)
691 *and = pdev->pen_and;
692 *xor = pdev->pen_xor;
696 get_pen_bkgnd_masks( pdev, and, xor );
700 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
705 get_dash_colors(pdev, &and, &xor);
711 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
715 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
717 const WINEREGION *clip = get_wine_region(pdev->clip);
721 const dash_pos start_pos = pdev->dash_pos;
723 if(start->y == end->y) /* hline */
726 INT left, right, cur_x;
729 rect.bottom = start->y + 1;
731 if(start->x <= end->x)
744 for(i = 0; i < clip->numRects; i++)
746 if(clip->rects[i].top > start->y) break;
747 if(clip->rects[i].bottom <= start->y) continue;
749 if(clip->rects[i].right > left && clip->rects[i].left <= right)
751 int clipped_left = max(clip->rects[i].left, left);
752 int clipped_right = min(clip->rects[i].right - 1, right);
754 pdev->dash_pos = start_pos;
758 cur_x = clipped_left;
760 skip_dash(pdev, clipped_left - left);
762 while(cur_x <= clipped_right)
764 get_dash_colors(pdev, &and, &xor);
765 dash_len = pdev->dash_pos.left_in_dash;
766 if(cur_x + dash_len > clipped_right + 1)
767 dash_len = clipped_right - cur_x + 1;
769 rect.right = cur_x + dash_len;
771 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
773 skip_dash(pdev, dash_len);
778 cur_x = clipped_right;
780 skip_dash(pdev, right - clipped_right);
782 while(cur_x >= clipped_left)
784 get_dash_colors(pdev, &and, &xor);
785 dash_len = pdev->dash_pos.left_in_dash;
786 if(cur_x - dash_len < clipped_left - 1)
787 dash_len = cur_x - clipped_left + 1;
788 rect.left = cur_x - dash_len + 1;
789 rect.right = cur_x + 1;
791 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
793 skip_dash(pdev, dash_len);
798 pdev->dash_pos = start_pos;
799 skip_dash(pdev, right - left + 1);
801 else if(start->x == end->x) /* vline */
804 INT top, bottom, cur_y;
806 rect.left = start->x;
807 rect.right = start->x + 1;
809 if(start->y <= end->y)
822 for(i = 0; i < clip->numRects; i++)
824 if(clip->rects[i].top > bottom) break;
825 if(clip->rects[i].bottom <= top) continue;
826 if(clip->rects[i].right > start->x && clip->rects[i].left <= start->x)
828 int clipped_top = max(clip->rects[i].top, top);
829 int clipped_bottom = min(clip->rects[i].bottom - 1, bottom);
831 pdev->dash_pos = start_pos;
837 skip_dash(pdev, clipped_top - top);
839 while(cur_y <= clipped_bottom)
841 get_dash_colors(pdev, &and, &xor);
842 dash_len = pdev->dash_pos.left_in_dash;
843 if(cur_y + dash_len > clipped_bottom + 1)
844 dash_len = clipped_bottom - cur_y + 1;
846 rect.bottom = cur_y + dash_len;
848 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
850 skip_dash(pdev, dash_len);
855 cur_y = clipped_bottom;
857 skip_dash(pdev, bottom - clipped_bottom);
859 while(cur_y >= clipped_top)
861 get_dash_colors(pdev, &and, &xor);
862 dash_len = pdev->dash_pos.left_in_dash;
863 if(cur_y - dash_len < clipped_top - 1)
864 dash_len = cur_y - clipped_top + 1;
865 rect.top = cur_y - dash_len + 1;
866 rect.bottom = cur_y + 1;
868 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
870 skip_dash(pdev, dash_len);
875 pdev->dash_pos = start_pos;
876 skip_dash(pdev, bottom - top + 1);
881 INT dx = end->x - start->x;
882 INT dy = end->y - start->y;
887 params.octant = get_octant_mask(dx, dy);
888 /* Octants 3, 5, 6 and 8 take a bias */
889 params.bias = (params.octant & 0xb4) ? 1 : 0;
891 for(i = 0; i < clip->numRects; i++)
893 POINT clipped_start, clipped_end;
895 clip_status = clip_line(start, end, clip->rects + i, ¶ms, &clipped_start, &clipped_end);
899 int m = abs(clipped_start.x - start->x);
900 int n = abs(clipped_start.y - start->y);
902 BOOL last_pt = FALSE;
904 pdev->dash_pos = start_pos;
906 if(is_xmajor(params.octant))
908 err = 2 * params.dy - params.dx + m * 2 * params.dy - n * 2 * params.dx;
913 err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
916 if(clip_status == 1 && (end->x != clipped_end.x || end->y != clipped_end.y)) last_pt = TRUE;
918 bres_line_with_bias(clipped_start.x, clipped_start.y, clipped_end.x, clipped_end.y, ¶ms,
919 err, last_pt, dashed_pen_line_callback, pdev);
921 if(clip_status == 2) break; /* completely unclipped, so we can finish */
924 pdev->dash_pos = start_pos;
925 if(is_xmajor(params.octant))
926 skip_dash(pdev, params.dx);
928 skip_dash(pdev, params.dy);
931 release_wine_region(pdev->clip);
935 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
940 for (i = 0; i < num - 1; i++)
941 if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
947 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
952 static const dash_pattern dash_patterns[5] =
954 {0, {0}, 0}, /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
955 {2, {18, 6}, 24}, /* PS_DASH */
956 {2, {3, 3}, 6}, /* PS_DOT */
957 {4, {9, 6, 3, 6}, 24}, /* PS_DASHDOT */
958 {6, {9, 3, 3, 3, 3, 3}, 24} /* PS_DASHDOTDOT */
961 /***********************************************************************
964 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
966 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
967 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
971 TRACE("(%p, %p)\n", dev, hpen);
973 if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
975 /* must be an extended pen */
977 INT size = GetObjectW( hpen, 0, NULL );
981 elp = HeapAlloc( GetProcessHeap(), 0, size );
983 GetObjectW( hpen, size, elp );
984 /* FIXME: add support for user style pens */
985 logpen.lopnStyle = elp->elpPenStyle;
986 logpen.lopnWidth.x = elp->elpWidth;
987 logpen.lopnWidth.y = 0;
988 logpen.lopnColor = elp->elpColor;
990 HeapFree( GetProcessHeap(), 0, elp );
993 if (hpen == GetStockObject( DC_PEN ))
994 logpen.lopnColor = GetDCPenColor( dev->hdc );
996 pdev->pen_colorref = logpen.lopnColor;
997 pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
998 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1000 pdev->pen_pattern = dash_patterns[PS_SOLID];
1002 pdev->defer |= DEFER_PEN;
1004 style = logpen.lopnStyle & PS_STYLE_MASK;
1009 if(logpen.lopnStyle & PS_GEOMETRIC) break;
1010 if(logpen.lopnWidth.x > 1) break;
1011 pdev->pen_lines = solid_pen_lines;
1012 pdev->defer &= ~DEFER_PEN;
1019 if(logpen.lopnStyle & PS_GEOMETRIC) break;
1020 if(logpen.lopnWidth.x > 1) break;
1021 pdev->pen_lines = dashed_pen_lines;
1022 pdev->pen_pattern = dash_patterns[style];
1023 pdev->defer &= ~DEFER_PEN;
1027 pdev->pen_lines = null_pen_lines;
1028 pdev->defer &= ~DEFER_PEN;
1035 return next->funcs->pSelectPen( next, hpen );
1038 /***********************************************************************
1039 * dibdrv_SetDCPenColor
1041 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1043 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
1044 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1046 if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1048 pdev->pen_colorref = color;
1049 pdev->pen_color = get_fg_color( pdev, pdev->pen_colorref );
1050 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1053 return next->funcs->pSetDCPenColor( next, color );
1056 /**********************************************************************
1059 * Fill a number of rectangles with the solid brush
1060 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1062 static BOOL solid_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1065 const WINEREGION *clip = get_wine_region(pdev->clip);
1067 for(i = 0; i < num; i++)
1069 for(j = 0; j < clip->numRects; j++)
1071 RECT rect = rects[i];
1073 /* Optimize unclipped case */
1074 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1075 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1077 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
1081 if(clip->rects[j].top >= rect.bottom) break;
1082 if(clip->rects[j].bottom <= rect.top) continue;
1084 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1086 rect.left = max(rect.left, clip->rects[j].left);
1087 rect.top = max(rect.top, clip->rects[j].top);
1088 rect.right = min(rect.right, clip->rects[j].right);
1089 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1091 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
1095 release_wine_region(pdev->clip);
1099 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
1101 HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
1102 HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
1103 pdev->brush_and_bits = NULL;
1104 pdev->brush_xor_bits = NULL;
1107 void free_pattern_brush( dibdrv_physdev *pdev )
1109 free_pattern_brush_bits( pdev );
1110 free_dib_info( &pdev->brush_dib );
1113 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
1115 DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
1116 DWORD *brush_bits = pdev->brush_dib.bits.ptr;
1117 DWORD *and_bits, *xor_bits;
1119 assert(pdev->brush_and_bits == NULL);
1120 assert(pdev->brush_xor_bits == NULL);
1122 assert(pdev->brush_dib.stride > 0);
1124 and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1125 xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1127 if(!and_bits || !xor_bits)
1129 ERR("Failed to create pattern brush bits\n");
1130 free_pattern_brush_bits( pdev );
1136 calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1143 static const DWORD hatches[6][8] =
1145 { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1146 { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL */
1147 { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL */
1148 { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL */
1149 { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS */
1150 { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 } /* HS_DIAGCROSS */
1153 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev)
1156 rop_mask fg_mask, bg_mask;
1157 rop_mask_bits mask_bits;
1161 assert(pdev->brush_and_bits == NULL);
1162 assert(pdev->brush_xor_bits == NULL);
1164 /* Just initialise brush_dib with the color / sizing info. We don't
1165 need the bits as we'll calculate the rop masks straight from
1166 the hatch patterns. */
1168 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1169 pdev->brush_dib.width = 8;
1170 pdev->brush_dib.height = 8;
1171 pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1173 size = pdev->brush_dib.height * pdev->brush_dib.stride;
1175 mask_bits.and = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1176 mask_bits.xor = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1178 if(!mask_bits.and || !mask_bits.xor)
1180 ERR("Failed to create pattern brush bits\n");
1181 free_pattern_brush_bits( pdev );
1185 hatch.bit_count = 1;
1186 hatch.height = hatch.width = 8;
1188 hatch.bits.ptr = (void *) hatches[pdev->brush_hatch];
1189 hatch.bits.free = hatch.bits.param = NULL;
1190 hatch.bits.is_copy = FALSE;
1192 fg_mask.and = pdev->brush_and;
1193 fg_mask.xor = pdev->brush_xor;
1194 get_brush_bkgnd_masks( pdev, &bg_mask.and, &bg_mask.xor );
1196 ret = pdev->brush_dib.funcs->create_rop_masks( &pdev->brush_dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1197 if(!ret) free_pattern_brush_bits( pdev );
1202 /**********************************************************************
1205 * Fill a number of rectangles with the pattern brush
1206 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1208 static BOOL pattern_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1211 const WINEREGION *clip;
1214 if(pdev->brush_and_bits == NULL)
1216 switch(pdev->brush_style)
1219 if(!create_pattern_brush_bits(pdev))
1224 if(!create_hatch_brush_bits(pdev))
1229 ERR("Unexpected brush style %d\n", pdev->brush_style);
1234 GetBrushOrgEx(pdev->dev.hdc, &origin);
1236 clip = get_wine_region(pdev->clip);
1237 for(i = 0; i < num; i++)
1239 for(j = 0; j < clip->numRects; j++)
1241 RECT rect = rects[i];
1243 /* Optimize unclipped case */
1244 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1245 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1247 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1251 if(clip->rects[j].top >= rect.bottom) break;
1252 if(clip->rects[j].bottom <= rect.top) continue;
1254 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1256 rect.left = max(rect.left, clip->rects[j].left);
1257 rect.top = max(rect.top, clip->rects[j].top);
1258 rect.right = min(rect.right, clip->rects[j].right);
1259 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1261 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1265 release_wine_region(pdev->clip);
1269 static BOOL null_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1274 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1276 pdev->brush_rop = rop;
1277 if(pdev->brush_style == BS_SOLID || pdev->brush_style == BS_HATCHED)
1278 calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1279 free_pattern_brush_bits( pdev );
1282 /***********************************************************************
1283 * dibdrv_SelectBrush
1285 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
1287 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1288 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1291 TRACE("(%p, %p)\n", dev, hbrush);
1293 if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1295 if (hbrush == GetStockObject( DC_BRUSH ))
1296 logbrush.lbColor = GetDCBrushColor( dev->hdc );
1298 pdev->brush_style = logbrush.lbStyle;
1300 pdev->defer |= DEFER_BRUSH;
1302 free_pattern_brush( pdev );
1304 switch(logbrush.lbStyle)
1307 pdev->brush_colorref = logbrush.lbColor;
1308 pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1309 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1310 pdev->brush_rects = solid_brush;
1311 pdev->defer &= ~DEFER_BRUSH;
1315 pdev->brush_rects = null_brush;
1316 pdev->defer &= ~DEFER_BRUSH;
1321 BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1323 WORD usage = LOWORD(logbrush.lbColor);
1324 HPALETTE pal = (usage == DIB_PAL_COLORS) ? GetCurrentObject(dev->hdc, OBJ_PAL) : NULL;
1327 if(!bi) return NULL;
1328 if(init_dib_info_from_packed(&orig_dib, bi, usage, pal))
1330 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1332 pdev->brush_dib.height = orig_dib.height;
1333 pdev->brush_dib.width = orig_dib.width;
1334 pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1336 pdev->brush_dib.bits.param = NULL;
1337 pdev->brush_dib.bits.ptr = HeapAlloc( GetProcessHeap(), 0,
1338 pdev->brush_dib.height * pdev->brush_dib.stride );
1339 pdev->brush_dib.bits.is_copy = TRUE;
1340 pdev->brush_dib.bits.free = free_heap_bits;
1342 rect.left = rect.top = 0;
1343 rect.right = orig_dib.width;
1344 rect.bottom = orig_dib.height;
1346 if(pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &orig_dib, &rect))
1348 pdev->brush_rects = pattern_brush;
1349 pdev->defer &= ~DEFER_BRUSH;
1352 free_dib_info(&pdev->brush_dib);
1353 free_dib_info(&orig_dib);
1355 GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1361 if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1362 pdev->brush_hatch = logbrush.lbHatch;
1363 pdev->brush_colorref = logbrush.lbColor;
1364 pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1365 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1366 pdev->brush_rects = pattern_brush;
1367 pdev->defer &= ~DEFER_BRUSH;
1375 return next->funcs->pSelectBrush( next, hbrush );
1378 /***********************************************************************
1379 * dibdrv_SetDCBrushColor
1381 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1383 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1384 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1386 if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1388 pdev->brush_colorref = color;
1389 pdev->brush_color = get_fg_color( pdev, pdev->brush_colorref );
1390 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1393 return next->funcs->pSetDCBrushColor( next, color );