| /* |
| * DIB driver graphics operations. |
| * |
| * Copyright 2011 Huw Davies |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <assert.h> |
| #include "gdi_private.h" |
| #include "dibdrv.h" |
| |
| #include "wine/unicode.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(dib); |
| |
| struct cached_glyph |
| { |
| GLYPHMETRICS metrics; |
| BYTE bits[1]; |
| }; |
| |
| enum glyph_type |
| { |
| GLYPH_INDEX, |
| GLYPH_WCHAR, |
| GLYPH_NBTYPES |
| }; |
| |
| #define GLYPH_CACHE_PAGE_SIZE 0x100 |
| #define GLYPH_CACHE_PAGES (0x10000 / GLYPH_CACHE_PAGE_SIZE) |
| |
| struct cached_font |
| { |
| struct list entry; |
| LONG ref; |
| DWORD hash; |
| LOGFONTW lf; |
| XFORM xform; |
| UINT aa_flags; |
| struct cached_glyph **glyphs[GLYPH_NBTYPES][GLYPH_CACHE_PAGES]; |
| }; |
| |
| static struct list font_cache = LIST_INIT( font_cache ); |
| |
| static CRITICAL_SECTION font_cache_cs; |
| static CRITICAL_SECTION_DEBUG critsect_debug = |
| { |
| 0, 0, &font_cache_cs, |
| { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_cs") } |
| }; |
| static CRITICAL_SECTION font_cache_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; |
| |
| |
| static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip ) |
| { |
| DC *dc = get_physdev_dc( &pdev->dev ); |
| struct clipped_rects clipped_rects; |
| BOOL ret; |
| |
| if (!get_clipped_rects( &pdev->dib, rect, clip, &clipped_rects )) return TRUE; |
| ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, |
| dc->ROPmode ); |
| free_clipped_rects( &clipped_rects ); |
| return ret; |
| } |
| |
| /* paint a region with the brush (note: the region can be modified) */ |
| static BOOL brush_region( dibdrv_physdev *pdev, HRGN region ) |
| { |
| if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND ); |
| return brush_rect( pdev, &pdev->brush, NULL, region ); |
| } |
| |
| /* paint a region with the pen (note: the region can be modified) */ |
| static BOOL pen_region( dibdrv_physdev *pdev, HRGN region ) |
| { |
| if (pdev->clip) CombineRgn( region, region, pdev->clip, RGN_AND ); |
| return brush_rect( pdev, &pdev->pen_brush, NULL, region ); |
| } |
| |
| static RECT get_device_rect( DC *dc, int left, int top, int right, int bottom, BOOL rtl_correction ) |
| { |
| RECT rect; |
| |
| rect.left = left; |
| rect.top = top; |
| rect.right = right; |
| rect.bottom = bottom; |
| if (rtl_correction && dc->layout & LAYOUT_RTL) |
| { |
| /* shift the rectangle so that the right border is included after mirroring */ |
| /* it would be more correct to do this after LPtoDP but that's not what Windows does */ |
| rect.left--; |
| rect.right--; |
| } |
| lp_to_dp( dc, (POINT *)&rect, 2 ); |
| order_rect( &rect ); |
| return rect; |
| } |
| |
| static BOOL get_pen_device_rect( DC *dc, dibdrv_physdev *dev, RECT *rect, |
| int left, int top, int right, int bottom ) |
| { |
| *rect = get_device_rect( dc, left, top, right, bottom, TRUE ); |
| if (rect->left == rect->right || rect->top == rect->bottom) return FALSE; |
| |
| if (dev->pen_style == PS_INSIDEFRAME) |
| { |
| rect->left += dev->pen_width / 2; |
| rect->top += dev->pen_width / 2; |
| rect->right -= (dev->pen_width - 1) / 2; |
| rect->bottom -= (dev->pen_width - 1) / 2; |
| } |
| return TRUE; |
| } |
| |
| static void add_pen_lines_bounds( dibdrv_physdev *dev, int count, const POINT *points, HRGN rgn ) |
| { |
| const WINEREGION *region; |
| RECT bounds, rect; |
| int width = 0; |
| |
| if (!dev->bounds) return; |
| reset_bounds( &bounds ); |
| |
| if (dev->pen_uses_region) |
| { |
| /* Windows uses some heuristics to estimate the distance from the point that will be painted */ |
| width = dev->pen_width + 2; |
| if (dev->pen_join == PS_JOIN_MITER) |
| { |
| width *= 5; |
| if (dev->pen_endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2; |
| } |
| else |
| { |
| if (dev->pen_endcap == PS_ENDCAP_SQUARE) width -= width / 4; |
| else width = (width + 1) / 2; |
| } |
| |
| /* in case the heuristics are wrong, add the actual region too */ |
| if ((region = get_wine_region( rgn ))) |
| { |
| add_bounds_rect( &bounds, ®ion->extents ); |
| release_wine_region( rgn ); |
| } |
| } |
| |
| while (count-- > 0) |
| { |
| rect.left = points->x - width; |
| rect.top = points->y - width; |
| rect.right = points->x + width + 1; |
| rect.bottom = points->y + width + 1; |
| add_bounds_rect( &bounds, &rect ); |
| points++; |
| } |
| |
| add_clipped_bounds( dev, &bounds, dev->clip ); |
| } |
| |
| /* compute the points for the first quadrant of an ellipse, counterclockwise from the x axis */ |
| /* 'data' must contain enough space, (width+height)/2 is a reasonable upper bound */ |
| static int ellipse_first_quadrant( int width, int height, POINT *data ) |
| { |
| const int a = width - 1; |
| const int b = height - 1; |
| const INT64 asq = (INT64)8 * a * a; |
| const INT64 bsq = (INT64)8 * b * b; |
| INT64 dx = (INT64)4 * b * b * (1 - a); |
| INT64 dy = (INT64)4 * a * a * (1 + (b % 2)); |
| INT64 err = dx + dy + a * a * (b % 2); |
| int pos = 0; |
| POINT pt; |
| |
| pt.x = a; |
| pt.y = height / 2; |
| |
| /* based on an algorithm by Alois Zingl */ |
| |
| while (pt.x >= width / 2) |
| { |
| INT64 e2 = 2 * err; |
| data[pos++] = pt; |
| if (e2 >= dx) |
| { |
| pt.x--; |
| err += dx += bsq; |
| } |
| if (e2 <= dy) |
| { |
| pt.y++; |
| err += dy += asq; |
| } |
| } |
| return pos; |
| } |
| |
| static int find_intersection( const POINT *points, int x, int y, int count ) |
| { |
| int i; |
| |
| if (y >= 0) |
| { |
| if (x >= 0) /* first quadrant */ |
| { |
| for (i = 0; i < count; i++) if (points[i].x * y <= points[i].y * x) break; |
| return i; |
| } |
| /* second quadrant */ |
| for (i = 0; i < count; i++) if (points[i].x * y < points[i].y * -x) break; |
| return 2 * count - i; |
| } |
| if (x >= 0) /* fourth quadrant */ |
| { |
| for (i = 0; i < count; i++) if (points[i].x * -y <= points[i].y * x) break; |
| return 4 * count - i; |
| } |
| /* third quadrant */ |
| for (i = 0; i < count; i++) if (points[i].x * -y < points[i].y * -x) break; |
| return 2 * count + i; |
| } |
| |
| static int get_arc_points( int arc_dir, const RECT *rect, POINT start, POINT end, POINT *points ) |
| { |
| int i, pos, count, start_pos, end_pos; |
| int width = rect->right - rect->left; |
| int height = rect->bottom - rect->top; |
| |
| count = ellipse_first_quadrant( width, height, points ); |
| for (i = 0; i < count; i++) |
| { |
| points[i].x -= width / 2; |
| points[i].y -= height / 2; |
| } |
| if (arc_dir != AD_CLOCKWISE) |
| { |
| start.y = -start.y; |
| end.y = -end.y; |
| } |
| start_pos = find_intersection( points, start.x, start.y, count ); |
| end_pos = find_intersection( points, end.x, end.y, count ); |
| if (end_pos <= start_pos) end_pos += 4 * count; |
| |
| pos = count; |
| if (arc_dir == AD_CLOCKWISE) |
| { |
| for (i = start_pos; i < end_pos; i++, pos++) |
| { |
| switch ((i / count) % 4) |
| { |
| case 0: |
| points[pos].x = rect->left + width/2 + points[i % count].x; |
| points[pos].y = rect->top + height/2 + points[i % count].y; |
| break; |
| case 1: |
| points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x; |
| points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y; |
| break; |
| case 2: |
| points[pos].x = rect->right-1 - width/2 - points[i % count].x; |
| points[pos].y = rect->bottom-1 - height/2 - points[i % count].y; |
| break; |
| case 3: |
| points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x; |
| points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y; |
| break; |
| } |
| } |
| } |
| else |
| { |
| for (i = start_pos; i < end_pos; i++, pos++) |
| { |
| switch ((i / count) % 4) |
| { |
| case 0: |
| points[pos].x = rect->left + width/2 + points[i % count].x; |
| points[pos].y = rect->bottom-1 - height/2 - points[i % count].y; |
| break; |
| case 1: |
| points[pos].x = rect->right-1 - width/2 - points[count - 1 - i % count].x; |
| points[pos].y = rect->bottom-1 - height/2 - points[count - 1 - i % count].y; |
| break; |
| case 2: |
| points[pos].x = rect->right-1 - width/2 - points[i % count].x; |
| points[pos].y = rect->top + height/2 + points[i % count].y; |
| break; |
| case 3: |
| points[pos].x = rect->left + width/2 + points[count - 1 - i % count].x; |
| points[pos].y = rect->top + height/2 + points[count - 1 - i % count].y; |
| break; |
| } |
| } |
| } |
| |
| memmove( points, points + count, (pos - count) * sizeof(POINT) ); |
| return pos - count; |
| } |
| |
| /* backend for arc functions; extra_lines is -1 for ArcTo, 0 for Arc, 1 for Chord, 2 for Pie */ |
| static BOOL draw_arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom, |
| INT start_x, INT start_y, INT end_x, INT end_y, INT extra_lines ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| DC *dc = get_physdev_dc( dev ); |
| RECT rect; |
| POINT pt[2], *points; |
| int width, height, count; |
| BOOL ret = TRUE; |
| HRGN outline = 0, interior = 0; |
| |
| if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE; |
| |
| width = rect.right - rect.left; |
| height = rect.bottom - rect.top; |
| |
| pt[0].x = start_x; |
| pt[0].y = start_y; |
| pt[1].x = end_x; |
| pt[1].y = end_y; |
| lp_to_dp( dc, pt, 2 ); |
| /* make them relative to the ellipse center */ |
| pt[0].x -= rect.left + width / 2; |
| pt[0].y -= rect.top + height / 2; |
| pt[1].x -= rect.left + width / 2; |
| pt[1].y -= rect.top + height / 2; |
| |
| points = HeapAlloc( GetProcessHeap(), 0, (width + height) * 3 * sizeof(*points) ); |
| if (!points) return FALSE; |
| |
| if (extra_lines == -1) |
| { |
| points[0] = dc->cur_pos; |
| lp_to_dp( dc, points, 1 ); |
| count = 1 + get_arc_points( dc->ArcDirection, &rect, pt[0], pt[1], points + 1 ); |
| } |
| else count = get_arc_points( dc->ArcDirection, &rect, pt[0], pt[1], points ); |
| |
| if (extra_lines == 2) |
| { |
| points[count].x = rect.left + width / 2; |
| points[count].y = rect.top + height / 2; |
| count++; |
| } |
| if (count < 2) |
| { |
| HeapFree( GetProcessHeap(), 0, points ); |
| return TRUE; |
| } |
| |
| if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) |
| { |
| HeapFree( GetProcessHeap(), 0, points ); |
| return FALSE; |
| } |
| |
| if (pdev->brush.style != BS_NULL && extra_lines > 0 && |
| !(interior = CreatePolygonRgn( points, count, WINDING ))) |
| { |
| HeapFree( GetProcessHeap(), 0, points ); |
| if (outline) DeleteObject( outline ); |
| return FALSE; |
| } |
| |
| /* if not using a region, paint the interior first so the outline can overlap it */ |
| if (interior && !outline) |
| { |
| ret = brush_region( pdev, interior ); |
| DeleteObject( interior ); |
| interior = 0; |
| } |
| |
| reset_dash_origin( pdev ); |
| pdev->pen_lines( pdev, count, points, extra_lines > 0, outline ); |
| add_pen_lines_bounds( pdev, count, points, outline ); |
| |
| if (interior) |
| { |
| CombineRgn( interior, interior, outline, RGN_DIFF ); |
| ret = brush_region( pdev, interior ); |
| DeleteObject( interior ); |
| } |
| if (outline) |
| { |
| if (ret) ret = pen_region( pdev, outline ); |
| DeleteObject( outline ); |
| } |
| HeapFree( GetProcessHeap(), 0, points ); |
| return ret; |
| } |
| |
| /* helper for path stroking and filling functions */ |
| static BOOL stroke_and_fill_path( dibdrv_physdev *dev, BOOL stroke, BOOL fill ) |
| { |
| DC *dc = get_physdev_dc( &dev->dev ); |
| struct gdi_path *path; |
| POINT *points; |
| BYTE *types; |
| BOOL ret = TRUE; |
| HRGN outline = 0, interior = 0; |
| int i, pos, total; |
| |
| if (dev->brush.style == BS_NULL) fill = FALSE; |
| |
| if (!(path = get_gdi_flat_path( dc, fill ? &interior : NULL ))) return FALSE; |
| if (!(total = get_gdi_path_data( path, &points, &types ))) goto done; |
| |
| if (stroke && dev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 ); |
| |
| /* if not using a region, paint the interior first so the outline can overlap it */ |
| if (interior && !outline) |
| { |
| ret = brush_region( dev, interior ); |
| DeleteObject( interior ); |
| interior = 0; |
| } |
| |
| if (stroke) |
| { |
| pos = 0; |
| for (i = 1; i < total; i++) |
| { |
| if (types[i] != PT_MOVETO) continue; |
| if (i > pos + 1) |
| { |
| reset_dash_origin( dev ); |
| dev->pen_lines( dev, i - pos, points + pos, |
| fill || types[i - 1] & PT_CLOSEFIGURE, outline ); |
| } |
| pos = i; |
| } |
| if (i > pos + 1) |
| { |
| reset_dash_origin( dev ); |
| dev->pen_lines( dev, i - pos, points + pos, |
| fill || types[i - 1] & PT_CLOSEFIGURE, outline ); |
| } |
| } |
| |
| add_pen_lines_bounds( dev, total, points, outline ); |
| |
| if (interior) |
| { |
| CombineRgn( interior, interior, outline, RGN_DIFF ); |
| ret = brush_region( dev, interior ); |
| DeleteObject( interior ); |
| } |
| if (outline) |
| { |
| if (ret) ret = pen_region( dev, outline ); |
| DeleteObject( outline ); |
| } |
| |
| done: |
| free_gdi_path( path ); |
| return ret; |
| } |
| |
| /* Intensities of the 17 glyph levels when drawn with text component of 0xff on a |
| black bkgnd. [A log-log plot of these data gives: y = 77.05 * x^0.4315]. */ |
| static const BYTE ramp[17] = |
| { |
| 0, 0x4d, 0x68, 0x7c, |
| 0x8c, 0x9a, 0xa7, 0xb2, |
| 0xbd, 0xc7, 0xd0, 0xd9, |
| 0xe1, 0xe9, 0xf0, 0xf8, |
| 0xff |
| }; |
| |
| /* For a give text-color component and a glyph level, calculate the |
| range of dst intensities, the min/max corresponding to 0/0xff bkgnd |
| components respectively. |
| |
| The minimum is a linear interpolation between 0 and the value in |
| the ramp table. |
| |
| The maximum is a linear interpolation between the value from the |
| ramp table read in reverse and 0xff. |
| |
| To find the resulting pixel intensity, we note that if the text and |
| bkgnd intensities are the same then the result must be that |
| intensity. Otherwise we linearly interpolate between either the |
| min or the max value and this intermediate value depending on which |
| side of the inequality we lie. |
| */ |
| |
| static inline void get_range( BYTE aa, DWORD text_comp, BYTE *min_comp, BYTE *max_comp ) |
| { |
| *min_comp = (ramp[aa] * text_comp) / 0xff; |
| *max_comp = ramp[16 - aa] + ((0xff - ramp[16 - aa]) * text_comp) / 0xff; |
| } |
| |
| static inline void get_aa_ranges( COLORREF col, struct intensity_range intensities[17] ) |
| { |
| int i; |
| |
| for (i = 0; i < 17; i++) |
| { |
| get_range( i, GetRValue(col), &intensities[i].r_min, &intensities[i].r_max ); |
| get_range( i, GetGValue(col), &intensities[i].g_min, &intensities[i].g_max ); |
| get_range( i, GetBValue(col), &intensities[i].b_min, &intensities[i].b_max ); |
| } |
| } |
| |
| static DWORD font_cache_hash( struct cached_font *font ) |
| { |
| DWORD hash = 0, *ptr, two_chars; |
| WORD *pwc; |
| int i; |
| |
| hash ^= font->aa_flags; |
| for(i = 0, ptr = (DWORD*)&font->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++) |
| hash ^= *ptr; |
| for(i = 0, ptr = (DWORD*)&font->lf; i < 7; i++, ptr++) |
| hash ^= *ptr; |
| for(i = 0, ptr = (DWORD*)font->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) { |
| two_chars = *ptr; |
| pwc = (WCHAR *)&two_chars; |
| if (!*pwc) break; |
| *pwc = toupperW(*pwc); |
| pwc++; |
| *pwc = toupperW(*pwc); |
| hash ^= two_chars; |
| if (!*pwc) break; |
| } |
| return hash; |
| } |
| |
| static int font_cache_cmp( const struct cached_font *p1, const struct cached_font *p2 ) |
| { |
| int ret = p1->hash - p2->hash; |
| if (!ret) ret = p1->aa_flags - p2->aa_flags; |
| if (!ret) ret = memcmp( &p1->xform, &p2->xform, sizeof(p1->xform) ); |
| if (!ret) ret = memcmp( &p1->lf, &p2->lf, FIELD_OFFSET( LOGFONTW, lfFaceName )); |
| if (!ret) ret = strcmpiW( p1->lf.lfFaceName, p2->lf.lfFaceName ); |
| return ret; |
| } |
| |
| static struct cached_font *add_cached_font( DC *dc, HFONT hfont, UINT aa_flags ) |
| { |
| struct cached_font font, *ptr, *last_unused = NULL; |
| UINT i = 0, j, k; |
| |
| GetObjectW( hfont, sizeof(font.lf), &font.lf ); |
| font.xform = dc->xformWorld2Vport; |
| font.xform.eDx = font.xform.eDy = 0; /* unused, would break hashing */ |
| if (dc->GraphicsMode == GM_COMPATIBLE) |
| { |
| font.lf.lfOrientation = font.lf.lfEscapement; |
| if (font.xform.eM11 * font.xform.eM22 < 0) |
| font.lf.lfOrientation = -font.lf.lfOrientation; |
| } |
| font.lf.lfWidth = abs( font.lf.lfWidth ); |
| font.aa_flags = aa_flags; |
| font.hash = font_cache_hash( &font ); |
| |
| EnterCriticalSection( &font_cache_cs ); |
| LIST_FOR_EACH_ENTRY( ptr, &font_cache, struct cached_font, entry ) |
| { |
| if (!font_cache_cmp( &font, ptr )) |
| { |
| InterlockedIncrement( &ptr->ref ); |
| list_remove( &ptr->entry ); |
| goto done; |
| } |
| if (!ptr->ref) |
| { |
| i++; |
| last_unused = ptr; |
| } |
| } |
| |
| if (i > 5) /* keep at least 5 of the most-recently used fonts around */ |
| { |
| ptr = last_unused; |
| for (i = 0; i < GLYPH_NBTYPES; i++) |
| { |
| for (j = 0; j < GLYPH_CACHE_PAGES; j++) |
| { |
| if (!ptr->glyphs[i][j]) continue; |
| for (k = 0; k < GLYPH_CACHE_PAGE_SIZE; k++) |
| HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j][k] ); |
| HeapFree( GetProcessHeap(), 0, ptr->glyphs[i][j] ); |
| } |
| } |
| list_remove( &ptr->entry ); |
| } |
| else if (!(ptr = HeapAlloc( GetProcessHeap(), 0, sizeof(*ptr) ))) |
| { |
| LeaveCriticalSection( &font_cache_cs ); |
| return NULL; |
| } |
| |
| *ptr = font; |
| ptr->ref = 1; |
| memset( ptr->glyphs, 0, sizeof(ptr->glyphs) ); |
| done: |
| list_add_head( &font_cache, &ptr->entry ); |
| LeaveCriticalSection( &font_cache_cs ); |
| TRACE( "%d %s -> %p\n", ptr->lf.lfHeight, debugstr_w(ptr->lf.lfFaceName), ptr ); |
| return ptr; |
| } |
| |
| void release_cached_font( struct cached_font *font ) |
| { |
| if (font) InterlockedDecrement( &font->ref ); |
| } |
| |
| static struct cached_glyph *add_cached_glyph( struct cached_font *font, UINT index, UINT flags, |
| struct cached_glyph *glyph ) |
| { |
| struct cached_glyph *ret; |
| enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR; |
| UINT page = index / GLYPH_CACHE_PAGE_SIZE; |
| UINT entry = index % GLYPH_CACHE_PAGE_SIZE; |
| |
| if (!font->glyphs[type][page]) |
| { |
| struct cached_glyph **ptr; |
| |
| ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, GLYPH_CACHE_PAGE_SIZE * sizeof(*ptr) ); |
| if (!ptr) |
| { |
| HeapFree( GetProcessHeap(), 0, glyph ); |
| return NULL; |
| } |
| if (InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page], ptr, NULL )) |
| HeapFree( GetProcessHeap(), 0, ptr ); |
| } |
| ret = InterlockedCompareExchangePointer( (void **)&font->glyphs[type][page][entry], glyph, NULL ); |
| if (!ret) ret = glyph; |
| else HeapFree( GetProcessHeap(), 0, glyph ); |
| return ret; |
| } |
| |
| static struct cached_glyph *get_cached_glyph( struct cached_font *font, UINT index, UINT flags ) |
| { |
| enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR; |
| UINT page = index / GLYPH_CACHE_PAGE_SIZE; |
| |
| if (!font->glyphs[type][page]) return NULL; |
| return font->glyphs[type][page][index % GLYPH_CACHE_PAGE_SIZE]; |
| } |
| |
| /********************************************************************** |
| * get_text_bkgnd_masks |
| * |
| * See the comment above get_pen_bkgnd_masks |
| */ |
| static inline void get_text_bkgnd_masks( DC *dc, const dib_info *dib, rop_mask *mask ) |
| { |
| COLORREF bg = dc->backgroundColor; |
| |
| mask->and = 0; |
| |
| if (dib->bit_count != 1) |
| mask->xor = get_pixel_color( dc, dib, bg, FALSE ); |
| else |
| { |
| COLORREF fg = dc->textColor; |
| mask->xor = get_pixel_color( dc, dib, fg, TRUE ); |
| if (fg != bg) mask->xor = ~mask->xor; |
| } |
| } |
| |
| static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics, |
| const dib_info *glyph_dib, DWORD text_color, |
| const struct intensity_range *ranges, const struct clipped_rects *clipped_rects, |
| RECT *bounds ) |
| { |
| int i; |
| RECT rect, clipped_rect; |
| POINT src_origin; |
| |
| rect.left = x + metrics->gmptGlyphOrigin.x; |
| rect.top = y - metrics->gmptGlyphOrigin.y; |
| rect.right = rect.left + metrics->gmBlackBoxX; |
| rect.bottom = rect.top + metrics->gmBlackBoxY; |
| if (bounds) add_bounds_rect( bounds, &rect ); |
| |
| for (i = 0; i < clipped_rects->count; i++) |
| { |
| if (intersect_rect( &clipped_rect, &rect, clipped_rects->rects + i )) |
| { |
| src_origin.x = clipped_rect.left - rect.left; |
| src_origin.y = clipped_rect.top - rect.top; |
| |
| if (glyph_dib->bit_count == 32) |
| dib->funcs->draw_subpixel_glyph( dib, &clipped_rect, glyph_dib, &src_origin, |
| text_color ); |
| else |
| dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin, |
| text_color, ranges ); |
| } |
| } |
| } |
| |
| static int get_glyph_depth( UINT aa_flags ) |
| { |
| switch (aa_flags) |
| { |
| case GGO_BITMAP: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */ |
| case GGO_GRAY2_BITMAP: |
| case GGO_GRAY4_BITMAP: |
| case GGO_GRAY8_BITMAP: |
| case WINE_GGO_GRAY16_BITMAP: return 8; |
| |
| case WINE_GGO_HRGB_BITMAP: |
| case WINE_GGO_HBGR_BITMAP: |
| case WINE_GGO_VRGB_BITMAP: |
| case WINE_GGO_VBGR_BITMAP: return 32; |
| |
| default: |
| ERR("Unexpected flags %08x\n", aa_flags); |
| return 0; |
| } |
| } |
| |
| static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; |
| static const int padding[4] = {0, 3, 2, 1}; |
| |
| /*********************************************************************** |
| * cache_glyph_bitmap |
| * |
| * Retrieve a 17-level bitmap for the appropriate glyph. |
| * |
| * For non-antialiased bitmaps convert them to the 17-level format |
| * using only values 0 or 16. |
| */ |
| static struct cached_glyph *cache_glyph_bitmap( DC *dc, struct cached_font *font, UINT index, UINT flags ) |
| { |
| UINT ggo_flags = font->aa_flags; |
| static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} }; |
| UINT indices[3] = {0, 0, 0x20}; |
| int i, x, y; |
| DWORD ret, size; |
| BYTE *dst, *src; |
| int pad = 0, stride, bit_count; |
| GLYPHMETRICS metrics; |
| struct cached_glyph *glyph; |
| |
| if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX; |
| indices[0] = index; |
| for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++) |
| { |
| index = indices[i]; |
| ret = GetGlyphOutlineW( dc->hSelf, index, ggo_flags, &metrics, 0, NULL, &identity ); |
| if (ret != GDI_ERROR) break; |
| } |
| if (ret == GDI_ERROR) return NULL; |
| if (!ret) metrics.gmBlackBoxX = metrics.gmBlackBoxY = 0; /* empty glyph */ |
| |
| bit_count = get_glyph_depth( font->aa_flags ); |
| stride = get_dib_stride( metrics.gmBlackBoxX, bit_count ); |
| size = metrics.gmBlackBoxY * stride; |
| glyph = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( struct cached_glyph, bits[size] )); |
| if (!glyph) return NULL; |
| if (!size) goto done; /* empty glyph */ |
| |
| if (bit_count == 8) pad = padding[ metrics.gmBlackBoxX % 4 ]; |
| |
| ret = GetGlyphOutlineW( dc->hSelf, index, ggo_flags, &metrics, size, glyph->bits, &identity ); |
| if (ret == GDI_ERROR) |
| { |
| HeapFree( GetProcessHeap(), 0, glyph ); |
| return NULL; |
| } |
| assert( ret <= size ); |
| if (font->aa_flags == GGO_BITMAP) |
| { |
| for (y = metrics.gmBlackBoxY - 1; y >= 0; y--) |
| { |
| src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 ); |
| dst = glyph->bits + y * stride; |
| |
| if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad ); |
| |
| for (x = metrics.gmBlackBoxX - 1; x >= 0; x--) |
| dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0; |
| } |
| } |
| else if (pad) |
| { |
| for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride) |
| memset( dst + metrics.gmBlackBoxX, 0, pad ); |
| } |
| |
| done: |
| glyph->metrics = metrics; |
| return add_cached_glyph( font, index, flags, glyph ); |
| } |
| |
| static void render_string( DC *dc, dib_info *dib, struct cached_font *font, INT x, INT y, |
| UINT flags, const WCHAR *str, UINT count, const INT *dx, |
| const struct clipped_rects *clipped_rects, RECT *bounds ) |
| { |
| UINT i; |
| struct cached_glyph *glyph; |
| dib_info glyph_dib; |
| DWORD text_color; |
| struct intensity_range ranges[17]; |
| |
| glyph_dib.bit_count = get_glyph_depth( font->aa_flags ); |
| glyph_dib.rect.left = 0; |
| glyph_dib.rect.top = 0; |
| glyph_dib.bits.is_copy = FALSE; |
| glyph_dib.bits.free = NULL; |
| |
| text_color = get_pixel_color( dc, dib, dc->textColor, TRUE ); |
| |
| if (glyph_dib.bit_count == 8) |
| get_aa_ranges( dib->funcs->pixel_to_colorref( dib, text_color ), ranges ); |
| |
| for (i = 0; i < count; i++) |
| { |
| if (!(glyph = get_cached_glyph( font, str[i], flags )) && |
| !(glyph = cache_glyph_bitmap( dc, font, str[i], flags ))) continue; |
| |
| glyph_dib.width = glyph->metrics.gmBlackBoxX; |
| glyph_dib.height = glyph->metrics.gmBlackBoxY; |
| glyph_dib.rect.right = glyph->metrics.gmBlackBoxX; |
| glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY; |
| glyph_dib.stride = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count ); |
| glyph_dib.bits.ptr = glyph->bits; |
| |
| draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds ); |
| |
| if (dx) |
| { |
| if (flags & ETO_PDY) |
| { |
| x += dx[ i * 2 ]; |
| y += dx[ i * 2 + 1]; |
| } |
| else |
| x += dx[ i ]; |
| } |
| else |
| { |
| x += glyph->metrics.gmCellIncX; |
| y += glyph->metrics.gmCellIncY; |
| } |
| } |
| } |
| |
| BOOL render_aa_text_bitmapinfo( DC *dc, BITMAPINFO *info, struct gdi_image_bits *bits, |
| struct bitblt_coords *src, INT x, INT y, UINT flags, |
| UINT aa_flags, LPCWSTR str, UINT count, const INT *dx ) |
| { |
| dib_info dib; |
| struct clipped_rects visrect; |
| struct cached_font *font; |
| |
| assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */ |
| |
| init_dib_info_from_bitmapinfo( &dib, info, bits->ptr ); |
| |
| visrect.count = 1; |
| visrect.rects = &src->visrect; |
| |
| if (flags & ETO_OPAQUE) |
| { |
| rop_mask bkgnd_color; |
| get_text_bkgnd_masks( dc, &dib, &bkgnd_color ); |
| dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor ); |
| } |
| |
| if (!(font = add_cached_font( dc, dc->hFont, aa_flags ))) return FALSE; |
| |
| render_string( dc, &dib, font, x, y, flags, str, count, dx, &visrect, NULL ); |
| release_cached_font( font ); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_ExtTextOut |
| */ |
| BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, |
| const RECT *rect, LPCWSTR str, UINT count, const INT *dx ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| DC *dc = get_physdev_dc( dev ); |
| struct clipped_rects clipped_rects; |
| RECT bounds; |
| |
| if (!pdev->font) return FALSE; |
| |
| init_clipped_rects( &clipped_rects ); |
| reset_bounds( &bounds ); |
| |
| if (flags & ETO_OPAQUE) |
| { |
| rop_mask bkgnd_color; |
| get_text_bkgnd_masks( dc, &pdev->dib, &bkgnd_color ); |
| add_bounds_rect( &bounds, rect ); |
| get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects ); |
| pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, |
| bkgnd_color.and, bkgnd_color.xor ); |
| } |
| |
| if (count == 0) goto done; |
| |
| if (flags & ETO_CLIPPED) |
| { |
| if (!(flags & ETO_OPAQUE)) /* otherwise we have done it already */ |
| get_clipped_rects( &pdev->dib, rect, pdev->clip, &clipped_rects ); |
| } |
| else |
| { |
| free_clipped_rects( &clipped_rects ); |
| get_clipped_rects( &pdev->dib, NULL, pdev->clip, &clipped_rects ); |
| } |
| if (!clipped_rects.count) goto done; |
| |
| render_string( dc, &pdev->dib, pdev->font, x, y, flags, str, count, dx, |
| &clipped_rects, &bounds ); |
| |
| done: |
| add_clipped_bounds( pdev, &bounds, pdev->clip ); |
| free_clipped_rects( &clipped_rects ); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_SelectFont |
| */ |
| HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| DC *dc = get_physdev_dc( dev ); |
| HFONT ret; |
| |
| if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */ |
| |
| dev = GET_NEXT_PHYSDEV( dev, pSelectFont ); |
| ret = dev->funcs->pSelectFont( dev, font, aa_flags ); |
| if (ret) |
| { |
| struct cached_font *prev = pdev->font; |
| pdev->font = add_cached_font( dc, font, *aa_flags ? *aa_flags : GGO_BITMAP ); |
| release_cached_font( prev ); |
| } |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_Arc |
| */ |
| BOOL dibdrv_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom, |
| INT start_x, INT start_y, INT end_x, INT end_y ) |
| { |
| return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 0 ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_ArcTo |
| */ |
| BOOL dibdrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom, |
| INT start_x, INT start_y, INT end_x, INT end_y ) |
| { |
| return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, -1 ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_Chord |
| */ |
| BOOL dibdrv_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom, |
| INT start_x, INT start_y, INT end_x, INT end_y ) |
| { |
| return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 1 ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_Ellipse |
| */ |
| BOOL dibdrv_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom ) |
| { |
| return dibdrv_RoundRect( dev, left, top, right, bottom, right - left, bottom - top ); |
| } |
| |
| static inline BOOL is_interior( dib_info *dib, HRGN clip, int x, int y, DWORD pixel, UINT type) |
| { |
| /* the clip rgn stops the flooding */ |
| if (clip && !PtInRegion( clip, x, y )) return FALSE; |
| |
| if (type == FLOODFILLBORDER) |
| return dib->funcs->get_pixel( dib, x, y ) != pixel; |
| else |
| return dib->funcs->get_pixel( dib, x, y ) == pixel; |
| } |
| |
| static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn ); |
| |
| static inline void do_next_row( dib_info *dib, HRGN clip, const RECT *row, int offset, DWORD pixel, UINT type, HRGN rgn ) |
| { |
| RECT next; |
| |
| next.top = row->top + offset; |
| next.bottom = next.top + 1; |
| next.left = next.right = row->left; |
| while (next.right < row->right) |
| { |
| if (is_interior( dib, clip, next.right, next.top, pixel, type)) next.right++; |
| else |
| { |
| if (next.left != next.right && !PtInRegion( rgn, next.left, next.top )) |
| fill_row( dib, clip, &next, pixel, type, rgn ); |
| next.left = ++next.right; |
| } |
| } |
| if (next.left != next.right && !PtInRegion( rgn, next.left, next.top )) |
| fill_row( dib, clip, &next, pixel, type, rgn ); |
| } |
| |
| static void fill_row( dib_info *dib, HRGN clip, RECT *row, DWORD pixel, UINT type, HRGN rgn ) |
| { |
| while (row->left > 0 && is_interior( dib, clip, row->left - 1, row->top, pixel, type)) row->left--; |
| while (row->right < dib->rect.right - dib->rect.left && |
| is_interior( dib, clip, row->right, row->top, pixel, type)) |
| row->right++; |
| |
| add_rect_to_region( rgn, row ); |
| |
| if (row->top > 0) do_next_row( dib, clip, row, -1, pixel, type, rgn ); |
| if (row->top < dib->rect.bottom - dib->rect.top - 1) |
| do_next_row( dib, clip, row, 1, pixel, type, rgn ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_ExtFloodFill |
| */ |
| BOOL dibdrv_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT type ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| DC *dc = get_physdev_dc( dev ); |
| DWORD pixel = get_pixel_color( dc, &pdev->dib, color, FALSE ); |
| RECT row; |
| HRGN rgn; |
| |
| TRACE( "(%p, %d, %d, %08x, %d)\n", pdev, x, y, color, type ); |
| |
| if (x < 0 || x >= pdev->dib.rect.right - pdev->dib.rect.left || |
| y < 0 || y >= pdev->dib.rect.bottom - pdev->dib.rect.top) return FALSE; |
| |
| if (!is_interior( &pdev->dib, pdev->clip, x, y, pixel, type )) return FALSE; |
| |
| if (!(rgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE; |
| row.left = x; |
| row.right = x + 1; |
| row.top = y; |
| row.bottom = y + 1; |
| |
| fill_row( &pdev->dib, pdev->clip, &row, pixel, type, rgn ); |
| |
| add_clipped_bounds( pdev, NULL, rgn ); |
| brush_region( pdev, rgn ); |
| |
| DeleteObject( rgn ); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_FillPath |
| */ |
| BOOL dibdrv_FillPath( PHYSDEV dev ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| |
| return stroke_and_fill_path( pdev, FALSE, TRUE ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_GetNearestColor |
| */ |
| COLORREF dibdrv_GetNearestColor( PHYSDEV dev, COLORREF color ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| DC *dc = get_physdev_dc( dev ); |
| DWORD pixel; |
| |
| TRACE( "(%p, %08x)\n", dev, color ); |
| |
| pixel = get_pixel_color( dc, &pdev->dib, color, FALSE ); |
| return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_GetPixel |
| */ |
| COLORREF dibdrv_GetPixel( PHYSDEV dev, INT x, INT y ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| DC *dc = get_physdev_dc( dev ); |
| POINT pt; |
| DWORD pixel; |
| |
| TRACE( "(%p, %d, %d)\n", dev, x, y ); |
| |
| pt.x = x; |
| pt.y = y; |
| lp_to_dp( dc, &pt, 1 ); |
| |
| if (pt.x < 0 || pt.x >= pdev->dib.rect.right - pdev->dib.rect.left || |
| pt.y < 0 || pt.y >= pdev->dib.rect.bottom - pdev->dib.rect.top) |
| return CLR_INVALID; |
| |
| pixel = pdev->dib.funcs->get_pixel( &pdev->dib, pt.x, pt.y ); |
| return pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_LineTo |
| */ |
| BOOL dibdrv_LineTo( PHYSDEV dev, INT x, INT y ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| DC *dc = get_physdev_dc( dev ); |
| POINT pts[2]; |
| HRGN region = 0; |
| BOOL ret; |
| |
| pts[0] = dc->cur_pos; |
| pts[1].x = x; |
| pts[1].y = y; |
| |
| lp_to_dp(dc, pts, 2); |
| |
| if (pdev->pen_uses_region && !(region = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE; |
| |
| reset_dash_origin(pdev); |
| |
| ret = pdev->pen_lines(pdev, 2, pts, FALSE, region); |
| add_pen_lines_bounds( pdev, 2, pts, region ); |
| |
| if (region) |
| { |
| ret = pen_region( pdev, region ); |
| DeleteObject( region ); |
| } |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * get_rop2_from_rop |
| * |
| * Returns the binary rop that is equivalent to the provided ternary rop |
| * if the src bits are ignored. |
| */ |
| static inline INT get_rop2_from_rop(INT rop) |
| { |
| return (((rop >> 18) & 0x0c) | ((rop >> 16) & 0x03)) + 1; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_PatBlt |
| */ |
| BOOL dibdrv_PatBlt( PHYSDEV dev, struct bitblt_coords *dst, DWORD rop ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| dib_brush *brush = &pdev->brush; |
| int rop2 = get_rop2_from_rop( rop ); |
| struct clipped_rects clipped_rects; |
| DWORD and = 0, xor = 0; |
| BOOL ret = TRUE; |
| |
| TRACE("(%p, %d, %d, %d, %d, %06x)\n", dev, dst->x, dst->y, dst->width, dst->height, rop); |
| |
| add_clipped_bounds( pdev, &dst->visrect, 0 ); |
| if (!get_clipped_rects( &pdev->dib, &dst->visrect, pdev->clip, &clipped_rects )) return TRUE; |
| |
| switch (rop2) /* shortcuts for rops that don't involve the brush */ |
| { |
| case R2_NOT: and = ~0u; |
| /* fall through */ |
| case R2_WHITE: xor = ~0u; |
| /* fall through */ |
| case R2_BLACK: |
| pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, and, xor ); |
| /* fall through */ |
| case R2_NOP: |
| break; |
| default: |
| ret = brush->rects( pdev, brush, &pdev->dib, clipped_rects.count, clipped_rects.rects, rop2 ); |
| break; |
| } |
| free_clipped_rects( &clipped_rects ); |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_PaintRgn |
| */ |
| BOOL dibdrv_PaintRgn( PHYSDEV dev, HRGN rgn ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| const WINEREGION *region; |
| int i; |
| RECT rect, bounds; |
| DC *dc = get_physdev_dc( dev ); |
| |
| TRACE("%p, %p\n", dev, rgn); |
| |
| reset_bounds( &bounds ); |
| |
| region = get_wine_region( rgn ); |
| if(!region) return FALSE; |
| |
| for(i = 0; i < region->numRects; i++) |
| { |
| rect = get_device_rect( dc, region->rects[i].left, region->rects[i].top, |
| region->rects[i].right, region->rects[i].bottom, FALSE ); |
| add_bounds_rect( &bounds, &rect ); |
| brush_rect( pdev, &pdev->brush, &rect, pdev->clip ); |
| } |
| |
| release_wine_region( rgn ); |
| add_clipped_bounds( pdev, &bounds, pdev->clip ); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_PolyPolygon |
| */ |
| BOOL dibdrv_PolyPolygon( PHYSDEV dev, const POINT *pt, const INT *counts, DWORD polygons ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| DC *dc = get_physdev_dc( dev ); |
| DWORD total, i, pos; |
| BOOL ret = TRUE; |
| POINT pt_buf[32]; |
| POINT *points = pt_buf; |
| HRGN outline = 0, interior = 0; |
| |
| for (i = total = 0; i < polygons; i++) |
| { |
| if (counts[i] < 2) return FALSE; |
| total += counts[i]; |
| } |
| |
| if (total > sizeof(pt_buf) / sizeof(pt_buf[0])) |
| { |
| points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) ); |
| if (!points) return FALSE; |
| } |
| memcpy( points, pt, total * sizeof(*pt) ); |
| lp_to_dp( dc, points, total ); |
| |
| if (pdev->brush.style != BS_NULL && |
| !(interior = CreatePolyPolygonRgn( points, counts, polygons, dc->polyFillMode ))) |
| { |
| ret = FALSE; |
| goto done; |
| } |
| |
| if (pdev->pen_uses_region) outline = CreateRectRgn( 0, 0, 0, 0 ); |
| |
| /* if not using a region, paint the interior first so the outline can overlap it */ |
| if (interior && !outline) |
| { |
| ret = brush_region( pdev, interior ); |
| DeleteObject( interior ); |
| interior = 0; |
| } |
| |
| for (i = pos = 0; i < polygons; i++) |
| { |
| reset_dash_origin( pdev ); |
| pdev->pen_lines( pdev, counts[i], points + pos, TRUE, outline ); |
| pos += counts[i]; |
| } |
| add_pen_lines_bounds( pdev, total, points, outline ); |
| |
| if (interior) |
| { |
| CombineRgn( interior, interior, outline, RGN_DIFF ); |
| ret = brush_region( pdev, interior ); |
| DeleteObject( interior ); |
| } |
| if (outline) |
| { |
| if (ret) ret = pen_region( pdev, outline ); |
| DeleteObject( outline ); |
| } |
| |
| done: |
| if (points != pt_buf) HeapFree( GetProcessHeap(), 0, points ); |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_PolyPolyline |
| */ |
| BOOL dibdrv_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| DC *dc = get_physdev_dc( dev ); |
| DWORD total, pos, i; |
| POINT pt_buf[32]; |
| POINT *points = pt_buf; |
| BOOL ret = TRUE; |
| HRGN outline = 0; |
| |
| for (i = total = 0; i < polylines; i++) |
| { |
| if (counts[i] < 2) return FALSE; |
| total += counts[i]; |
| } |
| |
| if (total > sizeof(pt_buf) / sizeof(pt_buf[0])) |
| { |
| points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) ); |
| if (!points) return FALSE; |
| } |
| memcpy( points, pt, total * sizeof(*pt) ); |
| lp_to_dp( dc, points, total ); |
| |
| if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) |
| { |
| ret = FALSE; |
| goto done; |
| } |
| |
| for (i = pos = 0; i < polylines; i++) |
| { |
| reset_dash_origin( pdev ); |
| pdev->pen_lines( pdev, counts[i], points + pos, FALSE, outline ); |
| pos += counts[i]; |
| } |
| add_pen_lines_bounds( pdev, total, points, outline ); |
| |
| if (outline) |
| { |
| ret = pen_region( pdev, outline ); |
| DeleteObject( outline ); |
| } |
| |
| done: |
| if (points != pt_buf) HeapFree( GetProcessHeap(), 0, points ); |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_Polygon |
| */ |
| BOOL dibdrv_Polygon( PHYSDEV dev, const POINT *pt, INT count ) |
| { |
| INT counts[1] = { count }; |
| |
| return dibdrv_PolyPolygon( dev, pt, counts, 1 ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_Polyline |
| */ |
| BOOL dibdrv_Polyline( PHYSDEV dev, const POINT* pt, INT count ) |
| { |
| DWORD counts[1] = { count }; |
| |
| if (count < 0) return FALSE; |
| return dibdrv_PolyPolyline( dev, pt, counts, 1 ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_Rectangle |
| */ |
| BOOL dibdrv_Rectangle( PHYSDEV dev, INT left, INT top, INT right, INT bottom ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev(dev); |
| DC *dc = get_physdev_dc( dev ); |
| RECT rect; |
| POINT pts[4]; |
| BOOL ret; |
| HRGN outline = 0; |
| |
| TRACE("(%p, %d, %d, %d, %d)\n", dev, left, top, right, bottom); |
| |
| if (dc->GraphicsMode == GM_ADVANCED) |
| { |
| pts[0].x = pts[3].x = left; |
| pts[0].y = pts[1].y = top; |
| pts[1].x = pts[2].x = right; |
| pts[2].y = pts[3].y = bottom; |
| return dibdrv_Polygon( dev, pts, 4 ); |
| } |
| |
| if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE; |
| |
| if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE; |
| |
| rect.right--; |
| rect.bottom--; |
| reset_dash_origin(pdev); |
| |
| if (dc->ArcDirection == AD_CLOCKWISE) |
| { |
| /* 4 pts going clockwise starting from bottom-right */ |
| pts[0].x = pts[3].x = rect.right; |
| pts[0].y = pts[1].y = rect.bottom; |
| pts[1].x = pts[2].x = rect.left; |
| pts[2].y = pts[3].y = rect.top; |
| } |
| else |
| { |
| /* 4 pts going anti-clockwise starting from top-right */ |
| pts[0].x = pts[3].x = rect.right; |
| pts[0].y = pts[1].y = rect.top; |
| pts[1].x = pts[2].x = rect.left; |
| pts[2].y = pts[3].y = rect.bottom; |
| } |
| |
| pdev->pen_lines(pdev, 4, pts, TRUE, outline); |
| add_pen_lines_bounds( pdev, 4, pts, outline ); |
| |
| if (outline) |
| { |
| if (pdev->brush.style != BS_NULL) |
| { |
| HRGN interior = CreateRectRgnIndirect( &rect ); |
| |
| CombineRgn( interior, interior, outline, RGN_DIFF ); |
| brush_region( pdev, interior ); |
| DeleteObject( interior ); |
| } |
| ret = pen_region( pdev, outline ); |
| DeleteObject( outline ); |
| } |
| else |
| { |
| rect.left += (pdev->pen_width + 1) / 2; |
| rect.top += (pdev->pen_width + 1) / 2; |
| rect.right -= pdev->pen_width / 2; |
| rect.bottom -= pdev->pen_width / 2; |
| ret = brush_rect( pdev, &pdev->brush, &rect, pdev->clip ); |
| } |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_RoundRect |
| */ |
| BOOL dibdrv_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom, |
| INT ellipse_width, INT ellipse_height ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| DC *dc = get_physdev_dc( dev ); |
| RECT rect; |
| POINT pt[2], *points; |
| int i, end, count; |
| BOOL ret = TRUE; |
| HRGN outline = 0, interior = 0; |
| |
| if (!get_pen_device_rect( dc, pdev, &rect, left, top, right, bottom )) return TRUE; |
| |
| pt[0].x = pt[0].y = 0; |
| pt[1].x = ellipse_width; |
| pt[1].y = ellipse_height; |
| lp_to_dp( dc, pt, 2 ); |
| ellipse_width = min( rect.right - rect.left, abs( pt[1].x - pt[0].x )); |
| ellipse_height = min( rect.bottom - rect.top, abs( pt[1].y - pt[0].y )); |
| if (ellipse_width <= 2|| ellipse_height <= 2) |
| return dibdrv_Rectangle( dev, left, top, right, bottom ); |
| |
| points = HeapAlloc( GetProcessHeap(), 0, (ellipse_width + ellipse_height) * 2 * sizeof(*points) ); |
| if (!points) return FALSE; |
| |
| if (pdev->pen_uses_region && !(outline = CreateRectRgn( 0, 0, 0, 0 ))) |
| { |
| HeapFree( GetProcessHeap(), 0, points ); |
| return FALSE; |
| } |
| |
| if (pdev->brush.style != BS_NULL && |
| !(interior = CreateRoundRectRgn( rect.left, rect.top, rect.right + 1, rect.bottom + 1, |
| ellipse_width, ellipse_height ))) |
| { |
| HeapFree( GetProcessHeap(), 0, points ); |
| if (outline) DeleteObject( outline ); |
| return FALSE; |
| } |
| |
| /* if not using a region, paint the interior first so the outline can overlap it */ |
| if (interior && !outline) |
| { |
| ret = brush_region( pdev, interior ); |
| DeleteObject( interior ); |
| interior = 0; |
| } |
| |
| count = ellipse_first_quadrant( ellipse_width, ellipse_height, points ); |
| |
| if (dc->ArcDirection == AD_CLOCKWISE) |
| { |
| for (i = 0; i < count; i++) |
| { |
| points[i].x = rect.right - ellipse_width + points[i].x; |
| points[i].y = rect.bottom - ellipse_height + points[i].y; |
| } |
| } |
| else |
| { |
| for (i = 0; i < count; i++) |
| { |
| points[i].x = rect.right - ellipse_width + points[i].x; |
| points[i].y = rect.top + ellipse_height - 1 - points[i].y; |
| } |
| } |
| |
| /* horizontal symmetry */ |
| |
| end = 2 * count - 1; |
| /* avoid duplicating the midpoint */ |
| if (ellipse_width % 2 && ellipse_width == rect.right - rect.left) end--; |
| for (i = 0; i < count; i++) |
| { |
| points[end - i].x = rect.left + rect.right - 1 - points[i].x; |
| points[end - i].y = points[i].y; |
| } |
| count = end + 1; |
| |
| /* vertical symmetry */ |
| |
| end = 2 * count - 1; |
| /* avoid duplicating the midpoint */ |
| if (ellipse_height % 2 && ellipse_height == rect.bottom - rect.top) end--; |
| for (i = 0; i < count; i++) |
| { |
| points[end - i].x = points[i].x; |
| points[end - i].y = rect.top + rect.bottom - 1 - points[i].y; |
| } |
| count = end + 1; |
| |
| reset_dash_origin( pdev ); |
| pdev->pen_lines( pdev, count, points, TRUE, outline ); |
| add_pen_lines_bounds( pdev, count, points, outline ); |
| |
| if (interior) |
| { |
| CombineRgn( interior, interior, outline, RGN_DIFF ); |
| ret = brush_region( pdev, interior ); |
| DeleteObject( interior ); |
| } |
| if (outline) |
| { |
| if (ret) ret = pen_region( pdev, outline ); |
| DeleteObject( outline ); |
| } |
| HeapFree( GetProcessHeap(), 0, points ); |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_Pie |
| */ |
| BOOL dibdrv_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom, |
| INT start_x, INT start_y, INT end_x, INT end_y ) |
| { |
| return draw_arc( dev, left, top, right, bottom, start_x, start_y, end_x, end_y, 2 ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_SetPixel |
| */ |
| COLORREF dibdrv_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| DC *dc = get_physdev_dc( dev ); |
| struct clipped_rects clipped_rects; |
| RECT rect; |
| POINT pt; |
| DWORD pixel; |
| |
| TRACE( "(%p, %d, %d, %08x)\n", dev, x, y, color ); |
| |
| pt.x = x; |
| pt.y = y; |
| lp_to_dp( dc, &pt, 1 ); |
| rect.left = pt.x; |
| rect.top = pt.y; |
| rect.right = rect.left + 1; |
| rect.bottom = rect.top + 1; |
| add_clipped_bounds( pdev, &rect, pdev->clip ); |
| |
| /* SetPixel doesn't do the 1bpp massaging like other fg colors */ |
| pixel = get_pixel_color( dc, &pdev->dib, color, FALSE ); |
| color = pdev->dib.funcs->pixel_to_colorref( &pdev->dib, pixel ); |
| |
| if (!get_clipped_rects( &pdev->dib, &rect, pdev->clip, &clipped_rects )) return color; |
| pdev->dib.funcs->solid_rects( &pdev->dib, clipped_rects.count, clipped_rects.rects, 0, pixel ); |
| free_clipped_rects( &clipped_rects ); |
| return color; |
| } |
| |
| /*********************************************************************** |
| * dibdrv_StrokeAndFillPath |
| */ |
| BOOL dibdrv_StrokeAndFillPath( PHYSDEV dev ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| |
| return stroke_and_fill_path( pdev, TRUE, TRUE ); |
| } |
| |
| /*********************************************************************** |
| * dibdrv_StrokePath |
| */ |
| BOOL dibdrv_StrokePath( PHYSDEV dev ) |
| { |
| dibdrv_physdev *pdev = get_dibdrv_pdev( dev ); |
| |
| return stroke_and_fill_path( pdev, TRUE, FALSE ); |
| } |