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 "gdi_private.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(dib);
30 * Decompose the 16 ROP2s into an expression of the form
34 * Where A and X depend only on P (and so can be precomputed).
39 * R2_NOTMERGEPEN ~(D | P) ~P ~P
40 * R2_MASKNOTPEN ~P & D ~P 0
41 * R2_NOTCOPYPEN ~P 0 ~P
42 * R2_MASKPENNOT P & ~D P P
45 * R2_NOTMASKPEN ~(P & D) P 1
46 * R2_MASKPEN P & D P 0
47 * R2_NOTXORPEN ~(P ^ D) 1 ~P
49 * R2_MERGENOTPEN ~P | D P ~P
51 * R2_MERGEPENNOT P | ~D ~P 1
52 * R2_MERGEPEN P | D ~P P
57 /* A = (P & A1) | (~P & A2) */
59 #define ONE {0xffffffff, 0xffffffff}
60 #define P {0xffffffff, 0}
61 #define NOT_P {0, 0xffffffff}
63 static const DWORD rop2_and_array[16][2] =
65 ZERO, NOT_P, NOT_P, ZERO,
68 ZERO, NOT_P, NOT_P, ZERO
71 /* X = (P & X1) | (~P & X2) */
72 static const DWORD rop2_xor_array[16][2] =
74 ZERO, NOT_P, ZERO, NOT_P,
76 ZERO, NOT_P, ZERO, NOT_P,
85 void calc_and_xor_masks(INT rop, DWORD color, DWORD *and, DWORD *xor)
87 /* NB The ROP2 codes start at one and the arrays are zero-based */
88 *and = (color & rop2_and_array[rop-1][0]) | ((~color) & rop2_and_array[rop-1][1]);
89 *xor = (color & rop2_xor_array[rop-1][0]) | ((~color) & rop2_xor_array[rop-1][1]);
92 static inline void order_end_points(int *s, int *e)
103 static inline BOOL pt_in_rect( const RECT *rect, const POINT *pt )
105 return ((pt->x >= rect->left) && (pt->x < rect->right) &&
106 (pt->y >= rect->top) && (pt->y < rect->bottom));
109 /**********************************************************************
112 * Return the octant number starting clockwise from the +ve x-axis.
114 static inline int get_octant_number(int dx, int dy)
118 return ( dx > dy) ? 1 : 2;
120 return (-dx > dy) ? 4 : 3;
123 return (-dx > -dy) ? 5 : 6;
125 return ( dx > -dy) ? 8 : 7;
128 static inline DWORD get_octant_mask(int dx, int dy)
130 return 1 << (get_octant_number(dx, dy) - 1);
133 static void solid_pen_line_callback(INT x, INT y, LPARAM lparam)
135 dibdrv_physdev *pdev = (dibdrv_physdev *)lparam;
142 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
146 static void bres_line_with_bias(INT x1, INT y1, INT x2, INT y2, void (* callback)(INT,INT,LPARAM), LPARAM lParam)
148 INT xadd = 1, yadd = 1;
153 DWORD octant = get_octant_mask(dx, dy);
156 /* Octants 3, 5, 6 and 8 take a bias */
157 if(octant & 0xb4) bias = 1;
169 if (dx > dy) /* line is "more horizontal" */
171 err = 2*dy - dx; erradd = 2*dy - 2*dx;
172 for(cnt = 0; cnt < dx; cnt++)
174 callback(x1, y1, lParam);
184 else /* line is "more vertical" */
186 err = 2*dx - dy; erradd = 2*dx - 2*dy;
187 for(cnt = 0; cnt < dy; cnt++)
189 callback(x1, y1, lParam);
201 static BOOL solid_pen_line(dibdrv_physdev *pdev, POINT *start, POINT *end)
203 const WINEREGION *clip = get_wine_region(pdev->clip);
206 if(start->y == end->y)
211 rect.left = start->x;
214 rect.bottom = end->y + 1;
215 order_end_points(&rect.left, &rect.right);
216 for(i = 0; i < clip->numRects; i++)
218 if(clip->rects[i].top >= rect.bottom) break;
219 if(clip->rects[i].bottom <= rect.top) continue;
220 /* Optimize the unclipped case */
221 if(clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
223 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
226 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
229 tmp.left = max(rect.left, clip->rects[i].left);
230 tmp.right = min(rect.right, clip->rects[i].right);
231 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
235 else if(start->x == end->x)
240 rect.left = start->x;
242 rect.right = end->x + 1;
243 rect.bottom = end->y;
244 order_end_points(&rect.top, &rect.bottom);
245 for(i = 0; i < clip->numRects; i++)
247 /* Optimize unclipped case */
248 if(clip->rects[i].top <= rect.top && clip->rects[i].bottom >= rect.bottom &&
249 clip->rects[i].left <= rect.left && clip->rects[i].right >= rect.right)
251 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->pen_and, pdev->pen_xor);
254 if(clip->rects[i].top >= rect.bottom) break;
255 if(clip->rects[i].bottom <= rect.top) continue;
256 if(clip->rects[i].right > rect.left && clip->rects[i].left < rect.right)
259 tmp.top = max(rect.top, clip->rects[i].top);
260 tmp.bottom = min(rect.bottom, clip->rects[i].bottom);
261 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &tmp, pdev->pen_and, pdev->pen_xor);
267 if(clip->numRects == 1 && pt_in_rect(&clip->extents, start) && pt_in_rect(&clip->extents, end))
268 /* FIXME: Optimize by moving Bresenham algorithm to the primitive functions,
269 or at least cache adjacent points in the callback */
270 bres_line_with_bias(start->x, start->y, end->x, end->y, solid_pen_line_callback, (LPARAM)pdev);
271 else if(clip->numRects >= 1)
274 release_wine_region(pdev->clip);
278 /***********************************************************************
281 HPEN CDECL dibdrv_SelectPen( PHYSDEV dev, HPEN hpen )
283 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectPen );
284 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
287 TRACE("(%p, %p)\n", dev, hpen);
289 if (!GetObjectW( hpen, sizeof(logpen), &logpen ))
291 /* must be an extended pen */
293 INT size = GetObjectW( hpen, 0, NULL );
297 elp = HeapAlloc( GetProcessHeap(), 0, size );
299 GetObjectW( hpen, size, elp );
300 /* FIXME: add support for user style pens */
301 logpen.lopnStyle = elp->elpPenStyle;
302 logpen.lopnWidth.x = elp->elpWidth;
303 logpen.lopnWidth.y = 0;
304 logpen.lopnColor = elp->elpColor;
306 HeapFree( GetProcessHeap(), 0, elp );
309 if (hpen == GetStockObject( DC_PEN ))
310 logpen.lopnColor = GetDCPenColor( dev->hdc );
312 pdev->pen_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, logpen.lopnColor);
313 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
315 pdev->defer |= DEFER_PEN;
317 switch(logpen.lopnStyle & PS_STYLE_MASK)
320 if(logpen.lopnStyle & PS_GEOMETRIC) break;
321 if(logpen.lopnWidth.x > 1) break;
322 pdev->pen_line = solid_pen_line;
323 pdev->defer &= ~DEFER_PEN;
329 return next->funcs->pSelectPen( next, hpen );
332 /***********************************************************************
333 * dibdrv_SetDCPenColor
335 COLORREF CDECL dibdrv_SetDCPenColor( PHYSDEV dev, COLORREF color )
337 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCPenColor );
338 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
340 if (GetCurrentObject(dev->hdc, OBJ_PEN) == GetStockObject( DC_PEN ))
342 pdev->pen_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, color);
343 calc_and_xor_masks(GetROP2(dev->hdc), pdev->pen_color, &pdev->pen_and, &pdev->pen_xor);
346 return next->funcs->pSetDCPenColor( next, color );
349 /**********************************************************************
352 * Fill a number of rectangles with the solid brush
353 * FIXME: Should we insist l < r && t < b? Currently we assume this.
355 static BOOL solid_brush(dibdrv_physdev *pdev, int num, RECT *rects)
358 const WINEREGION *clip = get_wine_region(pdev->clip);
360 for(i = 0; i < num; i++)
362 for(j = 0; j < clip->numRects; j++)
364 RECT rect = rects[i];
366 /* Optimize unclipped case */
367 if(clip->rects[j].top <= rect.top && clip->rects[j].bottom >= rect.bottom &&
368 clip->rects[j].left <= rect.left && clip->rects[j].right >= rect.right)
370 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
374 if(clip->rects[j].top >= rect.bottom) break;
375 if(clip->rects[j].bottom <= rect.top) continue;
377 if(clip->rects[j].right > rect.left && clip->rects[j].left < rect.right)
379 rect.left = max(rect.left, clip->rects[j].left);
380 rect.top = max(rect.top, clip->rects[j].top);
381 rect.right = min(rect.right, clip->rects[j].right);
382 rect.bottom = min(rect.bottom, clip->rects[j].bottom);
384 pdev->dib.funcs->solid_rects(&pdev->dib, 1, &rect, pdev->brush_and, pdev->brush_xor);
388 release_wine_region(pdev->clip);
392 void update_brush_rop( dibdrv_physdev *pdev, INT rop )
394 if(pdev->brush_style == BS_SOLID)
395 calc_and_xor_masks(rop, pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
398 /***********************************************************************
401 HBRUSH CDECL dibdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush )
403 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectBrush );
404 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
407 TRACE("(%p, %p)\n", dev, hbrush);
409 if (!GetObjectW( hbrush, sizeof(logbrush), &logbrush )) return 0;
411 if (hbrush == GetStockObject( DC_BRUSH ))
412 logbrush.lbColor = GetDCBrushColor( dev->hdc );
414 pdev->brush_style = logbrush.lbStyle;
416 pdev->defer |= DEFER_BRUSH;
418 switch(logbrush.lbStyle)
421 pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, logbrush.lbColor);
422 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
423 pdev->brush_rects = solid_brush;
424 pdev->defer &= ~DEFER_BRUSH;
430 return next->funcs->pSelectBrush( next, hbrush );
433 /***********************************************************************
434 * dibdrv_SetDCBrushColor
436 COLORREF CDECL dibdrv_SetDCBrushColor( PHYSDEV dev, COLORREF color )
438 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSetDCBrushColor );
439 dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
441 if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
443 pdev->brush_color = pdev->dib.funcs->colorref_to_pixel(&pdev->dib, color);
444 calc_and_xor_masks(GetROP2(dev->hdc), pdev->brush_color, &pdev->brush_and, &pdev->brush_xor);
447 return next->funcs->pSetDCBrushColor( next, color );