From db0a1f4baeb928b54d328d5dfbd0ec37b0b58bd3 Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sun, 6 Feb 2022 15:22:24 +0100 Subject: OpenGL implementation of all `fl_` "Drawing Fast Shapes" graphics calls (#385) * Fix build system for unites, * Updated unittest to check OpenGL drawing. Making sure that OpenGL drawing is exactly the same as native drawing to make FLTK widget rendering look the same in GL windows. * Make OpenGL optional. * Implemented clipping in OpenGL * unites drawing fast shapes * Fixed CMake * Updating unittest. Added tests for fl_pi and fl_arc (int) Renamed tab to render complex shapes. * Improved OpenGL FLTK drawing emulation. * Fixed GTK ROUND DOWN BOX * Fixing Makefile for unittest * Correctly aligning OpenGL text. * Fixed text alignment in GL windows. Explained the "FLTK over GL " example in Cube. * Overlapping test. * Better GL graphics alignment. * Drawing the focus rect. * Adding Alpha Channel support for GL. * Added FLTK-on-GL documentation. --- src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver.H | 19 +- .../OpenGL/Fl_OpenGL_Graphics_Driver_arci.cxx | 24 +- .../OpenGL/Fl_OpenGL_Graphics_Driver_color.cxx | 38 +-- .../Fl_OpenGL_Graphics_Driver_line_style.cxx | 30 +- .../OpenGL/Fl_OpenGL_Graphics_Driver_rect.cxx | 325 ++++++++++++++++----- 5 files changed, 312 insertions(+), 124 deletions(-) (limited to 'src/drivers/OpenGL') diff --git a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver.H b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver.H index b3c7e9de7..274d911dc 100644 --- a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver.H +++ b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver.H @@ -24,12 +24,20 @@ #define FL_OPENGL_GRAPHICS_DRIVER_H #include +#include /** \brief OpenGL specific graphics class. */ class FL_EXPORT Fl_OpenGL_Graphics_Driver : public Fl_Graphics_Driver { public: + float pixels_per_unit_; + float line_width_; + int line_stipple_; + Fl_OpenGL_Graphics_Driver() : + pixels_per_unit_(1.0f), + line_width_(1.0f), + line_stipple_(FL_SOLID) { } // --- line and polygon drawing with integer coordinates void point(int x, int y); void rect(int x, int y, int w, int h); @@ -46,10 +54,17 @@ public: void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); void polygon(int x0, int y0, int x1, int y1, int x2, int y2); void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); + void focus_rect(int x, int y, int w, int h); + // ---- clipping void push_clip(int x, int y, int w, int h); - int clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H); - int not_clipped(int x, int y, int w, int h); + void pop_clip(); + void push_no_clip(); + Fl_Region clip_region(); + void clip_region(Fl_Region r); void restore_clip(); + int not_clipped(int x, int y, int w, int h); + int clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H); + // ---- matrix transformed drawing void transformed_vertex(double xf, double yf); void begin_points(); void end_points(); diff --git a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_arci.cxx b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_arci.cxx index f933d10e2..73b216f01 100644 --- a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_arci.cxx +++ b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_arci.cxx @@ -27,20 +27,21 @@ #include #include #define _USE_MATH_DEFINES -#include +#include void Fl_OpenGL_Graphics_Driver::arc(int x,int y,int w,int h,double a1,double a2) { if (w <= 0 || h <= 0) return; while (a2h) rMax = rx; else rMax = ry; int nSeg = (int)(10 * sqrt(rMax))+1; double incr = (a2-a1)/(double)nSeg; glBegin(GL_LINE_STRIP); - for (int i=0; ih) rMax = rx; else rMax = ry; int nSeg = (int)(10 * sqrt(rMax))+1; double incr = (a2-a1)/(double)nSeg; glBegin(GL_TRIANGLE_FAN); glVertex2d(cx, cy); - for (int i=0; i= size-2) glIndexi(size-1); - else glIndexi(i); - } else { - glIndexi(i ? i : FL_GRAY_RAMP); - } - return; - } -#else - if (fl_overlay) {glIndexi(int(fl_xpixel(i))); return;} -#endif -#endif -*/ if (i & 0xffffff00) { - unsigned rgb = (unsigned)i; - color((uchar)(rgb >> 24), (uchar)(rgb >> 16), (uchar)(rgb >> 8)); - } else { + unsigned rgba = ((unsigned)i)^0x000000ff; Fl_Graphics_Driver::color(i); - uchar red, green, blue; - Fl::get_color(i, red, green, blue); - glColor3ub(red, green, blue); + glColor4ub(rgba>>24, rgba>>16, rgba>>8, rgba); + } else { + unsigned rgba = ((unsigned)fl_cmap[i])^0x000000ff; + Fl_Graphics_Driver::color(fl_cmap[i]); + glColor4ub(rgba>>24, rgba>>16, rgba>>8, rgba); } } -void Fl_OpenGL_Graphics_Driver::color(uchar r,uchar g,uchar b) { +void Fl_OpenGL_Graphics_Driver::color(uchar r, uchar g, uchar b) { Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) ); glColor3ub(r,g,b); } diff --git a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_line_style.cxx b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_line_style.cxx index 89dea699d..c1963352b 100644 --- a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_line_style.cxx +++ b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_line_style.cxx @@ -31,27 +31,41 @@ // OpenGL implementation does not support cap and join types void Fl_OpenGL_Graphics_Driver::line_style(int style, int width, char* dashes) { - if (width<1) width = 1; + line_width_ = width; + + int stipple = style & 0x00ff; + line_stipple_ = stipple; +// int cap = style & 0x0f00; +// int join = style & 0xf000; - if (style==FL_SOLID) { + if (stipple==FL_SOLID) { glLineStipple(1, 0xFFFF); glDisable(GL_LINE_STIPPLE); } else { - switch (style) { + char enable = 1; + switch (stipple & 0x00ff) { case FL_DASH: - glLineStipple(width, 0x0F0F); // ....****....**** + glLineStipple(pixels_per_unit_*line_width_, 0x0F0F); // ....****....**** break; case FL_DOT: - glLineStipple(width, 0x5555); // .*.*.*.*.*.*.*.* + glLineStipple(pixels_per_unit_*line_width_, 0x5555); // .*.*.*.*.*.*.*.* break; case FL_DASHDOT: - glLineStipple(width, 0x2727); // ..*..***..*..*** + glLineStipple(pixels_per_unit_*line_width_, 0x2727); // ..*..***..*..*** break; case FL_DASHDOTDOT: - glLineStipple(width, 0x5757); // .*.*.***.*.*.*** + glLineStipple(pixels_per_unit_*line_width_, 0x5757); // .*.*.***.*.*.*** break; + default: + glLineStipple(1, 0xFFFF); + enable = 0; } - glEnable(GL_LINE_STIPPLE); + if (enable) + glEnable(GL_LINE_STIPPLE); + else + glDisable(GL_LINE_STIPPLE); } + glLineWidth( (GLfloat)(pixels_per_unit_ * line_width_) ); + glPointSize( (GLfloat)(pixels_per_unit_) ); } diff --git a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_rect.cxx b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_rect.cxx index f47aaab27..5b61b3a11 100644 --- a/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_rect.cxx +++ b/src/drivers/OpenGL/Fl_OpenGL_Graphics_Driver_rect.cxx @@ -26,105 +26,109 @@ #include #include #include +#include // --- line and polygon drawing with integer coordinates void Fl_OpenGL_Graphics_Driver::point(int x, int y) { glBegin(GL_POINTS); - glVertex2i(x, y); + glVertex2f(x+0.5f, y+0.5f); glEnd(); } void Fl_OpenGL_Graphics_Driver::rect(int x, int y, int w, int h) { - glBegin(GL_LINE_LOOP); - glVertex2i(x, y); - glVertex2i(x+w, y); - glVertex2i(x+w, y+h); - glVertex2i(x, y+h); - glEnd(); + 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; - // OpenGL has the natural origin at the bottom left. Drawing in FLTK - // coordinates requires that we shift the rectangle one pixel up. - glBegin(GL_POLYGON); - glVertex2i(x, y-1); - glVertex2i(x+w, y-1); - glVertex2i(x+w, y+h-1); - glVertex2i(x, y+h-1); - glEnd(); + glRectf(x, y, x+w, y+h); } void Fl_OpenGL_Graphics_Driver::line(int x, int y, int x1, int y1) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x1, y1); - glEnd(); - point(x1, 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) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x1, y1); - glVertex2i(x2, y2); - glEnd(); - point(x2, 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) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x1, y); - glEnd(); - point(x1, y); + float offset = line_width_ / 2.0f; + float xx = 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) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x1, y); - glVertex2i(x1, y2); - glEnd(); - point(x1, y2); + float offset = line_width_ / 2.0f; + float xx = 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) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x1, y); - glVertex2i(x1, y2); - glVertex2i(x3, y2); - glEnd(); - point(x3, y2); + float offset = line_width_ / 2.0f; + float xx = x, yy = y+0.5f, xx1 = x1+0.5f, rr = x3+1.0f, bb = y2+0.5; + 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) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x, y1); - glEnd(); - point(x, y1); + float offset = line_width_ / 2.0f; + float xx = x+0.5f, yy = 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) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x, y1); - glVertex2i(x2, y1); - glEnd(); - point(x2, y1); + float offset = line_width_ / 2.0f; + float xx = x+0.5f, yy = 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) { - glBegin(GL_LINE_STRIP); - glVertex2i(x, y); - glVertex2i(x, y1); - glVertex2i(x2, y1); - glVertex2i(x2, y3); - glEnd(); - point(x2, y3); + float offset = line_width_ / 2.0f; + float xx = x+0.5f, yy = 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) { @@ -161,24 +165,197 @@ void Fl_OpenGL_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, 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, width); +} + + +// ----------------------------------------------------------------------------- + +static int gl_min(int a, int b) { return (ab) ? 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 = x*scale; + gl_y = (win->h()-h-y+1)*scale; + gl_w = (w-1)*scale; + gl_h = (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) { - // TODO: implement OpenGL clipping - if (rstackptr < region_stack_max) rstack[++rstackptr] = 0L; - else Fl::warning("Fl_OpenGL_Graphics_Driver::push_clip: clip stack overflow!\n"); + 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++; } -int Fl_OpenGL_Graphics_Driver::clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) { - // TODO: implement OpenGL clipping - X = x; Y = y; W = w; H = h; - return 0; +/* + 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(); } -int Fl_OpenGL_Graphics_Driver::not_clipped(int x, int y, int w, int h) { - // TODO: implement OpenGL clipping - return 1; +/* + 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() { - // TODO: implement OpenGL clipping - fl_clip_state_number++; + 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); } -- cgit v1.2.3