diff --git a/src/fl_line_style.cxx b/src/fl_line_style.cxx index 18b27be8d..87f00678c 100644 --- a/src/fl_line_style.cxx +++ b/src/fl_line_style.cxx @@ -3,7 +3,7 @@ // // Line style code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2009 by Bill Spitzak and others. +// Copyright 1998-2010 by Bill Spitzak and others. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public @@ -37,6 +37,11 @@ #include "flstring.h" #include +// We save the current line width (absolute value) here. +// This is currently used only for X11 clipping, see src/fl_rect.cxx. +// FIXME: this would probably better be in class Fl:: +int fl_line_width_ = 0; + #ifdef __APPLE_QUARTZ__ float fl_quartz_line_width_ = 1.0f; static enum CGLineCap fl_quartz_line_cap_ = kCGLineCapButt; @@ -53,6 +58,10 @@ void fl_quartz_restore_line_style_() { void Fl_Graphics_Driver::line_style(int style, int width, char* dashes) { + // save line width in global variable for X11 clipping + if (width == 0) fl_line_width_ = 1; + else fl_line_width_ = width>0 ? width : -width; + #if defined(USE_X11) int ndashes = dashes ? strlen(dashes) : 0; // emulate the WIN32 dash patterns on X diff --git a/src/fl_rect.cxx b/src/fl_rect.cxx index 6fce9b912..5f739fa23 100644 --- a/src/fl_rect.cxx +++ b/src/fl_rect.cxx @@ -3,7 +3,7 @@ // // Rectangle drawing routines for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2009 by Bill Spitzak and others. +// Copyright 1998-2010 by Bill Spitzak and others. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public @@ -42,50 +42,119 @@ #include #include +// fl_line_width_ must contain the absolute value of the current +// line width to be used for X11 clipping (see below). +// This is defined in src/fl_line_style.cxx +extern int fl_line_width_; + #ifdef __APPLE_QUARTZ__ extern float fl_quartz_line_width_; #define USINGQUARTZPRINTER (Fl_Surface_Device::surface()->type() == Fl_Printer::device_type) #endif #ifdef USE_X11 - + #ifndef SHRT_MAX #define SHRT_MAX (32767) #endif /* We need to check some coordinates for areas for clipping before we - use X calls, because X can't handle coordinates outside the 16-bit - range. Since windows use relative coordinates > 0, we do also check - for negative values. X11 only, see also STR #2304. + use X functions, because X can't handle coordinates outside the 16-bit + range. Since all windows use relative coordinates > 0, we do also + check for negative values. X11 only, see also STR #2304. + + Note that this is only necessary for large objects, where only a + part of the object is visible. The draw() functions (e.g. box + drawing) must be clipped correctly. This is usually only a matter + for large container widgets. The individual child widgets will be + clipped completely. + + We define the usable X coordinate space as [ -LW : SHRT_MAX - LW ] + where LW = current line width for drawing. This is done so that + horizontal and vertical line drawing works correctly, even in real + border cases, e.g. drawing a rectangle slightly outside the top left + window corner, but with a line width so that a part of the line should + be visible (in this case 2 of 5 pixels): + + fl_line_style (FL_SOLID,5); // line width = 5 + fl_rect (-1,-1,100,100); // top/left: 2 pixels visible + + In this example case, no clipping would be done, because X can + handle it and clip unneeded pixels. + + Todo: Arbitrary line drawings (e.g. polygons) and clip regions + are not yet done. Note: + We could use max. screen coordinates instead of SHRT_MAX, but that - would need more work and would probably be slower. + would need more work and would probably be slower. We assume that + all window coordinates are >= 0 and that no window extends up to + 32767 - LW (where LW = current line width). Thus it is safe to clip + all coordinates to this range before calling X functions. If this + is not true, then clip_to_short() and clip_x() must be redefined. - returns 1, if the area is invisible (clipped), because ... - (a) w or h are <= 0 i.e. nothing is visible - (b) x+w or y+h are < 0 i.e. left of or above visible area - (c) x or y are > SHRT_MAX i.e. right of or below visible area - - returns 0, if the area is potentially visible and X can handle the - clipping. x, y, w, and h may have been adjusted to fit into the - X coordinate space. + It would be somewhat easier if we had fl_clip_w and fl_clip_h, as + defined in FLTK 2.0 (for the upper clipping bounds)... */ + +/* + clip_to_short() returns 1, if the area is invisible (clipped), + because ... + + (a) w or h are <= 0 i.e. nothing is visible + (b) x+w or y+h are < kmin i.e. left of or above visible area + (c) x or y are > kmax i.e. right of or below visible area + + kmin and kmax are the minimal and maximal X coordinate values, + as defined above. In this case x, y, w, and h are not changed. + + It returns 0, if the area is potentially visible and X can handle + clipping. x, y, w, and h may have been adjusted to fit into the + X coordinate space. + + Use this for clipping rectangles, as used in fl_rect() and + fl_rectf(). +*/ + static int clip_to_short(int &x, int &y, int &w, int &h) { - if (w <= 0 || h <= 0) return 1; // (a) - if (x+w < 0 || y+h < 0) return 1; // (b) - if (x > SHRT_MAX || y > SHRT_MAX) return 1; // (c) + int kmin = -fl_line_width_; + int kmax = SHRT_MAX - fl_line_width_; - if (x < 0) { w += x; x = 0; } - if (y < 0) { h += y; y = 0; } - if (x+w > SHRT_MAX) w = SHRT_MAX - x; - if (y+h > SHRT_MAX) h = SHRT_MAX - y; + if (w <= 0 || h <= 0) return 1; // (a) + if (x+w < kmin || y+h < kmin) return 1; // (b) + if (x > kmax || y > kmax) return 1; // (c) + + if (x < kmin) { w -= (kmin-x); x = kmin; } + if (y < kmin) { h -= (kmin-y); y = kmin; } + if (x+w > kmax) w = kmax - x; + if (y+h > kmax) h = kmax - y; return 0; } +/* + clip_x() returns a coordinate value clipped to the 16-bit coordinate + space (see above). This can be used to draw horizontal and vertical + lines that can be handled by X11. Each single coordinate value can + be clipped individually, and the result can be used directly, e.g. + in fl_xyline() and fl_yxline(). Note that this can't be used for + arbitrary lines (not horizontal or vertical). +*/ +int clip_x (int x) { + + int kmin = -fl_line_width_; + int kmax = SHRT_MAX - fl_line_width_; + + if (x < kmin) + x = kmin; + else if (x > kmax) + x = kmax; + return x; +} + #endif // USE_X11 @@ -93,15 +162,7 @@ void Fl_Graphics_Driver::rect(int x, int y, int w, int h) { if (w<=0 || h<=0) return; #if defined(USE_X11) - if (x+w < 0 || y+h < 0 || x > SHRT_MAX || y > SHRT_MAX ) return; - - if (x+w > SHRT_MAX || y+h > SHRT_MAX || - w > SHRT_MAX || h > SHRT_MAX) { // X can't handle clipping - xyline(x, y, x+w-1); - yxline(x+w-1, y, y+h-1); - xyline(x+w-1, y+h-1, x); - yxline(x, y+h-1, y); - } else + if (!clip_to_short(x, y, w, h)) XDrawRectangle(fl_display, fl_window, fl_gc, x, y, w-1, h-1); #elif defined(WIN32) MoveToEx(fl_gc, x, y, 0L); @@ -122,10 +183,8 @@ void Fl_Graphics_Driver::rect(int x, int y, int w, int h) { void Fl_Graphics_Driver::rectf(int x, int y, int w, int h) { if (w<=0 || h<=0) return; #if defined(USE_X11) - if (w && h) { - if (!clip_to_short(x,y,w,h)) - XFillRectangle(fl_display, fl_window, fl_gc, x, y, w, h); - } + if (!clip_to_short(x, y, w, h)) + XFillRectangle(fl_display, fl_window, fl_gc, x, y, w, h); #elif defined(WIN32) RECT rect; rect.left = x; rect.top = y; @@ -143,18 +202,7 @@ void Fl_Graphics_Driver::rectf(int x, int y, int w, int h) { void Fl_Graphics_Driver::xyline(int x, int y, int x1) { #if defined(USE_X11) - // get rid of coordinates outside the 16-bit range - if (y < 0 || y > SHRT_MAX) return; - if (x <= x1) { - if (x1 < 0 || x > SHRT_MAX) return; - if (x < 0) x = 0; - if (x1 > SHRT_MAX) x1 = SHRT_MAX; - } else { // x1 < x - if (x < 0 || x1 > SHRT_MAX) return; - if (x1 < 0) x1 = 0; - if (x > SHRT_MAX) x = SHRT_MAX; - } - XDrawLine(fl_display, fl_window, fl_gc, x, y, x1, y); + XDrawLine(fl_display, fl_window, fl_gc, clip_x(x), clip_x(y), clip_x(x1), clip_x(y)); #elif defined(WIN32) MoveToEx(fl_gc, x, y, 0L); LineTo(fl_gc, x1+1, y); #elif defined(__APPLE_QUARTZ__) @@ -171,8 +219,8 @@ void Fl_Graphics_Driver::xyline(int x, int y, int x1) { void Fl_Graphics_Driver::xyline(int x, int y, int x1, int y2) { #if defined (USE_X11) XPoint p[3]; - p[0].x = x; p[0].y = p[1].y = y; - p[1].x = p[2].x = x1; p[2].y = y2; + p[0].x = clip_x(x); p[0].y = p[1].y = clip_x(y); + p[1].x = p[2].x = clip_x(x1); p[2].y = clip_x(y2); XDrawLines(fl_display, fl_window, fl_gc, p, 3, 0); #elif defined(WIN32) if (y2 < y) y2--; @@ -195,9 +243,9 @@ void Fl_Graphics_Driver::xyline(int x, int y, int x1, int y2) { void Fl_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3) { #if defined(USE_X11) XPoint p[4]; - p[0].x = x; p[0].y = p[1].y = y; - p[1].x = p[2].x = x1; p[2].y = p[3].y = y2; - p[3].x = x3; + p[0].x = clip_x(x); p[0].y = p[1].y = clip_x(y); + p[1].x = p[2].x = clip_x(x1); p[2].y = p[3].y = clip_x(y2); + p[3].x = clip_x(x3); XDrawLines(fl_display, fl_window, fl_gc, p, 4, 0); #elif defined(WIN32) if(x3 < x1) x3--; @@ -221,18 +269,7 @@ void Fl_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3) { void Fl_Graphics_Driver::yxline(int x, int y, int y1) { #if defined(USE_X11) - // get rid of coordinates outside the 16-bit range - if (x < 0 || x > SHRT_MAX) return; - if (y <= y1) { - if (y1 < 0 || y > SHRT_MAX) return; - if (y < 0) y = 0; - if (y1 > SHRT_MAX) y1 = SHRT_MAX; - } else { // y1 < y - if (y < 0 || y1 > SHRT_MAX) return; - if (y1 < 0) y1 = 0; - if (y > SHRT_MAX) y = SHRT_MAX; - } - XDrawLine(fl_display, fl_window, fl_gc, x, y, x, y1); + XDrawLine(fl_display, fl_window, fl_gc, clip_x(x), clip_x(y), clip_x(x), clip_x(y1)); #elif defined(WIN32) if (y1 < y) y1--; else y1++; @@ -251,8 +288,8 @@ void Fl_Graphics_Driver::yxline(int x, int y, int y1) { void Fl_Graphics_Driver::yxline(int x, int y, int y1, int x2) { #if defined(USE_X11) XPoint p[3]; - p[0].x = p[1].x = x; p[0].y = y; - p[1].y = p[2].y = y1; p[2].x = x2; + p[0].x = p[1].x = clip_x(x); p[0].y = clip_x(y); + p[1].y = p[2].y = clip_x(y1); p[2].x = clip_x(x2); XDrawLines(fl_display, fl_window, fl_gc, p, 3, 0); #elif defined(WIN32) if (x2 > x) x2++; @@ -275,9 +312,9 @@ void Fl_Graphics_Driver::yxline(int x, int y, int y1, int x2) { void Fl_Graphics_Driver::yxline(int x, int y, int y1, int x2, int y3) { #if defined(USE_X11) XPoint p[4]; - p[0].x = p[1].x = x; p[0].y = y; - p[1].y = p[2].y = y1; p[2].x = p[3].x = x2; - p[3].y = y3; + p[0].x = p[1].x = clip_x(x); p[0].y = clip_x(y); + p[1].y = p[2].y = clip_x(y1); p[2].x = p[3].x = clip_x(x2); + p[3].y = clip_x(y3); XDrawLines(fl_display, fl_window, fl_gc, p, 4, 0); #elif defined(WIN32) if(y3type() == Fl_Printer::device_type) { // in case of print context, convert coords from logical to device POINT pt[2] = { {x, y}, {x + w, y + h} }; @@ -599,7 +636,6 @@ int Fl_Graphics_Driver::not_clipped(int x, int y, int w, int h) { } return RectInRegion(r,&rect); #elif defined(__APPLE_QUARTZ__) - if (!r) return 1; CGRect arg = fl_cgrectmake_cocoa(x, y, w, h); for (int i = 0; i < r->count; i++) { CGRect test = CGRectIntersection(r->rects[i], arg);