Improve and simplify line segment rendering on Win32, especially the

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.
This commit is contained in:
Tor Lillqvist 2005-06-24 13:00:05 +00:00 committed by Tor Lillqvist
parent 621f16a442
commit baad14eee0
6 changed files with 427 additions and 294 deletions

View File

@ -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> 2005-06-23 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder): * gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder):

View File

@ -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> 2005-06-23 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder): * gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder):

View File

@ -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> 2005-06-23 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder): * gtk/gtkfilesystemunix.c (gtk_file_system_unix_get_folder):

View File

@ -45,6 +45,12 @@
#define LINE_ATTRIBUTES (GDK_GC_LINE_WIDTH|GDK_GC_LINE_STYLE| \ #define LINE_ATTRIBUTES (GDK_GC_LINE_WIDTH|GDK_GC_LINE_STYLE| \
GDK_GC_CAP_STYLE|GDK_GC_JOIN_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, static void gdk_win32_draw_rectangle (GdkDrawable *drawable,
GdkGC *gc, GdkGC *gc,
gboolean filled, gboolean filled,
@ -230,7 +236,51 @@ gdk_win32_set_colormap (GdkDrawable *drawable,
/* Drawing /* 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 static inline int
align_with_dash_offset (int a, DWORD *dashes, int num_dashes, GdkGCWin32 *gcwin32) 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 static inline gboolean
render_line_horizontal (GdkGCWin32 *gcwin32, render_line_horizontal (GdkGCWin32 *gcwin32,
int x1, int x1,
int x2, int x2,
int y) int y)
{ {
int n = 0; int n = 0;
HDC hdc = gcwin32->hdc; const int pen_width = MAX (gcwin32->pen_width, 1);
int pen_width = gcwin32->pen_width; const int _x1 = x1;
DWORD *dashes;
int num_dashes;
int _x1 = x1;
if (gcwin32->pen_dashes) g_assert (gcwin32->pen_dashes);
{
dashes = gcwin32->pen_dashes; x1 = align_with_dash_offset (x1, gcwin32->pen_dashes, gcwin32->pen_num_dashes, gcwin32);
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);
}
for (n = 0; x1 < x2; n++) 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) if (x1 + len > x2)
len = x2 - x1; len = x2 - x1;
if (n % 2 == 0 && x1 + len > _x1) if (n % 2 == 0 && x1 + len > _x1)
if (!GDI_CALL (PatBlt, (hdc, if (!GDI_CALL (PatBlt, (gcwin32->hdc,
x1 < _x1 ? _x1 : x1, x1 < _x1 ? _x1 : x1,
y - pen_width / 2, y - pen_width / 2,
len, pen_width, len, pen_width,
PATCOPY))) rop2_to_patblt_rop (gcwin32->rop2))))
return FALSE; 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; HBRUSH hbr;
if ((hbr = SelectObject (hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR) if ((hbr = SelectObject (gcwin32->hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR)
return FALSE; return FALSE;
x1 = _x1; x1 = _x1;
if (gcwin32->pen_dashes) x1 += gcwin32->pen_dash_offset;
x1 += gcwin32->pen_dash_offset;
for (n = 0; x1 < x2; n++) 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) if (x1 + len > x2)
len = x2 - x1; len = x2 - x1;
if (n % 2) 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, len, pen_width,
PATCOPY))) rop2_to_patblt_rop (gcwin32->rop2))))
return FALSE; 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; return FALSE;
} }
@ -331,67 +369,54 @@ render_line_horizontal (GdkGCWin32 *gcwin32,
static inline gboolean static inline gboolean
render_line_vertical (GdkGCWin32 *gcwin32, render_line_vertical (GdkGCWin32 *gcwin32,
int x, int x,
int y1, int y1,
int y2) int y2)
{ {
int n; int n;
HDC hdc = gcwin32->hdc; const int pen_width = MAX (gcwin32->pen_width, 1);
int pen_width = gcwin32->pen_width; const int _y1 = y1;
DWORD *dashes;
int num_dashes;
int _y1 = y1;
if (gcwin32->pen_dashes) g_assert (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);
}
y1 = align_with_dash_offset (y1, gcwin32->pen_dashes, gcwin32->pen_num_dashes, gcwin32);
for (n = 0; y1 < y2; n++) 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) if (y1 + len > y2)
len = y2 - y1; len = y2 - y1;
if (n % 2 == 0 && y1 + len > _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, y1 < _y1 ? _y1 : y1,
pen_width, len, pen_width, len,
PATCOPY))) rop2_to_patblt_rop (gcwin32->rop2))))
return FALSE; 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; HBRUSH hbr;
if ((hbr = SelectObject (hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR) if ((hbr = SelectObject (gcwin32->hdc, gcwin32->pen_hbrbg)) == HGDI_ERROR)
return FALSE; return FALSE;
y1 = _y1; y1 = _y1;
if (gcwin32->pen_dashes) y1 += gcwin32->pen_dash_offset;
y1 += gcwin32->pen_dash_offset;
for (n = 0; y1 < y2; n++) 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) if (y1 + len > y2)
len = y2 - y1; len = y2 - y1;
if (n % 2) 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, pen_width, len,
PATCOPY))) rop2_to_patblt_rop (gcwin32->rop2))))
return FALSE; 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; return FALSE;
} }
@ -485,34 +510,6 @@ draw_tiles (GdkDrawable *drawable,
gdk_gc_unref (gc_copy); 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 static void
generic_draw (GdkDrawable *drawable, generic_draw (GdkDrawable *drawable,
GdkGC *gc, GdkGC *gc,
@ -835,9 +832,7 @@ draw_rectangle (GdkGCWin32 *gcwin32,
x -= x_offset; x -= x_offset;
y -= y_offset; y -= y_offset;
if (!filled && (gcwin32->pen_double_dash || if (!filled && MUST_RENDER_DASHES_MANUALLY (gcwin32))
(gcwin32->pen_dashes && (gcwin32->pen_dash_offset ||
!G_WIN32_IS_NT_BASED ()))))
{ {
render_line_vertical (gcwin32, x, y, y+height+1) && render_line_vertical (gcwin32, x, y, y+height+1) &&
render_line_horizontal (gcwin32, x, x+width+1, y) && render_line_horizontal (gcwin32, x, x+width+1, y) &&
@ -1284,9 +1279,7 @@ draw_segments (GdkGCWin32 *gcwin32,
} }
} }
if (gcwin32->pen_double_dash || if (MUST_RENDER_DASHES_MANUALLY (gcwin32))
(gcwin32->pen_dashes && (gcwin32->pen_dash_offset ||
!G_WIN32_IS_NT_BASED ())))
{ {
for (i = 0; i < nsegs; i++) for (i = 0; i < nsegs; i++)
{ {
@ -1315,42 +1308,22 @@ draw_segments (GdkGCWin32 *gcwin32,
else else
GDI_CALL (MoveToEx, (hdc, segs[i].x1, segs[i].y1, NULL)) && GDI_CALL (MoveToEx, (hdc, segs[i].x1, segs[i].y1, NULL)) &&
GDI_CALL (LineTo, (hdc, segs[i].x2, segs[i].y2)); GDI_CALL (LineTo, (hdc, segs[i].x2, segs[i].y2));
} }
} }
else else
{ {
for (i = 0; i < nsegs; i++) 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)); const GdkSegment *ps = &segs[i];
const int x1 = ps->x1, y1 = ps->y1;
int x2 = ps->x2, y2 = ps->y2;
/* not drawing the end pixel does produce a crippled mask, look GDK_NOTE (MISC, g_print (" +%d+%d..+%d+%d", x1, y1, x2, y2));
* e.g. at xpm icons produced with gdk_pixbuf_new_from_xpm_data trough GDI_CALL (MoveToEx, (hdc, x1, y1, NULL)) &&
* gdk_pixbuf_render_threshold_alpha (testgtk folder icon or GDI_CALL (LineTo, (hdc, x2, y2));
* 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) GDK_NOTE (MISC, g_print ("\n"));
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));
}
} }
if (x_offset != 0 || y_offset != 0) if (x_offset != 0 || y_offset != 0)
g_free (segs); g_free (segs);
@ -1393,8 +1366,7 @@ gdk_win32_draw_segments (GdkDrawable *drawable,
region = widen_bounds (&bounds, GDK_GC_WIN32 (gc)->pen_width); region = widen_bounds (&bounds, GDK_GC_WIN32 (gc)->pen_width);
generic_draw (drawable, gc, GDK_GC_FOREGROUND | GDK_GC_FOREGROUND | generic_draw (drawable, gc, GDK_GC_FOREGROUND | LINE_ATTRIBUTES,
LINE_ATTRIBUTES,
draw_segments, region, segs, nsegs); draw_segments, region, segs, nsegs);
gdk_region_destroy (region); gdk_region_destroy (region);
@ -1421,9 +1393,7 @@ draw_lines (GdkGCWin32 *gcwin32,
pts[i].y -= y_offset; pts[i].y -= y_offset;
} }
if (gcwin32->pen_double_dash || if (MUST_RENDER_DASHES_MANUALLY (gcwin32))
(gcwin32->pen_dashes && (gcwin32->pen_dash_offset ||
!G_WIN32_IS_NT_BASED ())))
{ {
for (i = 0; i < npoints - 1; i++) for (i = 0; i < npoints - 1; i++)
{ {

View File

@ -113,6 +113,115 @@ gdk_gc_win32_finalize (GObject *object)
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (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 static void
gdk_win32_gc_values_to_win32values (GdkGCValues *values, gdk_win32_gc_values_to_win32values (GdkGCValues *values,
GdkGCValuesMask mask, GdkGCValuesMask mask,
@ -217,45 +326,6 @@ gdk_win32_gc_values_to_win32values (GdkGCValues *values,
{ {
if (values->stipple != NULL) 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; win32_gc->values_mask |= GDK_GC_STIPPLE;
GDK_NOTE (GC, GDK_NOTE (GC,
(g_print ("%sstipple=%p", s, (g_print ("%sstipple=%p", s,
@ -343,78 +413,32 @@ gdk_win32_gc_values_to_win32values (GdkGCValues *values,
if (mask & GDK_GC_LINE_STYLE) if (mask & GDK_GC_LINE_STYLE)
{ {
switch (values->line_style) win32_gc->line_style = 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->values_mask |= GDK_GC_LINE_STYLE; win32_gc->values_mask |= GDK_GC_LINE_STYLE;
} }
if (mask & GDK_GC_CAP_STYLE) if (mask & GDK_GC_CAP_STYLE)
{ {
win32_gc->pen_style &= ~(PS_ENDCAP_MASK); win32_gc->cap_style = values->cap_style;
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->values_mask |= GDK_GC_CAP_STYLE; win32_gc->values_mask |= GDK_GC_CAP_STYLE;
} }
if (mask & GDK_GC_JOIN_STYLE) if (mask & GDK_GC_JOIN_STYLE)
{ {
win32_gc->pen_style &= ~(PS_JOIN_MASK); win32_gc->join_style = values->join_style;
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->values_mask |= GDK_GC_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))); 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->subwindow_mode = GDK_CLIP_BY_CHILDREN;
win32_gc->graphics_exposures = TRUE; win32_gc->graphics_exposures = TRUE;
win32_gc->pen_width = 0; 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_dashes = NULL;
win32_gc->pen_num_dashes = 0; win32_gc->pen_num_dashes = 0;
win32_gc->pen_dash_offset = 0; win32_gc->pen_dash_offset = 0;
win32_gc->pen_double_dash = FALSE;
win32_gc->pen_hbrbg = NULL; win32_gc->pen_hbrbg = NULL;
win32_gc->values_mask = GDK_GC_FUNCTION | GDK_GC_FILL; 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->graphics_exposures = win32_gc->graphics_exposures;
values->line_width = win32_gc->pen_width; values->line_width = win32_gc->pen_width;
if (win32_gc->pen_style & PS_SOLID) values->line_style = win32_gc->line_style;
values->line_style = GDK_LINE_SOLID; values->cap_style = win32_gc->cap_style;
else if (win32_gc->pen_style & PS_DASH) values->join_style = win32_gc->join_style;
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;
} }
static void static void
@ -573,19 +583,13 @@ gdk_win32_gc_set_dashes (GdkGC *gc,
win32_gc = GDK_GC_WIN32 (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; 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); win32_gc->pen_dashes = g_new (DWORD, n);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
win32_gc->pen_dashes[i] = dash_list[i]; win32_gc->pen_dashes[i] = dash_list[i];
win32_gc->pen_dash_offset = dash_offset; win32_gc->pen_dash_offset = dash_offset;
fixup_pen (win32_gc);
} }
void 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->graphics_exposures = src_win32_gc->graphics_exposures;
dst_win32_gc->pen_width = src_win32_gc->pen_width; dst_win32_gc->pen_width = src_win32_gc->pen_width;
dst_win32_gc->pen_style = src_win32_gc->pen_style; dst_win32_gc->pen_style = src_win32_gc->pen_style;
dst_win32_gc->pen_dashes = src_win32_gc->pen_dashes; dst_win32_gc->line_style = src_win32_gc->line_style;
if (dst_win32_gc->pen_dashes) 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, dst_win32_gc->pen_dashes = g_memdup (src_win32_gc->pen_dashes,
sizeof (DWORD) * src_win32_gc->pen_num_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_num_dashes = src_win32_gc->pen_num_dashes;
dst_win32_gc->pen_dash_offset = src_win32_gc->pen_dash_offset; 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; dst_win32_gc->hdc = NULL;
@ -825,7 +832,6 @@ gdk_win32_hdc_get (GdkDrawable *drawable,
GdkDrawableImplWin32 *impl = NULL; GdkDrawableImplWin32 *impl = NULL;
gboolean ok = TRUE; gboolean ok = TRUE;
COLORREF fg = RGB (0, 0, 0), bg = RGB (255, 255, 255); COLORREF fg = RGB (0, 0, 0), bg = RGB (255, 255, 255);
LOGBRUSH logbrush;
HPEN hpen; HPEN hpen;
HBRUSH hbr; HBRUSH hbr;
@ -858,7 +864,7 @@ gdk_win32_hdc_get (GdkDrawable *drawable,
if (ok && (usage & LINE_ATTRIBUTES)) if (ok && (usage & LINE_ATTRIBUTES))
{ {
/* For drawing GDK_LINE_DOUBLE_DASH */ /* 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)); bg = _gdk_win32_colormap_color (impl->colormap, _gdk_gc_get_bg_pixel (gc));
if ((win32_gc->pen_hbrbg = CreateSolidBrush (bg)) == NULL) if ((win32_gc->pen_hbrbg = CreateSolidBrush (bg)) == NULL)
@ -867,33 +873,26 @@ gdk_win32_hdc_get (GdkDrawable *drawable,
if (ok) if (ok)
{ {
LOGBRUSH logbrush;
DWORD style_count = 0;
const DWORD *style = NULL;
/* Create and select pen */ /* Create and select pen */
logbrush.lbStyle = BS_SOLID; logbrush.lbStyle = BS_SOLID;
logbrush.lbColor = fg; logbrush.lbColor = fg;
logbrush.lbHatch = 0; logbrush.lbHatch = 0;
if (win32_gc->pen_num_dashes > 0 && !G_WIN32_IS_NT_BASED ()) if ((win32_gc->pen_style & PS_STYLE_MASK) == PS_USERSTYLE)
{ {
/* The Win9x GDI is rather limited so we either draw dashed style_count = win32_gc->pen_num_dashes;
* lines ourselves (only horizontal and vertical) or let them be style = win32_gc->pen_dashes;
* 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 ((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;
} }
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) if (ok && SelectObject (win32_gc->hdc, hpen) == NULL)
WIN32_GDI_FAILED ("SelectObject"), ok = FALSE; WIN32_GDI_FAILED ("SelectObject"), ok = FALSE;

View File

@ -274,10 +274,12 @@ struct _GdkGCWin32
gint graphics_exposures; gint graphics_exposures;
gint pen_width; gint pen_width;
DWORD pen_style; DWORD pen_style;
GdkLineStyle line_style;
GdkCapStyle cap_style;
GdkJoinStyle join_style;
DWORD *pen_dashes; /* use for PS_USERSTYLE or step-by-step rendering */ DWORD *pen_dashes; /* use for PS_USERSTYLE or step-by-step rendering */
gint pen_num_dashes; gint pen_num_dashes;
gint pen_dash_offset; gint pen_dash_offset;
gboolean pen_double_dash;
HBRUSH pen_hbrbg; HBRUSH pen_hbrbg;
/* Following fields are valid while the GC exists as a Windows DC */ /* Following fields are valid while the GC exists as a Windows DC */