More clipping updates for X11 with coordinates > 32767.

All horizontal and vertical line drawings, as well as fl_rect(), fl_rectf(),
and fl_point() should be clipped correctly. Nothing has been done for
arbitrary (non-orthogonal) lines or other drawings, but this should suffice
for box drawings of large container widgets (STR #2304).


git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@7888 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Albrecht Schlosser 2010-11-23 17:18:42 +00:00
parent e7c17c4010
commit 36ffb5c5d5
2 changed files with 118 additions and 73 deletions

View File

@ -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 <stdio.h>
// 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

View File

@ -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 <FL/fl_draw.H>
#include <FL/x.H>
// 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(y3<y1) y3--;
@ -454,7 +491,7 @@ void Fl_Graphics_Driver::polygon(int x, int y, int x1, int y1, int x2, int y2, i
void Fl_Graphics_Driver::point(int x, int y) {
#if defined(USE_X11)
XDrawPoint(fl_display, fl_window, fl_gc, x, y);
XDrawPoint(fl_display, fl_window, fl_gc, clip_x(x), clip_x(y));
#elif defined(WIN32)
SetPixel(fl_gc, x, y, fl_RGB());
#elif defined(__APPLE_QUARTZ__)
@ -481,6 +518,7 @@ int fl_clip_state_number=0; // used by gl_begin.cxx to update GL clip
// MSWindows equivalent exists, implemented inline in win32.H
Fl_Region XRectangleRegion(int x, int y, int w, int h) {
XRectangle R;
clip_to_short(x, y, w, h);
R.x = x; R.y = y; R.width = w; R.height = h;
Fl_Region r = XCreateRegion();
XUnionRectWithRegion(&R, r, r);
@ -582,13 +620,12 @@ void Fl_Graphics_Driver::pop_clip() {
int Fl_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
if (x+w <= 0 || y+h <= 0) return 0;
Fl_Region r = rstack[rstackptr];
#if defined (USE_X11)
if (!r) return 1;
#if defined (USE_X11)
// get rid of coordinates outside the 16-bit range the X calls take.
if (clip_to_short(x,y,w,h)) return 0; // clipped
return XRectInRegion(r, x, y, w, h);
#elif defined(WIN32)
if (!r) return 1;
RECT rect;
if (Fl_Surface_Device::surface()->type() == 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);