gdi32: Add support for the null pen.
[wine.git] / dlls / gdi32 / dibdrv / objects.c
blob30be49666e564d55a535c763036104a2494b48b4
1 /*
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
21 #include <assert.h>
22 #include <stdlib.h>
24 #include "gdi_private.h"
25 #include "dibdrv.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dib);
33 * Decompose the 16 ROP2s into an expression of the form
35 * D = (D & A) ^ X
37 * Where A and X depend only on P (and so can be precomputed).
39 * A X
41 * R2_BLACK 0 0 0
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
46 * R2_NOT ~D 1 1
47 * R2_XORPEN P ^ D 1 P
48 * R2_NOTMASKPEN ~(P & D) P 1
49 * R2_MASKPEN P & D P 0
50 * R2_NOTXORPEN ~(P ^ D) 1 ~P
51 * R2_NOP D 1 0
52 * R2_MERGENOTPEN ~P | D P ~P
53 * R2_COPYPEN P 0 P
54 * R2_MERGEPENNOT P | ~D ~P 1
55 * R2_MERGEPEN P | D ~P P
56 * R2_WHITE 1 0 1
60 /* A = (P & A1) | (~P & A2) */
61 #define ZERO {0, 0}
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,
69 P, ONE, ONE, P,
70 P, ONE, ONE, P,
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,
78 P, ONE, P, ONE,
79 ZERO, NOT_P, ZERO, NOT_P,
80 P, ONE, P, ONE
83 #undef NOT_P
84 #undef P
85 #undef ONE
86 #undef ZERO
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)
97 if(*s > *e)
99 int tmp;
100 tmp = *s + 1;
101 *s = *e + 1;
102 *e = tmp;
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 /**********************************************************************
138 * get_octant_number
140 * Return the octant number starting clockwise from the +ve x-axis.
142 static inline int get_octant_number(int dx, int dy)
144 if(dy > 0)
145 if(dx > 0)
146 return ( dx > dy) ? 1 : 2;
147 else
148 return (-dx > dy) ? 4 : 3;
149 else
150 if(dx < 0)
151 return (-dx > -dy) ? 5 : 6;
152 else
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)
163 RECT rect;
165 rect.left = x;
166 rect.right = x + 1;
167 rect.top = y;
168 rect.bottom = y + 1;
169 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
170 return;
173 #define OUT_LEFT 1
174 #define OUT_RIGHT 2
175 #define OUT_TOP 4
176 #define OUT_BOTTOM 8
178 static inline DWORD calc_outcode(const POINT *pt, const RECT *clip)
180 DWORD out = 0;
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;
186 return out;
189 typedef struct
191 unsigned int dx, dy;
192 int bias;
193 DWORD octant;
194 } bres_params;
196 /******************************************************************************
197 * clip_line
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
210 * will also differ.
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)
269 unsigned int n, m;
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);
280 *pt1 = *start;
281 *pt2 = *end;
283 start_oc = calc_outcode(start, clip);
284 end_oc = calc_outcode(end, clip);
286 while(1)
288 if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
289 if(start_oc & end_oc) return 0; /* trivial reject */
291 clipped = TRUE;
292 if(start_oc & OUT_LEFT)
294 m = clip->left - start->x;
295 if(xmajor)
296 n = (m * two_dy + bias + dx - 1) / two_dx;
297 else
298 n = (m * two_dy - bias - dy) / two_dx + 1;
300 pt1->x = clip->left;
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;
308 if(xmajor)
309 n = (m * two_dy + bias + dx - 1) / two_dx;
310 else
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;
321 if(xmajor)
322 m = (n * two_dx - bias - dx) / two_dy + 1;
323 else
324 m = (n * two_dx + bias + dy - 1) / two_dy;
326 pt1->y = clip->top;
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;
334 if(xmajor)
335 m = (n * two_dx - bias - dx) / two_dy + 1;
336 else
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;
347 if(xmajor)
348 n = (m * two_dy - bias + dx) / two_dx;
349 else
350 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
352 pt2->x = clip->left;
353 if(neg_slope) n = -n;
354 pt2->y = end->y + n;
355 end_oc = calc_outcode(pt2, clip);
357 else if(end_oc & OUT_RIGHT)
359 m = end->x - clip->right + 1;
360 if(xmajor)
361 n = (m * two_dy - bias + dx) / two_dx;
362 else
363 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
365 pt2->x = clip->right - 1;
366 if(neg_slope) n = -n;
367 pt2->y = end->y - n;
368 end_oc = calc_outcode(pt2, clip);
370 else if(end_oc & OUT_TOP)
372 n = clip->top - end->y;
373 if(xmajor)
374 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
375 else
376 m = (n * two_dx - bias + dy) / two_dy;
378 pt2->y = clip->top;
379 if(neg_slope) m = -m;
380 pt2->x = end->x + m;
381 end_oc = calc_outcode(pt2, clip);
383 else if(end_oc & OUT_BOTTOM)
385 n = end->y - clip->bottom + 1;
386 if(xmajor)
387 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
388 else
389 m = (n * two_dx - bias + dy) / two_dy;
391 pt2->y = clip->bottom - 1;
392 if(neg_slope) m = -m;
393 pt2->x = end->x - 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;
404 INT erradd;
406 if (is_xmajor(params->octant)) /* line is "more horizontal" */
408 erradd = 2*params->dy - 2*params->dx;
409 while(x1 != x2)
411 callback(pdev, x1, y1);
412 if (err + params->bias > 0)
414 y1 += yadd;
415 err += erradd;
417 else err += 2*params->dy;
418 x1 += xadd;
420 if(last_pt) callback(pdev, x1, y1);
422 else /* line is "more vertical" */
424 erradd = 2*params->dx - 2*params->dy;
425 while(y1 != y2)
427 callback(pdev, x1, y1);
428 if (err + params->bias > 0)
430 x1 += xadd;
431 err += erradd;
433 else err += 2*params->dx;
434 y1 += yadd;
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)
446 RECT rect;
447 int i;
449 rect.left = start->x;
450 rect.top = start->y;
451 rect.right = end->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);
462 break;
464 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
466 RECT tmp = rect;
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)
475 RECT rect;
476 int i;
478 rect.left = start->x;
479 rect.top = start->y;
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);
490 break;
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)
496 RECT tmp = rect;
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);
503 else
505 bres_params params;
506 INT dx = end->x - start->x;
507 INT dy = end->y - start->y;
508 INT i;
510 params.dx = abs(dx);
511 params.dy = abs(dy);
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;
519 int clip_status;
520 clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
522 if(clip_status)
524 int m = abs(clipped_start.x - start->x);
525 int n = abs(clipped_start.y - start->y);
526 int err;
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;
531 else
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, &params,
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);
545 return TRUE;
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;
558 while(skip)
560 if(pdev->dash_pos.left_in_dash > skip)
562 pdev->dash_pos.left_in_dash -= skip;
563 return;
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;
580 else /* space */
582 *and = pdev->bkgnd_and;
583 *xor = pdev->bkgnd_xor;
587 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
589 RECT rect;
590 DWORD and, xor;
592 get_dash_colors(pdev, &and, &xor);
593 skip_dash(pdev, 1);
594 rect.left = x;
595 rect.right = x + 1;
596 rect.top = y;
597 rect.bottom = y + 1;
598 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
599 return;
602 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
604 const WINEREGION *clip = get_wine_region(pdev->clip);
605 DWORD and, xor;
606 int i, dash_len;
607 RECT rect;
608 const dash_pos start_pos = pdev->dash_pos;
610 if(start->y == end->y) /* hline */
612 BOOL l_to_r;
613 INT left, right, cur_x;
615 rect.top = start->y;
616 rect.bottom = start->y + 1;
618 if(start->x <= end->x)
620 left = start->x;
621 right = end->x - 1;
622 l_to_r = TRUE;
624 else
626 left = end->x + 1;
627 right = start->x;
628 l_to_r = FALSE;
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;
643 if(l_to_r)
645 cur_x = clipped_left;
646 if(cur_x != 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;
655 rect.left = cur_x;
656 rect.right = cur_x + dash_len;
658 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
659 cur_x += dash_len;
660 skip_dash(pdev, dash_len);
663 else
665 cur_x = clipped_right;
666 if(cur_x != 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);
679 cur_x -= dash_len;
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 */
690 BOOL t_to_b;
691 INT top, bottom, cur_y;
693 rect.left = start->x;
694 rect.right = start->x + 1;
696 if(start->y <= end->y)
698 top = start->y;
699 bottom = end->y - 1;
700 t_to_b = TRUE;
702 else
704 top = end->y + 1;
705 bottom = start->y;
706 t_to_b = FALSE;
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;
720 if(t_to_b)
722 cur_y = clipped_top;
723 if(cur_y != top)
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;
732 rect.top = cur_y;
733 rect.bottom = cur_y + dash_len;
735 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
736 cur_y += dash_len;
737 skip_dash(pdev, dash_len);
740 else
742 cur_y = clipped_bottom;
743 if(cur_y != 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);
756 cur_y -= dash_len;
757 skip_dash(pdev, dash_len);
762 pdev->dash_pos = start_pos;
763 skip_dash(pdev, bottom - top + 1);
765 else
767 bres_params params;
768 INT dx = end->x - start->x;
769 INT dy = end->y - start->y;
770 INT i;
772 params.dx = abs(dx);
773 params.dy = abs(dy);
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;
781 int clip_status;
782 clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
784 if(clip_status)
786 int m = abs(clipped_start.x - start->x);
787 int n = abs(clipped_start.y - start->y);
788 int err;
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;
796 skip_dash(pdev, m);
798 else
800 err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
801 skip_dash(pdev, n);
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, &params,
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);
814 else
815 skip_dash(pdev, params.dy);
818 release_wine_region(pdev->clip);
819 return TRUE;
822 static BOOL null_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
824 return TRUE;
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 /***********************************************************************
837 * dibdrv_SelectPen
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);
843 LOGPEN logpen;
844 DWORD style;
846 TRACE("(%p, %p)\n", dev, hpen);
848 if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
850 /* must be an extended pen */
851 EXTLOGPEN *elp;
852 INT size = GetObjectW( hpen, 0, NULL );
854 if (!size) return 0;
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;
880 switch(style)
882 case PS_SOLID:
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;
887 break;
889 case PS_DASH:
890 case PS_DOT:
891 case PS_DASHDOT:
892 case PS_DASHDOTDOT:
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;
898 break;
900 case PS_NULL:
901 pdev->pen_line = null_pen_line;
902 pdev->defer &= ~DEFER_PEN;
903 break;
905 default:
906 break;
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 /**********************************************************************
930 * solid_brush
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)
937 int i, j;
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);
951 break;
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);
969 return TRUE;
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 and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
996 xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
998 if(!and_bits || !xor_bits)
1000 ERR("Failed to create pattern brush bits\n");
1001 free_pattern_brush_bits( pdev );
1002 return FALSE;
1005 if(pdev->brush_dib.stride < 0)
1006 brush_bits = (DWORD*)((BYTE*)brush_bits + (pdev->brush_dib.height - 1) * pdev->brush_dib.stride);
1008 while(size)
1010 calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1011 size -= 4;
1014 if(pdev->brush_dib.stride < 0)
1016 /* Update the bits ptrs if the dib is bottom up. The subtraction is because stride is -ve */
1017 pdev->brush_and_bits = (BYTE*)pdev->brush_and_bits - (pdev->brush_dib.height - 1) * pdev->brush_dib.stride;
1018 pdev->brush_xor_bits = (BYTE*)pdev->brush_xor_bits - (pdev->brush_dib.height - 1) * pdev->brush_dib.stride;
1021 return TRUE;
1024 /**********************************************************************
1025 * pattern_brush
1027 * Fill a number of rectangles with the pattern brush
1028 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1030 static BOOL pattern_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1032 int i, j;
1033 const WINEREGION *clip;
1034 POINT origin;
1036 if(pdev->brush_and_bits == NULL)
1037 if(!create_pattern_brush_bits(pdev))
1038 return FALSE;
1040 GetBrushOrgEx(pdev->dev.hdc, &origin);
1042 clip = get_wine_region(pdev->clip);
1043 for(i = 0; i < num; i++)
1045 for(j = 0; j < clip->numRects; j++)
1047 RECT rect = rects[i];
1049 /* Optimize unclipped case */
1050 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1051 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1053 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1054 break;
1057 if(clip->rects[j].top >= rect.bottom) break;
1058 if(clip->rects[j].bottom <= rect.top) continue;
1060 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1062 rect.left = max(rect.left, clip->rects[j].left);
1063 rect.top = max(rect.top, clip->rects[j].top);
1064 rect.right = min(rect.right, clip->rects[j].right);
1065 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1067 pdev->dib.funcs->pattern_rects(&pdev->dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits);
1071 release_wine_region(pdev->clip);
1072 return TRUE;
1075 static BOOL null_brush(dibdrv_physdev *pdev, int num, RECT *rects)
1077 return TRUE;
1080 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1082 pdev->brush_rop = rop;
1083 if(pdev->brush_style == BS_SOLID)
1084 calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1085 free_pattern_brush_bits( pdev );
1088 /***********************************************************************
1089 * dibdrv_SelectBrush
1091 HBRUSH CDECL dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
1093 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1094 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1095 LOGBRUSH logbrush;
1097 TRACE("(%p, %p)\n", dev, hbrush);
1099 if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1101 if (hbrush == GetStockObject( DC_BRUSH ))
1102 logbrush.lbColor = GetDCBrushColor( dev->hdc );
1104 pdev->brush_style = logbrush.lbStyle;
1106 pdev->defer |= DEFER_BRUSH;
1108 free_pattern_brush( pdev );
1110 switch(logbrush.lbStyle)
1112 case BS_SOLID:
1113 pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, logbrush.lbColor);
1114 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1115 pdev->brush_rects = solid_brush;
1116 pdev->defer &= ~DEFER_BRUSH;
1117 break;
1119 case BS_NULL:
1120 pdev->brush_rects = null_brush;
1121 pdev->defer &= ~DEFER_BRUSH;
1122 break;
1124 case BS_DIBPATTERN:
1126 BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1127 dib_info orig_dib;
1129 if(!bi) return NULL;
1130 if(init_dib_info_from_packed(&orig_dib, bi, LOWORD(logbrush.lbColor)))
1132 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1133 if(convert_dib(&pdev->brush_dib, &orig_dib))
1135 pdev->brush_rects = pattern_brush;
1136 pdev->defer &= ~DEFER_BRUSH;
1138 free_dib_info(&orig_dib, FALSE);
1140 GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1141 break;
1144 default:
1145 break;
1148 return next->funcs->pSelectBrush( next, hbrush );
1151 /***********************************************************************
1152 * dibdrv_SetDCBrushColor
1154 COLORREF CDECL dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1156 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1157 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1159 if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1161 pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, color);
1162 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1165 return next->funcs->pSetDCBrushColor( next, color );