gdi32: Fix handling of COLORREFs with illegal values for the high byte.
[wine/multimedia.git] / dlls / gdi32 / dibdrv / objects.c
blobf8a9f7dc9f7d4a536c26c286b9dd17afbe438ef8
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 COLORREF make_rgb_colorref( HDC hdc, dib_info *dib, COLORREF color, BOOL *got_pixel, DWORD *pixel )
128 *pixel = 0;
129 *got_pixel = FALSE;
131 if (color & (1 << 24)) /* PALETTEINDEX */
133 HPALETTE pal = GetCurrentObject( hdc, OBJ_PAL );
134 PALETTEENTRY pal_ent;
136 if (!GetPaletteEntries( pal, LOWORD(color), 1, &pal_ent ))
137 GetPaletteEntries( pal, 0, 1, &pal_ent );
138 return RGB( pal_ent.peRed, pal_ent.peGreen, pal_ent.peBlue );
141 if (color >> 16 == 0x10ff) /* DIBINDEX */
143 WORD index = LOWORD( color );
144 *got_pixel = TRUE;
145 if (!dib->color_table || index >= (1 << dib->bit_count)) return 0;
146 *pixel = index;
147 return RGB( dib->color_table[index].rgbRed,
148 dib->color_table[index].rgbGreen,
149 dib->color_table[index].rgbBlue );
152 return color & 0xffffff;
155 /******************************************************************
156 * get_pixel_color
158 * 1 bit bitmaps map the fg/bg colors as follows:
159 * If the fg colorref exactly matches one of the color table entries then
160 * that entry is the fg color and the other is the bg.
161 * Otherwise the bg color is mapped to the closest entry in the table and
162 * the fg takes the other one.
164 DWORD get_pixel_color( dibdrv_physdev *pdev, COLORREF color, BOOL mono_fixup )
166 RGBQUAD fg_quad;
167 BOOL got_pixel;
168 DWORD pixel;
169 COLORREF rgb_ref;
171 rgb_ref = make_rgb_colorref( pdev->dev.hdc, &pdev->dib, color, &got_pixel, &pixel );
172 if (got_pixel) return pixel;
174 if (pdev->dib.bit_count != 1 || !mono_fixup)
175 return pdev->dib.funcs->colorref_to_pixel( &pdev->dib, rgb_ref );
177 fg_quad = rgbquad_from_colorref( rgb_ref );
178 if(rgbquad_equal(&fg_quad, pdev->dib.color_table))
179 return 0;
180 if(rgbquad_equal(&fg_quad, pdev->dib.color_table + 1))
181 return 1;
183 if(color == GetBkColor(pdev->dev.hdc)) return pdev->bkgnd_color;
184 else return pdev->bkgnd_color ? 0 : 1;
187 /***************************************************************************
188 * get_pen_bkgnd_masks
190 * Returns the pre-calculated bkgnd color masks unless the dib is 1 bpp.
191 * In this case since there are several fg sources (pen, brush, text)
192 * this makes pdev->bkgnd_color unusable. So here we take the inverse
193 * of the relevant fg color (which is always set up correctly).
195 static inline void get_pen_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
197 if(pdev->dib.bit_count != 1 || GetBkMode(pdev->dev.hdc) == TRANSPARENT)
199 *and = pdev->bkgnd_and;
200 *xor = pdev->bkgnd_xor;
202 else
204 DWORD color = ~pdev->pen_color;
205 if(pdev->pen_colorref == GetBkColor(pdev->dev.hdc)) color = pdev->pen_color;
206 calc_and_xor_masks( GetROP2(pdev->dev.hdc), color, and, xor );
210 static inline void get_brush_bkgnd_masks(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
212 if(GetBkMode(pdev->dev.hdc) == TRANSPARENT)
214 *and = pdev->bkgnd_and;
215 *xor = pdev->bkgnd_xor;
217 else
219 DWORD color = pdev->bkgnd_color;
221 if(pdev->dib.bit_count == 1)
223 if(pdev->brush_colorref == GetBkColor(pdev->dev.hdc))
224 color = pdev->brush_color;
225 else
226 color = ~pdev->brush_color;
228 calc_and_xor_masks( pdev->brush_rop, color, and, xor );
232 static inline void order_end_points(int *s, int *e)
234 if(*s > *e)
236 int tmp;
237 tmp = *s + 1;
238 *s = *e + 1;
239 *e = tmp;
243 #define Y_INCREASING_MASK 0x0f
244 #define X_INCREASING_MASK 0xc3
245 #define X_MAJOR_MASK 0x99
246 #define POS_SLOPE_MASK 0x33
248 static inline BOOL is_xmajor(DWORD octant)
250 return octant & X_MAJOR_MASK;
253 static inline BOOL is_pos_slope(DWORD octant)
255 return octant & POS_SLOPE_MASK;
258 static inline BOOL is_x_increasing(DWORD octant)
260 return octant & X_INCREASING_MASK;
263 static inline BOOL is_y_increasing(DWORD octant)
265 return octant & Y_INCREASING_MASK;
268 /**********************************************************************
269 * get_octant_number
271 * Return the octant number starting clockwise from the +ve x-axis.
273 static inline int get_octant_number(int dx, int dy)
275 if(dy > 0)
276 if(dx > 0)
277 return ( dx > dy) ? 1 : 2;
278 else
279 return (-dx > dy) ? 4 : 3;
280 else
281 if(dx < 0)
282 return (-dx > -dy) ? 5 : 6;
283 else
284 return ( dx > -dy) ? 8 : 7;
287 static inline DWORD get_octant_mask(int dx, int dy)
289 return 1 << (get_octant_number(dx, dy) - 1);
292 static inline int get_bias( DWORD mask )
294 /* Octants 3, 5, 6 and 8 take a bias */
295 return (mask & 0xb4) ? 1 : 0;
298 #define OUT_LEFT 1
299 #define OUT_RIGHT 2
300 #define OUT_TOP 4
301 #define OUT_BOTTOM 8
303 static inline DWORD calc_outcode(const POINT *pt, const RECT *clip)
305 DWORD out = 0;
306 if(pt->x < clip->left) out |= OUT_LEFT;
307 else if(pt->x >= clip->right) out |= OUT_RIGHT;
308 if(pt->y < clip->top) out |= OUT_TOP;
309 else if(pt->y >= clip->bottom) out |= OUT_BOTTOM;
311 return out;
314 /******************************************************************************
315 * clip_line
317 * Clips the start and end points to a rectangle.
319 * Note, this treats the end point like the start point. If the
320 * caller doesn't want it displayed, it should exclude it. If the end
321 * point is clipped out, then the likelihood is that the new end point
322 * should be displayed.
324 * Returns 0 if totally excluded, 1 if partially clipped and 2 if unclipped.
326 * This derivation is based on the comments in X.org's xserver/mi/mizerclip.c,
327 * however the Bresenham error term is defined differently so the equations
328 * will also differ.
330 * For x major lines we have 2dy >= err + bias > 2dy - 2dx
331 * 0 >= err + bias - 2dy > -2dx
333 * Note dx, dy, m and n are all +ve.
335 * Moving the start pt from x1 to x1 + m, we need to figure out y1 + n.
336 * err = 2dy - dx + 2mdy - 2ndx
337 * 0 >= 2dy - dx + 2mdy - 2ndx + bias - 2dy > -2dx
338 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
339 * which of course will give exactly one solution for n,
340 * so looking at the >= inequality
341 * n >= (2mdy + bias - dx) / 2dx
342 * n = ceiling((2mdy + bias - dx) / 2dx)
343 * = (2mdy + bias + dx - 1) / 2dx (assuming division truncation)
345 * Moving start pt from y1 to y1 + n we need to figure out x1 + m - there may be several
346 * solutions we pick the one that minimizes m (ie that first unlipped pt). As above:
347 * 0 >= 2mdy - 2ndx + bias - dx > -2dx
348 * 2mdy > 2ndx - bias - dx
349 * m > (2ndx - bias - dx) / 2dy
350 * m = floor((2ndx - bias - dx) / 2dy) + 1
351 * m = (2ndx - bias - dx) / 2dy + 1
353 * Moving end pt from x2 to x2 - m, we need to figure out y2 - n
354 * err = 2dy - dx + 2(dx - m)dy - 2(dy - n)dx
355 * = 2dy - dx - 2mdy + 2ndx
356 * 0 >= 2dy - dx - 2mdy + 2ndx + bias - 2dy > -2dx
357 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
358 * again exactly one solution.
359 * 2ndx <= 2mdy - bias + dx
360 * n = floor((2mdy - bias + dx) / 2dx)
361 * = (2mdy - bias + dx) / 2dx
363 * Moving end pt from y2 to y2 - n when need x2 - m this time maximizing x2 - m so
364 * mininizing m to include all of the points at y = y2 - n. As above:
365 * 0 >= 2ndx - 2mdy + bias - dx > -2dx
366 * 2mdy >= 2ndx + bias - dx
367 * m = ceiling((2ndx + bias - dx) / 2dy)
368 * = (2ndx + bias - dx - 1) / 2dy + 1
370 * For y major lines, symmetry (dx <-> dy and swap the cases over) gives:
372 * Moving start point from y1 to y1 + n find x1 + m
373 * m = (2ndx + bias + dy - 1) / 2dy
375 * Moving start point from x1 to x1 + m find y1 + n
376 * n = (2mdy - bias - dy) / 2ndx + 1
378 * Moving end point from y2 to y2 - n find x1 - m
379 * m = (2ndx - bias + dy) / 2dy
381 * Moving end point from x2 to x2 - m find y2 - n
382 * n = (2mdy + bias - dy - 1) / 2dx + 1
384 int clip_line(const POINT *start, const POINT *end, const RECT *clip,
385 const bres_params *params, POINT *pt1, POINT *pt2)
387 int m, n;
388 BOOL clipped = FALSE;
389 DWORD start_oc, end_oc;
390 const int bias = params->bias;
391 const unsigned int dx = params->dx;
392 const unsigned int dy = params->dy;
393 const unsigned int two_dx = params->dx * 2;
394 const unsigned int two_dy = params->dy * 2;
395 const BOOL xmajor = is_xmajor(params->octant);
396 const BOOL neg_slope = !is_pos_slope(params->octant);
398 *pt1 = *start;
399 *pt2 = *end;
401 start_oc = calc_outcode(start, clip);
402 end_oc = calc_outcode(end, clip);
404 while(1)
406 if(start_oc == 0 && end_oc == 0) return clipped ? 1 : 2; /* trivial accept */
407 if(start_oc & end_oc) return 0; /* trivial reject */
409 clipped = TRUE;
410 if(start_oc & OUT_LEFT)
412 m = clip->left - start->x;
413 if(xmajor)
414 n = (m * two_dy + bias + dx - 1) / two_dx;
415 else
416 n = (m * two_dy - bias - dy) / two_dx + 1;
418 pt1->x = clip->left;
419 if(neg_slope) n = -n;
420 pt1->y = start->y + n;
421 start_oc = calc_outcode(pt1, clip);
423 else if(start_oc & OUT_RIGHT)
425 m = start->x - clip->right + 1;
426 if(xmajor)
427 n = (m * two_dy + bias + dx - 1) / two_dx;
428 else
429 n = (m * two_dy - bias - dy) / two_dx + 1;
431 pt1->x = clip->right - 1;
432 if(neg_slope) n = -n;
433 pt1->y = start->y - n;
434 start_oc = calc_outcode(pt1, clip);
436 else if(start_oc & OUT_TOP)
438 n = clip->top - start->y;
439 if(xmajor)
440 m = (n * two_dx - bias - dx) / two_dy + 1;
441 else
442 m = (n * two_dx + bias + dy - 1) / two_dy;
444 pt1->y = clip->top;
445 if(neg_slope) m = -m;
446 pt1->x = start->x + m;
447 start_oc = calc_outcode(pt1, clip);
449 else if(start_oc & OUT_BOTTOM)
451 n = start->y - clip->bottom + 1;
452 if(xmajor)
453 m = (n * two_dx - bias - dx) / two_dy + 1;
454 else
455 m = (n * two_dx + bias + dy - 1) / two_dy;
457 pt1->y = clip->bottom - 1;
458 if(neg_slope) m = -m;
459 pt1->x = start->x - m;
460 start_oc = calc_outcode(pt1, clip);
462 else if(end_oc & OUT_LEFT)
464 m = clip->left - end->x;
465 if(xmajor)
466 n = (m * two_dy - bias + dx) / two_dx;
467 else
468 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
470 pt2->x = clip->left;
471 if(neg_slope) n = -n;
472 pt2->y = end->y + n;
473 end_oc = calc_outcode(pt2, clip);
475 else if(end_oc & OUT_RIGHT)
477 m = end->x - clip->right + 1;
478 if(xmajor)
479 n = (m * two_dy - bias + dx) / two_dx;
480 else
481 n = (m * two_dy + bias - dy - 1) / two_dx + 1;
483 pt2->x = clip->right - 1;
484 if(neg_slope) n = -n;
485 pt2->y = end->y - n;
486 end_oc = calc_outcode(pt2, clip);
488 else if(end_oc & OUT_TOP)
490 n = clip->top - end->y;
491 if(xmajor)
492 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
493 else
494 m = (n * two_dx - bias + dy) / two_dy;
496 pt2->y = clip->top;
497 if(neg_slope) m = -m;
498 pt2->x = end->x + m;
499 end_oc = calc_outcode(pt2, clip);
501 else if(end_oc & OUT_BOTTOM)
503 n = end->y - clip->bottom + 1;
504 if(xmajor)
505 m = (n * two_dx + bias - dx - 1) / two_dy + 1;
506 else
507 m = (n * two_dx - bias + dy) / two_dy;
509 pt2->y = clip->bottom - 1;
510 if(neg_slope) m = -m;
511 pt2->x = end->x - m;
512 end_oc = calc_outcode(pt2, clip);
517 static void bres_line_with_bias(const POINT *start, const struct line_params *params,
518 void (* callback)(dibdrv_physdev*,INT,INT), dibdrv_physdev *pdev)
520 POINT pt = *start;
521 int len = params->length, err = params->err_start;
523 if (params->x_major)
525 while(len--)
527 callback(pdev, pt.x, pt.y);
528 if (err + params->bias > 0)
530 pt.y += params->y_inc;
531 err += params->err_add_1;
533 else err += params->err_add_2;
534 pt.x += params->x_inc;
537 else
539 while(len--)
541 callback(pdev, pt.x, pt.y);
542 if (err + params->bias > 0)
544 pt.x += params->x_inc;
545 err += params->err_add_1;
547 else err += params->err_add_2;
548 pt.y += params->y_inc;
553 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
555 const WINEREGION *clip = get_wine_region(pdev->clip);
557 if(start->y == end->y)
559 RECT rect;
560 int i;
562 rect.left = start->x;
563 rect.top = start->y;
564 rect.right = end->x;
565 rect.bottom = end->y + 1;
566 order_end_points(&rect.left, &rect.right);
567 for(i = 0; i < clip->numRects; i++)
569 if(clip->rects[i].top >= rect.bottom) break;
570 if(clip->rects[i].bottom <= rect.top) continue;
571 /* Optimize the unclipped case */
572 if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
574 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
575 break;
577 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
579 RECT tmp = rect;
580 tmp.left = max(rect.left, clip->rects[i].left);
581 tmp.right = min(rect.right, clip->rects[i].right);
582 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
586 else if(start->x == end->x)
588 RECT rect;
589 int i;
591 rect.left = start->x;
592 rect.top = start->y;
593 rect.right = end->x + 1;
594 rect.bottom = end->y;
595 order_end_points(&rect.top, &rect.bottom);
596 for(i = 0; i < clip->numRects; i++)
598 /* Optimize unclipped case */
599 if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
600 clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
602 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
603 break;
605 if(clip->rects[i].top >= rect.bottom) break;
606 if(clip->rects[i].bottom <= rect.top) continue;
607 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
609 RECT tmp = rect;
610 tmp.top = max(rect.top, clip->rects[i].top);
611 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
612 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
616 else
618 bres_params clip_params;
619 struct line_params line_params;
620 INT dx = end->x - start->x, dy = end->y - start->y;
621 INT abs_dx = abs(dx), abs_dy = abs(dy);
622 INT i;
624 clip_params.dx = abs_dx;
625 clip_params.dy = abs_dy;
626 clip_params.octant = get_octant_mask(dx, dy);
627 clip_params.bias = get_bias( clip_params.octant );
629 line_params.bias = clip_params.bias;
630 line_params.x_major = is_xmajor( clip_params.octant );
631 line_params.x_inc = is_x_increasing( clip_params.octant ) ? 1 : -1;
632 line_params.y_inc = is_y_increasing( clip_params.octant ) ? 1 : -1;
634 if (line_params.x_major)
636 line_params.err_add_1 = 2 * abs_dy - 2 * abs_dx;
637 line_params.err_add_2 = 2 * abs_dy;
639 else
641 line_params.err_add_1 = 2 * abs_dx - 2 * abs_dy;
642 line_params.err_add_2 = 2 * abs_dx;
645 for(i = 0; i < clip->numRects; i++)
647 POINT clipped_start, clipped_end;
648 int clip_status;
649 clip_status = clip_line(start, end, clip->rects + i, &clip_params, &clipped_start, &clipped_end);
651 if(clip_status)
653 int m = abs(clipped_start.x - start->x);
654 int n = abs(clipped_start.y - start->y);
656 if (line_params.x_major)
658 line_params.err_start = 2 * abs_dy - abs_dx + m * 2 * abs_dy - n * 2 * abs_dx;
659 line_params.length = abs( clipped_end.x - clipped_start.x ) + 1;
661 else
663 line_params.err_start = 2 * abs_dx - abs_dy + n * 2 * abs_dx - m * 2 * abs_dy;
664 line_params.length = abs( clipped_end.y - clipped_start.y ) + 1;
667 if (clipped_end.x == end->x && clipped_end.y == end->y) line_params.length--;
669 pdev->dib.funcs->solid_line( &pdev->dib, &clipped_start, &line_params,
670 pdev->pen_and, pdev->pen_xor );
672 if(clip_status == 2) break; /* completely unclipped, so we can finish */
677 release_wine_region(pdev->clip);
678 return TRUE;
681 static BOOL solid_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
683 int i;
685 assert( num >= 2 );
686 for (i = 0; i < num - 1; i++)
687 if (!solid_pen_line( pdev, pts + i, pts + i + 1 ))
688 return FALSE;
690 if (close) return solid_pen_line( pdev, pts + num - 1, pts );
692 return TRUE;
695 void reset_dash_origin(dibdrv_physdev *pdev)
697 pdev->dash_pos.cur_dash = 0;
698 pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[0];
699 pdev->dash_pos.mark = TRUE;
702 static inline void skip_dash(dibdrv_physdev *pdev, unsigned int skip)
704 skip %= pdev->pen_pattern.total_len;
705 while(skip)
707 if(pdev->dash_pos.left_in_dash > skip)
709 pdev->dash_pos.left_in_dash -= skip;
710 return;
712 skip -= pdev->dash_pos.left_in_dash;
713 pdev->dash_pos.cur_dash++;
714 if(pdev->dash_pos.cur_dash == pdev->pen_pattern.count) pdev->dash_pos.cur_dash = 0;
715 pdev->dash_pos.left_in_dash = pdev->pen_pattern.dashes[pdev->dash_pos.cur_dash];
716 pdev->dash_pos.mark = !pdev->dash_pos.mark;
720 static inline void get_dash_colors(const dibdrv_physdev *pdev, DWORD *and, DWORD *xor)
722 if(pdev->dash_pos.mark)
724 *and = pdev->pen_and;
725 *xor = pdev->pen_xor;
727 else /* space */
729 get_pen_bkgnd_masks( pdev, and, xor );
733 static void dashed_pen_line_callback(dibdrv_physdev *pdev, INT x, INT y)
735 RECT rect;
736 DWORD and, xor;
738 get_dash_colors(pdev, &and, &xor);
739 skip_dash(pdev, 1);
740 rect.left = x;
741 rect.right = x + 1;
742 rect.top = y;
743 rect.bottom = y + 1;
744 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
745 return;
748 static BOOL dashed_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
750 const WINEREGION *clip = get_wine_region(pdev->clip);
751 DWORD and, xor;
752 int i, dash_len;
753 RECT rect;
754 const dash_pos start_pos = pdev->dash_pos;
756 if(start->y == end->y) /* hline */
758 BOOL l_to_r;
759 INT left, right, cur_x;
761 rect.top = start->y;
762 rect.bottom = start->y + 1;
764 if(start->x <= end->x)
766 left = start->x;
767 right = end->x - 1;
768 l_to_r = TRUE;
770 else
772 left = end->x + 1;
773 right = start->x;
774 l_to_r = FALSE;
777 for(i = 0; i < clip->numRects; i++)
779 if(clip->rects[i].top > start->y) break;
780 if(clip->rects[i].bottom <= start->y) continue;
782 if(clip->rects[i].right > left && clip->rects[i].left <= right)
784 int clipped_left = max(clip->rects[i].left, left);
785 int clipped_right = min(clip->rects[i].right - 1, right);
787 pdev->dash_pos = start_pos;
789 if(l_to_r)
791 cur_x = clipped_left;
792 if(cur_x != left)
793 skip_dash(pdev, clipped_left - left);
795 while(cur_x <= clipped_right)
797 get_dash_colors(pdev, &and, &xor);
798 dash_len = pdev->dash_pos.left_in_dash;
799 if(cur_x + dash_len > clipped_right + 1)
800 dash_len = clipped_right - cur_x + 1;
801 rect.left = cur_x;
802 rect.right = cur_x + dash_len;
804 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
805 cur_x += dash_len;
806 skip_dash(pdev, dash_len);
809 else
811 cur_x = clipped_right;
812 if(cur_x != right)
813 skip_dash(pdev, right - clipped_right);
815 while(cur_x >= clipped_left)
817 get_dash_colors(pdev, &and, &xor);
818 dash_len = pdev->dash_pos.left_in_dash;
819 if(cur_x - dash_len < clipped_left - 1)
820 dash_len = cur_x - clipped_left + 1;
821 rect.left = cur_x - dash_len + 1;
822 rect.right = cur_x + 1;
824 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
825 cur_x -= dash_len;
826 skip_dash(pdev, dash_len);
831 pdev->dash_pos = start_pos;
832 skip_dash(pdev, right - left + 1);
834 else if(start->x == end->x) /* vline */
836 BOOL t_to_b;
837 INT top, bottom, cur_y;
839 rect.left = start->x;
840 rect.right = start->x + 1;
842 if(start->y <= end->y)
844 top = start->y;
845 bottom = end->y - 1;
846 t_to_b = TRUE;
848 else
850 top = end->y + 1;
851 bottom = start->y;
852 t_to_b = FALSE;
855 for(i = 0; i < clip->numRects; i++)
857 if(clip->rects[i].top > bottom) break;
858 if(clip->rects[i].bottom <= top) continue;
859 if(clip->rects[i].right > start->x && clip->rects[i].left <= start->x)
861 int clipped_top = max(clip->rects[i].top, top);
862 int clipped_bottom = min(clip->rects[i].bottom - 1, bottom);
864 pdev->dash_pos = start_pos;
866 if(t_to_b)
868 cur_y = clipped_top;
869 if(cur_y != top)
870 skip_dash(pdev, clipped_top - top);
872 while(cur_y <= clipped_bottom)
874 get_dash_colors(pdev, &and, &xor);
875 dash_len = pdev->dash_pos.left_in_dash;
876 if(cur_y + dash_len > clipped_bottom + 1)
877 dash_len = clipped_bottom - cur_y + 1;
878 rect.top = cur_y;
879 rect.bottom = cur_y + dash_len;
881 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
882 cur_y += dash_len;
883 skip_dash(pdev, dash_len);
886 else
888 cur_y = clipped_bottom;
889 if(cur_y != bottom)
890 skip_dash(pdev, bottom - clipped_bottom);
892 while(cur_y >= clipped_top)
894 get_dash_colors(pdev, &and, &xor);
895 dash_len = pdev->dash_pos.left_in_dash;
896 if(cur_y - dash_len < clipped_top - 1)
897 dash_len = cur_y - clipped_top + 1;
898 rect.top = cur_y - dash_len + 1;
899 rect.bottom = cur_y + 1;
901 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, and, xor);
902 cur_y -= dash_len;
903 skip_dash(pdev, dash_len);
908 pdev->dash_pos = start_pos;
909 skip_dash(pdev, bottom - top + 1);
911 else
913 bres_params clip_params;
914 struct line_params line_params;
915 INT dx = end->x - start->x, dy = end->y - start->y;
916 INT abs_dx = abs(dx), abs_dy = abs(dy);
917 INT i;
919 clip_params.dx = abs_dx;
920 clip_params.dy = abs_dy;
921 clip_params.octant = get_octant_mask(dx, dy);
922 clip_params.bias = get_bias( clip_params.octant );
924 line_params.bias = clip_params.bias;
925 line_params.x_major = is_xmajor( clip_params.octant );
926 line_params.x_inc = is_x_increasing( clip_params.octant ) ? 1 : -1;
927 line_params.y_inc = is_y_increasing( clip_params.octant ) ? 1 : -1;
929 if (line_params.x_major)
931 line_params.err_add_1 = 2 * abs_dy - 2 * abs_dx;
932 line_params.err_add_2 = 2 * abs_dy;
934 else
936 line_params.err_add_1 = 2 * abs_dx - 2 * abs_dy;
937 line_params.err_add_2 = 2 * abs_dx;
940 for(i = 0; i < clip->numRects; i++)
942 POINT clipped_start, clipped_end;
943 int clip_status;
944 clip_status = clip_line(start, end, clip->rects + i, &clip_params, &clipped_start, &clipped_end);
946 if(clip_status)
948 int m = abs(clipped_start.x - start->x);
949 int n = abs(clipped_start.y - start->y);
951 pdev->dash_pos = start_pos;
953 if (line_params.x_major)
955 line_params.err_start = 2 * abs_dy - abs_dx + m * 2 * abs_dy - n * 2 * abs_dx;
956 line_params.length = abs( clipped_end.x - clipped_start.x ) + 1;
957 skip_dash(pdev, m);
959 else
961 line_params.err_start = 2 * abs_dx - abs_dy + n * 2 * abs_dx - m * 2 * abs_dy;
962 line_params.length = abs( clipped_end.y - clipped_start.y ) + 1;
963 skip_dash(pdev, n);
965 if (clipped_end.x == end->x && clipped_end.y == end->y) line_params.length--;
967 bres_line_with_bias( &clipped_start, &line_params, dashed_pen_line_callback, pdev );
969 if(clip_status == 2) break; /* completely unclipped, so we can finish */
972 pdev->dash_pos = start_pos;
973 if(line_params.x_major)
974 skip_dash(pdev, abs_dx);
975 else
976 skip_dash(pdev, abs_dy);
979 release_wine_region(pdev->clip);
980 return TRUE;
983 static BOOL dashed_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
985 int i;
987 assert( num >= 2 );
988 for (i = 0; i < num - 1; i++)
989 if (!dashed_pen_line( pdev, pts + i, pts + i + 1 ))
990 return FALSE;
992 if (close) return dashed_pen_line( pdev, pts + num - 1, pts );
994 return TRUE;
997 static BOOL null_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
999 return TRUE;
1002 static void add_cap( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
1004 HRGN cap;
1006 switch (pdev->pen_endcap)
1008 case PS_ENDCAP_ROUND:
1009 cap = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2,
1010 pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 );
1011 break;
1013 default: /* only supporting cosmetic pens so far, so always PS_ENDCAP_ROUND */
1014 return;
1017 CombineRgn( region, region, cap, RGN_OR );
1018 DeleteObject( cap );
1019 return;
1022 static void add_join( dibdrv_physdev *pdev, HRGN region, const POINT *pt )
1024 HRGN join;
1026 switch (pdev->pen_join)
1028 case PS_JOIN_ROUND:
1029 join = CreateEllipticRgn( pt->x - pdev->pen_width / 2, pt->y - pdev->pen_width / 2,
1030 pt->x + (pdev->pen_width + 1) / 2, pt->y + (pdev->pen_width + 1) / 2 );
1031 break;
1033 default: /* only supporting cosmetic pens so far, so always PS_JOIN_ROUND */
1034 return;
1037 CombineRgn( region, region, join, RGN_OR );
1038 DeleteObject( join );
1039 return;
1042 #define round( f ) (((f) > 0) ? (f) + 0.5 : (f) - 0.5)
1044 static HRGN get_wide_lines_region( dibdrv_physdev *pdev, int num, POINT *pts, BOOL close )
1046 int i;
1047 HRGN total, segment;
1049 assert( num >= 2 );
1051 total = CreateRectRgn( 0, 0, 0, 0 );
1053 if (!close) num--;
1054 for (i = 0; i < num; i++)
1056 const POINT *pt_1 = pts + i;
1057 const POINT *pt_2 = pts + ((close && i == num - 1) ? 0 : i + 1);
1058 int dx = pt_2->x - pt_1->x;
1059 int dy = pt_2->y - pt_1->y;
1060 RECT rect;
1062 if (dx == 0 && dy == 0) continue;
1064 if (dy == 0)
1066 rect.left = min( pt_1->x, pt_2->x );
1067 rect.right = rect.left + abs( dx );
1068 rect.top = pt_1->y - pdev->pen_width / 2;
1069 rect.bottom = rect.top + pdev->pen_width;
1070 segment = CreateRectRgnIndirect( &rect );
1072 else if (dx == 0)
1074 rect.top = min( pt_1->y, pt_2->y );
1075 rect.bottom = rect.top + abs( dy );
1076 rect.left = pt_1->x - pdev->pen_width / 2;
1077 rect.right = rect.left + pdev->pen_width;
1078 segment = CreateRectRgnIndirect( &rect );
1080 else
1082 double len = hypot( dx, dy );
1083 double width_x, width_y;
1084 POINT seg_pts[4];
1085 POINT wide_half, narrow_half;
1087 width_x = pdev->pen_width * abs( dy ) / len;
1088 width_y = pdev->pen_width * abs( dx ) / len;
1090 narrow_half.x = round( width_x / 2 );
1091 narrow_half.y = round( width_y / 2 );
1092 wide_half.x = round( (width_x + 1) / 2 );
1093 wide_half.y = round( (width_y + 1) / 2 );
1095 if (dx < 0)
1097 wide_half.y = -wide_half.y;
1098 narrow_half.y = -narrow_half.y;
1101 if (dy < 0)
1103 POINT tmp = narrow_half; narrow_half = wide_half; wide_half = tmp;
1104 wide_half.x = -wide_half.x;
1105 narrow_half.x = -narrow_half.x;
1108 seg_pts[0].x = pt_1->x - narrow_half.x;
1109 seg_pts[0].y = pt_1->y + narrow_half.y;
1110 seg_pts[1].x = pt_1->x + wide_half.x;
1111 seg_pts[1].y = pt_1->y - wide_half.y;
1112 seg_pts[2].x = pt_2->x + wide_half.x;
1113 seg_pts[2].y = pt_2->y - wide_half.y;
1114 seg_pts[3].x = pt_2->x - narrow_half.x;
1115 seg_pts[3].y = pt_2->y + narrow_half.y;
1117 segment = CreatePolygonRgn( seg_pts, 4, ALTERNATE );
1120 CombineRgn( total, total, segment, RGN_OR );
1121 DeleteObject( segment );
1123 if (i == 0)
1125 if (!close) add_cap( pdev, total, pt_1 );
1127 else
1128 add_join( pdev, total, pt_1 );
1130 if (i == num - 1)
1132 if (close) add_join( pdev, total, pt_2 );
1133 else add_cap( pdev, total, pt_2 );
1137 return total;
1140 static BOOL wide_pen_lines(dibdrv_physdev *pdev, int num, POINT *pts, BOOL close)
1142 const WINEREGION *data;
1143 rop_mask color;
1144 HRGN region;
1146 color.and = pdev->pen_and;
1147 color.xor = pdev->pen_xor;
1149 region = get_wide_lines_region( pdev, num, pts, close );
1151 if (CombineRgn( region, region, pdev->clip, RGN_AND ) != ERROR)
1153 data = get_wine_region( region );
1154 solid_rects( &pdev->dib, data->numRects, data->rects, &color, NULL );
1155 release_wine_region( region );
1158 DeleteObject( region );
1159 return TRUE;
1162 static const dash_pattern dash_patterns[5] =
1164 {0, {0}, 0}, /* PS_SOLID - a pseudo-pattern used to initialise unpatterned pens. */
1165 {2, {18, 6}, 24}, /* PS_DASH */
1166 {2, {3, 3}, 6}, /* PS_DOT */
1167 {4, {9, 6, 3, 6}, 24}, /* PS_DASHDOT */
1168 {6, {9, 3, 3, 3, 3, 3}, 24} /* PS_DASHDOTDOT */
1171 static inline int get_pen_device_width( dibdrv_physdev *pdev, LOGPEN *pen )
1173 int width = pen->lopnWidth.x;
1175 if (pen->lopnStyle & PS_GEOMETRIC && width > 1)
1177 POINT pts[2];
1178 pts[0].x = pts[0].y = pts[1].y = 0;
1179 pts[1].x = width;
1180 LPtoDP( pdev->dev.hdc, pts, 2 );
1181 width = max( abs( pts[1].x - pts[0].x ), 1 );
1183 return width;
1186 /***********************************************************************
1187 * dibdrv_SelectPen
1189 HPEN dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
1191 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
1192 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1193 LOGPEN logpen;
1194 DWORD style;
1196 TRACE("(%p, %p)\n", dev, hpen);
1198 if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
1200 /* must be an extended pen */
1201 EXTLOGPEN *elp;
1202 INT size = GetObjectW( hpen, 0, NULL );
1204 if (!size) return 0;
1206 elp = HeapAlloc( GetProcessHeap(), 0, size );
1208 GetObjectW( hpen, size, elp );
1209 /* FIXME: add support for user style pens */
1210 logpen.lopnStyle = elp->elpPenStyle;
1211 logpen.lopnWidth.x = elp->elpWidth;
1212 logpen.lopnWidth.y = 0;
1213 logpen.lopnColor = elp->elpColor;
1215 HeapFree( GetProcessHeap(), 0, elp );
1218 pdev->pen_join = logpen.lopnStyle & PS_JOIN_MASK;
1219 pdev->pen_endcap = logpen.lopnStyle & PS_ENDCAP_MASK;
1220 pdev->pen_width = get_pen_device_width( pdev, &logpen );
1222 if (hpen == GetStockObject( DC_PEN ))
1223 logpen.lopnColor = GetDCPenColor( dev->hdc );
1225 pdev->pen_colorref = logpen.lopnColor;
1226 pdev->pen_color = get_pixel_color( pdev, pdev->pen_colorref, TRUE );
1227 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1229 pdev->pen_pattern = dash_patterns[PS_SOLID];
1231 pdev->defer |= DEFER_PEN;
1233 style = logpen.lopnStyle & PS_STYLE_MASK;
1235 switch(style)
1237 case PS_SOLID:
1238 if(logpen.lopnStyle & PS_GEOMETRIC) break;
1239 if(pdev->pen_width <= 1)
1240 pdev->pen_lines = solid_pen_lines;
1241 else
1242 pdev->pen_lines = wide_pen_lines;
1243 pdev->defer &= ~DEFER_PEN;
1244 break;
1246 case PS_DASH:
1247 case PS_DOT:
1248 case PS_DASHDOT:
1249 case PS_DASHDOTDOT:
1250 if(logpen.lopnStyle & PS_GEOMETRIC) break;
1251 if(logpen.lopnWidth.x > 1) break;
1252 pdev->pen_lines = dashed_pen_lines;
1253 pdev->pen_pattern = dash_patterns[style];
1254 pdev->defer &= ~DEFER_PEN;
1255 break;
1257 case PS_NULL:
1258 pdev->pen_lines = null_pen_lines;
1259 pdev->defer &= ~DEFER_PEN;
1260 break;
1262 default:
1263 break;
1266 return next->funcs->pSelectPen( next, hpen );
1269 /***********************************************************************
1270 * dibdrv_SetDCPenColor
1272 COLORREF dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
1274 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
1275 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1277 if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
1279 pdev->pen_colorref = color;
1280 pdev->pen_color = get_pixel_color( pdev, pdev->pen_colorref, TRUE );
1281 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
1284 return next->funcs->pSetDCPenColor( next, color );
1287 void solid_rects( dib_info *dib, int num, const RECT *rects, const rop_mask *color, HRGN region )
1289 int i, j;
1290 const WINEREGION *clip;
1292 if (!region)
1294 dib->funcs->solid_rects( dib, num, rects, color->and, color->xor );
1295 return;
1298 clip = get_wine_region( region );
1300 for(i = 0; i < num; i++)
1302 for(j = 0; j < clip->numRects; j++)
1304 RECT clipped_rect;
1306 if (intersect_rect( &clipped_rect, rects + i, clip->rects + j ))
1307 dib->funcs->solid_rects( dib, 1, &clipped_rect, color->and, color->xor );
1310 release_wine_region( region );
1313 /**********************************************************************
1314 * solid_brush
1316 * Fill a number of rectangles with the solid brush
1318 static BOOL solid_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1320 rop_mask brush_color;
1322 brush_color.and = pdev->brush_and;
1323 brush_color.xor = pdev->brush_xor;
1325 solid_rects( dib, num, rects, &brush_color, region );
1326 return TRUE;
1329 static void free_pattern_brush_bits( dibdrv_physdev *pdev )
1331 HeapFree(GetProcessHeap(), 0, pdev->brush_and_bits);
1332 HeapFree(GetProcessHeap(), 0, pdev->brush_xor_bits);
1333 pdev->brush_and_bits = NULL;
1334 pdev->brush_xor_bits = NULL;
1337 void free_pattern_brush( dibdrv_physdev *pdev )
1339 free_pattern_brush_bits( pdev );
1340 free_dib_info( &pdev->brush_dib );
1343 static BOOL create_pattern_brush_bits(dibdrv_physdev *pdev)
1345 DWORD size = pdev->brush_dib.height * abs(pdev->brush_dib.stride);
1346 DWORD *brush_bits = pdev->brush_dib.bits.ptr;
1347 DWORD *and_bits, *xor_bits;
1349 assert(pdev->brush_and_bits == NULL);
1350 assert(pdev->brush_xor_bits == NULL);
1352 assert(pdev->brush_dib.stride > 0);
1354 and_bits = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1355 xor_bits = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1357 if(!and_bits || !xor_bits)
1359 ERR("Failed to create pattern brush bits\n");
1360 free_pattern_brush_bits( pdev );
1361 return FALSE;
1364 while(size)
1366 calc_and_xor_masks(pdev->brush_rop, *brush_bits++, and_bits++, xor_bits++);
1367 size -= 4;
1370 return TRUE;
1373 static const DWORD hatches[6][8] =
1375 { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
1376 { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL */
1377 { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_FDIAGONAL */
1378 { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_BDIAGONAL */
1379 { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS */
1380 { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 } /* HS_DIAGCROSS */
1383 static BOOL create_hatch_brush_bits(dibdrv_physdev *pdev)
1385 dib_info hatch;
1386 rop_mask fg_mask, bg_mask;
1387 rop_mask_bits mask_bits;
1388 DWORD size;
1389 BOOL ret;
1391 assert(pdev->brush_and_bits == NULL);
1392 assert(pdev->brush_xor_bits == NULL);
1394 /* Just initialise brush_dib with the color / sizing info. We don't
1395 need the bits as we'll calculate the rop masks straight from
1396 the hatch patterns. */
1398 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1399 pdev->brush_dib.width = 8;
1400 pdev->brush_dib.height = 8;
1401 pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1403 size = pdev->brush_dib.height * pdev->brush_dib.stride;
1405 mask_bits.and = pdev->brush_and_bits = HeapAlloc(GetProcessHeap(), 0, size);
1406 mask_bits.xor = pdev->brush_xor_bits = HeapAlloc(GetProcessHeap(), 0, size);
1408 if(!mask_bits.and || !mask_bits.xor)
1410 ERR("Failed to create pattern brush bits\n");
1411 free_pattern_brush_bits( pdev );
1412 return FALSE;
1415 hatch.bit_count = 1;
1416 hatch.height = hatch.width = 8;
1417 hatch.stride = 4;
1418 hatch.bits.ptr = (void *) hatches[pdev->brush_hatch];
1419 hatch.bits.free = hatch.bits.param = NULL;
1420 hatch.bits.is_copy = FALSE;
1422 fg_mask.and = pdev->brush_and;
1423 fg_mask.xor = pdev->brush_xor;
1424 get_brush_bkgnd_masks( pdev, &bg_mask.and, &bg_mask.xor );
1426 ret = pdev->brush_dib.funcs->create_rop_masks( &pdev->brush_dib, &hatch, &fg_mask, &bg_mask, &mask_bits );
1427 if(!ret) free_pattern_brush_bits( pdev );
1429 return ret;
1432 static BOOL matching_pattern_format( dib_info *dib, dib_info *pattern )
1434 if (dib->bit_count != pattern->bit_count) return FALSE;
1435 if (dib->stride != pattern->stride) return FALSE;
1437 switch (dib->bit_count)
1439 case 1:
1440 case 4:
1441 case 8:
1442 if (dib->color_table_size != pattern->color_table_size) return FALSE;
1443 return !memcmp( dib->color_table, pattern->color_table, dib->color_table_size * sizeof(RGBQUAD) );
1444 case 16:
1445 case 32:
1446 return (dib->red_mask == pattern->red_mask &&
1447 dib->green_mask == pattern->green_mask &&
1448 dib->blue_mask == pattern->blue_mask);
1450 return TRUE;
1453 static BOOL select_pattern_brush( dibdrv_physdev *pdev, BOOL *needs_reselect )
1455 char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
1456 BITMAPINFO *info = (BITMAPINFO *)buffer;
1457 RGBQUAD color_table[2];
1458 RECT rect;
1459 dib_info pattern;
1461 if (!pdev->brush_pattern_info)
1463 BITMAPOBJ *bmp = GDI_GetObjPtr( pdev->brush_pattern_bitmap, OBJ_BITMAP );
1464 BOOL ret;
1466 if (!bmp) return FALSE;
1467 ret = init_dib_info_from_bitmapobj( &pattern, bmp, 0 );
1468 GDI_ReleaseObj( pdev->brush_pattern_bitmap );
1469 if (!ret) return FALSE;
1471 else if (pdev->brush_pattern_info->bmiHeader.biClrUsed && pdev->brush_pattern_usage == DIB_PAL_COLORS)
1473 copy_bitmapinfo( info, pdev->brush_pattern_info );
1474 fill_color_table_from_pal_colors( info, pdev->dev.hdc );
1475 init_dib_info_from_bitmapinfo( &pattern, info, pdev->brush_pattern_bits, 0 );
1476 *needs_reselect = TRUE;
1478 else
1480 init_dib_info_from_bitmapinfo( &pattern, pdev->brush_pattern_info, pdev->brush_pattern_bits, 0 );
1483 if (pattern.bit_count == 1 && !pattern.color_table)
1485 /* monochrome DDB pattern uses DC colors */
1486 COLORREF color = GetTextColor( pdev->dev.hdc );
1487 color_table[0].rgbRed = GetRValue( color );
1488 color_table[0].rgbGreen = GetGValue( color );
1489 color_table[0].rgbBlue = GetBValue( color );
1490 color_table[0].rgbReserved = 0;
1491 color = GetBkColor( pdev->dev.hdc );
1492 color_table[1].rgbRed = GetRValue( color );
1493 color_table[1].rgbGreen = GetGValue( color );
1494 color_table[1].rgbBlue = GetBValue( color );
1495 color_table[1].rgbReserved = 0;
1496 pattern.color_table = color_table;
1497 pattern.color_table_size = 2;
1498 *needs_reselect = TRUE;
1501 copy_dib_color_info(&pdev->brush_dib, &pdev->dib);
1503 pdev->brush_dib.height = pattern.height;
1504 pdev->brush_dib.width = pattern.width;
1505 pdev->brush_dib.stride = get_dib_stride( pdev->brush_dib.width, pdev->brush_dib.bit_count );
1507 if (matching_pattern_format( &pdev->brush_dib, &pattern ))
1509 pdev->brush_dib.bits.ptr = pattern.bits.ptr;
1510 pdev->brush_dib.bits.is_copy = FALSE;
1511 pdev->brush_dib.bits.free = NULL;
1513 else
1515 pdev->brush_dib.bits.ptr = HeapAlloc( GetProcessHeap(), 0,
1516 pdev->brush_dib.height * pdev->brush_dib.stride );
1517 pdev->brush_dib.bits.is_copy = TRUE;
1518 pdev->brush_dib.bits.free = free_heap_bits;
1520 rect.left = rect.top = 0;
1521 rect.right = pattern.width;
1522 rect.bottom = pattern.height;
1524 pdev->brush_dib.funcs->convert_to(&pdev->brush_dib, &pattern, &rect);
1526 return TRUE;
1529 /**********************************************************************
1530 * pattern_brush
1532 * Fill a number of rectangles with the pattern brush
1533 * FIXME: Should we insist l < r && t < b? Currently we assume this.
1535 static BOOL pattern_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1537 int i, j;
1538 const WINEREGION *clip;
1539 POINT origin;
1540 BOOL needs_reselect = FALSE;
1542 if(pdev->brush_and_bits == NULL)
1544 switch(pdev->brush_style)
1546 case BS_DIBPATTERN:
1547 if (!pdev->brush_dib.bits.ptr && !select_pattern_brush( pdev, &needs_reselect ))
1548 return FALSE;
1549 if(!create_pattern_brush_bits(pdev))
1550 return FALSE;
1551 break;
1553 case BS_HATCHED:
1554 if(!create_hatch_brush_bits(pdev))
1555 return FALSE;
1556 break;
1558 default:
1559 ERR("Unexpected brush style %d\n", pdev->brush_style);
1560 return FALSE;
1564 GetBrushOrgEx(pdev->dev.hdc, &origin);
1566 clip = get_wine_region( region );
1568 if (!clip)
1570 dib->funcs->pattern_rects( dib, num, rects, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1571 return TRUE;
1574 for(i = 0; i < num; i++)
1576 for(j = 0; j < clip->numRects; j++)
1578 RECT rect = rects[i];
1580 /* Optimize unclipped case */
1581 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
1582 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
1584 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1585 break;
1588 if(clip->rects[j].top >= rect.bottom) break;
1589 if(clip->rects[j].bottom <= rect.top) continue;
1591 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
1593 rect.left = max(rect.left, clip->rects[j].left);
1594 rect.top = max(rect.top, clip->rects[j].top);
1595 rect.right = min(rect.right, clip->rects[j].right);
1596 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
1598 dib->funcs->pattern_rects( dib, 1, &rect, &origin, &pdev->brush_dib, pdev->brush_and_bits, pdev->brush_xor_bits );
1602 release_wine_region( region );
1604 if (needs_reselect) free_pattern_brush( pdev );
1606 return TRUE;
1609 static BOOL null_brush(dibdrv_physdev *pdev, dib_info *dib, int num, const RECT *rects, HRGN region)
1611 return TRUE;
1614 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
1616 pdev->brush_rop = rop;
1617 if(pdev->brush_style == BS_SOLID || pdev->brush_style == BS_HATCHED)
1618 calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1619 free_pattern_brush_bits( pdev );
1622 /***********************************************************************
1623 * dibdrv_SelectBrush
1625 HBRUSH dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, HBITMAP bitmap,
1626 const BITMAPINFO *info, void *bits, UINT usage )
1628 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
1629 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1630 LOGBRUSH logbrush;
1632 TRACE("(%p, %p)\n", dev, hbrush);
1634 if (bitmap || info) /* pattern brush */
1636 pdev->brush_rects = pattern_brush;
1637 pdev->brush_style = BS_DIBPATTERN;
1638 pdev->brush_pattern_info = info;
1639 pdev->brush_pattern_bits = bits;
1640 pdev->brush_pattern_usage = usage;
1641 pdev->brush_pattern_bitmap = bitmap;
1642 free_pattern_brush( pdev ); /* brush is actually selected only when it's used */
1644 return next->funcs->pSelectBrush( next, hbrush, bitmap, info, bits, usage );
1647 if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
1649 if (hbrush == GetStockObject( DC_BRUSH ))
1650 logbrush.lbColor = GetDCBrushColor( dev->hdc );
1652 pdev->brush_style = logbrush.lbStyle;
1654 free_pattern_brush( pdev );
1656 switch(logbrush.lbStyle)
1658 case BS_SOLID:
1659 pdev->brush_colorref = logbrush.lbColor;
1660 pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1661 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1662 pdev->brush_rects = solid_brush;
1663 break;
1665 case BS_NULL:
1666 pdev->brush_rects = null_brush;
1667 break;
1669 case BS_HATCHED:
1670 if(logbrush.lbHatch > HS_DIAGCROSS) return 0;
1671 pdev->brush_hatch = logbrush.lbHatch;
1672 pdev->brush_colorref = logbrush.lbColor;
1673 pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1674 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1675 pdev->brush_rects = pattern_brush;
1676 break;
1678 default:
1679 return 0;
1682 return next->funcs->pSelectBrush( next, hbrush, bitmap, info, bits, usage );
1685 /***********************************************************************
1686 * dibdrv_SetDCBrushColor
1688 COLORREF dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
1690 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
1691 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
1693 if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
1695 pdev->brush_colorref = color;
1696 pdev->brush_color = get_pixel_color( pdev, pdev->brush_colorref, TRUE );
1697 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
1700 return next->funcs->pSetDCBrushColor( next, color );
1703 BOOL brush_rects(dibdrv_physdev *pdev, int num, const RECT *rects)
1705 return pdev->brush_rects( pdev, &pdev->dib, num, rects, pdev->clip );