fltk/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_rect.cxx

362 lines
10 KiB
C++
Raw Normal View History

//
// Rectangle drawing routines for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2020 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
/**
\file Fl_OpenGL_Graphics_Driver_rect.cxx
\brief OpenGL specific line and polygon drawing with integer coordinates.
*/
#include <config.h>
#include "Fl_OpenGL_Graphics_Driver.H"
#include <FL/gl.h>
#include <FL/Fl_Gl_Window.H>
#include <FL/Fl_RGB_Image.H>
#include <FL/Fl.H>
#include <FL/math.h>
// --- line and polygon drawing with integer coordinates
void Fl_OpenGL_Graphics_Driver::point(int x, int y) {
glBegin(GL_POINTS);
glVertex2f(x+0.5f, y+0.5f);
glEnd();
}
void Fl_OpenGL_Graphics_Driver::rect(int x, int y, int w, int h) {
float offset = line_width_ / 2.0f;
float xx = x+0.5f, yy = y+0.5f;
float rr = x+w-0.5f, bb = y+h-0.5f;
glRectf(xx-offset, yy-offset, rr+offset, yy+offset);
glRectf(xx-offset, bb-offset, rr+offset, bb+offset);
glRectf(xx-offset, yy-offset, xx+offset, bb+offset);
glRectf(rr-offset, yy-offset, rr+offset, bb+offset);
}
void Fl_OpenGL_Graphics_Driver::rectf(int x, int y, int w, int h) {
if (w<=0 || h<=0) return;
glRectf((GLfloat)x, (GLfloat)y, (GLfloat)(x+w), (GLfloat)(y+h));
}
void Fl_OpenGL_Graphics_Driver::line(int x, int y, int x1, int y1) {
if (x==x1 && y==y1) return;
if (x==x1) {
yxline(x, y, y1);
return;
}
if (y==y1) {
xyline(x, y, x1);
return;
}
float xx = x+0.5f, xx1 = x1+0.5f;
float yy = y+0.5f, yy1 = y1+0.5f;
if (line_width_==1.0f) {
glBegin(GL_LINE_STRIP);
glVertex2f(xx, yy);
glVertex2f(xx1, yy1);
glEnd();
} else {
float dx = xx1-xx, dy = yy1-yy;
float len = sqrtf(dx*dx+dy*dy);
dx = dx/len*line_width_*0.5f;
dy = dy/len*line_width_*0.5f;
glBegin(GL_TRIANGLE_STRIP);
glVertex2f(xx-dy, yy+dx);
glVertex2f(xx+dy, yy-dx);
glVertex2f(xx1-dy, yy1+dx);
glVertex2f(xx1+dy, yy1-dx);
glEnd();
}
}
void Fl_OpenGL_Graphics_Driver::line(int x, int y, int x1, int y1, int x2, int y2) {
// TODO: no corner types (miter) yet
line(x, y, x1, y1);
line(x1, y1, x2, y2);
}
void Fl_OpenGL_Graphics_Driver::xyline(int x, int y, int x1) {
float offset = line_width_ / 2.0f;
float xx = (float)x, yy = y+0.5f, rr = x1+1.0f;
glRectf(xx, yy-offset, rr, yy+offset);
}
void Fl_OpenGL_Graphics_Driver::xyline(int x, int y, int x1, int y2) {
float offset = line_width_ / 2.0f;
float xx = (float)x, yy = y+0.5f, rr = x1+0.5f, bb = y2+1.0f;
glRectf(xx, yy-offset, rr+offset, yy+offset);
glRectf(rr-offset, yy+offset, rr+offset, bb);
}
void Fl_OpenGL_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3) {
float offset = line_width_ / 2.0f;
float xx = (float)x, yy = y+0.5f, xx1 = x1+0.5f, rr = x3+1.0f, bb = y2+0.5f;
glRectf(xx, yy-offset, xx1+offset, yy+offset);
glRectf(xx1-offset, yy+offset, xx1+offset, bb+offset);
glRectf(xx1+offset, bb-offset, rr, bb+offset);
}
void Fl_OpenGL_Graphics_Driver::yxline(int x, int y, int y1) {
float offset = line_width_ / 2.0f;
float xx = x+0.5f, yy = (float)y, bb = y1+1.0f;
glRectf(xx-offset, yy, xx+offset, bb);
}
void Fl_OpenGL_Graphics_Driver::yxline(int x, int y, int y1, int x2) {
float offset = line_width_ / 2.0f;
float xx = x+0.5f, yy = (float)y, rr = x2+1.0f, bb = y1+0.5f;
glRectf(xx-offset, yy, xx+offset, bb+offset);
glRectf(xx+offset, bb-offset, rr, bb+offset);
}
void Fl_OpenGL_Graphics_Driver::yxline(int x, int y, int y1, int x2, int y3) {
float offset = line_width_ / 2.0f;
float xx = x+0.5f, yy = (float)y, yy1 = y1+0.5f, rr = x2+0.5f, bb = y3+1.0f;
glRectf(xx-offset, yy, xx+offset, yy1+offset);
glRectf(xx+offset, yy1-offset, rr+offset, yy1+offset);
glRectf(rr-offset, yy1+offset, rr+offset, bb);
}
void Fl_OpenGL_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) {
glBegin(GL_LINE_LOOP);
glVertex2i(x0, y0);
glVertex2i(x1, y1);
glVertex2i(x2, y2);
glEnd();
}
void Fl_OpenGL_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
glBegin(GL_LINE_LOOP);
glVertex2i(x0, y0);
glVertex2i(x1, y1);
glVertex2i(x2, y2);
glVertex2i(x3, y3);
glEnd();
}
void Fl_OpenGL_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) {
glBegin(GL_POLYGON);
glVertex2i(x0, y0);
glVertex2i(x1, y1);
glVertex2i(x2, y2);
glEnd();
}
void Fl_OpenGL_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
glBegin(GL_POLYGON);
glVertex2i(x0, y0);
glVertex2i(x1, y1);
glVertex2i(x2, y2);
glVertex2i(x3, y3);
glEnd();
}
void Fl_OpenGL_Graphics_Driver::focus_rect(int x, int y, int w, int h) {
float width = line_width_;
int stipple = line_stipple_;
line_style(FL_DOT, 1);
glBegin(GL_LINE_LOOP);
glVertex2f(x+0.5f, y+0.5f);
glVertex2f(x+w+0.5f, y+0.5f);
glVertex2f(x+w+0.5f, y+h+0.5f);
glVertex2f(x+0.5f, y+h+0.5f);
glEnd();
line_style(stipple, (int)width);
}
// -----------------------------------------------------------------------------
static int gl_min(int a, int b) { return (a<b) ? a : b; }
static int gl_max(int a, int b) { return (a>b) ? a : b; }
enum {
kStateFull, // Region is the full window
kStateRect, // Region is a rectangle
kStateEmpty // Region is an empty space
};
typedef struct Fl_Gl_Region {
int x, y, w, h;
int gl_x, gl_y, gl_w, gl_h;
char state;
void set(int inX, int inY, int inW, int inH) {
if (inW<=0 || inH<=0) {
state = kStateEmpty;
x = inX; y = inY; w = 1; h = 1; // or 0?
} else {
x = inX; y = inY; w = inW; h = inH;
state = kStateRect;
}
Fl_Gl_Window *win = Fl_Gl_Window::current()->as_gl_window();
if (win) {
float scale = win->pixels_per_unit();
gl_x = int(x*scale);
gl_y = int((win->h()-h-y+1)*scale);
gl_w = int((w-1)*scale);
gl_h = int((h-1)*scale);
if (inX<=0 && inY<=0 && inX+inW>win->w() && inY+inH>=win->h()) {
state = kStateFull;
}
} else {
state = kStateFull;
}
}
void set_full() { state = kStateFull; }
void set_empty() { state = kStateEmpty; }
void set_intersect(int inX, int inY, int inW, int inH, Fl_Gl_Region &g) {
if (g.state==kStateFull) {
set(inX, inY, inW, inH);
} else if (g.state==kStateEmpty) {
set_empty();
} else {
int rx = gl_max(inX, g.x);
int ry = gl_max(inY, g.y);
int rr = gl_min(inX+inW, g.x+g.w);
int rb = gl_max(inY+inH, g.y+g.h);
set(rx, ry, rr-rx, rb-ry);
}
}
void apply() {
if (state==kStateFull) {
glDisable(GL_SCISSOR_TEST);
} else {
glScissor(gl_x, gl_y, gl_w, gl_h);
glEnable(GL_SCISSOR_TEST);
}
}
} Fl_Gl_Region;
static int gl_rstackptr = 0;
static const int gl_region_stack_max = FL_REGION_STACK_SIZE - 1;
static Fl_Gl_Region gl_rstack[FL_REGION_STACK_SIZE];
/*
Intersect the given rect with the current rect, push the result on the stack,
and apply the new clipping area.
*/
void Fl_OpenGL_Graphics_Driver::push_clip(int x, int y, int w, int h) {
if (gl_rstackptr==gl_region_stack_max) {
Fl::warning("Fl_OpenGL_Graphics_Driver::push_clip: clip stack overflow!\n");
return;
}
if (gl_rstackptr==0) {
gl_rstack[gl_rstackptr].set(x, y, w, h);
} else {
gl_rstack[gl_rstackptr].set_intersect(x, y, w, h, gl_rstack[gl_rstackptr-1]);
}
gl_rstack[gl_rstackptr].apply();
gl_rstackptr++;
}
/*
Remove the current clipping area and apply the previous one on the stack.
*/
void Fl_OpenGL_Graphics_Driver::pop_clip() {
if (gl_rstackptr==0) {
glDisable(GL_SCISSOR_TEST);
Fl::warning("Fl_OpenGL_Graphics_Driver::pop_clip: clip stack underflow!\n");
return;
}
gl_rstackptr--;
restore_clip();
}
/*
Push a full area onton the stack, so no clipping will take place.
*/
void Fl_OpenGL_Graphics_Driver::push_no_clip() {
if (gl_rstackptr==gl_region_stack_max) {
Fl::warning("Fl_OpenGL_Graphics_Driver::push_no_clip: clip stack overflow!\n");
return;
}
gl_rstack[gl_rstackptr].set_full();
gl_rstack[gl_rstackptr].apply();
gl_rstackptr++;
}
/*
We don't know the format of clip regions of the default driver, so return NULL.
*/
Fl_Region Fl_OpenGL_Graphics_Driver::clip_region() {
return NULL;
}
/*
We don't know the format of clip regions of the default driver, so do the best
we can.
*/
void Fl_OpenGL_Graphics_Driver::clip_region(Fl_Region r) {
if (r==NULL) {
glDisable(GL_SCISSOR_TEST);
} else {
restore_clip();
}
}
/*
Apply the current clipping rect.
*/
void Fl_OpenGL_Graphics_Driver::restore_clip() {
if (gl_rstackptr==0) {
glDisable(GL_SCISSOR_TEST);
} else {
gl_rstack[gl_rstackptr-1].apply();
}
}
/*
Does the rectangle intersect the current clip region?
0 = regions don't intersect, nothing to draw
1 = region is fully inside current clipping region
2 = region is partially inside current clipping region
*/
int Fl_OpenGL_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
if (gl_rstackptr==0)
return 1;
Fl_Gl_Region &g = gl_rstack[gl_rstackptr-1];
if (g.state==kStateFull)
return 1;
if (g.state==kStateEmpty)
return 0;
int r = x+w, b = y + h;
int gr = g.x+g.w, gb = g.y+g.h;
if (r<=g.x || x>=gr || b<=g.y || y>=gb) return 0;
if (x>=g.x && y>=g.y && r<=gr && b<=gb) return 1;
return 2;
}
/*
Calculate the intersection of the given rect and the clipping area.
Return 0 if the result did not change.
*/
int Fl_OpenGL_Graphics_Driver::clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) {
X = x; Y = y; W = w; H = h;
if (gl_rstackptr==0)
return 0;
Fl_Gl_Region &g = gl_rstack[gl_rstackptr-1];
if (g.state==kStateFull)
return 0;
int r = x+w, b = y + h;
int gr = g.x+g.w, gb = g.y+g.h;
X = gl_max(x, g.x);
Y = gl_max(y, g.y);
W = gl_min(r, gr) - X;
H = gl_min(b, gb) - Y;
return (x!=X || y!=Y || w!=W || h!=H);
}