gdi32: Introduce a unified function to perform COLORREF to pixel color mapping.
[wine.git] / dlls / gdi32 / dibdrv / objects.c
blob6b278c8b678954aada4806916f103d16f5cae079
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) ^ A2 */
61 #define ZERO { 0u, 0u}
62 #define ONE { 0u, ~0u}
63 #define P {~0u, 0u}
64 #define NOT_P {~0u, ~0u}
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) ^ 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 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)
108 RGBQUAD ret;
110 ret.rgbRed = GetRValue(c);
111 ret.rgbGreen = GetGValue(c);
112 ret.rgbBlue = GetBValue(c);
113 ret.rgbReserved = 0;
114 return ret;
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)
122 return TRUE;
123 return FALSE;
126 /******************************************************************
127 * get_pixel_color
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_pixel_color( dibdrv_physdev *pdev, COLORREF color, BOOL mono_fixup )
137 RGBQUAD fg_quad;
139 if (pdev->dib.bit_count != 1 || !mono_fixup)
140 return pdev->dib.funcs->colorref_to_pixel( &pdev->dib, color );
142 fg_quad = rgbquad_from_colorref( color );
143 if(rgbquad_equal(&fg_quad, pdev->dib.color_table))
144 return 0;
145 if(rgbquad_equal(&fg_quad, pdev->dib.color_table + 1))
146 return 1;
148 if(color == 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;
167 else
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;
182 else
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;
190 else
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)
199 if(*s > *e)
201 int tmp;
202 tmp = *s + 1;
203 *s = *e + 1;
204 *e = tmp;
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 /**********************************************************************
240 * get_octant_number
242 * Return the octant number starting clockwise from the +ve x-axis.
244 static inline int get_octant_number(int dx, int dy)
246 if(dy > 0)
247 if(dx > 0)
248 return ( dx > dy) ? 1 : 2;
249 else
250 return (-dx > dy) ? 4 : 3;
251 else
252 if(dx < 0)
253 return (-dx > -dy) ? 5 : 6;
254 else
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)
265 RECT rect;
267 rect.left = x;
268 rect.right = x + 1;
269 rect.top = y;
270 rect.bottom = y + 1;
271 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
272 return;
275 #define OUT_LEFT 1
276 #define OUT_RIGHT 2
277 #define OUT_TOP 4
278 #define OUT_BOTTOM 8
280 static inline DWORD calc_outcode(const POINT *pt, const RECT *clip)
282 DWORD out = 0;
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;
288 return out;
291 /******************************************************************************
292 * clip_line
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
305 * will also differ.
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)
364 int m, n;
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);
375 *pt1 = *start;
376 *pt2 = *end;
378 start_oc = calc_outcode(start, clip);
379 end_oc = calc_outcode(end, clip);
381 while(1)
383 if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
384 if(start_oc & end_oc) return 0; /* trivial reject */
386 clipped = TRUE;
387 if(start_oc & OUT_LEFT)
389 m = clip->left - start->x;
390 if(xmajor)
391 n = (m * two_dy + bias + dx - 1) / two_dx;
392 else
393 n = (m * two_dy - bias - dy) / two_dx + 1;
395 pt1->x = clip->left;
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;
403 if(xmajor)
404 n = (m * two_dy + bias + dx - 1) / two_dx;
405 else
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;
416 if(xmajor)
417 m = (n * two_dx - bias - dx) / two_dy + 1;
418 else
419 m = (n * two_dx + bias + dy - 1) / two_dy;
421 pt1->y = clip->top;
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;
429 if(xmajor)
430 m = (n * two_dx - bias - dx) / two_dy + 1;
431 else
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;
442 if(xmajor)
443 n = (m * two_dy - bias + dx) / two_dx;
444 else
445 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
447 pt2->x = clip->left;
448 if(neg_slope) n = -n;
449 pt2->y = end->y + n;
450 end_oc = calc_outcode(pt2, clip);
452 else if(end_oc & OUT_RIGHT)
454 m = end->x - clip->right + 1;
455 if(xmajor)
456 n = (m * two_dy - bias + dx) / two_dx;
457 else
458 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
460 pt2->x = clip->right - 1;
461 if(neg_slope) n = -n;
462 pt2->y = end->y - n;
463 end_oc = calc_outcode(pt2, clip);
465 else if(end_oc & OUT_TOP)
467 n = clip->top - end->y;
468 if(xmajor)
469 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
470 else
471 m = (n * two_dx - bias + dy) / two_dy;
473 pt2->y = clip->top;
474 if(neg_slope) m = -m;
475 pt2->x = end->x + m;
476 end_oc = calc_outcode(pt2, clip);
478 else if(end_oc & OUT_BOTTOM)
480 n = end->y - clip->bottom + 1;
481 if(xmajor)
482 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
483 else
484 m = (n * two_dx - bias + dy) / two_dy;
486 pt2->y = clip->bottom - 1;
487 if(neg_slope) m = -m;
488 pt2->x = end->x - 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;
499 INT erradd;
501 if (is_xmajor(params->octant)) /* line is "more horizontal" */
503 erradd = 2*params->dy - 2*params->dx;
504 while(x1 != x2)
506 callback(pdev, x1, y1);
507 if (err + params->bias > 0)
509 y1 += yadd;
510 err += erradd;
512 else err += 2*params->dy;
513 x1 += xadd;
515 if(last_pt) callback(pdev, x1, y1);
517 else /* line is "more vertical" */
519 erradd = 2*params->dx - 2*params->dy;
520 while(y1 != y2)
522 callback(pdev, x1, y1);
523 if (err + params->bias > 0)
525 x1 += xadd;
526 err += erradd;
528 else err += 2*params->dx;
529 y1 += yadd;
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)
541 RECT rect;
542 int i;
544 rect.left = start->x;
545 rect.top = start->y;
546 rect.right = end->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);
557 break;
559 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
561 RECT tmp = rect;
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)
570 RECT rect;
571 int i;
573 rect.left = start->x;
574 rect.top = start->y;
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);
585 break;
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)
591 RECT tmp = rect;
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);
598 else
600 bres_params params;
601 INT dx = end->x - start->x;
602 INT dy = end->y - start->y;
603 INT i;
605 params.dx = abs(dx);
606 params.dy = abs(dy);
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;
614 int clip_status;
615 clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
617 if(clip_status)
619 int m = abs(clipped_start.x - start->x);
620 int n = abs(clipped_start.y - start->y);
621 int err;
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;
626 else
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, &params,
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);
640 return TRUE;
643 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
645 int i;
647 assert( num >= 2 );
648 for (i = 0; i < num - 1; i++)
649 if (!solid_pen_line( pdev, pts + i, pts + i + 1 ))
650 return FALSE;
652 return TRUE;
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;
665 while(skip)
667 if(pdev->dash_pos.left_in_dash > skip)
669 pdev->dash_pos.left_in_dash -= skip;
670 return;
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;
687 else /* space */
689 get_pen_bkgnd_masks( pdev, and, xor );
693 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
695 RECT rect;
696 DWORD and, xor;
698 get_dash_colors(pdev, &and, &xor);
699 skip_dash(pdev, 1);
700 rect.left = x;
701 rect.right = x + 1;
702 rect.top = y;
703 rect.bottom = y + 1;
704 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
705 return;
708 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
710 const WINEREGION *clip = get_wine_region(pdev->clip);
711 DWORD and, xor;
712 int i, dash_len;
713 RECT rect;
714 const dash_pos start_pos = pdev->dash_pos;
716 if(start->y == end->y) /* hline */
718 BOOL l_to_r;
719 INT left, right, cur_x;
721 rect.top = start->y;
722 rect.bottom = start->y + 1;
724 if(start->x <= end->x)
726 left = start->x;
727 right = end->x - 1;
728 l_to_r = TRUE;
730 else
732 left = end->x + 1;
733 right = start->x;
734 l_to_r = FALSE;
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;
749 if(l_to_r)
751 cur_x = clipped_left;
752 if(cur_x != 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;
761 rect.left = cur_x;
762 rect.right = cur_x + dash_len;
764 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
765 cur_x += dash_len;
766 skip_dash(pdev, dash_len);
769 else
771 cur_x = clipped_right;
772 if(cur_x != 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);
785 cur_x -= dash_len;
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 */
796 BOOL t_to_b;
797 INT top, bottom, cur_y;
799 rect.left = start->x;
800 rect.right = start->x + 1;
802 if(start->y <= end->y)
804 top = start->y;
805 bottom = end->y - 1;
806 t_to_b = TRUE;
808 else
810 top = end->y + 1;
811 bottom = start->y;
812 t_to_b = FALSE;
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;
826 if(t_to_b)
828 cur_y = clipped_top;
829 if(cur_y != top)
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;
838 rect.top = cur_y;
839 rect.bottom = cur_y + dash_len;
841 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
842 cur_y += dash_len;
843 skip_dash(pdev, dash_len);
846 else
848 cur_y = clipped_bottom;
849 if(cur_y != 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);
862 cur_y -= dash_len;
863 skip_dash(pdev, dash_len);
868 pdev->dash_pos = start_pos;
869 skip_dash(pdev, bottom - top + 1);
871 else
873 bres_params params;
874 INT dx = end->x - start->x;
875 INT dy = end->y - start->y;
876 INT i;
878 params.dx = abs(dx);
879 params.dy = abs(dy);
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;
887 int clip_status;
888 clip_status = clip_line(start, end, clip->rects + i, &params, &clipped_start, &clipped_end);
890 if(clip_status)
892 int m = abs(clipped_start.x - start->x);
893 int n = abs(clipped_start.y - start->y);
894 int err;
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;
902 skip_dash(pdev, m);
904 else
906 err = 2 * params.dx - params.dy + n * 2 * params.dx - m * 2 * params.dy;
907 skip_dash(pdev, n);
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, &params,
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);
920 else
921 skip_dash(pdev, params.dy);
924 release_wine_region(pdev->clip);
925 return TRUE;
928 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
930 int i;
932 assert( num >= 2 );
933 for (i = 0; i < num - 1; i++)
934 if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
935 return FALSE;
937 return TRUE;
940 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts)
942 return TRUE;
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 /***********************************************************************
955 * dibdrv_SelectPen
957 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
959 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
960 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
961 LOGPEN logpen;
962 DWORD style;
964 TRACE("(%p, %p)\n", dev, hpen);
966 if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
968 /* must be an extended pen */
969 EXTLOGPEN *elp;
970 INT size = GetObjectW( hpen, 0, NULL );
972 if (!size) return 0;
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_pixel_color( pdev, pdev->pen_colorref, TRUE );
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;
999 switch(style)
1001 case PS_SOLID:
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;
1006 break;
1008 case PS_DASH:
1009 case PS_DOT:
1010 case PS_DASHDOT:
1011 case PS_DASHDOTDOT:
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;
1017 break;
1019 case PS_NULL:
1020 pdev->pen_lines = null_pen_lines;
1021 pdev->defer &= ~DEFER_PEN;
1022 break;
1024 default:
1025 break;
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_pixel_color( pdev, pdev->pen_colorref, TRUE );
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 /**********************************************************************
1050 * solid_brush
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)
1057 int i, j;
1058 const WINEREGION *clip = get_wine_region( region );
1060 if (!clip)
1062 dib->funcs->solid_rects( dib, num, rects, pdev->brush_and, pdev->brush_xor );
1063 return TRUE;
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 );
1077 break;
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 );
1095 return TRUE;
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 );
1130 return FALSE;
1133 while(size)
1135 calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1136 size -= 4;
1139 return TRUE;
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)
1154 dib_info hatch;
1155 rop_mask fg_mask, bg_mask;
1156 rop_mask_bits mask_bits;
1157 DWORD size;
1158 BOOL ret;
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 );
1181 return FALSE;
1184 hatch.bit_count = 1;
1185 hatch.height = hatch.width = 8;
1186 hatch.stride = 4;
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 );
1198 return ret;
1201 /**********************************************************************
1202 * pattern_brush
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)
1209 int i, j;
1210 const WINEREGION *clip;
1211 POINT origin;
1213 if(pdev->brush_and_bits == NULL)
1215 switch(pdev->brush_style)
1217 case BS_DIBPATTERN:
1218 if(!create_pattern_brush_bits(pdev))
1219 return FALSE;
1220 break;
1222 case BS_HATCHED:
1223 if(!create_hatch_brush_bits(pdev))
1224 return FALSE;
1225 break;
1227 default:
1228 ERR("Unexpected brush style %d\n", pdev->brush_style);
1229 return FALSE;
1233 GetBrushOrgEx(pdev->dev.hdc, &origin);
1235 clip = get_wine_region( region );
1237 if (!clip)
1239 dib->funcs->pattern_rects( dib, num, rects, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1240 return TRUE;
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 );
1254 break;
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 );
1272 return TRUE;
1275 static BOOL null_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1277 return TRUE;
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);
1295 LOGBRUSH logbrush;
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)
1312 case BS_SOLID:
1313 pdev->brush_colorref = logbrush.lbColor;
1314 pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
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;
1318 break;
1320 case BS_NULL:
1321 pdev->brush_rects = null_brush;
1322 pdev->defer &= ~DEFER_BRUSH;
1323 break;
1325 case BS_DIBPATTERN:
1327 BITMAPINFOHEADER *bi = GlobalLock((HGLOBAL)logbrush.lbHatch);
1328 dib_info orig_dib;
1329 WORD usage = LOWORD(logbrush.lbColor);
1330 HPALETTE pal = (usage == DIB_PAL_COLORS) ? GetCurrentObject(dev->hdc, OBJ_PAL) : NULL;
1331 RECT rect;
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 pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &orig_dib, &rect);
1353 pdev->brush_rects = pattern_brush;
1354 pdev->defer &= ~DEFER_BRUSH;
1355 free_dib_info(&orig_dib);
1357 GlobalUnlock((HGLOBAL)logbrush.lbHatch);
1358 break;
1361 case BS_HATCHED:
1363 if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1364 pdev->brush_hatch = logbrush.lbHatch;
1365 pdev->brush_colorref = logbrush.lbColor;
1366 pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1367 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1368 pdev->brush_rects = pattern_brush;
1369 pdev->defer &= ~DEFER_BRUSH;
1370 break;
1373 default:
1374 break;
1377 return next->funcs->pSelectBrush( next, hbrush );
1380 /***********************************************************************
1381 * dibdrv_SetDCBrushColor
1383 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1385 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1386 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1388 if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1390 pdev->brush_colorref = color;
1391 pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1392 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1395 return next->funcs->pSetDCBrushColor( next, color );
1398 BOOL brush_rects(dibdrv_physdev *pdev, int num, const RECT *rects)
1400 return pdev->brush_rects( pdev, &pdev->dib, num, rects, pdev->clip );