gdi32: Add helpers to paint a region with the brush or pen.
[wine] / dlls / gdi32 / dibdrv / graphics.c
1 /*
2  * DIB driver graphics operations.
3  *
4  * Copyright 2011 Huw Davies
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <assert.h>
22 #include "gdi_private.h"
23 #include "dibdrv.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(dib);
28
29 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom, BOOL rtl_correction )
30 {
31     RECT rect;
32
33     rect.left   = left;
34     rect.top    = top;
35     rect.right  = right;
36     rect.bottom = bottom;
37     if (rtl_correction && GetLayout( hdc ) & LAYOUT_RTL)
38     {
39         /* shift the rectangle so that the right border is included after mirroring */
40         /* it would be more correct to do this after LPtoDP but that's not what Windows does */
41         rect.left--;
42         rect.right--;
43     }
44     LPtoDP( hdc, (POINT *)&rect, 2 );
45     if (rect.left > rect.right)
46     {
47         int tmp = rect.left;
48         rect.left = rect.right;
49         rect.right = tmp;
50     }
51     if (rect.top > rect.bottom)
52     {
53         int tmp = rect.top;
54         rect.top = rect.bottom;
55         rect.bottom = tmp;
56     }
57     return rect;
58 }
59
60 /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a
61    black bkgnd.  [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */
62 static const BYTE ramp[17] =
63 {
64     0,    0x4d, 0x68, 0x7c,
65     0x8c, 0x9a, 0xa7, 0xb2,
66     0xbd, 0xc7, 0xd0, 0xd9,
67     0xe1, 0xe9, 0xf0, 0xf8,
68     0xff
69 };
70
71 /* For a give text-color component and a glyph level, calculate the
72    range of dst intensities, the min/max corresponding to 0/0xff bkgnd
73    components respectively.
74
75    The minimum is a linear interpolation between 0 and the value in
76    the ramp table.
77
78    The maximum is a linear interpolation between the value from the
79    ramp table read in reverse and 0xff.
80
81    To find the resulting pixel intensity, we note that if the text and
82    bkgnd intensities are the same then the result must be that
83    intensity.  Otherwise we linearly interpolate between either the
84    min or the max value and this intermediate value depending on which
85    side of the inequality we lie.
86 */
87
88 static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp )
89 {
90     *min_comp = (ramp[aa] * text_comp) / 0xff;
91     *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff;
92 }
93
94 static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] )
95 {
96     int i;
97
98     for (i = 0; i < 17; i++)
99     {
100         get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max );
101         get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max );
102         get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max );
103     }
104 }
105
106 /**********************************************************************
107  *                 get_text_bkgnd_masks
108  *
109  * See the comment above get_pen_bkgnd_masks
110  */
111 static inline void get_text_bkgnd_masks( dibdrv_physdev *pdev, rop_mask *mask )
112 {
113     COLORREF bg = GetBkColor( pdev->dev.hdc );
114
115     mask->and = 0;
116
117     if (pdev->dib.bit_count != 1)
118         mask->xor = get_pixel_color( pdev, bg, FALSE );
119     else
120     {
121         COLORREF fg = GetTextColor( pdev->dev.hdc );
122         mask->xor = get_pixel_color( pdev, fg, TRUE );
123         if (fg != bg) mask->xor = ~mask->xor;
124     }
125 }
126
127 static void draw_glyph( dibdrv_physdev *pdev, const POINT *origin, const GLYPHMETRICS *metrics,
128                         const struct gdi_image_bits *image, DWORD text_color,
129                         const struct intensity_range *ranges, const struct clipped_rects *clipped_rects )
130 {
131     int i;
132     RECT rect, clipped_rect;
133     POINT src_origin;
134     dib_info glyph_dib;
135
136     glyph_dib.bit_count = 8;
137     glyph_dib.width     = metrics->gmBlackBoxX;
138     glyph_dib.height    = metrics->gmBlackBoxY;
139     glyph_dib.stride    = get_dib_stride( metrics->gmBlackBoxX, 8 );
140     glyph_dib.bits      = *image;
141
142     rect.left   = origin->x  + metrics->gmptGlyphOrigin.x;
143     rect.top    = origin->y  - metrics->gmptGlyphOrigin.y;
144     rect.right  = rect.left  + metrics->gmBlackBoxX;
145     rect.bottom = rect.top   + metrics->gmBlackBoxY;
146
147     for (i = 0; i < clipped_rects->count; i++)
148     {
149         if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i ))
150         {
151             src_origin.x = clipped_rect.left - rect.left;
152             src_origin.y = clipped_rect.top  - rect.top;
153
154             pdev->dib.funcs->draw_glyph( &pdev->dib, &clipped_rect, &glyph_dib, &src_origin,
155                                          text_color, ranges );
156         }
157     }
158 }
159
160 static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
161 static const int padding[4] = {0, 3, 2, 1};
162
163 /***********************************************************************
164  *         get_glyph_bitmap
165  *
166  * Retrieve a 17-level bitmap for the appropiate glyph.
167  *
168  * For non-antialiased bitmaps convert them to the 17-level format
169  * using only values 0 or 16.
170  */
171 static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
172                                struct gdi_image_bits *image )
173 {
174     UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
175     static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
176     UINT indices[3] = {0, 0, 0x20};
177     int i, x, y;
178     DWORD ret, size;
179     BYTE *buf, *dst, *src;
180     int pad, stride;
181
182     image->ptr = NULL;
183     image->is_copy = FALSE;
184     image->free = free_heap_bits;
185     image->param = NULL;
186
187     indices[0] = index;
188
189     for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
190     {
191         index = indices[i];
192         ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
193         if (ret != GDI_ERROR) break;
194     }
195
196     if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
197     if (!ret) return ERROR_SUCCESS; /* empty glyph */
198
199     /* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
200     pad = padding[ metrics->gmBlackBoxX % 4 ];
201     stride = get_dib_stride( metrics->gmBlackBoxX, 8 );
202     size = metrics->gmBlackBoxY * stride;
203
204     buf = HeapAlloc( GetProcessHeap(), 0, size );
205     if (!buf) return ERROR_OUTOFMEMORY;
206
207     ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
208     if (ret == GDI_ERROR)
209     {
210         HeapFree( GetProcessHeap(), 0, buf );
211         return ERROR_NOT_FOUND;
212     }
213
214     if (aa_flags == GGO_BITMAP)
215     {
216         for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
217         {
218             src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
219             dst = buf + y * stride;
220
221             if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
222
223             for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
224                 dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
225         }
226     }
227     else if (pad)
228     {
229         for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += stride)
230             memset( dst + metrics->gmBlackBoxX, 0, pad );
231     }
232
233     image->ptr = buf;
234     return ERROR_SUCCESS;
235 }
236
237 BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
238                                 struct bitblt_coords *src, INT x, INT y, UINT flags,
239                                 UINT aa_flags, LPCWSTR str, UINT count, const INT *dx )
240 {
241     dib_info dib;
242     UINT i;
243     DWORD err;
244     BOOL got_pixel;
245     COLORREF fg, bg;
246     DWORD fg_pixel, bg_pixel;
247     struct intensity_range glyph_intensities[17];
248
249     assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
250
251     init_dib_info_from_bitmapinfo( &dib, info, bits->ptr, 0 );
252
253     fg = make_rgb_colorref( hdc, &dib, GetTextColor( hdc ), &got_pixel, &fg_pixel);
254     if (!got_pixel) fg_pixel = dib.funcs->colorref_to_pixel( &dib, fg );
255
256     get_aa_ranges( fg, glyph_intensities );
257
258     if (flags & ETO_OPAQUE)
259     {
260         rop_mask bkgnd_color;
261
262         bg = make_rgb_colorref( hdc, &dib, GetBkColor( hdc ), &got_pixel, &bg_pixel);
263         if (!got_pixel) bg_pixel = dib.funcs->colorref_to_pixel( &dib, bg );
264
265         bkgnd_color.and = 0;
266         bkgnd_color.xor = bg_pixel;
267         dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
268     }
269
270     for (i = 0; i < count; i++)
271     {
272         GLYPHMETRICS metrics;
273         struct gdi_image_bits image;
274
275         err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &image );
276         if (err) continue;
277
278         if (image.ptr)
279         {
280             RECT rect, clipped_rect;
281             POINT src_origin;
282             dib_info glyph_dib;
283
284             glyph_dib.bit_count = 8;
285             glyph_dib.width     = metrics.gmBlackBoxX;
286             glyph_dib.height    = metrics.gmBlackBoxY;
287             glyph_dib.stride    = get_dib_stride( metrics.gmBlackBoxX, 8 );
288             glyph_dib.bits      = image;
289
290             rect.left   = x + metrics.gmptGlyphOrigin.x;
291             rect.top    = y - metrics.gmptGlyphOrigin.y;
292             rect.right  = rect.left + metrics.gmBlackBoxX;
293             rect.bottom = rect.top  + metrics.gmBlackBoxY;
294
295             if (intersect_rect( &clipped_rect, &rect, &src->visrect ))
296             {
297                 src_origin.x = clipped_rect.left - rect.left;
298                 src_origin.y = clipped_rect.top  - rect.top;
299
300                 dib.funcs->draw_glyph( &dib, &clipped_rect, &glyph_dib, &src_origin,
301                                        fg_pixel, glyph_intensities );
302             }
303         }
304         if (image.free) image.free( &image );
305
306         if (dx)
307         {
308             if (flags & ETO_PDY)
309             {
310                 x += dx[ i * 2 ];
311                 y += dx[ i * 2 + 1];
312             }
313             else
314                 x += dx[ i ];
315         }
316         else
317         {
318             x += metrics.gmCellIncX;
319             y += metrics.gmCellIncY;
320         }
321     }
322     return TRUE;
323 }
324
325 /***********************************************************************
326  *           dibdrv_ExtTextOut
327  */
328 BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
329                         const RECT *rect, LPCWSTR str, UINT count, const INT *dx )
330 {
331     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
332     struct clipped_rects clipped_rects;
333     UINT aa_flags, i;
334     POINT origin;
335     DWORD text_color, err;
336     struct intensity_range ranges[17];
337
338     init_clipped_rects( &clipped_rects );
339
340     if (flags & ETO_OPAQUE)
341     {
342         rop_mask bkgnd_color;
343         get_text_bkgnd_masks( pdev, &bkgnd_color );
344         get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
345         pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects,
346                                       bkgnd_color.and, bkgnd_color.xor );
347     }
348
349     if (count == 0) goto done;
350
351     if (flags & ETO_CLIPPED)
352     {
353         if (!(flags & ETO_OPAQUE))  /* otherwise we have done it already */
354             get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects );
355     }
356     else
357     {
358         free_clipped_rects( &clipped_rects );
359         get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects );
360     }
361     if (!clipped_rects.count) return TRUE;
362
363     text_color = get_pixel_color( pdev, GetTextColor( pdev->dev.hdc ), TRUE );
364     get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
365
366     aa_flags = get_font_aa_flags( dev->hdc );
367     origin.x = x;
368     origin.y = y;
369     for (i = 0; i < count; i++)
370     {
371         GLYPHMETRICS metrics;
372         struct gdi_image_bits image;
373
374         err = get_glyph_bitmap( dev->hdc, (UINT)str[i], aa_flags, &metrics, &image );
375         if (err) continue;
376
377         if (image.ptr) draw_glyph( pdev, &origin, &metrics, &image, text_color, ranges, &clipped_rects );
378         if (image.free) image.free( &image );
379
380         if (dx)
381         {
382             if (flags & ETO_PDY)
383             {
384                 origin.x += dx[ i * 2 ];
385                 origin.y += dx[ i * 2 + 1];
386             }
387             else
388                 origin.x += dx[ i ];
389         }
390         else
391         {
392             origin.x += metrics.gmCellIncX;
393             origin.y += metrics.gmCellIncY;
394         }
395     }
396
397 done:
398     free_clipped_rects( &clipped_rects );
399     return TRUE;
400 }
401
402 /***********************************************************************
403  *           dibdrv_GetNearestColor
404  */
405 COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color )
406 {
407     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
408     DWORD pixel;
409
410     TRACE( "(%p, %08x)\n", dev, color );
411
412     pixel = get_pixel_color( pdev, color, FALSE );
413     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
414 }
415
416 /***********************************************************************
417  *           dibdrv_GetPixel
418  */
419 COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y )
420 {
421     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
422     POINT pt;
423     DWORD pixel;
424
425     TRACE( "(%p, %d, %d)\n", dev, x, y );
426
427     pt.x = x;
428     pt.y = y;
429     LPtoDP( dev->hdc, &pt, 1 );
430
431     if (pt.x < 0 || pt.x >= pdev->dib.width ||
432         pt.y < 0 || pt.y >= pdev->dib.height)
433         return CLR_INVALID;
434
435     pixel = pdev->dib.funcs->get_pixel( &pdev->dib, &pt );
436     return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
437 }
438
439 /***********************************************************************
440  *           dibdrv_LineTo
441  */
442 BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
443 {
444     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pLineTo );
445     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
446     POINT pts[2];
447     HRGN region = 0;
448     BOOL ret;
449
450     if(defer_pen(pdev)) return next->funcs->pLineTo( next, x, y );
451
452     GetCurrentPositionEx(dev->hdc, pts);
453     pts[1].x = x;
454     pts[1].y = y;
455
456     LPtoDP(dev->hdc, pts, 2);
457
458     if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
459
460     reset_dash_origin(pdev);
461
462     ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
463
464     if (region)
465     {
466         if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND );
467         ret = pen_region( pdev, region );
468         DeleteObject( region );
469     }
470     return ret;
471 }
472
473 /***********************************************************************
474  *           get_rop2_from_rop
475  *
476  * Returns the binary rop that is equivalent to the provided ternary rop
477  * if the src bits are ignored.
478  */
479 static inline INT get_rop2_from_rop(INT rop)
480 {
481     return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1;
482 }
483
484 /***********************************************************************
485  *           dibdrv_PatBlt
486  */
487 BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop )
488 {
489     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
490
491     TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop);
492
493     return brush_rect( pdev, &dst->visrect, pdev->clip, get_rop2_from_rop(rop) );
494 }
495
496 /***********************************************************************
497  *           dibdrv_PaintRgn
498  */
499 BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn )
500 {
501     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
502     const WINEREGION *region;
503     int i;
504     RECT rect;
505
506     TRACE("%p, %p\n", dev, rgn);
507
508     region = get_wine_region( rgn );
509     if(!region) return FALSE;
510
511     for(i = 0; i < region->numRects; i++)
512     {
513         rect = get_device_rect( dev->hdc, region->rects[i].left, region->rects[i].top,
514                                 region->rects[i].right, region->rects[i].bottom, FALSE );
515         brush_rect( pdev, &rect, pdev->clip, GetROP2( dev->hdc ) );
516     }
517
518     release_wine_region( rgn );
519     return TRUE;
520 }
521
522 /***********************************************************************
523  *           dibdrv_PolyPolygon
524  */
525 BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons )
526 {
527     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
528     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPolyPolygon );
529     DWORD total, i, pos;
530     BOOL ret = FALSE;
531     POINT *points;
532     HRGN outline = 0, interior;
533
534     if (defer_pen( pdev )) return next->funcs->pPolyPolygon( next, pt, counts, polygons );
535
536     for (i = total = 0; i < polygons; i++)
537     {
538         if (counts[i] < 2) return FALSE;
539         total += counts[i];
540     }
541
542     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
543     if (!points) return FALSE;
544     memcpy( points, pt, total * sizeof(*pt) );
545     LPtoDP( dev->hdc, points, total );
546
547     if (!(interior = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ))))
548     {
549         HeapFree( GetProcessHeap(), 0, points );
550         return FALSE;
551     }
552
553     /* if not using a region, paint the interior first so the outline can overlap it */
554     if (!pdev->pen_uses_region || !(outline = CreateRectRgn( 0, 0, 0, 0 )))
555         ret = brush_region( pdev, interior );
556
557     for (i = pos = 0; i < polygons; i++)
558     {
559         reset_dash_origin( pdev );
560         pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline );
561         pos += counts[i];
562     }
563
564     if (outline)
565     {
566         CombineRgn( interior, interior, outline, RGN_DIFF );
567         ret = pen_region( pdev, outline ) && brush_region( pdev, interior );
568         DeleteObject( outline );
569     }
570
571     DeleteObject( interior );
572     HeapFree( GetProcessHeap(), 0, points );
573     return ret;
574 }
575
576 /***********************************************************************
577  *           dibdrv_PolyPolyline
578  */
579 BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
580 {
581     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
582     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pPolyPolyline );
583     DWORD max_points = 0, i;
584     POINT *points;
585     BOOL ret = TRUE;
586     HRGN outline = 0;
587
588     if (defer_pen( pdev )) return next->funcs->pPolyPolyline( next, pt, counts, polylines );
589
590     for (i = 0; i < polylines; i++)
591     {
592         if (counts[i] < 2) return FALSE;
593         max_points = max( counts[i], max_points );
594     }
595
596     points = HeapAlloc( GetProcessHeap(), 0, max_points * sizeof(*pt) );
597     if (!points) return FALSE;
598
599     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 )))
600     {
601         HeapFree( GetProcessHeap(), 0, points );
602         return FALSE;
603     }
604
605     for (i = 0; i < polylines; i++)
606     {
607         memcpy( points, pt, counts[i] * sizeof(*pt) );
608         pt += counts[i];
609         LPtoDP( dev->hdc, points, counts[i] );
610
611         reset_dash_origin( pdev );
612         pdev->pen_lines( pdev, counts[i], points, FALSE, outline );
613     }
614
615     if (outline)
616     {
617         if (pdev->clip) CombineRgn( outline, outline, pdev->clip, RGN_AND );
618         ret = pen_region( pdev, outline );
619         DeleteObject( outline );
620     }
621
622     HeapFree( GetProcessHeap(), 0, points );
623     return ret;
624 }
625
626 /***********************************************************************
627  *           dibdrv_Polygon
628  */
629 BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count )
630 {
631     INT counts[1] = { count };
632
633     return dibdrv_PolyPolygon( dev, pt, counts, 1 );
634 }
635
636 /***********************************************************************
637  *           dibdrv_Polyline
638  */
639 BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count )
640 {
641     DWORD counts[1] = { count };
642
643     if (count < 0) return FALSE;
644     return dibdrv_PolyPolyline( dev, pt, counts, 1 );
645 }
646
647 /***********************************************************************
648  *           dibdrv_Rectangle
649  */
650 BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
651 {
652     PHYSDEV next = GET_NEXT_PHYSDEV( dev, pRectangle );
653     dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
654     RECT rect = get_device_rect( dev->hdc, left, top, right, bottom, TRUE );
655     POINT pts[4];
656     BOOL ret;
657     HRGN outline = 0;
658
659     TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom);
660
661     if(rect.left == rect.right || rect.top == rect.bottom) return TRUE;
662
663     if(defer_pen(pdev))
664         return next->funcs->pRectangle( next, left, top, right, bottom );
665
666     if (pdev->pen_style == PS_INSIDEFRAME)
667     {
668         rect.left   += pdev->pen_width / 2;
669         rect.top    += pdev->pen_width / 2;
670         rect.right  -= (pdev->pen_width - 1) / 2;
671         rect.bottom -= (pdev->pen_width - 1) / 2;
672     }
673
674     if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
675
676     rect.right--;
677     rect.bottom--;
678     reset_dash_origin(pdev);
679
680     /* 4 pts going anti-clockwise starting from top-right */
681     pts[0].x = pts[3].x = rect.right;
682     pts[0].y = pts[1].y = rect.top;
683     pts[1].x = pts[2].x = rect.left;
684     pts[2].y = pts[3].y = rect.bottom;
685
686     pdev->pen_lines(pdev, 4, pts, TRUE, outline);
687
688     if (outline)
689     {
690         HRGN interior = CreateRectRgnIndirect( &rect );
691
692         CombineRgn( interior, interior, outline, RGN_DIFF );
693         ret = pen_region( pdev, outline ) && brush_region( pdev, interior );
694         DeleteObject( outline );
695         DeleteObject( interior );
696     }
697     else
698     {
699         rect.left   += (pdev->pen_width + 1) / 2;
700         rect.top    += (pdev->pen_width + 1) / 2;
701         rect.right  -= pdev->pen_width / 2;
702         rect.bottom -= pdev->pen_width / 2;
703         ret = brush_rect( pdev, &rect, pdev->clip, GetROP2(dev->hdc) );
704     }
705     return ret;
706 }
707
708 /***********************************************************************
709  *           dibdrv_SetPixel
710  */
711 COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
712 {
713     dibdrv_physdev *pdev = get_dibdrv_pdev( dev );
714     struct clipped_rects clipped_rects;
715     RECT rect;
716     POINT pt;
717     DWORD pixel;
718
719     TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color );
720
721     pt.x = x;
722     pt.y = y;
723     LPtoDP( dev->hdc, &pt, 1 );
724     rect.left = pt.x;
725     rect.top =  pt.y;
726     rect.right = rect.left + 1;
727     rect.bottom = rect.top + 1;
728
729     /* SetPixel doesn't do the 1bpp massaging like other fg colors */
730     pixel = get_pixel_color( pdev, color, FALSE );
731     color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel );
732
733     if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color;
734     pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel );
735     free_clipped_rects( &clipped_rects );
736     return color;
737 }