diff options
-rw-r--r-- | ChangeLog | 54 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 54 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 54 | ||||
-rw-r--r-- | gdk/win32/gdkdrawable-win32.c | 248 | ||||
-rw-r--r-- | gdk/win32/gdkgc-win32.c | 315 | ||||
-rw-r--r-- | gdk/win32/gdkprivate-win32.h | 4 |
6 files changed, 431 insertions, 298 deletions
@@ -1,3 +1,57 @@ +2005-06-23 Tor Lillqvist <tml@novell.com> + + Improve and simplify line segment rendering on Win32, especially + the implementation of GDK_CAP_NOT_LAST, and dashed lines. Fixes + bug #306396. + + * gdk/win32/gdkprivate-win32.h (GdkGCWin32): Save the GdkGC's + line_style, cap_style and join_style as such in the + GdkGCWin32. Don't need to keep the pen_double_dash flag, we can + check the line_style. + + * gdk/win32/gdkgc-win32.c (fixup_pen): New internal function. Sets + up the GDI pen type, style, end cap and join attributes to use + based on the pen width, GDK line style, end cap style, and join + style. + + For a narrow (zero-width) GDK pen with the GDK_CAP_NOT_LAST end + cap style, which typically are used for XOR drawing where it is + essential that the last pixel is not drawn, use a GDI cosmetic + pen. Only for a cosmetic pen does GDI not draw the last pixel. I + deduced this by experimetation, the documentation is rather vague. + + For other GDK pens use a geometric GDI pen. If the width is 0 or 1 + and the GDK end cap style is GDK_CAP_BUTT, and the line style is + GDK_LINE_SOLID, use PS_ENDCAP_ROUND. This ensures that also + single-pixel length lines are drawn. (For sngle-pixel width lines + roundness as such is of course irrelevant.) For dashed lines, use + PS_ENDCAP_FLAT. + + For wide lines use PS_ENDCAP_FLAT, _ROUND or _SQUARE, + respectively, for GDK_CAP_BUTT, GDK_CAP_ROUND and GDK_CAP_PROJECTING. + + For one pixel on-off dashed lines, use PS_ALTERNATE, it seems to + work better than PS_USERSTYLE. For other dashed lines, use + PS_USERSTYLE and the dashes as set by the user (or the default + four-pixel on-off style). + + (gdk_win32_gc_values_to_win32values, gdk_win32_gc_set_dashes): + Call fixup_pen() to do the pen settings after modifying some of + the GDK GC attributes that affect pens. + + * gdk/win32/gdkdrawable-win32.c (render_line_horizontal, + render_line_vertical, draw_segments): Check GdkGCWin32::line_style + instead of the the removed pen_double_dash member. Don't use + PATCOPY unconditionally in the PatBlt() call, use a raster ope + code that depends on the GC function in use. + + (draw_rectangle, draw_segments, draw_lines): Be more careful in + deciding when to do the manual dash rendering. + + (draw_segments): Don't do any manual "last point" drawing at + all. The above changes takes care of narrow line segments being + drawn correctly in most cases, at least on NT-based Windows. + 2005-06-23 Matthias Clasen <mclasen@redhat.com> * gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder): diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index b7adf589f..d8fdbadf3 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,57 @@ +2005-06-23 Tor Lillqvist <tml@novell.com> + + Improve and simplify line segment rendering on Win32, especially + the implementation of GDK_CAP_NOT_LAST, and dashed lines. Fixes + bug #306396. + + * gdk/win32/gdkprivate-win32.h (GdkGCWin32): Save the GdkGC's + line_style, cap_style and join_style as such in the + GdkGCWin32. Don't need to keep the pen_double_dash flag, we can + check the line_style. + + * gdk/win32/gdkgc-win32.c (fixup_pen): New internal function. Sets + up the GDI pen type, style, end cap and join attributes to use + based on the pen width, GDK line style, end cap style, and join + style. + + For a narrow (zero-width) GDK pen with the GDK_CAP_NOT_LAST end + cap style, which typically are used for XOR drawing where it is + essential that the last pixel is not drawn, use a GDI cosmetic + pen. Only for a cosmetic pen does GDI not draw the last pixel. I + deduced this by experimetation, the documentation is rather vague. + + For other GDK pens use a geometric GDI pen. If the width is 0 or 1 + and the GDK end cap style is GDK_CAP_BUTT, and the line style is + GDK_LINE_SOLID, use PS_ENDCAP_ROUND. This ensures that also + single-pixel length lines are drawn. (For sngle-pixel width lines + roundness as such is of course irrelevant.) For dashed lines, use + PS_ENDCAP_FLAT. + + For wide lines use PS_ENDCAP_FLAT, _ROUND or _SQUARE, + respectively, for GDK_CAP_BUTT, GDK_CAP_ROUND and GDK_CAP_PROJECTING. + + For one pixel on-off dashed lines, use PS_ALTERNATE, it seems to + work better than PS_USERSTYLE. For other dashed lines, use + PS_USERSTYLE and the dashes as set by the user (or the default + four-pixel on-off style). + + (gdk_win32_gc_values_to_win32values, gdk_win32_gc_set_dashes): + Call fixup_pen() to do the pen settings after modifying some of + the GDK GC attributes that affect pens. + + * gdk/win32/gdkdrawable-win32.c (render_line_horizontal, + render_line_vertical, draw_segments): Check GdkGCWin32::line_style + instead of the the removed pen_double_dash member. Don't use + PATCOPY unconditionally in the PatBlt() call, use a raster ope + code that depends on the GC function in use. + + (draw_rectangle, draw_segments, draw_lines): Be more careful in + deciding when to do the manual dash rendering. + + (draw_segments): Don't do any manual "last point" drawing at + all. The above changes takes care of narrow line segments being + drawn correctly in most cases, at least on NT-based Windows. + 2005-06-23 Matthias Clasen <mclasen@redhat.com> * gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder): diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index b7adf589f..d8fdbadf3 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,57 @@ +2005-06-23 Tor Lillqvist <tml@novell.com> + + Improve and simplify line segment rendering on Win32, especially + the implementation of GDK_CAP_NOT_LAST, and dashed lines. Fixes + bug #306396. + + * gdk/win32/gdkprivate-win32.h (GdkGCWin32): Save the GdkGC's + line_style, cap_style and join_style as such in the + GdkGCWin32. Don't need to keep the pen_double_dash flag, we can + check the line_style. + + * gdk/win32/gdkgc-win32.c (fixup_pen): New internal function. Sets + up the GDI pen type, style, end cap and join attributes to use + based on the pen width, GDK line style, end cap style, and join + style. + + For a narrow (zero-width) GDK pen with the GDK_CAP_NOT_LAST end + cap style, which typically are used for XOR drawing where it is + essential that the last pixel is not drawn, use a GDI cosmetic + pen. Only for a cosmetic pen does GDI not draw the last pixel. I + deduced this by experimetation, the documentation is rather vague. + + For other GDK pens use a geometric GDI pen. If the width is 0 or 1 + and the GDK end cap style is GDK_CAP_BUTT, and the line style is + GDK_LINE_SOLID, use PS_ENDCAP_ROUND. This ensures that also + single-pixel length lines are drawn. (For sngle-pixel width lines + roundness as such is of course irrelevant.) For dashed lines, use + PS_ENDCAP_FLAT. + + For wide lines use PS_ENDCAP_FLAT, _ROUND or _SQUARE, + respectively, for GDK_CAP_BUTT, GDK_CAP_ROUND and GDK_CAP_PROJECTING. + + For one pixel on-off dashed lines, use PS_ALTERNATE, it seems to + work better than PS_USERSTYLE. For other dashed lines, use + PS_USERSTYLE and the dashes as set by the user (or the default + four-pixel on-off style). + + (gdk_win32_gc_values_to_win32values, gdk_win32_gc_set_dashes): + Call fixup_pen() to do the pen settings after modifying some of + the GDK GC attributes that affect pens. + + * gdk/win32/gdkdrawable-win32.c (render_line_horizontal, + render_line_vertical, draw_segments): Check GdkGCWin32::line_style + instead of the the removed pen_double_dash member. Don't use + PATCOPY unconditionally in the PatBlt() call, use a raster ope + code that depends on the GC function in use. + + (draw_rectangle, draw_segments, draw_lines): Be more careful in + deciding when to do the manual dash rendering. + + (draw_segments): Don't do any manual "last point" drawing at + all. The above changes takes care of narrow line segments being + drawn correctly in most cases, at least on NT-based Windows. + 2005-06-23 Matthias Clasen <mclasen@redhat.com> * gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder): diff --git a/gdk/win32/gdkdrawable-win32.c b/gdk/win32/gdkdrawable-win32.c index 2d45a40f5..5d4883751 100644 --- a/gdk/win32/gdkdrawable-win32.c +++ b/gdk/win32/gdkdrawable-win32.c @@ -45,6 +45,12 @@ #define LINE_ATTRIBUTES (GDK_GC_LINE_WIDTH|GDK_GC_LINE_STYLE| \ GDK_GC_CAP_STYLE|GDK_GC_JOIN_STYLE) +#define MUST_RENDER_DASHES_MANUALLY(gcwin32) \ + (gcwin32->line_style == GDK_LINE_DOUBLE_DASH || \ + (gcwin32->line_style == GDK_LINE_ON_OFF_DASH && \ + (gcwin32->pen_dash_offset || \ + (!G_WIN32_IS_NT_BASED () && (gcwin32->pen_style & PS_STYLE_MASK) == PS_SOLID)))) + static void gdk_win32_draw_rectangle (GdkDrawable *drawable, GdkGC *gc, gboolean filled, @@ -230,7 +236,51 @@ gdk_win32_set_colormap (GdkDrawable *drawable, /* Drawing */ -static DWORD default_double_dashes[] = { 3, 3 }; +static int +rop2_to_rop3 (int rop2) +{ + switch (rop2) + { + /* Oh, Microsoft's silly names for binary and ternary rops. */ +#define CASE(rop2,rop3) case R2_##rop2: return rop3 + CASE (BLACK, BLACKNESS); + CASE (NOTMERGEPEN, NOTSRCERASE); + CASE (MASKNOTPEN, 0x00220326); + CASE (NOTCOPYPEN, NOTSRCCOPY); + CASE (MASKPENNOT, SRCERASE); + CASE (NOT, DSTINVERT); + CASE (XORPEN, SRCINVERT); + CASE (NOTMASKPEN, 0x007700E6); + CASE (MASKPEN, SRCAND); + CASE (NOTXORPEN, 0x00990066); + CASE (NOP, 0x00AA0029); + CASE (MERGENOTPEN, MERGEPAINT); + CASE (COPYPEN, SRCCOPY); + CASE (MERGEPENNOT, 0x00DD0228); + CASE (MERGEPEN, SRCPAINT); + CASE (WHITE, WHITENESS); +#undef CASE + default: return SRCCOPY; + } +} + +static int +rop2_to_patblt_rop (int rop2) +{ + switch (rop2) + { +#define CASE(rop2,patblt_rop) case R2_##rop2: return patblt_rop + CASE (COPYPEN, PATCOPY); + CASE (XORPEN, PATINVERT); + CASE (NOT, DSTINVERT); + CASE (BLACK, BLACKNESS); + CASE (WHITE, WHITENESS); +#undef CASE + default: + g_warning ("Unhandled rop2 in GC to be used in PatBlt: %#x", rop2); + return PATCOPY; + } +} static inline int align_with_dash_offset (int a, DWORD *dashes, int num_dashes, GdkGCWin32 *gcwin32) @@ -259,70 +309,58 @@ align_with_dash_offset (int a, DWORD *dashes, int num_dashes, GdkGCWin32 *gcwin3 */ static inline gboolean render_line_horizontal (GdkGCWin32 *gcwin32, - int x1, - int x2, - int y) + int x1, + int x2, + int y) { - int n = 0; - HDC hdc = gcwin32->hdc; - int pen_width = gcwin32->pen_width; - DWORD *dashes; - int num_dashes; - int _x1 = x1; - - if (gcwin32->pen_dashes) - { - dashes = gcwin32->pen_dashes; - num_dashes = gcwin32->pen_num_dashes; - x1 = align_with_dash_offset (x1, dashes, num_dashes, gcwin32); - } - else - { - dashes = default_double_dashes; - num_dashes = G_N_ELEMENTS (default_double_dashes); - } + int n = 0; + const int pen_width = MAX (gcwin32->pen_width, 1); + const int _x1 = x1; + + g_assert (gcwin32->pen_dashes); + + x1 = align_with_dash_offset (x1, gcwin32->pen_dashes, gcwin32->pen_num_dashes, gcwin32); for (n = 0; x1 < x2; n++) { - int len = dashes[n % num_dashes]; + int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; if (x1 + len > x2) len = x2 - x1; if (n % 2 == 0 && x1 + len > _x1) - if (!GDI_CALL (PatBlt, (hdc, + if (!GDI_CALL (PatBlt, (gcwin32->hdc, x1 < _x1 ? _x1 : x1, y - pen_width / 2, len, pen_width, - PATCOPY))) + rop2_to_patblt_rop (gcwin32->rop2)))) return FALSE; - x1 += dashes[n % num_dashes]; + x1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; } - if (gcwin32->pen_double_dash) + if (gcwin32->line_style == GDK_LINE_DOUBLE_DASH) { HBRUSH hbr; - if ((hbr = SelectObject (hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR) + if ((hbr = SelectObject (gcwin32->hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR) return FALSE; x1 = _x1; - if (gcwin32->pen_dashes) - x1 += gcwin32->pen_dash_offset; + x1 += gcwin32->pen_dash_offset; for (n = 0; x1 < x2; n++) { - int len = dashes[n % num_dashes]; + int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; if (x1 + len > x2) len = x2 - x1; if (n % 2) - if (!GDI_CALL (PatBlt, (hdc, x1, y - pen_width / 2, + if (!GDI_CALL (PatBlt, (gcwin32->hdc, x1, y - pen_width / 2, len, pen_width, - PATCOPY))) + rop2_to_patblt_rop (gcwin32->rop2)))) return FALSE; - x1 += dashes[n % num_dashes]; + x1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; } - if (SelectObject (hdc, hbr) == HGDI_ERROR) + if (SelectObject (gcwin32->hdc, hbr) == HGDI_ERROR) return FALSE; } @@ -331,67 +369,54 @@ render_line_horizontal (GdkGCWin32 *gcwin32, static inline gboolean render_line_vertical (GdkGCWin32 *gcwin32, - int x, - int y1, - int y2) + int x, + int y1, + int y2) { - int n; - HDC hdc = gcwin32->hdc; - int pen_width = gcwin32->pen_width; - DWORD *dashes; - int num_dashes; - int _y1 = y1; - - if (gcwin32->pen_dashes) - { - dashes = gcwin32->pen_dashes; - num_dashes = gcwin32->pen_num_dashes; - y1 = align_with_dash_offset (y1, dashes, num_dashes, gcwin32); - } - else - { - dashes = default_double_dashes; - num_dashes = G_N_ELEMENTS (default_double_dashes); - } + int n; + const int pen_width = MAX (gcwin32->pen_width, 1); + const int _y1 = y1; + g_assert (gcwin32->pen_dashes); + + y1 = align_with_dash_offset (y1, gcwin32->pen_dashes, gcwin32->pen_num_dashes, gcwin32); for (n = 0; y1 < y2; n++) { - int len = dashes[n % num_dashes]; + int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; if (y1 + len > y2) len = y2 - y1; if (n % 2 == 0 && y1 + len > _y1) - if (!GDI_CALL (PatBlt, (hdc, x - pen_width / 2, + if (!GDI_CALL (PatBlt, (gcwin32->hdc, x - pen_width / 2, y1 < _y1 ? _y1 : y1, pen_width, len, - PATCOPY))) + rop2_to_patblt_rop (gcwin32->rop2)))) return FALSE; - y1 += dashes[n % num_dashes]; + y1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; } - if (gcwin32->pen_double_dash) + if (gcwin32->line_style == GDK_LINE_DOUBLE_DASH) { HBRUSH hbr; - if ((hbr = SelectObject (hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR) + if ((hbr = SelectObject (gcwin32->hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR) return FALSE; y1 = _y1; - if (gcwin32->pen_dashes) - y1 += gcwin32->pen_dash_offset; + y1 += gcwin32->pen_dash_offset; for (n = 0; y1 < y2; n++) { - int len = dashes[n % num_dashes]; + int len = gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; if (y1 + len > y2) len = y2 - y1; if (n % 2) - if (!GDI_CALL (PatBlt, (hdc, x - pen_width / 2, y1, + if (!GDI_CALL (PatBlt, (gcwin32->hdc, x - pen_width / 2, y1, pen_width, len, - PATCOPY))) + rop2_to_patblt_rop (gcwin32->rop2)))) return FALSE; - y1 += dashes[n % num_dashes]; + y1 += gcwin32->pen_dashes[n % gcwin32->pen_num_dashes]; } - if (SelectObject (hdc, hbr) == HGDI_ERROR) + if (SelectObject (gcwin32->hdc, hbr) == HGDI_ERROR) return FALSE; } @@ -485,34 +510,6 @@ draw_tiles (GdkDrawable *drawable, gdk_gc_unref (gc_copy); } -static int -rop2_to_rop3 (int rop2) -{ - switch (rop2) - { - /* Oh, Microsoft's silly names for binary and ternary rops. */ -#define CASE(rop2,rop3) case R2_##rop2: return rop3 - CASE (BLACK, BLACKNESS); - CASE (NOTMERGEPEN, NOTSRCERASE); - CASE (MASKNOTPEN, 0x00220326); - CASE (NOTCOPYPEN, NOTSRCCOPY); - CASE (MASKPENNOT, SRCERASE); - CASE (NOT, DSTINVERT); - CASE (XORPEN, SRCINVERT); - CASE (NOTMASKPEN, 0x007700E6); - CASE (MASKPEN, SRCAND); - CASE (NOTXORPEN, 0x00990066); - CASE (NOP, 0x00AA0029); - CASE (MERGENOTPEN, MERGEPAINT); - CASE (COPYPEN, SRCCOPY); - CASE (MERGEPENNOT, 0x00DD0228); - CASE (MERGEPEN, SRCPAINT); - CASE (WHITE, WHITENESS); -#undef CASE - default: return SRCCOPY; - } -} - static void generic_draw (GdkDrawable *drawable, GdkGC *gc, @@ -835,9 +832,7 @@ draw_rectangle (GdkGCWin32 *gcwin32, x -= x_offset; y -= y_offset; - if (!filled && (gcwin32->pen_double_dash || - (gcwin32->pen_dashes && (gcwin32->pen_dash_offset || - !G_WIN32_IS_NT_BASED ())))) + if (!filled && MUST_RENDER_DASHES_MANUALLY (gcwin32)) { render_line_vertical (gcwin32, x, y, y+height+1) && render_line_horizontal (gcwin32, x, x+width+1, y) && @@ -1284,9 +1279,7 @@ draw_segments (GdkGCWin32 *gcwin32, } } - if (gcwin32->pen_double_dash || - (gcwin32->pen_dashes && (gcwin32->pen_dash_offset || - !G_WIN32_IS_NT_BASED ()))) + if (MUST_RENDER_DASHES_MANUALLY (gcwin32)) { for (i = 0; i < nsegs; i++) { @@ -1315,42 +1308,22 @@ draw_segments (GdkGCWin32 *gcwin32, else GDI_CALL (MoveToEx, (hdc, segs[i].x1, segs[i].y1, NULL)) && GDI_CALL (LineTo, (hdc, segs[i].x2, segs[i].y2)); - } } else { for (i = 0; i < nsegs; i++) - GDI_CALL (MoveToEx, (hdc, segs[i].x1, segs[i].y1, NULL)) && - GDI_CALL (LineTo, (hdc, segs[i].x2, segs[i].y2)); - - /* not drawing the end pixel does produce a crippled mask, look - * e.g. at xpm icons produced with gdk_pixbuf_new_from_xpm_data trough - * gdk_pixbuf_render_threshold_alpha (testgtk folder icon or - * Dia's toolbox icons) but only on win9x ... --hb - * - * Update : see bug #81895 and bug #126710 why this is finally - * needed on any win32 platform ;-) - */ - if (gcwin32->pen_width <= 1) - { - GdkSegment *ps = &segs[nsegs-1]; - int xc = 0, yc = 0; - - if (ps->y2 == ps->y1 && ps->x2 == ps->x1) - xc = 1; /* just a point */ - else if (ps->y2 == ps->y1) - xc = (ps->x1 < ps->x2) ? 1 : -1; /* advance x only */ - else if (ps->x2 == ps->x1) - yc = (ps->y1 < ps->y2) ? 1 : -1; /* advance y only */ - else - { - xc = (ps->x1 < ps->x2) ? 1 : -1; - yc = (ps->y1 < ps->y2) ? 1 : -1; - } - - GDI_CALL (LineTo, (hdc, ps->x2 + xc, ps->y2 + yc)); - } + { + const GdkSegment *ps = &segs[i]; + const int x1 = ps->x1, y1 = ps->y1; + int x2 = ps->x2, y2 = ps->y2; + + GDK_NOTE (MISC, g_print (" +%d+%d..+%d+%d", x1, y1, x2, y2)); + GDI_CALL (MoveToEx, (hdc, x1, y1, NULL)) && + GDI_CALL (LineTo, (hdc, x2, y2)); + } + + GDK_NOTE (MISC, g_print ("\n")); } if (x_offset != 0 || y_offset != 0) g_free (segs); @@ -1393,8 +1366,7 @@ gdk_win32_draw_segments (GdkDrawable *drawable, region = widen_bounds (&bounds, GDK_GC_WIN32 (gc)->pen_width); - generic_draw (drawable, gc, GDK_GC_FOREGROUND | GDK_GC_FOREGROUND | - LINE_ATTRIBUTES, + generic_draw (drawable, gc, GDK_GC_FOREGROUND | LINE_ATTRIBUTES, draw_segments, region, segs, nsegs); gdk_region_destroy (region); @@ -1421,9 +1393,7 @@ draw_lines (GdkGCWin32 *gcwin32, pts[i].y -= y_offset; } - if (gcwin32->pen_double_dash || - (gcwin32->pen_dashes && (gcwin32->pen_dash_offset || - !G_WIN32_IS_NT_BASED ()))) + if (MUST_RENDER_DASHES_MANUALLY (gcwin32)) { for (i = 0; i < npoints - 1; i++) { diff --git a/gdk/win32/gdkgc-win32.c b/gdk/win32/gdkgc-win32.c index 73f001f19..f1cc6c3c1 100644 --- a/gdk/win32/gdkgc-win32.c +++ b/gdk/win32/gdkgc-win32.c @@ -114,6 +114,115 @@ gdk_gc_win32_finalize (GObject *object) } static void +fixup_pen (GdkGCWin32 *win32_gc) +{ + win32_gc->pen_style = 0; + + /* First look at GDK width and end cap style, set GDI pen type and + * end cap. + */ + if (win32_gc->pen_width == 0 && + win32_gc->cap_style == GDK_CAP_NOT_LAST) + { + /* Use a cosmetic pen, always width 1 */ + win32_gc->pen_style |= PS_COSMETIC; + } + else if (win32_gc->pen_width <= 1 && + win32_gc->cap_style == GDK_CAP_BUTT) + { + /* For 1 pixel wide lines PS_ENDCAP_ROUND means draw both ends, + * even for one pixel length lines. But if we are drawing dashed + * lines we can't use PS_ENDCAP_ROUND. + */ + if (win32_gc->line_style == GDK_LINE_SOLID) + win32_gc->pen_style |= PS_GEOMETRIC | PS_ENDCAP_ROUND; + else + win32_gc->pen_style |= PS_GEOMETRIC | PS_ENDCAP_FLAT; + } + else + { + win32_gc->pen_style |= PS_GEOMETRIC; + switch (win32_gc->cap_style) + { + /* For non-zero-width lines X11's CapNotLast works like CapButt */ + case GDK_CAP_NOT_LAST: + case GDK_CAP_BUTT: + win32_gc->pen_style |= PS_ENDCAP_FLAT; + break; + case GDK_CAP_ROUND: + win32_gc->pen_style |= PS_ENDCAP_ROUND; + break; + case GDK_CAP_PROJECTING: + win32_gc->pen_style |= PS_ENDCAP_SQUARE; + break; + } + } + + /* Next look at GDK line style, set GDI pen style attribute */ + switch (win32_gc->line_style) + { + case GDK_LINE_SOLID: + win32_gc->pen_style |= PS_SOLID; + break; + case GDK_LINE_ON_OFF_DASH: + case GDK_LINE_DOUBLE_DASH: + if (win32_gc->pen_dashes == NULL) + { + win32_gc->pen_dashes = g_new (DWORD, 1); + win32_gc->pen_dashes[0] = 4; + win32_gc->pen_num_dashes = 1; + } + if (G_WIN32_IS_NT_BASED ()) + { + if (!(win32_gc->pen_style & PS_TYPE_MASK) == PS_GEOMETRIC && + win32_gc->pen_dashes[0] == 1 && + (win32_gc->pen_num_dashes == 1 || + (win32_gc->pen_num_dashes == 2 && win32_gc->pen_dashes[0] == 1))) + win32_gc->pen_style |= PS_ALTERNATE; + else + win32_gc->pen_style |= PS_USERSTYLE; + } + else + { + /* Render "short" on-off dashes drawn with R2_COPYPEN and a + * cosmetic pen using PS_DOT + */ + if (win32_gc->line_style == GDK_LINE_ON_OFF_DASH && + win32_gc->rop2 == R2_COPYPEN && + (win32_gc->pen_style & PS_TYPE_MASK) == PS_COSMETIC && + win32_gc->pen_dashes[0] <= 2 && + (win32_gc->pen_num_dashes == 1 || + (win32_gc->pen_num_dashes == 2 && win32_gc->pen_dashes[1] <= 2))) + win32_gc->pen_style |= PS_DOT; + else + /* Otherwise render opaque lines solid, horizontal or + * vertical ones will be dashed manually, see + * gdkdrawable-win32.c. + */ + win32_gc->pen_style |= PS_SOLID; + } + break; + } + + /* Last, for if the GDI pen is geometric, set the join attribute */ + if ((win32_gc->pen_style & PS_TYPE_MASK) == PS_GEOMETRIC) + { + switch (win32_gc->join_style) + { + case GDK_JOIN_MITER: + win32_gc->pen_style |= PS_JOIN_MITER; + break; + case GDK_JOIN_ROUND: + win32_gc->pen_style |= PS_JOIN_ROUND; + break; + case GDK_JOIN_BEVEL: + win32_gc->pen_style |= PS_JOIN_BEVEL; + break; + } + } +} + +static void gdk_win32_gc_values_to_win32values (GdkGCValues *values, GdkGCValuesMask mask, GdkGCWin32 *win32_gc) @@ -217,45 +326,6 @@ gdk_win32_gc_values_to_win32values (GdkGCValues *values, { if (values->stipple != NULL) { -#if 0 /* HB: this size limitation is disabled to make radio and check - * buttons work. I got the impression from the API docs, that - * it shouldn't be necessary at all, but win9x would do the clipping - * - * This code will need some work if reenabled since the stipple is - * now stored in the backend-independent code. - */ - gint sw, sh; - - gdk_drawable_get_size (values->stipple, &sw, &sh); - - if ( (sw != 8 || sh != 8) - && !G_WIN32_IS_NT_BASED ()) /* HB: the MSDN says it's a Win95 limitation */ - { - /* It seems that it *must* be 8x8, at least on my machine. - * Thus, tile an 8x8 bitmap with the stipple in case it is - * smaller, or simply use just the top left 8x8 in case it is - * larger. - */ - gchar dummy[8]; - GdkPixmap *bm = gdk_bitmap_create_from_data (NULL, dummy, 8, 8); - GdkGC *gc = gdk_gc_new (bm); - gint i, j; - - i = 0; - while (i < 8) - { - j = 0; - while (j < 8) - { - gdk_draw_drawable (bm, gc, values->stipple, 0, 0, i, j, sw, sh); - j += sh; - } - i += sw; - } - win32_gc->stipple = bm; - gdk_gc_unref (gc); - } -#endif win32_gc->values_mask |= GDK_GC_STIPPLE; GDK_NOTE (GC, (g_print ("%sstipple=%p", s, @@ -343,78 +413,32 @@ gdk_win32_gc_values_to_win32values (GdkGCValues *values, if (mask & GDK_GC_LINE_STYLE) { - switch (values->line_style) - { - case GDK_LINE_SOLID: - if (win32_gc->pen_dashes) - { - g_free (win32_gc->pen_dashes); - win32_gc->pen_dashes = NULL; - win32_gc->pen_num_dashes = 0; - } - win32_gc->pen_style &= ~(PS_STYLE_MASK); - win32_gc->pen_style |= PS_SOLID; - win32_gc->pen_double_dash = FALSE; - break; - case GDK_LINE_ON_OFF_DASH: - case GDK_LINE_DOUBLE_DASH: - if (!win32_gc->pen_dashes) - { - /* setting to PS_DASH probably isn't correct. If I understand the - * xlib docs correctly it should influence the handling of - * line endings ? --hb - */ - win32_gc->pen_style &= ~(PS_STYLE_MASK); - win32_gc->pen_style |= PS_DASH; - } - win32_gc->pen_double_dash = values->line_style == GDK_LINE_DOUBLE_DASH; - break; - } - GDK_NOTE (GC, (g_print ("%sps|=PS_STYLE_%s", s, _gdk_win32_psstyle_to_string (win32_gc->pen_style)), - s = ",")); + win32_gc->line_style = values->line_style; win32_gc->values_mask |= GDK_GC_LINE_STYLE; } if (mask & GDK_GC_CAP_STYLE) { - win32_gc->pen_style &= ~(PS_ENDCAP_MASK); - switch (values->cap_style) - { - case GDK_CAP_NOT_LAST: /* ??? */ - case GDK_CAP_BUTT: - win32_gc->pen_style |= PS_ENDCAP_FLAT; - break; - case GDK_CAP_ROUND: - win32_gc->pen_style |= PS_ENDCAP_ROUND; - break; - case GDK_CAP_PROJECTING: - win32_gc->pen_style |= PS_ENDCAP_SQUARE; - break; - } - GDK_NOTE (GC, (g_print ("%sps|=PS_ENDCAP_%s", s, _gdk_win32_psendcap_to_string (win32_gc->pen_style)), - s = ",")); + win32_gc->cap_style = values->cap_style; win32_gc->values_mask |= GDK_GC_CAP_STYLE; } if (mask & GDK_GC_JOIN_STYLE) { - win32_gc->pen_style &= ~(PS_JOIN_MASK); - switch (values->join_style) - { - case GDK_JOIN_MITER: - win32_gc->pen_style |= PS_JOIN_MITER; - break; - case GDK_JOIN_ROUND: - win32_gc->pen_style |= PS_JOIN_ROUND; - break; - case GDK_JOIN_BEVEL: - win32_gc->pen_style |= PS_JOIN_BEVEL; - break; - } - GDK_NOTE (GC, (g_print ("%sps|=PS_JOIN_%s", s, _gdk_win32_psjoin_to_string (win32_gc->pen_style)), - s = ",")); + win32_gc->join_style = values->join_style; win32_gc->values_mask |= GDK_GC_JOIN_STYLE; } + + if (mask & (GDK_GC_LINE_WIDTH|GDK_GC_LINE_STYLE|GDK_GC_CAP_STYLE|GDK_GC_JOIN_STYLE)) + { + fixup_pen (win32_gc); + GDK_NOTE (GC, (g_print ("%sps|=PS_STYLE_%s|PS_ENDCAP_%s|PS_JOIN_%s", s, + _gdk_win32_psstyle_to_string (win32_gc->pen_style), + _gdk_win32_psendcap_to_string (win32_gc->pen_style), + _gdk_win32_psjoin_to_string (win32_gc->pen_style)), + s = ",")); + } + GDK_NOTE (GC, g_print ("} mask=(%s)", _gdk_win32_gcvalues_mask_to_string (win32_gc->values_mask))); } @@ -443,11 +467,17 @@ _gdk_win32_gc_new (GdkDrawable *drawable, win32_gc->subwindow_mode = GDK_CLIP_BY_CHILDREN; win32_gc->graphics_exposures = TRUE; win32_gc->pen_width = 0; - win32_gc->pen_style = PS_GEOMETRIC|PS_ENDCAP_FLAT|PS_JOIN_MITER; + /* Don't get confused by the PS_ENDCAP_ROUND. For narrow GDI pens + * (width == 1), PS_GEOMETRIC|PS_ENDCAP_ROUND works like X11's + * CapButt. + */ + win32_gc->pen_style = PS_GEOMETRIC|PS_ENDCAP_ROUND|PS_JOIN_MITER; + win32_gc->line_style = GDK_LINE_SOLID; + win32_gc->cap_style = GDK_CAP_BUTT; + win32_gc->join_style = GDK_JOIN_MITER; win32_gc->pen_dashes = NULL; win32_gc->pen_num_dashes = 0; win32_gc->pen_dash_offset = 0; - win32_gc->pen_double_dash = FALSE; win32_gc->pen_hbrbg = NULL; win32_gc->values_mask = GDK_GC_FUNCTION | GDK_GC_FILL; @@ -522,29 +552,9 @@ gdk_win32_gc_get_values (GdkGC *gc, values->graphics_exposures = win32_gc->graphics_exposures; values->line_width = win32_gc->pen_width; - if (win32_gc->pen_style & PS_SOLID) - values->line_style = GDK_LINE_SOLID; - else if (win32_gc->pen_style & PS_DASH) - values->line_style = win32_gc->pen_double_dash ? GDK_LINE_DOUBLE_DASH : - GDK_LINE_ON_OFF_DASH; - else - values->line_style = GDK_LINE_SOLID; - - /* PS_ENDCAP_ROUND is zero */ - if (win32_gc->pen_style & PS_ENDCAP_FLAT) - values->cap_style = GDK_CAP_BUTT; - else if (win32_gc->pen_style & PS_ENDCAP_SQUARE) - values->cap_style = GDK_CAP_PROJECTING; - else - values->cap_style = GDK_CAP_ROUND; - - /* PS_JOIN_ROUND is zero */ - if (win32_gc->pen_style & PS_JOIN_MITER) - values->join_style = GDK_JOIN_MITER; - else if (win32_gc->pen_style & PS_JOIN_BEVEL) - values->join_style = GDK_JOIN_BEVEL; - else - values->join_style = GDK_JOIN_ROUND; + values->line_style = win32_gc->line_style; + values->cap_style = win32_gc->cap_style; + values->join_style = win32_gc->join_style; } static void @@ -573,19 +583,13 @@ gdk_win32_gc_set_dashes (GdkGC *gc, win32_gc = GDK_GC_WIN32 (gc); - /* mark as set, see gdk_win32_gc_values_to_win32values () for the reason */ - win32_gc->values_mask |= GDK_GC_LINE_STYLE; - - win32_gc->pen_style &= ~(PS_STYLE_MASK); - - win32_gc->pen_style |= (PS_GEOMETRIC | PS_USERSTYLE); win32_gc->pen_num_dashes = n; - if (win32_gc->pen_dashes != NULL) - g_free (win32_gc->pen_dashes); + g_free (win32_gc->pen_dashes); win32_gc->pen_dashes = g_new (DWORD, n); for (i = 0; i < n; i++) win32_gc->pen_dashes[i] = dash_list[i]; win32_gc->pen_dash_offset = dash_offset; + fixup_pen (win32_gc); } void @@ -659,13 +663,16 @@ _gdk_windowing_gc_copy (GdkGC *dst_gc, dst_win32_gc->graphics_exposures = src_win32_gc->graphics_exposures; dst_win32_gc->pen_width = src_win32_gc->pen_width; dst_win32_gc->pen_style = src_win32_gc->pen_style; - dst_win32_gc->pen_dashes = src_win32_gc->pen_dashes; - if (dst_win32_gc->pen_dashes) + dst_win32_gc->line_style = src_win32_gc->line_style; + dst_win32_gc->cap_style = src_win32_gc->cap_style; + dst_win32_gc->join_style = src_win32_gc->join_style; + if (src_win32_gc->pen_dashes) dst_win32_gc->pen_dashes = g_memdup (src_win32_gc->pen_dashes, sizeof (DWORD) * src_win32_gc->pen_num_dashes); + else + dst_win32_gc->pen_dashes = NULL; dst_win32_gc->pen_num_dashes = src_win32_gc->pen_num_dashes; dst_win32_gc->pen_dash_offset = src_win32_gc->pen_dash_offset; - dst_win32_gc->pen_double_dash = src_win32_gc->pen_double_dash; dst_win32_gc->hdc = NULL; @@ -825,7 +832,6 @@ gdk_win32_hdc_get (GdkDrawable *drawable, GdkDrawableImplWin32 *impl = NULL; gboolean ok = TRUE; COLORREF fg = RGB (0, 0, 0), bg = RGB (255, 255, 255); - LOGBRUSH logbrush; HPEN hpen; HBRUSH hbr; @@ -858,7 +864,7 @@ gdk_win32_hdc_get (GdkDrawable *drawable, if (ok && (usage & LINE_ATTRIBUTES)) { /* For drawing GDK_LINE_DOUBLE_DASH */ - if ((usage & GDK_GC_BACKGROUND) && win32_gc->pen_double_dash) + if ((usage & GDK_GC_BACKGROUND) && win32_gc->line_style == GDK_LINE_DOUBLE_DASH) { bg = _gdk_win32_colormap_color (impl->colormap, _gdk_gc_get_bg_pixel (gc)); if ((win32_gc->pen_hbrbg = CreateSolidBrush (bg)) == NULL) @@ -867,33 +873,26 @@ gdk_win32_hdc_get (GdkDrawable *drawable, if (ok) { + LOGBRUSH logbrush; + DWORD style_count = 0; + const DWORD *style = NULL; + /* Create and select pen */ logbrush.lbStyle = BS_SOLID; logbrush.lbColor = fg; logbrush.lbHatch = 0; - - if (win32_gc->pen_num_dashes > 0 && !G_WIN32_IS_NT_BASED ()) - { - /* The Win9x GDI is rather limited so we either draw dashed - * lines ourselves (only horizontal and vertical) or let them be - * drawn solid to avoid implementing a whole line renderer. - */ - if ((hpen = ExtCreatePen ( - (win32_gc->pen_style & ~(PS_STYLE_MASK)) | PS_SOLID, - MAX (win32_gc->pen_width, 1), - &logbrush, - 0, NULL)) == NULL) - WIN32_GDI_FAILED ("ExtCreatePen"), ok = FALSE; - } - else + + if ((win32_gc->pen_style & PS_STYLE_MASK) == PS_USERSTYLE) { - if ((hpen = ExtCreatePen (win32_gc->pen_style, - MAX (win32_gc->pen_width, 1), - &logbrush, - win32_gc->pen_num_dashes, - win32_gc->pen_dashes)) == NULL) - WIN32_GDI_FAILED ("ExtCreatePen"), ok = FALSE; + style_count = win32_gc->pen_num_dashes; + style = win32_gc->pen_dashes; } + + if ((hpen = ExtCreatePen (win32_gc->pen_style, + MAX (win32_gc->pen_width, 1), + &logbrush, + style_count, style)) == NULL) + WIN32_GDI_FAILED ("ExtCreatePen"), ok = FALSE; if (ok && SelectObject (win32_gc->hdc, hpen) == NULL) WIN32_GDI_FAILED ("SelectObject"), ok = FALSE; diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index a2764a2b9..d76c8cb51 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -274,10 +274,12 @@ struct _GdkGCWin32 gint graphics_exposures; gint pen_width; DWORD pen_style; + GdkLineStyle line_style; + GdkCapStyle cap_style; + GdkJoinStyle join_style; DWORD *pen_dashes; /* use for PS_USERSTYLE or step-by-step rendering */ gint pen_num_dashes; gint pen_dash_offset; - gboolean pen_double_dash; HBRUSH pen_hbrbg; /* Following fields are valid while the GC exists as a Windows DC */ |