From af58c7c81b5614a0b54c7a89b94a0e476356da78 Mon Sep 17 00:00:00 2001 From: John Wiggins Date: Thu, 28 Jan 2021 10:46:06 +0100 Subject: [PATCH] Split out the GL backend (#392) This splits the kiva OpenGL backend out into its own independent C-extension. Until now, it was part of the Agg backend which forced users to have OpenGL installed even if they only wanted to use the software renderer. --- .gitignore | 4 +- MANIFEST.in | 1 + enable/qt4/base_window.py | 1 + enable/qt4/gl.py | 28 +- enable/wx/gl.py | 5 +- kiva/agg/setup.py | 25 +- kiva/agg/src/gl_graphics_context.h | 159 -- kiva/agg/src/gl_test/Lesson2.cpp | 420 ----- kiva/agg/src/gl_test/Lesson2.h | 32 - kiva/agg/src/gl_test/gl_test.cpp | 67 - kiva/agg/src/gl_test/gl_test.vcproj | 140 -- kiva/agg/src/graphics_context.i | 99 -- kiva/agg/src/kiva.sln | 4 - kiva/agg/src/kiva.vcproj | 9 - kiva/agg/src/{gl => osx}/agg_bmp.cpp | 106 +- kiva/agg/src/{gl => osx}/agg_bmp.h | 31 +- kiva/agg/src/{gl => osx}/plat_support.i | 48 +- kiva/gl.py | 428 ----- kiva/gl/LICENSES/LICENSE_Agg | 65 + kiva/gl/LICENSES/LICENSE_Matplotlib | 99 ++ kiva/gl/__init__.py | 506 ++++++ kiva/gl/gl.i | 13 + kiva/gl/setup.py | 93 + kiva/gl/src/agg/agg_array.h | 1119 ++++++++++++ kiva/gl/src/agg/agg_basics.h | 560 ++++++ kiva/gl/src/agg/agg_bezier_arc.cpp | 255 +++ kiva/gl/src/agg/agg_bezier_arc.h | 154 ++ kiva/gl/src/agg/agg_color_rgba.h | 1351 +++++++++++++++ kiva/gl/src/agg/agg_config.h | 44 + kiva/gl/src/agg/agg_conv_transform.h | 67 + kiva/gl/src/agg/agg_gamma_functions.h | 128 ++ kiva/gl/src/agg/agg_gamma_lut.h | 305 ++++ kiva/gl/src/agg/agg_math.h | 437 +++++ kiva/gl/src/agg/agg_path_storage.h | 1539 +++++++++++++++++ kiva/gl/src/agg/agg_sqrt_tables.cpp | 115 ++ kiva/gl/src/agg/agg_trans_affine.cpp | 190 ++ kiva/gl/src/agg/agg_trans_affine.h | 518 ++++++ kiva/gl/src/kiva_gl_affine_helpers.cpp | 55 + kiva/gl/src/kiva_gl_affine_helpers.h | 15 + kiva/gl/src/kiva_gl_basics.h | 125 ++ kiva/gl/src/kiva_gl_compiled_path.cpp | 330 ++++ kiva/gl/src/kiva_gl_compiled_path.h | 129 ++ kiva/gl/src/kiva_gl_constants.h | 143 ++ kiva/gl/src/kiva_gl_dash_type.h | 54 + kiva/gl/src/kiva_gl_exceptions.h | 16 + kiva/gl/src/kiva_gl_font_type.cpp | 121 ++ kiva/gl/src/kiva_gl_font_type.h | 56 + .../src/kiva_gl_graphics_context.cpp} | 474 ++--- kiva/gl/src/kiva_gl_graphics_context.h | 155 ++ kiva/gl/src/kiva_gl_graphics_context_base.cpp | 426 +++++ kiva/gl/src/kiva_gl_graphics_context_base.h | 239 +++ kiva/gl/src/kiva_gl_graphics_state.h | 97 ++ kiva/gl/src/kiva_gl_rect.cpp | 292 ++++ kiva/gl/src/kiva_gl_rect.h | 112 ++ kiva/gl/src/swig/affine_matrix.i | 218 +++ kiva/gl/src/swig/agg_std_string.i | 34 + kiva/gl/src/swig/agg_typemaps.i | 313 ++++ kiva/gl/src/swig/compiled_path.i | 122 ++ kiva/gl/src/swig/constants.i | 142 ++ kiva/gl/src/swig/font_type.i | 78 + kiva/gl/src/swig/graphics_context.i | 387 +++++ kiva/gl/src/swig/numpy.i | 351 ++++ kiva/gl/src/swig/rect.i | 38 + kiva/gl/src/swig/rgba.i | 95 + kiva/gl/src/swig/rgba_array.i | 91 + kiva/gl/src/swig/sequence_to_array.i | 34 + kiva/setup.py | 1 + kiva/tests/test_gl_drawing.py | 2 +- kiva/tests/test_qpainter_drawing.py | 15 + setup.py | 10 + 70 files changed, 12194 insertions(+), 1741 deletions(-) delete mode 100644 kiva/agg/src/gl_graphics_context.h delete mode 100644 kiva/agg/src/gl_test/Lesson2.cpp delete mode 100644 kiva/agg/src/gl_test/Lesson2.h delete mode 100644 kiva/agg/src/gl_test/gl_test.cpp delete mode 100644 kiva/agg/src/gl_test/gl_test.vcproj rename kiva/agg/src/{gl => osx}/agg_bmp.cpp (63%) rename kiva/agg/src/{gl => osx}/agg_bmp.h (84%) rename kiva/agg/src/{gl => osx}/plat_support.i (59%) delete mode 100644 kiva/gl.py create mode 100644 kiva/gl/LICENSES/LICENSE_Agg create mode 100644 kiva/gl/LICENSES/LICENSE_Matplotlib create mode 100644 kiva/gl/__init__.py create mode 100644 kiva/gl/gl.i create mode 100644 kiva/gl/setup.py create mode 100644 kiva/gl/src/agg/agg_array.h create mode 100644 kiva/gl/src/agg/agg_basics.h create mode 100644 kiva/gl/src/agg/agg_bezier_arc.cpp create mode 100644 kiva/gl/src/agg/agg_bezier_arc.h create mode 100644 kiva/gl/src/agg/agg_color_rgba.h create mode 100644 kiva/gl/src/agg/agg_config.h create mode 100644 kiva/gl/src/agg/agg_conv_transform.h create mode 100644 kiva/gl/src/agg/agg_gamma_functions.h create mode 100644 kiva/gl/src/agg/agg_gamma_lut.h create mode 100644 kiva/gl/src/agg/agg_math.h create mode 100644 kiva/gl/src/agg/agg_path_storage.h create mode 100644 kiva/gl/src/agg/agg_sqrt_tables.cpp create mode 100644 kiva/gl/src/agg/agg_trans_affine.cpp create mode 100644 kiva/gl/src/agg/agg_trans_affine.h create mode 100644 kiva/gl/src/kiva_gl_affine_helpers.cpp create mode 100644 kiva/gl/src/kiva_gl_affine_helpers.h create mode 100644 kiva/gl/src/kiva_gl_basics.h create mode 100644 kiva/gl/src/kiva_gl_compiled_path.cpp create mode 100644 kiva/gl/src/kiva_gl_compiled_path.h create mode 100644 kiva/gl/src/kiva_gl_constants.h create mode 100644 kiva/gl/src/kiva_gl_dash_type.h create mode 100644 kiva/gl/src/kiva_gl_exceptions.h create mode 100755 kiva/gl/src/kiva_gl_font_type.cpp create mode 100644 kiva/gl/src/kiva_gl_font_type.h rename kiva/{agg/src/gl_graphics_context.cpp => gl/src/kiva_gl_graphics_context.cpp} (70%) create mode 100644 kiva/gl/src/kiva_gl_graphics_context.h create mode 100755 kiva/gl/src/kiva_gl_graphics_context_base.cpp create mode 100644 kiva/gl/src/kiva_gl_graphics_context_base.h create mode 100644 kiva/gl/src/kiva_gl_graphics_state.h create mode 100755 kiva/gl/src/kiva_gl_rect.cpp create mode 100644 kiva/gl/src/kiva_gl_rect.h create mode 100644 kiva/gl/src/swig/affine_matrix.i create mode 100644 kiva/gl/src/swig/agg_std_string.i create mode 100644 kiva/gl/src/swig/agg_typemaps.i create mode 100644 kiva/gl/src/swig/compiled_path.i create mode 100644 kiva/gl/src/swig/constants.i create mode 100644 kiva/gl/src/swig/font_type.i create mode 100644 kiva/gl/src/swig/graphics_context.i create mode 100644 kiva/gl/src/swig/numpy.i create mode 100644 kiva/gl/src/swig/rect.i create mode 100644 kiva/gl/src/swig/rgba.i create mode 100644 kiva/gl/src/swig/rgba_array.i create mode 100644 kiva/gl/src/swig/sequence_to_array.i diff --git a/.gitignore b/.gitignore index 9561042fd..74c12bc4b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,10 +21,12 @@ dist/ kiva/agg/agg.py kiva/agg/agg_wrap.cpp kiva/agg/plat_support.py -kiva/agg/src/gl/plat_support_wrap.cpp kiva/agg/src/gtk1/plat_support_wrap.cpp +kiva/agg/src/osx/plat_support_wrap.cpp kiva/agg/src/win32/plat_support_wrap.cpp kiva/agg/src/x11/plat_support_wrap.cpp +kiva/gl/gl.py +kiva/gl/gl_wrap.cpp kiva/quartz/ABCGI.c kiva/quartz/CTFont.c diff --git a/MANIFEST.in b/MANIFEST.in index dbbe094b7..cb1543f21 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -11,5 +11,6 @@ include docs/kiva/agg/notes recursive-include docs *.py *.rst *.txt *.css *.png *.ico *.doc recursive-include enable/examples *.py *.svg *.jpg *.enaml recursive-include kiva/examples *.py *.txt *.gif *.jpg +recursive-include kiva/gl *.h *.cpp *.i LICENSE_* recursive-include kiva/quartz *.pyx *.pxi *.pxd mac_context*.* recursive-include kiva/fonttools/tests/data *.ttc *.ttf diff --git a/enable/qt4/base_window.py b/enable/qt4/base_window.py index 2894ad45b..83e7ce05d 100644 --- a/enable/qt4/base_window.py +++ b/enable/qt4/base_window.py @@ -270,6 +270,7 @@ def closeEvent(self, event): def paintEvent(self, event): super(_QtGLWindow, self).paintEvent(event) self.handler.paintEvent(event) + self.swapBuffers() def resizeEvent(self, event): super(_QtGLWindow, self).resizeEvent(event) diff --git a/enable/qt4/gl.py b/enable/qt4/gl.py index 94e3379ba..19c1eb319 100644 --- a/enable/qt4/gl.py +++ b/enable/qt4/gl.py @@ -1,4 +1,4 @@ -#------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------- # Copyright (c) 2011, Enthought, Inc. # All rights reserved. # @@ -7,35 +7,30 @@ # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! -#------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------- +from enable.qt4.base_window import BaseGLWindow +from enable.qt4.scrollbar import NativeScrollBar +from kiva.gl import CompiledPath, FakePygletContext, GraphicsContext -import pyglet -pyglet.options['shadow_window'] = False - -from traits.api import Bool, Instance -from kiva.gl import CompiledPath, GraphicsContext - -from .base_window import BaseGLWindow -from .scrollbar import NativeScrollBar class Window(BaseGLWindow): def _create_gc(self, size, pix_format=None): """ Create a GraphicsContext instance. """ - from pyglet.gl import Context - gc = GraphicsContext((size[0]+1, size[1]+1)) - self._pyglet_gl_context = Context() + self._fake_pyglet_context = FakePygletContext() gc.gl_init() gc.translate_ctm(0.5, 0.5) return gc def _init_gc(self): - """ Gives the GC a chance to initialize itself before components perform layout - and draw. This is called every time through the paint loop. + """ Gives the GC a chance to initialize itself before components + perform layout and draw. + + This is called every time through the paint loop. """ - self._pyglet_gl_context.set_current() self.control.makeCurrent() + self._fake_pyglet_context.set_current() super(Window, self)._init_gc() def _paint(self, event=None): @@ -54,6 +49,7 @@ def _paint(self, event=None): self.component.draw(self._gc, view_bounds=(0, 0, size[0], size[1])) self._update_region = [] + def font_metrics_provider(): from kiva.fonttools import Font gc = GraphicsContext((1, 1)) diff --git a/enable/wx/gl.py b/enable/wx/gl.py index e16d4f80f..a2410e3c9 100644 --- a/enable/wx/gl.py +++ b/enable/wx/gl.py @@ -5,7 +5,7 @@ from wx.glcanvas import GLCanvas from traits.api import Instance -from kiva.gl import CompiledPath, GraphicsContext +from kiva.gl import CompiledPath, FakePygletContext, GraphicsContext from .base_window import BaseWindow from .scrollbar import NativeScrollBar @@ -33,8 +33,7 @@ def _create_gc(self, size, pix_format=None): """ gc = GraphicsContext((size[0]+1,size[1]+1)) if self._pyglet_gl_context is None: - from pyglet.gl import Context - self._pyglet_gl_context = Context() + self._pyglet_gl_context = FakePygletContext() gc.gl_init() gc.translate_ctm(0.5, 0.5) return gc diff --git a/kiva/agg/setup.py b/kiva/agg/setup.py index 3860fbf74..8d8e8e225 100644 --- a/kiva/agg/setup.py +++ b/kiva/agg/setup.py @@ -79,7 +79,7 @@ def configuration(parent_package='', top_path=None): if sys.platform=='win32': plat = 'win32' elif sys.platform == 'darwin': - plat = 'gl' + plat = 'osx' else: #plat = 'gtk1' # use with gtk1, it's fast plat = 'x11' # use with gtk2, it's slow but reliable @@ -144,8 +144,7 @@ def get_ft2_sources(name_info, build_dir): macros = [] kiva_include_dirs = ['src'] + agg_include_dirs - config.add_library('kiva_src', - ['src/kiva_*.cpp', 'src/gl_graphics_context.cpp'], + config.add_library('kiva_src', ['src/kiva_*.cpp'], include_dirs = kiva_include_dirs, # Use "macros" instead of "define_macros" because the # latter is only used for extensions, and not clibs @@ -177,11 +176,11 @@ def get_ft2_sources(name_info, build_dir): if use_32bit_workaround: define_macros.append(("ALWAYS_32BIT_WORKAROUND", 1)) - # Options to make OS X link OpenGL + # Options to make OS X link if '64bit' not in platform.architecture(): - darwin_frameworks = ['Carbon', 'ApplicationServices', 'OpenGL'] + darwin_frameworks = ['Carbon', 'ApplicationServices'] else: - darwin_frameworks = ['ApplicationServices', 'OpenGL'] + darwin_frameworks = ['ApplicationServices'] darwin_extra_link_args = [] for framework in darwin_frameworks: @@ -198,13 +197,8 @@ def get_ft2_sources(name_info, build_dir): build_info = {} kiva_lib = 'kiva_src' build_libraries = [kiva_lib, agg_lib, freetype_lib] - if sys.platform == "win32": - build_libraries += ["opengl32", "glu32"] - elif sys.platform == "darwin": + if sys.platform == "darwin": dict_append(build_info, **darwin_opengl_opts) - else: - # This should work for most linuxes (linuces?) - build_libraries += ["GL", "GLU"] dict_append(build_info, sources = ['agg.i'], include_dirs = kiva_include_dirs, @@ -220,7 +214,7 @@ def get_ft2_sources(name_info, build_dir): sources = [os.path.join('src',plat,'plat_support.i'), os.path.join('src',plat,'agg_bmp.cpp'), ] - if plat != 'gl': + if plat != 'osx': sources.append(os.path.join('src',plat,'agg_platform_specific.cpp')) plat_info = {} @@ -249,12 +243,9 @@ def get_ft2_sources(name_info, build_dir): #x11_info = get_info('x11',notfound_action=1) #dict_append(plat_info,**x11_info) - elif plat == 'gl': + elif plat == 'osx': if sys.platform == 'darwin': dict_append(plat_info, **darwin_opengl_opts) - else: - msg = "OpenGL build support only on MacOSX right now." - raise NotImplementedError(msg) config.add_extension('_plat_support', diff --git a/kiva/agg/src/gl_graphics_context.h b/kiva/agg/src/gl_graphics_context.h deleted file mode 100644 index 404dff07c..000000000 --- a/kiva/agg/src/gl_graphics_context.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef KIVA_GL_GRAPHICS_CONTEXT_H -#define KIVA_GL_GRAPHICS_CONTEXT_H - -#ifdef _MSC_VER -#pragma warning(disable:4786) -#endif - -#ifdef __DARWIN__ - #include - #include -#else - #ifdef _MSC_VER - #include - #endif - #include - #include - - // The following mechanism is necessary in order to use MultiDrawElements - // on Windows with mingw. - // 11/24/2010: glMultiDrawElements is not being used right now in the GL - // backend, so comment this out for the time being, especially since it - // causes build problems with 64-bit mingw. - //#ifdef __MINGW32__ - // #define GL_GLEXT_PROTOTYPES 1 - // #include - // #define MULTI_DRAW_ELEMENTS glMultiDrawElementsEXT - //#endif -#endif - -#include "agg_basics.h" - -#include "kiva_compiled_path.h" -#include "kiva_graphics_context_base.h" -#include "kiva_pix_format.h" - - -namespace kiva -{ - - // This function pointer is used by various draw_marker functions - class gl_graphics_context; - typedef void(gl_graphics_context::* PathDefinitionFunc)(int); - - class gl_graphics_context : public graphics_context_base - { - public: - - gl_graphics_context(int width, int height, - kiva::pix_format_e format=kiva::pix_format_rgb24); - ~gl_graphics_context(); - - //--------------------------------------------------------------- - // GL-specific methods - //--------------------------------------------------------------- - void gl_init(); - void gl_cleanup(); - void begin_page(); - void gl_render_path(kiva::compiled_path *path, bool polygon=false, bool fill=false); - void gl_render_points(double** points, bool polygon, bool fill, - kiva::draw_mode_e mode = FILL); - - //--------------------------------------------------------------- - // GraphicsContextBase interface - //--------------------------------------------------------------- - - kiva::pix_format_e format(); - void save_state(); - void restore_state(); - - //--------------------------------------------------------------- - // Clipping path manipulation - //--------------------------------------------------------------- - void clip(); - void even_odd_clip(); - void clip_to_rect(double x, double y, double sx, double sy); - void clip_to_rect(kiva::rect_type &rect); - void clip_to_rects(double* new_rects, int Nrects); - void clip_to_rects(kiva::rect_list_type &rects); - kiva::rect_type transform_clip_rectangle(const kiva::rect_type &rect); - void clear_clip_path(); - - int get_num_clip_regions(); - kiva::rect_type get_clip_region(unsigned int i); - - //--------------------------------------------------------------- - // Painting paths (drawing and filling contours) - //--------------------------------------------------------------- - void clear(agg24::rgba value=agg24::rgba(1,1,1,1)); - void fill_path(); - void eof_fill_path(); - void stroke_path(); - // empty function; for some reason this is abstract in the base class - inline void _stroke_path() { } - - void draw_path(draw_mode_e mode=FILL_STROKE); - void draw_rect(double rect[4], - draw_mode_e mode=FILL_STROKE); - - int draw_marker_at_points(double* pts,int Npts,int size, - agg24::marker_e type=agg24::marker_square); - - void draw_path_at_points(double* pts,int Npts, - kiva::compiled_path& marker, - draw_mode_e mode); - - inline bool show_text(char *text) { return false; } - - void draw_glyphs(kiva::graphics_context_base* img, double tx, double ty); - int draw_image(kiva::graphics_context_base* img, double rect[4], bool force_copy=false); - int draw_image(kiva::graphics_context_base* img); - - protected: - - void draw_display_list_at_pts(GLuint list, double *pts, int Npts, - kiva::draw_mode_e mode, double x0, double y0); - void draw_display_list_at_pts(GLuint fill_list, GLuint stroke_list, - double *pts, int Npts, - kiva::draw_mode_e mode, double x0, double y0); - - // Given a path function, returns two OpenGL display lists representing - // the list to fill and the list to stroke. The caller is responsible - // for calling glDeleteLists on the two. - // Only the list name of the first list (fill list) will be returned; - // the stroke list can be accessed by just adding 1. - GLuint make_marker_lists(kiva::PathDefinitionFunc path_func, - kiva::draw_mode_e mode, int size); - - void circle_path_func(int size); - void triangle_up_func(int size); - void triangle_down_func(int size); - - void draw_square(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0); - void draw_diamond(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0); - void draw_crossed_circle(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0); - void draw_x_marker(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0); - void draw_cross(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0); - void draw_dot(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0); - void draw_pixel(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0); - - private: - int m_width; - int m_height; - bool m_gl_initialized; - kiva::pix_format_e m_pixfmt; - - }; - -} - - -#endif /* KIVA_GL_GRAPHICS_CONTEXT_H */ - diff --git a/kiva/agg/src/gl_test/Lesson2.cpp b/kiva/agg/src/gl_test/Lesson2.cpp deleted file mode 100644 index f2d5b6ff0..000000000 --- a/kiva/agg/src/gl_test/Lesson2.cpp +++ /dev/null @@ -1,420 +0,0 @@ -/* Modified Lesson2.cpp from NeHe tutorials - * - * This has been modified in the following ways: - * WinMain has been renamed to DefaultWinMain - * DrawGLScene has been renamed to DefaultDrawGLScene - * - * -- pzw - */ - -/* - * This Code Was Created By Jeff Molofee 2000 - * A HUGE Thanks To Fredric Echols For Cleaning Up - * And Optimizing The Base Code, Making It More Flexible! - * If You've Found This Code Useful, Please Let Me Know. - * Visit My Site At nehe.gamedev.net - */ - -#include "Lesson2.h" - -HDC hDC=NULL; // Private GDI Device Context -HGLRC hRC=NULL; // Permanent Rendering Context -HWND hWnd=NULL; // Holds Our Window Handle -HINSTANCE hInstance; // Holds The Instance Of The Application - -bool keys[256]; // Array Used For The Keyboard Routine -bool active=TRUE; // Window Active Flag Set To TRUE By Default -bool fullscreen=FALSE; // Fullscreen Flag Set To Fullscreen Mode By Default - -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc - -GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window -{ - if (height==0) // Prevent A Divide By Zero By - { - height=1; // Making Height Equal One - } - - glViewport(0,0,width,height); // Reset The Current Viewport - - glMatrixMode(GL_PROJECTION); // Select The Projection Matrix - glLoadIdentity(); // Reset The Projection Matrix - - // Calculate The Aspect Ratio Of The Window - //gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); - glOrtho(0, width, 0, height, 1, -1); - - glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix - glLoadIdentity(); // Reset The Modelview Matrix -} - -int InitGL(GLvoid) // All Setup For OpenGL Goes Here -{ - glShadeModel(GL_SMOOTH); // Enable Smooth Shading - glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background - glClearDepth(1.0f); // Depth Buffer Setup - //glEnable(GL_DEPTH_TEST); // Enables Depth Testing - //glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do - //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations - return TRUE; // Initialization Went OK -} - -int DefaultDrawGLScene(GLvoid) // Here's Where We Do All The Drawing -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer - glLoadIdentity(); // Reset The Current Modelview Matrix - glTranslatef(-1.5f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0 - glBegin(GL_TRIANGLES); // Drawing Using Triangles - glVertex3f( 0.0f, 1.0f, 0.0f); // Top - glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left - glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right - glEnd(); // Finished Drawing The Triangle - glTranslatef(3.0f,0.0f,0.0f); // Move Right 3 Units - glBegin(GL_QUADS); // Draw A Quad - glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left - glVertex3f( 1.0f, 1.0f, 0.0f); // Top Right - glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right - glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left - glEnd(); // Done Drawing The Quad - return TRUE; // Keep Going -} - -GLvoid KillGLWindow(GLvoid) // Properly Kill The Window -{ - if (fullscreen) // Are We In Fullscreen Mode? - { - ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop - ShowCursor(TRUE); // Show Mouse Pointer - } - - if (hRC) // Do We Have A Rendering Context? - { - if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts? - { - MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); - } - - if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC? - { - MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); - } - hRC=NULL; // Set RC To NULL - } - - if (hDC && !ReleaseDC(hWnd,hDC)) // Are We Able To Release The DC - { - MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); - hDC=NULL; // Set DC To NULL - } - - if (hWnd && !DestroyWindow(hWnd)) // Are We Able To Destroy The Window? - { - MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); - hWnd=NULL; // Set hWnd To NULL - } - - if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class - { - MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); - hInstance=NULL; // Set hInstance To NULL - } -} - -/* This Code Creates Our OpenGL Window. Parameters Are: * - * title - Title To Appear At The Top Of The Window * - * width - Width Of The GL Window Or Fullscreen Mode * - * height - Height Of The GL Window Or Fullscreen Mode * - * bits - Number Of Bits To Use For Color (8/16/24/32) * - * fullscreenflag - Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE) */ - -BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag) -{ - GLuint PixelFormat; // Holds The Results After Searching For A Match - WNDCLASS wc; // Windows Class Structure - DWORD dwExStyle; // Window Extended Style - DWORD dwStyle; // Window Style - RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values - WindowRect.left=(long)0; // Set Left Value To 0 - WindowRect.right=(long)width; // Set Right Value To Requested Width - WindowRect.top=(long)0; // Set Top Value To 0 - WindowRect.bottom=(long)height; // Set Bottom Value To Requested Height - - fullscreen=fullscreenflag; // Set The Global Fullscreen Flag - - hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window - wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Size, And Own DC For Window. - wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc Handles Messages - wc.cbClsExtra = 0; // No Extra Window Data - wc.cbWndExtra = 0; // No Extra Window Data - wc.hInstance = hInstance; // Set The Instance - wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon - wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer - wc.hbrBackground = NULL; // No Background Required For GL - wc.lpszMenuName = NULL; // We Don't Want A Menu - wc.lpszClassName = "OpenGL"; // Set The Class Name - - if (!RegisterClass(&wc)) // Attempt To Register The Window Class - { - MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - if (fullscreen) // Attempt Fullscreen Mode? - { - DEVMODE dmScreenSettings; // Device Mode - memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared - dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure - dmScreenSettings.dmPelsWidth = width; // Selected Screen Width - dmScreenSettings.dmPelsHeight = height; // Selected Screen Height - dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel - dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; - - // Try To Set Selected Mode And Get Results. NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar. - if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) - { - // If The Mode Fails, Offer Two Options. Quit Or Use Windowed Mode. - if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES) - { - fullscreen=FALSE; // Windowed Mode Selected. Fullscreen = FALSE - } - else - { - // Pop Up A Message Box Letting User Know The Program Is Closing. - MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP); - return FALSE; // Return FALSE - } - } - } - - if (fullscreen) // Are We Still In Fullscreen Mode? - { - dwExStyle=WS_EX_APPWINDOW; // Window Extended Style - dwStyle=WS_POPUP; // Windows Style - ShowCursor(FALSE); // Hide Mouse Pointer - } - else - { - dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style - dwStyle=WS_OVERLAPPEDWINDOW; // Windows Style - } - - AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size - - // Create The Window - if (!(hWnd=CreateWindowEx( dwExStyle, // Extended Style For The Window - "OpenGL", // Class Name - title, // Window Title - dwStyle | // Defined Window Style - WS_CLIPSIBLINGS | // Required Window Style - WS_CLIPCHILDREN, // Required Window Style - 0, 0, // Window Position - WindowRect.right-WindowRect.left, // Calculate Window Width - WindowRect.bottom-WindowRect.top, // Calculate Window Height - NULL, // No Parent Window - NULL, // No Menu - hInstance, // Instance - NULL))) // Dont Pass Anything To WM_CREATE - { - KillGLWindow(); // Reset The Display - MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be - { - sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor - 1, // Version Number - PFD_DRAW_TO_WINDOW | // Format Must Support Window - PFD_SUPPORT_OPENGL | // Format Must Support OpenGL - PFD_DOUBLEBUFFER, // Must Support Double Buffering - PFD_TYPE_RGBA, // Request An RGBA Format - bits, // Select Our Color Depth - 0, 0, 0, 0, 0, 0, // Color Bits Ignored - 0, // No Alpha Buffer - 0, // Shift Bit Ignored - 0, // No Accumulation Buffer - 0, 0, 0, 0, // Accumulation Bits Ignored - 16, // 16Bit Z-Buffer (Depth Buffer) - 0, // No Stencil Buffer - 0, // No Auxiliary Buffer - PFD_MAIN_PLANE, // Main Drawing Layer - 0, // Reserved - 0, 0, 0 // Layer Masks Ignored - }; - - if (!(hDC=GetDC(hWnd))) // Did We Get A Device Context? - { - KillGLWindow(); // Reset The Display - MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format? - { - KillGLWindow(); // Reset The Display - MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Are We Able To Set The Pixel Format? - { - KillGLWindow(); // Reset The Display - MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context? - { - KillGLWindow(); // Reset The Display - MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - if(!wglMakeCurrent(hDC,hRC)) // Try To Activate The Rendering Context - { - KillGLWindow(); // Reset The Display - MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - ShowWindow(hWnd,SW_SHOW); // Show The Window - SetForegroundWindow(hWnd); // Slightly Higher Priority - SetFocus(hWnd); // Sets Keyboard Focus To The Window - ReSizeGLScene(width, height); // Set Up Our Perspective GL Screen - - if (!InitGL()) // Initialize Our Newly Created GL Window - { - KillGLWindow(); // Reset The Display - MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION); - return FALSE; // Return FALSE - } - - return TRUE; // Success -} - -LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window - UINT uMsg, // Message For This Window - WPARAM wParam, // Additional Message Information - LPARAM lParam) // Additional Message Information -{ - switch (uMsg) // Check For Windows Messages - { - case WM_ACTIVATE: // Watch For Window Activate Message - { - if (!HIWORD(wParam)) // Check Minimization State - { - active=TRUE; // Program Is Active - } - else - { - active=FALSE; // Program Is No Longer Active - } - - return 0; // Return To The Message Loop - } - - case WM_SYSCOMMAND: - { - switch (wParam) - { - case SC_SCREENSAVE: - case SC_MONITORPOWER: - return 0; - } - break; - } - - case WM_CLOSE: // Did We Receive A Close Message? - { - PostQuitMessage(0); // Send A Quit Message - return 0; // Jump Back - } - - case WM_KEYDOWN: // Is A Key Being Held Down? - { - keys[wParam] = TRUE; // If So, Mark It As TRUE - return 0; // Jump Back - } - - case WM_KEYUP: // Has A Key Been Released? - { - keys[wParam] = FALSE; // If So, Mark It As FALSE - return 0; // Jump Back - } - - case WM_SIZE: // Resize The OpenGL Window - { - ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height - return 0; // Jump Back - } - } - - // Pass All Unhandled Messages To DefWindowProc - return DefWindowProc(hWnd,uMsg,wParam,lParam); -} - -int WINAPI WinMain( HINSTANCE hInstance, // Instance - HINSTANCE hPrevInstance, // Previous Instance - LPSTR lpCmdLine, // Command Line Parameters - int nCmdShow) // Window Show State -{ - MSG msg; // Windows Message Structure - BOOL done=FALSE; // Bool Variable To Exit Loop - - // Ask The User Which Screen Mode They Prefer - //if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO) - //{ - // fullscreen=FALSE; // Windowed Mode - //} - - // Create Our OpenGL Window - if (!CreateGLWindow("NeHe's First Polygon Tutorial",640,480,16,fullscreen)) - { - return 0; // Quit If Window Was Not Created - } - - - while(!done) // Loop That Runs While done=FALSE - { - if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting? - { - if (msg.message==WM_QUIT) // Have We Received A Quit Message? - { - done=TRUE; // If So done=TRUE - } - else // If Not, Deal With Window Messages - { - TranslateMessage(&msg); // Translate The Message - DispatchMessage(&msg); // Dispatch The Message - } - } - else // If There Are No Messages - { - // Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene() - if ((active && !DrawGLScene()) || keys[VK_ESCAPE]) // Active? Was There A Quit Received? - { - done=TRUE; // ESC or DrawGLScene Signalled A Quit - } - else // Not Time To Quit, Update Screen - { - SwapBuffers(hDC); // Swap Buffers (Double Buffering) - } - - if (keys[VK_F1]) // Is F1 Being Pressed? - { - keys[VK_F1]=FALSE; // If So Make Key FALSE - KillGLWindow(); // Kill Our Current Window - fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode - // Recreate Our OpenGL Window - if (!CreateGLWindow("NeHe's First Polygon Tutorial",640,480,16,fullscreen)) - { - return 0; // Quit If Window Was Not Created - } - } - } - } - - // Shutdown - KillGLWindow(); // Kill The Window - return (msg.wParam); // Exit The Program -} diff --git a/kiva/agg/src/gl_test/Lesson2.h b/kiva/agg/src/gl_test/Lesson2.h deleted file mode 100644 index 5bf6b2238..000000000 --- a/kiva/agg/src/gl_test/Lesson2.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef LESSON2_H -#define LESSON2_H - -#include // Header File For Windows - -#include // Header File For The OpenGL32 Library -#include // Header File For The GLu32 Library -#include - - - -// Implement your own version of this function -int DrawGLScene(GLvoid); - - -// Functions implemented in Lesson2.cpp -GLvoid ReSizeGLScene(GLsizei width, GLsizei height); -int InitGL(GLvoid); -GLvoid KillGLWindow(GLvoid); -BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag); -int DefaultDrawGLScene(GLvoid); - -int WINAPI WinMain( HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPSTR lpCmdLine, - int nCmdShow); - - - -#endif /* LESSON2_H */ - - diff --git a/kiva/agg/src/gl_test/gl_test.cpp b/kiva/agg/src/gl_test/gl_test.cpp deleted file mode 100644 index 148f4a761..000000000 --- a/kiva/agg/src/gl_test/gl_test.cpp +++ /dev/null @@ -1,67 +0,0 @@ - - -#include "Lesson2.h" -#include "gl_graphics_context.h" -using namespace kiva; - -#define WIDTH 640 -#define HEIGHT 480 - -int OrigDrawGLScene(GLvoid) -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer - glLoadIdentity(); // Reset The Current Modelview Matrix - glTranslatef(-1.5f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0 - glBegin(GL_TRIANGLES); // Drawing Using Triangles - glVertex3f( 0.0f, 1.0f, 0.0f); // Top - glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left - glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right - glEnd(); // Finished Drawing The Triangle - glTranslatef(3.0f,0.0f,0.0f); // Move Right 3 Units - glBegin(GL_QUADS); // Draw A Quad - glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left - glVertex3f( 1.0f, 1.0f, 0.0f); // Top Right - glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right - glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left - glEnd(); // Done Drawing The Quad - return TRUE; // Keep Going - -} - - -int KivaDrawGLScene(GLvoid) -{ - - gl_graphics_context gc(WIDTH, HEIGHT); - gc.gl_init(); - - // XXX: Verify antialiasing from python - //gc.set_antialias(1); - - gc.set_fill_color(agg24::rgba(1.0, 0.0, 0.0)); - gc.set_stroke_color(agg24::rgba(0.0, 1.0, 0.0)); - gc.set_line_width(1.0); - gc.move_to(100.0, 100.0); - gc.line_to(100.0, 200.0); - gc.line_to(200.0, 200.0); - gc.close_path(); - gc.draw_path(FILL_STROKE); - - gc.begin_path(); - gc.line_to(50, 50); - gc.line_to(75, 75); - gc.line_to(275, 75); - gc.line_to(275, 50); - gc.close_path(); - gc.draw_path(FILL_STROKE); - - return TRUE; -} - - -int DrawGLScene(GLvoid) -{ - //return OrigDrawGLScene(); - - return KivaDrawGLScene(); -} \ No newline at end of file diff --git a/kiva/agg/src/gl_test/gl_test.vcproj b/kiva/agg/src/gl_test/gl_test.vcproj deleted file mode 100644 index d2a7b767f..000000000 --- a/kiva/agg/src/gl_test/gl_test.vcproj +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/kiva/agg/src/graphics_context.i b/kiva/agg/src/graphics_context.i index acdbff6a0..e9ba22a32 100644 --- a/kiva/agg/src/graphics_context.i +++ b/kiva/agg/src/graphics_context.i @@ -947,102 +947,3 @@ class Image(GraphicsContextArray): %} - - - -%{ -#include "gl_graphics_context.h" -%} - -namespace kiva { - - %rename(GraphicsContextGL) gl_graphics_context; - - class gl_graphics_context : public graphics_context_base - { - public: - gl_graphics_context(int width, int height, - kiva::pix_format_e format=kiva::pix_format_rgb24); - - ~gl_graphics_context(); - - //--------------------------------------------------------------- - // GL-specific methods - //--------------------------------------------------------------- - void gl_init(); - void gl_cleanup(); - void begin_page(); - void gl_render_path(kiva::compiled_path *path, bool polygon=false, bool fill=false); - void gl_render_points(double** points, bool polygon, bool fill, - kiva::draw_mode_e mode = FILL); - - //--------------------------------------------------------------- - // GraphicsContextBase interface - //--------------------------------------------------------------- - - kiva::pix_format_e format(); - void save_state(); - void restore_state(); - - //--------------------------------------------------------------- - // Clipping path manipulation - //--------------------------------------------------------------- - void clip(); - void even_odd_clip(); - void clip_to_rect(double x, double y, double sx, double sy); - void clip_to_rect(kiva::rect_type &rect); - void clip_to_rects(double* new_rects, int Nrects); - void clip_to_rects(kiva::rect_list_type &rects); - kiva::rect_type transform_clip_rectangle(const kiva::rect_type &rect); - void clear_clip_path(); - - int get_num_clip_regions(); - kiva::rect_type get_clip_region(unsigned int i); - - //--------------------------------------------------------------- - // Painting paths (drawing and filling contours) - //--------------------------------------------------------------- - - // Declare clear() to pass by reference so that the typemap applies, - // even though it is pass by value in the actual C++ class - void clear(agg24::rgba& value=_clear_color); - - void fill_path(); - void eof_fill_path(); - void stroke_path(); - // empty function; for some reason this is abstract in the base class - inline void _stroke_path() { } - - void draw_path(draw_mode_e mode=FILL_STROKE); - void draw_rect(double rect[4], - draw_mode_e mode=FILL_STROKE); - - %feature("shadow") draw_marker_at_points(double* pts,int Npts, int size, - agg24::marker_e type = agg24::marker_square) - %{ - def draw_marker_at_points(self,pts,size,kiva_marker_type): - marker = kiva_marker_to_agg.get(kiva_marker_type, None) - if marker is None: - success = 0 - else: - args = (self,pts,int(size),marker) - success = _agg.GraphicsContextGL_draw_marker_at_points(self, pts, - int(size), marker) - return success - %} - int draw_marker_at_points(double* pts,int Npts,int size, - agg24::marker_e type=agg24::marker_square); - - void draw_path_at_points(double* pts,int Npts, - kiva::compiled_path& marker, - draw_mode_e mode); - - bool show_text(char *text); - - void draw_glyphs(kiva::graphics_context_base* img, double tx, double ty); - int draw_image(kiva::graphics_context_base* img, double rect[4], bool force_copy=false); - int draw_image(kiva::graphics_context_base* img); - - }; - -} diff --git a/kiva/agg/src/kiva.sln b/kiva/agg/src/kiva.sln index de38fedac..1f4787523 100644 --- a/kiva/agg/src/kiva.sln +++ b/kiva/agg/src/kiva.sln @@ -3,10 +3,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kiva", "kiva.vcproj", "{4B7 ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gl_test", "gl_test\gl_test.vcproj", "{FB096497-AD67-49E8-9C91-790B5C59B5F8}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug diff --git a/kiva/agg/src/kiva.vcproj b/kiva/agg/src/kiva.vcproj index f0e8028c5..05ab4f0f4 100644 --- a/kiva/agg/src/kiva.vcproj +++ b/kiva/agg/src/kiva.vcproj @@ -132,12 +132,6 @@ - - - - - - diff --git a/kiva/agg/src/gl/agg_bmp.cpp b/kiva/agg/src/osx/agg_bmp.cpp similarity index 63% rename from kiva/agg/src/gl/agg_bmp.cpp rename to kiva/agg/src/osx/agg_bmp.cpp index fd65bfa46..717e5f2e1 100644 --- a/kiva/agg/src/gl/agg_bmp.cpp +++ b/kiva/agg/src/osx/agg_bmp.cpp @@ -1,7 +1,7 @@ #include #include -#include "gl/agg_bmp.h" -//#include "gl/agg_platform_specific.h" + +#include "osx/agg_bmp.h" #include "agg_pixfmt_rgba.h" #include "agg_color_rgba.h" @@ -24,17 +24,17 @@ namespace agg24 //------------------------------------------------------------------------ pixel_map::pixel_map(unsigned width, unsigned height, pix_format_e format, - unsigned clear_val, bool bottom_up): - m_buf(NULL), - m_buf2(NULL), - m_format(format) -// m_specific(new platform_specific(format, bottom_up)) + unsigned clear_val, bool bottom_up) + : m_format(format) + , m_buf(NULL) + , m_buf2(NULL) +//, m_specific(new platform_specific(format, bottom_up)) { DEBUG_MTH5("pixel_map::pixel_map(%d,%d,%d,%d,%d)",width,height,format,clear_val,bottom_up); init_platform(format, bottom_up); create(width, height, clear_val); - + } @@ -48,8 +48,6 @@ namespace agg24 m_sys_format = pix_format_gray8; m_bpp = 8; m_sys_bpp = 8; - m_gl_format = GL_LUMINANCE; - m_gl_pixel_type = GL_UNSIGNED_BYTE; break; case pix_format_rgb555: @@ -57,24 +55,18 @@ namespace agg24 m_sys_format = pix_format_rgb565; m_bpp = 16; m_sys_bpp = 16; - m_gl_format = GL_RGB; - m_gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; break; case pix_format_rgb24: m_sys_format = pix_format_rgb24; m_bpp = 24; m_sys_bpp = 24; - m_gl_format = GL_RGB; - m_gl_pixel_type = GL_UNSIGNED_BYTE; break; case pix_format_bgr24: m_sys_format = pix_format_bgr24; m_bpp = 24; m_sys_bpp = 24; - m_gl_format = GL_BGR; - m_gl_pixel_type = GL_UNSIGNED_BYTE; break; case pix_format_bgra32: @@ -82,8 +74,6 @@ namespace agg24 m_sys_format = pix_format_bgra32; m_bpp = 32; m_sys_bpp = 32; - m_gl_format = GL_BGRA; - m_gl_pixel_type = GL_UNSIGNED_BYTE; break; case pix_format_argb32: @@ -91,8 +81,6 @@ namespace agg24 m_sys_format = pix_format_rgba32; m_bpp = 32; m_sys_bpp = 32; - m_gl_format = GL_RGBA; - m_gl_pixel_type = GL_UNSIGNED_BYTE; break; case pix_format_undefined: @@ -106,22 +94,22 @@ namespace agg24 unsigned pixel_map::calc_row_len(unsigned width, unsigned bits_per_pixel) { unsigned n = width; - unsigned k; + unsigned k; switch(bits_per_pixel) { case 1: k = n; n = n >> 3; - if(k & 7) n++; + if(k & 7) n++; break; case 4: k = n; n = n >> 1; - if(k & 3) n++; + if(k & 3) n++; break; case 8: break; case 16: n = n << 1; break; - case 24: n = (n << 1) + n; + case 24: n = (n << 1) + n; break; case 32: n = n << 2; break; @@ -130,7 +118,7 @@ namespace agg24 } return ((n + 3) >> 2) << 2; } - + //------------------------------------------------------------------------ pixel_map::~pixel_map() { @@ -152,9 +140,9 @@ namespace agg24 } //------------------------------------------------------------------------ - void pixel_map::create(unsigned width, - unsigned height, - unsigned clear_val) + void pixel_map::create(unsigned width, + unsigned height, + unsigned clear_val) { destroy(); if(width == 0) width = 1; @@ -184,64 +172,20 @@ namespace agg24 } - //------------------------------------------------------------------------ - void pixel_map::draw(int x, int y, double scale) - { - DEBUG_MTH("pixel_map::draw"); - if(m_buf == 0) return; -// m_specific->display_pmap(&m_rbuf_window); - - if (m_sys_format == m_format) - { - glDrawPixels(width(), height(), m_gl_format, m_gl_pixel_type, m_buf); - } - else - { - switch(m_format) - { - case pix_format_abgr32: - color_conv(&m_rbuf_window2, &m_rbuf_window, color_conv_abgr32_to_bgra32()); - break; - - case pix_format_argb32: - color_conv(&m_rbuf_window2, &m_rbuf_window, color_conv_argb32_to_bgra32()); - break; - - case pix_format_rgb555: - color_conv(&m_rbuf_window2, &m_rbuf_window, color_conv_rgb555_to_rgb565()); - break; - -// case pix_format_rgb565: -// case pix_format_rgb24: -// case pix_format_bgr24: -// case pix_format_rgba32: -// case pix_format_bgra32: -// case pix_format_gray8: - case end_of_pix_formats: - case pix_format_undefined: - ; - } - - glDrawPixels(width(), height(), m_gl_format, m_gl_pixel_type, m_buf2); - } - + pix_format_e pixel_map::get_pix_format() const { + return m_format; } - pix_format_e pixel_map::get_pix_format() const { - return m_format; - } - - - unsigned char* pixel_map::buf() { return m_buf; } - unsigned char* pixel_map::buf2() { return m_buf2; } - unsigned pixel_map::width() const { return m_rbuf_window.width(); } - unsigned pixel_map::height() const { return m_rbuf_window.height(); } - int pixel_map::stride() { return calc_row_len(width(), m_bpp); } + unsigned char* pixel_map::buf() { return m_buf; } + unsigned char* pixel_map::buf2() { return m_buf2; } + unsigned pixel_map::width() const { return m_rbuf_window.width(); } + unsigned pixel_map::height() const { return m_rbuf_window.height(); } + int pixel_map::stride() { return calc_row_len(width(), m_bpp); } - // Convert to a Python string containing 32 bit ARGB values. - PyObject* pixel_map::convert_to_argb32string() const { + // Convert to a Python string containing 32 bit ARGB values. + PyObject* pixel_map::convert_to_argb32string() const { unsigned w = width(); unsigned h = height(); diff --git a/kiva/agg/src/gl/agg_bmp.h b/kiva/agg/src/osx/agg_bmp.h similarity index 84% rename from kiva/agg/src/gl/agg_bmp.h rename to kiva/agg/src/osx/agg_bmp.h index 780a61cfd..515b8e334 100644 --- a/kiva/agg/src/gl/agg_bmp.h +++ b/kiva/agg/src/osx/agg_bmp.h @@ -1,26 +1,18 @@ // -*- c++ -*- -#ifndef AGG_GL_BMP_INCLUDED -#define AGG_GL_BMP_INCLUDED +#ifndef AGG_MAC_BMP_INCLUDED +#define AGG_MAC_BMP_INCLUDED #include "Python.h" + #include "agg_basics.h" #include "agg_rendering_buffer.h" #include "util/agg_color_conv_rgb8.h" -#ifdef __DARWIN__ -#include -#include -#else -#include -#include -#endif - - namespace agg24 { enum pix_format_e { - pix_format_undefined = 0, // By default. No conversions are applied + pix_format_undefined = 0, // By default. No conversions are applied pix_format_gray8, // Simple 256 level grayscale pix_format_rgb555, // 15 bit rgb. Depends on the byte ordering! pix_format_rgb565, // 16 bit rgb. Depends on the byte ordering! @@ -30,19 +22,17 @@ namespace agg24 pix_format_argb32, // A-R-G-B, native MAC format pix_format_abgr32, // A-B-G-R, one byte per color component pix_format_bgra32, // B-G-R-A, native win32 BMP format - + end_of_pix_formats }; - - class pixel_map { public: pixel_map(unsigned width, unsigned height, pix_format_e format, - unsigned clear_val, bool bottom_up); + unsigned clear_val, bool bottom_up); ~pixel_map(); - void draw(int x=0, int y=0, double scale=1.0); + void init_platform(pix_format_e format, bool bottom_up); unsigned calc_row_len(unsigned width, unsigned bits_per_pixel); @@ -57,12 +47,13 @@ namespace agg24 rendering_buffer& rbuf() { return m_rbuf_window; } rendering_buffer& rbuf2() { return m_rbuf_window2; } PyObject* convert_to_argb32string() const; + pix_format_e m_format; pix_format_e m_sys_format; private: void destroy(); - void create(unsigned width, + void create(unsigned width, unsigned height, unsigned clear_val=256); @@ -72,16 +63,12 @@ namespace agg24 rendering_buffer m_rbuf_window2; unsigned m_bpp; unsigned m_sys_bpp; - GLenum m_gl_format; - GLenum m_gl_pixel_type; // public: // platform_specific* m_specific; - }; } - #endif diff --git a/kiva/agg/src/gl/plat_support.i b/kiva/agg/src/osx/plat_support.i similarity index 59% rename from kiva/agg/src/gl/plat_support.i rename to kiva/agg/src/osx/plat_support.i index c96172aaa..5c2744226 100644 --- a/kiva/agg/src/gl/plat_support.i +++ b/kiva/agg/src/osx/plat_support.i @@ -1,23 +1,16 @@ // -*- c++ -*- -// OpenGL support for AGG +// MacOS support for AGG // Author: Robert Kern // I've consolidated agg_platform_specific and agg_bmp in the process of // understanding the code. -// plat_support defines a function resize_gl(width, height) which should be called -// every time the window gets resized. All OpenGL initialization and glViewport -// calls need to be done by the widget toolkit. - -// Currently, OpenGL support is only tested with wxWidgets 2.5.1.5 on MacOS X -// version 10.3 - %module plat_support %include numeric.i %{ -#include "gl/agg_bmp.h" +#include "osx/agg_bmp.h" namespace agg24 { PyObject* pixel_map_as_unowned_array(agg24::pixel_map& pix_map) @@ -33,21 +26,6 @@ namespace agg24 return PyArray_SimpleNewFromData(3,dims,NPY_UINT8,(void*)pix_map.buf()); } - - void resize_gl(unsigned width, unsigned height) - { - GLint viewport[4]; - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluOrtho2D(0.0, (GLfloat)width, 0.0, (GLfloat)height); - glPixelZoom(1.0, -1.0); - glGetIntegerv(GL_VIEWPORT, viewport); - glRasterPos2d(0.0, ((double)height*height)/viewport[3]); - } - } %} @@ -87,34 +65,18 @@ namespace agg24 pixel_map(unsigned width, unsigned height, pix_format_e format, unsigned clear_val, bool bottom_up); public: - %feature("shadow") draw(int x, int y, double scale) - %{ - def draw(self, x=0, y=0, scale=1.0): - # fix me: brittle becuase we are hard coding - # module and class name. Done cause SWIG 1.3.24 does - # some funky overloading stuff in it that breaks keyword - # arguments. - result = _plat_support.PixelMap_draw(self, x, y, scale) - return result - %} - void draw(int x, int y, double scale); PyObject* convert_to_argb32string() const; %pythoncode %{ - - def set_bmp_array(self): - self.bmp_array = pixel_map_as_unowned_array(self) - return self - - def draw_to_glcanvas(self, x, y): - self.draw(x, y) + def set_bmp_array(self): + self.bmp_array = pixel_map_as_unowned_array(self) + return self %} }; PyObject* pixel_map_as_unowned_array(pixel_map& pix_map); -void resize_gl(unsigned width, unsigned height); } // clear the "permissive" unsigned typemap we are using. diff --git a/kiva/gl.py b/kiva/gl.py deleted file mode 100644 index 0c133f8be..000000000 --- a/kiva/gl.py +++ /dev/null @@ -1,428 +0,0 @@ -# Major library imports -import ctypes -from math import floor -from numpy import array, ndarray - -# Pyglet and pyglet-related imports -# Before we import anything else from pyglet, we need to set the shadow_window -# option to False, so that it does not conflict with WX, in case someone is -# trying to use the kiva GL GraphicsContext from within WX. -# This is necessary as of pyglet 1.1. -import pyglet -pyglet.options['shadow_window'] = False - -from pyglet.text import Label -from pyglet.font import load as load_font -from pyglet.font.base import Font as PygletFont -from pyglet import gl -from pygarrayimage.arrayimage import ArrayInterfaceImage - -# Local kiva imports -from .affine import affine_from_values, transform_points -from .agg import GraphicsContextGL as _GCL -from .agg import AggFontType -from .agg import CompiledPath -from .constants import BOLD, BOLD_ITALIC, ITALIC -from .fonttools import Font - - -class ArrayImage(ArrayInterfaceImage): - """ pyglet ImageData made from numpy arrays. - - Customized from pygarrayimage's ArrayInterfaceImage to override the texture - creation. - """ - - def create_texture(self, cls, rectangle=False, force_rectangle=False): - '''Create a texture containing this image. - - If the image's dimensions are not powers of 2, a TextureRegion of - a larger Texture will be returned that matches the dimensions of this - image. - - :Parameters: - `cls` : class (subclass of Texture) - Class to construct. - `rectangle` : bool - ``True`` if a rectangle can be created; see - `AbstractImage.get_texture`. - - :rtype: cls or cls.region_class - ''' - - _is_pow2 = lambda v: (v & (v - 1)) == 0 - - target = gl.GL_TEXTURE_2D - if rectangle and not (_is_pow2(self.width) and _is_pow2(self.height)): - if gl.gl_info.have_extension('GL_ARB_texture_rectangle'): - target = gl.GL_TEXTURE_RECTANGLE_ARB - elif gl.gl_info.have_extension('GL_NV_texture_rectangle'): - target = gl.GL_TEXTURE_RECTANGLE_NV - - texture = cls.create_for_size(target, self.width, self.height) - subimage = False - if texture.width != self.width or texture.height != self.height: - texture = texture.get_region(0, 0, self.width, self.height) - subimage = True - - internalformat = self._get_internalformat(self.format) - - gl.glBindTexture(texture.target, texture.id) - gl.glTexParameteri(texture.target, gl.GL_TEXTURE_WRAP_S, - gl.GL_CLAMP_TO_EDGE) - gl.glTexParameteri(texture.target, gl.GL_TEXTURE_WRAP_T, - gl.GL_CLAMP_TO_EDGE) - gl.glTexParameteri(texture.target, gl.GL_TEXTURE_MAG_FILTER, - gl.GL_LINEAR) - gl.glTexParameteri(texture.target, gl.GL_TEXTURE_MIN_FILTER, - gl.GL_LINEAR) - - if subimage: - width = texture.owner.width - height = texture.owner.height - blank = (ctypes.c_ubyte * (width * height * 4))() - gl.glTexImage2D(texture.target, texture.level, - internalformat, - width, height, - 1, - gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, - blank) - internalformat = None - - self.blit_to_texture(texture.target, texture.level, - 0, 0, 0, internalformat) - - return texture - - def blit_to_texture(self, target, level, x, y, z, internalformat=None): - '''Draw this image to to the currently bound texture at `target`. - - If `internalformat` is specified, glTexImage is used to initialise - the texture; otherwise, glTexSubImage is used to update a region. - ''' - - data_format = self.format - data_pitch = abs(self._current_pitch) - - # Determine pixel format from format string - matrix = None - format, type = self._get_gl_format_and_type(data_format) - if format is None: - if (len(data_format) in (3, 4) and - gl.gl_info.have_extension('GL_ARB_imaging')): - - # Construct a color matrix to convert to GL_RGBA - def component_column(component): - try: - pos = 'RGBA'.index(component) - return [0] * pos + [1] + [0] * (3 - pos) - except ValueError: - return [0, 0, 0, 0] - - # pad to avoid index exceptions - lookup_format = data_format + 'XXX' - matrix = (component_column(lookup_format[0]) + - component_column(lookup_format[1]) + - component_column(lookup_format[2]) + - component_column(lookup_format[3])) - format = { - 3: gl.GL_RGB, - 4: gl.GL_RGBA}.get(len(data_format)) - type = gl.GL_UNSIGNED_BYTE - - gl.glMatrixMode(gl.GL_COLOR) - gl.glPushMatrix() - gl.glLoadMatrixf((gl.GLfloat * 16)(*matrix)) - else: - # Need to convert data to a standard form - data_format = { - 1: 'L', - 2: 'LA', - 3: 'RGB', - 4: 'RGBA'}.get(len(data_format)) - format, type = self._get_gl_format_and_type(data_format) - - # Workaround: don't use GL_UNPACK_ROW_LENGTH - if gl.current_context._workaround_unpack_row_length: - data_pitch = self.width * len(data_format) - - # Get data in required format (hopefully will be the same format it's - # already in, unless that's an obscure format, upside-down or the - # driver is old). - data = self._convert(data_format, data_pitch) - - if data_pitch & 0x1: - alignment = 1 - elif data_pitch & 0x2: - alignment = 2 - else: - alignment = 4 - row_length = data_pitch / len(data_format) - gl.glPushClientAttrib(gl.GL_CLIENT_PIXEL_STORE_BIT) - gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, alignment) - gl.glPixelStorei(gl.GL_UNPACK_ROW_LENGTH, row_length) - self._apply_region_unpack() - gl.glTexParameteri(target, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE) - gl.glTexParameteri(target, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE) - gl.glTexParameteri(target, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) - gl.glTexParameteri(target, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) - - if target == gl.GL_TEXTURE_3D: - assert not internalformat - gl.glTexSubImage3D(target, level, x, y, z, self.width, self.height, - 1, format, type, data) - elif internalformat: - gl.glTexImage2D(target, level, internalformat, self.width, - self.height, 0, format, type, data) - else: - gl.glTexSubImage2D(target, level, x, y, self.width, self.height, - format, type, data) - gl.glPopClientAttrib() - - if matrix: - gl.glPopMatrix() - gl.glMatrixMode(gl.GL_MODELVIEW) - - -def image_as_array(img): - """ Adapt an image object into a numpy array. - - Typically, this is used to adapt an agg GraphicsContextArray which has been - used for image storage in Kiva applications. - """ - if hasattr(img, 'bmp_array'): - # Yup, a GraphicsContextArray. - return img.bmp_array - elif isinstance(img, ndarray): - return img - else: - msg = "can't convert %r into a numpy array" % (img,) - raise NotImplementedError(msg) - - -def get_dpi(): - """ Returns the appropriate DPI setting for the system""" - pass - - -class MRU(dict): - def __init__(self, *args, **kw): - # An ordering of keys in self; the last item was the most recently used - self.__order__ = [] - self.__maxlength__ = 30 - dict.__init__(self, *args, **kw) - - def __getitem__(self, key): - val = dict.__getitem__(self, key) - # If we get here, then key was found in our dict - self.__touch__(key) - return val - - def __setitem__(self, key, value): - dict.__setitem__(self, key, value) - self.__touch__(key) - - def __delitem__(self, key): - dict.__delitem__(self, key) - if key in self.__order__: - self.__order__.remove(key) - - def __touch__(self, key): - """ Puts **key** as the most recently used item """ - if len(self.__order__) == 0: - self.__order__.append(key) - if (len(self.__order__) == self.__maxlength__) and \ - key not in self.__order__: - # The MRU is full, so pop the oldest element - del self[self.__order__[0]] - if key != self.__order__[-1]: - try: - ndx = self.__order__.index(key) - self.__order__[ndx:-1] = self.__order__[ndx+1:] - self.__order__[-1] = key - except ValueError: - # new key that's not already in the cache - if len(self.__order__) == self.__maxlength__: - self.__order__ = self.__order__[1:] + [key] - else: - self.__order__.append(key) - - -# Use a singleton for the font cache -GlobalFontCache = MRU() - - -def GetFont(font): - """ Returns a Pylget Font object for the given Agg or Kiva font """ - if isinstance(font, PygletFont): - pyglet_font = font - else: - # AggFontType - key = (font.name, font.size, font.family, font.style) - if key not in GlobalFontCache: - if isinstance(font, AggFontType): - agg_font = font - font = Font(face_name=agg_font.name, size=agg_font.size, - family=agg_font.family, style=agg_font.style) - bold = False - italic = False - if font.style in [BOLD, BOLD_ITALIC] or font.weight == BOLD: - bold = True - if font.style in [ITALIC, BOLD_ITALIC]: - italic = True - pyglet_font = load_font(font.findfontname(), font.size, bold, - italic) - GlobalFontCache[key] = pyglet_font - else: - pyglet_font = GlobalFontCache[key] - return pyglet_font - - -# Because Pyglet 1.1 uses persistent Label objects to efficiently lay -# out and render text, we cache these globally to minimize the creation -# time. An MRU is not really the right structure to use here, though. -# (We typically expect that the same numbers of labels will be rendered.) -GlobalTextCache = MRU() -GlobalTextCache.__maxlength__ = 100 - - -def GetLabel(text, pyglet_font): - """ Returns a Pyglet Label object for the given text and font """ - key = (text, pyglet_font) - if key not in GlobalTextCache: - # Use anchor_y="bottom" because by default, pyglet sets the baseline to - # the y coordinate given. Unfortunately, it doesn't expose a per-Text - # descent (only a per-Font descent), so it's impossible to know how to - # offset the y value properly for a given string. - label = Label(text, font_name=pyglet_font.name, - font_size=pyglet_font.size, anchor_y="bottom") - GlobalTextCache[key] = label - else: - label = GlobalTextCache[key] - return label - - -class GraphicsContext(_GCL): - def __init__(self, size, *args, **kw): - # Ignore the pix_format argument for now - if "pix_format" in kw: - kw.pop("pix_format") - _GCL.__init__(self, size[0], size[1], *args, **kw) - self.corner_pixel_origin = True - - self._font_stack = [] - self._current_font = None - - def save_state(self): - super(GraphicsContext, self).save_state() - self._font_stack.append(self._current_font) - - def restore_state(self): - super(GraphicsContext, self).restore_state() - self._current_font = self._font_stack.pop() - - def set_font(self, font): - super(GraphicsContext, self).set_font(font) - self._current_font = font - - def get_text_extent(self, text): - if self._current_font is None: - return (0, 0, 0, 0) - - pyglet_font = GetFont(self._current_font) - label = GetLabel(text, pyglet_font) - return (0, 0, label.content_width, label.content_height) - - def show_text(self, text, point=None): - if point is None: - point = (0, 0) - return self.show_text_at_point(text, *point) - - def show_text_at_point(self, text, x, y): - if self._current_font is None: - return - - pyglet_font = GetFont(self._current_font) - label = GetLabel(text, pyglet_font) - - xform = self.get_ctm() - x0 = xform[4] - y0 = xform[5] - - # The GL backend places the center of a pixel at (0.5, 0.5); however, - # for show_text_at_point, we don't actually want to render the text - # offset by half a pixel. There is probably a better, more uniform way - # to handle this across all of Kiva, because this is probably a common - # issue that will arise, but for now, we just round the position down. - x = floor(x + x0) - y = floor(y + y0) - - label.x = x - label.y = y - c = self.get_fill_color() - label.color = (int(c[0]*255), int(c[1]*255), int(c[2]*255), - int(c[3]*255)) - label.draw() - return True - - def linear_gradient(self, x1, y1, x2, y2, stops, spread_method, - units='userSpaceOnUse'): - """ Not implemented. - """ - pass - - def radial_gradient(self, cx, cy, r, fx, fy, stops, spread_method, - units='userSpaceOnUse'): - """ Not implemented. - """ - pass - - def draw_image(self, img, rect=None, force_copy=False): - """ Renders a GraphicsContextArray into this GC """ - xform = self.get_ctm() - - image = image_as_array(img) - shape = image.shape - if shape[2] == 4: - fmt = "RGBA" - else: - fmt = "RGB" - aii = ArrayImage(image, format=fmt) - texture = aii.texture - - # The texture coords consists of (u,v,r) for each corner of the - # texture rectangle. The coordinates are stored in the order - # bottom left, bottom right, top right, top left. - x, y, w, h = rect - texture.width = w - texture.height = h - t = texture.tex_coords - points = array([ - [x, y+h], - [x+w, y+h], - [x+w, y], - [x, y], - ]) - p = transform_points(affine_from_values(*xform), points) - a = (gl.GLfloat*32)( - t[0], t[1], t[2], 1., - p[0, 0], p[0, 1], 0, 1., - t[3], t[4], t[5], 1., - p[1, 0], p[1, 1], 0, 1., - t[6], t[7], t[8], 1., - p[2, 0], p[2, 1], 0, 1., - t[9], t[10], t[11], 1., - p[3, 0], p[3, 1], 0, 1., - ) - gl.glPushAttrib(gl.GL_ENABLE_BIT) - gl.glEnable(texture.target) - gl.glBindTexture(texture.target, texture.id) - gl.glPushClientAttrib(gl.GL_CLIENT_VERTEX_ARRAY_BIT) - gl.glInterleavedArrays(gl.GL_T4F_V4F, 0, a) - gl.glDrawArrays(gl.GL_QUADS, 0, 4) - gl.glPopClientAttrib() - gl.glPopAttrib() - - -def font_metrics_provider(): - return GraphicsContext((1, 1)) diff --git a/kiva/gl/LICENSES/LICENSE_Agg b/kiva/gl/LICENSES/LICENSE_Agg new file mode 100644 index 000000000..f17681401 --- /dev/null +++ b/kiva/gl/LICENSES/LICENSE_Agg @@ -0,0 +1,65 @@ +The Anti-Grain Geometry Project +A high quality rendering engine for C++ +http://antigrain.com + +Anti-Grain Geometry has dual licensing model. The Modified BSD +License was first added in version v2.4 just for convenience. +It is a simple, permissive non-copyleft free software license, +compatible with the GNU GPL. It's well proven and recognizable. +See http://www.fsf.org/licensing/licenses/index_html#ModifiedBSD +for details. + +Note that the Modified BSD license DOES NOT restrict your rights +if you choose the Anti-Grain Geometry Public License. + + + + +Anti-Grain Geometry Public License +==================================================== + +Anti-Grain Geometry - Version 2.4 +Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) + +Permission to copy, use, modify, sell and distribute this software +is granted provided this copyright notice appears in all copies. +This software is provided "as is" without express or implied +warranty, and with no claim as to its suitability for any purpose. + + + + + +Modified BSD License +==================================================== +Anti-Grain Geometry - Version 2.4 +Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/kiva/gl/LICENSES/LICENSE_Matplotlib b/kiva/gl/LICENSES/LICENSE_Matplotlib new file mode 100644 index 000000000..ec51537db --- /dev/null +++ b/kiva/gl/LICENSES/LICENSE_Matplotlib @@ -0,0 +1,99 @@ +License agreement for matplotlib versions 1.3.0 and later +========================================================= + +1. This LICENSE AGREEMENT is between the Matplotlib Development Team +("MDT"), and the Individual or Organization ("Licensee") accessing and +otherwise using matplotlib software in source or binary form and its +associated documentation. + +2. Subject to the terms and conditions of this License Agreement, MDT +hereby grants Licensee a nonexclusive, royalty-free, world-wide license +to reproduce, analyze, test, perform and/or display publicly, prepare +derivative works, distribute, and otherwise use matplotlib +alone or in any derivative version, provided, however, that MDT's +License Agreement and MDT's notice of copyright, i.e., "Copyright (c) +2012- Matplotlib Development Team; All Rights Reserved" are retained in +matplotlib alone or in any derivative version prepared by +Licensee. + +3. In the event Licensee prepares a derivative work that is based on or +incorporates matplotlib or any part thereof, and wants to +make the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to matplotlib . + +4. MDT is making matplotlib available to Licensee on an "AS +IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB +WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR +LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING +MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF +THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between MDT and +Licensee. This License Agreement does not grant permission to use MDT +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using matplotlib , +Licensee agrees to be bound by the terms and conditions of this License +Agreement. + +License agreement for matplotlib versions prior to 1.3.0 +======================================================== + +1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the +Individual or Organization ("Licensee") accessing and otherwise using +matplotlib software in source or binary form and its associated +documentation. + +2. Subject to the terms and conditions of this License Agreement, JDH +hereby grants Licensee a nonexclusive, royalty-free, world-wide license +to reproduce, analyze, test, perform and/or display publicly, prepare +derivative works, distribute, and otherwise use matplotlib +alone or in any derivative version, provided, however, that JDH's +License Agreement and JDH's notice of copyright, i.e., "Copyright (c) +2002-2011 John D. Hunter; All Rights Reserved" are retained in +matplotlib alone or in any derivative version prepared by +Licensee. + +3. In the event Licensee prepares a derivative work that is based on or +incorporates matplotlib or any part thereof, and wants to +make the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to matplotlib. + +4. JDH is making matplotlib available to Licensee on an "AS +IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB +WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR +LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING +MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF +THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between JDH and +Licensee. This License Agreement does not grant permission to use JDH +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using matplotlib, +Licensee agrees to be bound by the terms and conditions of this License +Agreement. \ No newline at end of file diff --git a/kiva/gl/__init__.py b/kiva/gl/__init__.py new file mode 100644 index 000000000..2cfb3f906 --- /dev/null +++ b/kiva/gl/__init__.py @@ -0,0 +1,506 @@ +from __future__ import absolute_import, print_function + +import ctypes +from math import floor +import sys + +from numpy import array, ndarray + +# Local kiva imports +from kiva.affine import affine_from_values, transform_points +from kiva.constants import BOLD, BOLD_ITALIC, ITALIC +from kiva.fonttools import Font +from kiva.gl.gl import CompiledPath, GraphicsContextGL, KivaGLFontType + + +def image_as_array(img): + """ Adapt an image object into a numpy array. + + Typically, this is used to adapt an agg GraphicsContextArray which has been + used for image storage in Kiva applications. + """ + if hasattr(img, 'bmp_array'): + # Yup, a GraphicsContextArray. + return img.bmp_array + elif isinstance(img, ndarray): + return img + else: + msg = "can't convert %r into a numpy array" % (img,) + raise NotImplementedError(msg) + + +def get_dpi(): + """ Returns the appropriate DPI setting for the system""" + pass + + +class MRU(dict): + def __init__(self, *args, **kw): + # An ordering of keys in self; the last item was the most recently used + self.__order__ = [] + self.__maxlength__ = 30 + dict.__init__(self, *args, **kw) + + def __getitem__(self, key): + val = dict.__getitem__(self, key) + # If we get here, then key was found in our dict + self.__touch__(key) + return val + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + self.__touch__(key) + + def __delitem__(self, key): + dict.__delitem__(self, key) + if key in self.__order__: + self.__order__.remove(key) + + def __touch__(self, key): + """ Puts **key** as the most recently used item """ + if len(self.__order__) == 0: + self.__order__.append(key) + if (len(self.__order__) == self.__maxlength__) and \ + key not in self.__order__: + # The MRU is full, so pop the oldest element + del self[self.__order__[0]] + if key != self.__order__[-1]: + try: + ndx = self.__order__.index(key) + self.__order__[ndx:-1] = self.__order__[ndx+1:] + self.__order__[-1] = key + except ValueError: + # new key that's not already in the cache + if len(self.__order__) == self.__maxlength__: + self.__order__ = self.__order__[1:] + [key] + else: + self.__order__.append(key) + + +# Pyglet and pyglet-related imports +# Before we import anything else from pyglet, we need to set the shadow_window +# option to False, so that it does not conflict with WX, in case someone is +# trying to use the kiva GL GraphicsContext from within WX. +# This is necessary as of pyglet 1.1. +try: + import pyglet + pyglet.options['shadow_window'] = False + + from pyglet.text import Label + from pyglet.font import load as load_font + from pyglet.font.base import Font as PygletFont + from pyglet import gl + from pygarrayimage.arrayimage import ArrayInterfaceImage + + class _ObjectSpace(object): + """ Object space mocker + + Source: https://github.com/ColinDuquesnoy/QPygletWidget + """ + def __init__(self): + # Textures and buffers scheduled for deletion the next time this + # object space is active. + self._doomed_textures = [] + self._doomed_buffers = [] + + class FakePygletContext(object): + """ pyglet.gl.Context mocker. + + This is used to make pyglet believe that a valid context has already + been setup. (Qt takes care of creating the open gl context) + + _Most of the methods are empty, there is just the minimum required to + make it look like a duck..._ + + Source: https://github.com/ColinDuquesnoy/QPygletWidget + """ + # define the same class attribute as pyglet.gl.Context + CONTEXT_SHARE_NONE = None + CONTEXT_SHARE_EXISTING = 1 + _gl_begin = False + _info = None + _workaround_checks = [ + ('_workaround_unpack_row_length', + lambda info: info.get_renderer() == 'GDI Generic'), + ('_workaround_vbo', + lambda info: info.get_renderer().startswith('ATI Radeon X')), + ('_workaround_vbo_finish', + lambda info: ('ATI' in info.get_renderer() and + info.have_version(1, 5) and + sys.platform == 'darwin')) + ] + + def __init__(self, context_share=None): + """ Setup workaround attr and object spaces + (again to mock what is done in pyglet context) + """ + self.object_space = _ObjectSpace() + for attr, check in self._workaround_checks: + setattr(self, attr, None) + + def __repr__(self): + return '%s()' % self.__class__.__name__ + + def set_current(self): + gl.current_context = self + + def destroy(self): + pass + + def delete_texture(self, texture_id): + pass + + def delete_buffer(self, buffer_id): + pass + + class ArrayImage(ArrayInterfaceImage): + """ pyglet ImageData made from numpy arrays. + + Customized from pygarrayimage's ArrayInterfaceImage to override the + texture creation. + """ + + def create_texture(self, cls, rectangle=False, force_rectangle=False): + '''Create a texture containing this image. + + If the image's dimensions are not powers of 2, a TextureRegion of + a larger Texture will be returned that matches the dimensions of + this image. + + :Parameters: + `cls` : class (subclass of Texture) + Class to construct. + `rectangle` : bool + ``True`` if a rectangle can be created; see + `AbstractImage.get_texture`. + + :rtype: cls or cls.region_class + ''' + + _is_pow2 = lambda v: (v & (v - 1)) == 0 + + target = gl.GL_TEXTURE_2D + if (rectangle + and not (_is_pow2(self.width) and _is_pow2(self.height))): + if gl.gl_info.have_extension('GL_ARB_texture_rectangle'): + target = gl.GL_TEXTURE_RECTANGLE_ARB + elif gl.gl_info.have_extension('GL_NV_texture_rectangle'): + target = gl.GL_TEXTURE_RECTANGLE_NV + + texture = cls.create_for_size(target, self.width, self.height) + subimage = False + if texture.width != self.width or texture.height != self.height: + texture = texture.get_region(0, 0, self.width, self.height) + subimage = True + + internalformat = self._get_internalformat(self.format) + + gl.glBindTexture(texture.target, texture.id) + gl.glTexParameteri(texture.target, gl.GL_TEXTURE_WRAP_S, + gl.GL_CLAMP_TO_EDGE) + gl.glTexParameteri(texture.target, gl.GL_TEXTURE_WRAP_T, + gl.GL_CLAMP_TO_EDGE) + gl.glTexParameteri(texture.target, gl.GL_TEXTURE_MAG_FILTER, + gl.GL_LINEAR) + gl.glTexParameteri(texture.target, gl.GL_TEXTURE_MIN_FILTER, + gl.GL_LINEAR) + + if subimage: + width = texture.owner.width + height = texture.owner.height + blank = (ctypes.c_ubyte * (width * height * 4))() + gl.glTexImage2D(texture.target, texture.level, + internalformat, + width, height, + 1, + gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, + blank) + internalformat = None + + self.blit_to_texture(texture.target, texture.level, + 0, 0, 0, internalformat) + + return texture + + def blit_to_texture(self, target, level, x, y, z, internalformat=None): + '''Draw this image to to the currently bound texture at `target`. + + If `internalformat` is specified, glTexImage is used to initialise + the texture; otherwise, glTexSubImage is used to update a region. + ''' + + data_format = self.format + data_pitch = abs(self._current_pitch) + + # Determine pixel format from format string + matrix = None + format, type = self._get_gl_format_and_type(data_format) + if format is None: + if (len(data_format) in (3, 4) and + gl.gl_info.have_extension('GL_ARB_imaging')): + + # Construct a color matrix to convert to GL_RGBA + def component_column(component): + try: + pos = 'RGBA'.index(component) + return [0] * pos + [1] + [0] * (3 - pos) + except ValueError: + return [0, 0, 0, 0] + + # pad to avoid index exceptions + lookup_format = data_format + 'XXX' + matrix = (component_column(lookup_format[0]) + + component_column(lookup_format[1]) + + component_column(lookup_format[2]) + + component_column(lookup_format[3])) + format = { + 3: gl.GL_RGB, + 4: gl.GL_RGBA}.get(len(data_format)) + type = gl.GL_UNSIGNED_BYTE + + gl.glMatrixMode(gl.GL_COLOR) + gl.glPushMatrix() + gl.glLoadMatrixf((gl.GLfloat * 16)(*matrix)) + else: + # Need to convert data to a standard form + data_format = { + 1: 'L', + 2: 'LA', + 3: 'RGB', + 4: 'RGBA'}.get(len(data_format)) + format, type = self._get_gl_format_and_type(data_format) + + # Workaround: don't use GL_UNPACK_ROW_LENGTH + if gl.current_context._workaround_unpack_row_length: + data_pitch = self.width * len(data_format) + + # Get data in required format (hopefully will be the same format + # it's already in, unless that's an obscure format, upside-down or + # the driver is old). + data = self._convert(data_format, data_pitch) + + if data_pitch & 0x1: + alignment = 1 + elif data_pitch & 0x2: + alignment = 2 + else: + alignment = 4 + row_length = data_pitch / len(data_format) + gl.glPushClientAttrib(gl.GL_CLIENT_PIXEL_STORE_BIT) + gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, alignment) + gl.glPixelStorei(gl.GL_UNPACK_ROW_LENGTH, row_length) + self._apply_region_unpack() + gl.glTexParameteri(target, gl.GL_TEXTURE_WRAP_S, + gl.GL_CLAMP_TO_EDGE) + gl.glTexParameteri(target, gl.GL_TEXTURE_WRAP_T, + gl.GL_CLAMP_TO_EDGE) + gl.glTexParameteri(target, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) + gl.glTexParameteri(target, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) + + if target == gl.GL_TEXTURE_3D: + assert not internalformat + gl.glTexSubImage3D(target, level, x, y, z, + self.width, self.height, + 1, format, type, data) + elif internalformat: + gl.glTexImage2D(target, level, internalformat, self.width, + self.height, 0, format, type, data) + else: + gl.glTexSubImage2D(target, level, x, y, + self.width, self.height, + format, type, data) + gl.glPopClientAttrib() + + if matrix: + gl.glPopMatrix() + gl.glMatrixMode(gl.GL_MODELVIEW) + + # Use a singleton for the font cache + GlobalFontCache = MRU() + + def GetFont(font): + """ Returns a Pylget Font object for the given Agg or Kiva font """ + if isinstance(font, PygletFont): + pyglet_font = font + else: + # KivaGLFontType + key = (font.name, font.size, font.family, font.style) + if key not in GlobalFontCache: + if isinstance(font, KivaGLFontType): + kiva_gl_font = font + font = Font( + face_name=kiva_gl_font.name, + size=kiva_gl_font.size, + family=kiva_gl_font.family, + style=kiva_gl_font.style, + ) + bold = False + italic = False + if font.style in [BOLD, BOLD_ITALIC] or font.weight == BOLD: + bold = True + if font.style in [ITALIC, BOLD_ITALIC]: + italic = True + pyglet_font = load_font(font.findfontname(), font.size, bold, + italic) + GlobalFontCache[key] = pyglet_font + else: + pyglet_font = GlobalFontCache[key] + return pyglet_font + + # Because Pyglet 1.1 uses persistent Label objects to efficiently lay + # out and render text, we cache these globally to minimize the creation + # time. An MRU is not really the right structure to use here, though. + # (We typically expect that the same numbers of labels will be rendered.) + GlobalTextCache = MRU() + GlobalTextCache.__maxlength__ = 100 + + def GetLabel(text, pyglet_font): + """ Returns a Pyglet Label object for the given text and font """ + key = (text, pyglet_font) + if key not in GlobalTextCache: + # Use anchor_y="bottom" because by default, pyglet sets the + # baseline to the y coordinate given. Unfortunately, it doesn't + # expose a per-Text descent (only a per-Font descent), so it's + # impossible to know how to offset the y value properly for a + # given string. + label = Label(text, font_name=pyglet_font.name, + font_size=pyglet_font.size, anchor_y="bottom") + GlobalTextCache[key] = label + else: + label = GlobalTextCache[key] + return label + +except ImportError: + # Pyglet is not available, so we forgo some features + ArrayImage = None + GetFont = None + GetLabel = None + gl = None + + +class GraphicsContext(GraphicsContextGL): + def __init__(self, size, *args, **kw): + # Ignore the pix_format argument for now + kw.pop('pix_format', None) + GraphicsContextGL.__init__(self, size[0], size[1], *args, **kw) + self.corner_pixel_origin = True + + self._font_stack = [] + self._current_font = None + self._text_pos = (0, 0) + + def save_state(self): + super(GraphicsContext, self).save_state() + self._font_stack.append(self._current_font) + + def restore_state(self): + super(GraphicsContext, self).restore_state() + self._current_font = self._font_stack.pop() + + def set_font(self, font): + self._current_font = font + + def get_text_extent(self, text): + if self._current_font is None: + return (0, 0, 0, 0) + + pyglet_font = GetFont(self._current_font) + label = GetLabel(text, pyglet_font) + return (0, 0, label.content_width, label.content_height) + + def set_text_position(self, x, y): + self._text_pos = (x, y) + + def show_text(self, text, point=None): + if point is None: + point = self._text_pos + return self.show_text_at_point(text, *point) + + def show_text_at_point(self, text, x, y): + if self._current_font is None: + return + + pyglet_font = GetFont(self._current_font) + label = GetLabel(text, pyglet_font) + + xform = self.get_ctm() + x0 = xform[4] + y0 = xform[5] + + # The GL backend places the center of a pixel at (0.5, 0.5); however, + # for show_text_at_point, we don't actually want to render the text + # offset by half a pixel. There is probably a better, more uniform way + # to handle this across all of Kiva, because this is probably a common + # issue that will arise, but for now, we just round the position down. + x = floor(x + x0) + y = floor(y + y0) + + label.x = x + label.y = y + c = self.get_fill_color() + label.color = (int(c[0]*255), int(c[1]*255), int(c[2]*255), + int(c[3]*255)) + label.draw() + return True + + def linear_gradient(self, x1, y1, x2, y2, stops, spread_method, + units='userSpaceOnUse'): + """ Not implemented. + """ + pass + + def radial_gradient(self, cx, cy, r, fx, fy, stops, spread_method, + units='userSpaceOnUse'): + """ Not implemented. + """ + pass + + def draw_image(self, img, rect=None, force_copy=False): + """ Renders a GraphicsContextArray into this GC """ + xform = self.get_ctm() + + image = image_as_array(img) + shape = image.shape + if shape[2] == 4: + fmt = "RGBA" + else: + fmt = "RGB" + aii = ArrayImage(image, format=fmt) + texture = aii.texture + + # The texture coords consists of (u,v,r) for each corner of the + # texture rectangle. The coordinates are stored in the order + # bottom left, bottom right, top right, top left. + x, y, w, h = rect + texture.width = w + texture.height = h + t = texture.tex_coords + points = array([ + [x, y+h], + [x+w, y+h], + [x+w, y], + [x, y], + ]) + p = transform_points(affine_from_values(*xform), points) + a = (gl.GLfloat*32)( + t[0], t[1], t[2], 1., + p[0, 0], p[0, 1], 0, 1., + t[3], t[4], t[5], 1., + p[1, 0], p[1, 1], 0, 1., + t[6], t[7], t[8], 1., + p[2, 0], p[2, 1], 0, 1., + t[9], t[10], t[11], 1., + p[3, 0], p[3, 1], 0, 1., + ) + gl.glPushAttrib(gl.GL_ENABLE_BIT) + gl.glEnable(texture.target) + gl.glBindTexture(texture.target, texture.id) + gl.glPushClientAttrib(gl.GL_CLIENT_VERTEX_ARRAY_BIT) + gl.glInterleavedArrays(gl.GL_T4F_V4F, 0, a) + gl.glDrawArrays(gl.GL_QUADS, 0, 4) + gl.glPopClientAttrib() + gl.glPopAttrib() + + +def font_metrics_provider(): + return GraphicsContext((1, 1)) diff --git a/kiva/gl/gl.i b/kiva/gl/gl.i new file mode 100644 index 000000000..9b7b63091 --- /dev/null +++ b/kiva/gl/gl.i @@ -0,0 +1,13 @@ +/* -*- c++ -*- */ +%module gl + +#if (SWIG_VERSION > 0x010322) +%feature("compactdefaultargs"); +#endif // (SWIG_VERSION > 0x010322) + +%include "constants.i" +%include "rgba.i" +%include "affine_matrix.i" +%include "compiled_path.i" +%include "font_type.i" +%include "graphics_context.i" diff --git a/kiva/gl/setup.py b/kiva/gl/setup.py new file mode 100644 index 000000000..a26a241e9 --- /dev/null +++ b/kiva/gl/setup.py @@ -0,0 +1,93 @@ +import platform + + +def configuration(parent_package='', top_path=None): + import numpy + from numpy.distutils.misc_util import Configuration + + config = Configuration('gl', parent_package, top_path) + is_windows = (platform.system() == 'Windows') + is_macos = (platform.system() == 'Darwin') + + sources = [ + # agg pieces + 'src/agg/agg_bezier_arc.cpp', + 'src/agg/agg_sqrt_tables.cpp', + 'src/agg/agg_trans_affine.cpp', + # kiva_gl + 'src/kiva_gl_affine_helpers.cpp', + 'src/kiva_gl_compiled_path.cpp', + 'src/kiva_gl_font_type.cpp', + 'src/kiva_gl_graphics_context_base.cpp', + 'src/kiva_gl_graphics_context.cpp', + 'src/kiva_gl_rect.cpp', + ] + include_dirs = ['src', 'src/agg', numpy.get_include()] + macros = [] + if is_macos: + macros = [ + ('__DARWIN__', None), + # OpenGL is deprecated starting with macOS 10.14 and gone in 10.15 + # But that doesn't mean we want to hear about it. We know, Apple. + ('GL_SILENCE_DEPRECATION', None), + ] + + config.add_library( + 'kiva_gl_src', sources, + include_dirs=include_dirs, + # Use "macros" instead of "define_macros" because the + # latter is only used for extensions, and not clibs + macros=macros, + ) + + include_dirs.append('src/swig') + + build_libraries = ['kiva_gl_src'] + extra_link_args = [] + define_macros = [] + if is_windows: + # Visual studio does not support/need these + extra_compile_args = [] + else: + extra_compile_args = [ + '-Wfatal-errors', + '-Wno-unused-function', + ] + + if is_windows: + build_libraries += ['opengl32', 'glu32'] + elif is_macos: + # Options to make macOS link OpenGL + if '64bit' not in platform.architecture(): + darwin_frameworks = ['Carbon', 'ApplicationServices', 'OpenGL'] + else: + darwin_frameworks = ['ApplicationServices', 'OpenGL'] + + for framework in darwin_frameworks: + extra_link_args.extend(['-framework', framework]) + + include_dirs.extend([ + '/System/Library/Frameworks/%s.framework/Versions/A/Headers' % x + for x in darwin_frameworks + ]) + define_macros = [ + ('__DARWIN__', None), + # OpenGL is deprecated starting with macOS 10.14 and gone in 10.15 + # But that doesn't mean we want to hear about it. We know, Apple. + ('GL_SILENCE_DEPRECATION', None), + ] + else: + # This should work for most linuxes (linuces?) + build_libraries += ['GL', 'GLU'] + + config.add_extension( + '_gl', + sources=['gl.i'], + include_dirs=include_dirs, + define_macros=define_macros, + libraries=build_libraries, + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, + language='c++', + ) + return config diff --git a/kiva/gl/src/agg/agg_array.h b/kiva/gl/src/agg/agg_array.h new file mode 100644 index 000000000..485347c56 --- /dev/null +++ b/kiva/gl/src/agg/agg_array.h @@ -0,0 +1,1119 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +#ifndef AGG_ARRAY_INCLUDED +#define AGG_ARRAY_INCLUDED + +#include +#include +#include "agg_basics.h" + +namespace kiva_gl_agg +{ + + //-------------------------------------------------------pod_array_adaptor + template class pod_array_adaptor + { + public: + typedef T value_type; + pod_array_adaptor(T* array, unsigned size) : + m_array(array), m_size(size) {} + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T* m_array; + unsigned m_size; + }; + + + //---------------------------------------------------------pod_auto_array + template class pod_auto_array + { + public: + typedef T value_type; + typedef pod_auto_array self_type; + + pod_auto_array() {} + explicit pod_auto_array(const T* c) + { + memcpy(m_array, c, sizeof(T) * Size); + } + + const self_type& operator = (const T* c) + { + memcpy(m_array, c, sizeof(T) * Size); + return *this; + } + + static unsigned size() { return Size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T m_array[Size]; + }; + + + //--------------------------------------------------------pod_auto_vector + template class pod_auto_vector + { + public: + typedef T value_type; + typedef pod_auto_vector self_type; + + pod_auto_vector() : m_size(0) {} + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void add(const T& v) { m_array[m_size++] = v; } + void push_back(const T& v) { m_array[m_size++] = v; } + void inc_size(unsigned size) { m_size += size; } + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T m_array[Size]; + unsigned m_size; + }; + + + //---------------------------------------------------------------pod_array + template class pod_array + { + public: + typedef T value_type; + typedef pod_array self_type; + + ~pod_array() { pod_allocator::deallocate(m_array, m_size); } + pod_array() : m_array(0), m_size(0) {} + + pod_array(unsigned size) : + m_array(pod_allocator::allocate(size)), + m_size(size) + {} + + pod_array(const self_type& v) : + m_array(pod_allocator::allocate(v.m_size)), + m_size(v.m_size) + { + memcpy(m_array, v.m_array, sizeof(T) * m_size); + } + + void resize(unsigned size) + { + if(size != m_size) + { + pod_allocator::deallocate(m_array, m_size); + m_array = pod_allocator::allocate(m_size = size); + } + } + const self_type& operator = (const self_type& v) + { + resize(v.size()); + memcpy(m_array, v.m_array, sizeof(T) * m_size); + return *this; + } + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + const T* data() const { return m_array; } + T* data() { return m_array; } + private: + T* m_array; + unsigned m_size; + }; + + + + //--------------------------------------------------------------pod_vector + // A simple class template to store Plain Old Data, a vector + // of a fixed size. The data is continous in memory + //------------------------------------------------------------------------ + template class pod_vector + { + public: + typedef T value_type; + + ~pod_vector() { pod_allocator::deallocate(m_array, m_capacity); } + pod_vector() : m_size(0), m_capacity(0), m_array(0) {} + pod_vector(unsigned cap, unsigned extra_tail=0); + + // Copying + pod_vector(const pod_vector&); + const pod_vector& operator = (const pod_vector&); + + // Set new capacity. All data is lost, size is set to zero. + void capacity(unsigned cap, unsigned extra_tail=0); + unsigned capacity() const { return m_capacity; } + + // Allocate n elements. All data is lost, + // but elements can be accessed in range 0...size-1. + void allocate(unsigned size, unsigned extra_tail=0); + + // Resize keeping the content. + void resize(unsigned new_size); + + void zero() + { + memset(m_array, 0, sizeof(T) * m_size); + } + + void add(const T& v) { m_array[m_size++] = v; } + void push_back(const T& v) { m_array[m_size++] = v; } + void insert_at(unsigned pos, const T& val); + void inc_size(unsigned size) { m_size += size; } + unsigned size() const { return m_size; } + unsigned byte_size() const { return m_size * sizeof(T); } + void serialize(int8u* ptr) const; + void deserialize(const int8u* data, unsigned byte_size); + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + const T* data() const { return m_array; } + T* data() { return m_array; } + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void cut_at(unsigned num) { if(num < m_size) m_size = num; } + + private: + unsigned m_size; + unsigned m_capacity; + T* m_array; + }; + + //------------------------------------------------------------------------ + template + void pod_vector::capacity(unsigned cap, unsigned extra_tail) + { + m_size = 0; + if(cap > m_capacity) + { + pod_allocator::deallocate(m_array, m_capacity); + m_capacity = cap + extra_tail; + m_array = m_capacity ? pod_allocator::allocate(m_capacity) : 0; + } + } + + //------------------------------------------------------------------------ + template + void pod_vector::allocate(unsigned size, unsigned extra_tail) + { + capacity(size, extra_tail); + m_size = size; + } + + + //------------------------------------------------------------------------ + template + void pod_vector::resize(unsigned new_size) + { + if(new_size > m_size) + { + if(new_size > m_capacity) + { + T* data = pod_allocator::allocate(new_size); + memcpy(data, m_array, m_size * sizeof(T)); + pod_allocator::deallocate(m_array, m_capacity); + m_array = data; + } + } + else + { + m_size = new_size; + } + } + + //------------------------------------------------------------------------ + template pod_vector::pod_vector(unsigned cap, unsigned extra_tail) : + m_size(0), + m_capacity(cap + extra_tail), + m_array(pod_allocator::allocate(m_capacity)) {} + + //------------------------------------------------------------------------ + template pod_vector::pod_vector(const pod_vector& v) : + m_size(v.m_size), + m_capacity(v.m_capacity), + m_array(v.m_capacity ? pod_allocator::allocate(v.m_capacity) : 0) + { + memcpy(m_array, v.m_array, sizeof(T) * v.m_size); + } + + //------------------------------------------------------------------------ + template const pod_vector& + pod_vector::operator = (const pod_vector&v) + { + allocate(v.m_size); + if(v.m_size) memcpy(m_array, v.m_array, sizeof(T) * v.m_size); + return *this; + } + + //------------------------------------------------------------------------ + template void pod_vector::serialize(int8u* ptr) const + { + if(m_size) memcpy(ptr, m_array, m_size * sizeof(T)); + } + + //------------------------------------------------------------------------ + template + void pod_vector::deserialize(const int8u* data, unsigned byte_size) + { + byte_size /= sizeof(T); + allocate(byte_size); + if(byte_size) memcpy(m_array, data, byte_size * sizeof(T)); + } + + //------------------------------------------------------------------------ + template + void pod_vector::insert_at(unsigned pos, const T& val) + { + if(pos >= m_size) + { + m_array[m_size] = val; + } + else + { + memmove(m_array + pos + 1, m_array + pos, (m_size - pos) * sizeof(T)); + m_array[pos] = val; + } + ++m_size; + } + + //---------------------------------------------------------------pod_bvector + // A simple class template to store Plain Old Data, similar to std::deque + // It doesn't reallocate memory but instead, uses blocks of data of size + // of (1 << S), that is, power of two. The data is NOT contiguous in memory, + // so the only valid access method is operator [] or curr(), prev(), next() + // + // There reallocs occure only when the pool of pointers to blocks needs + // to be extended (it happens very rarely). You can control the value + // of increment to reallocate the pointer buffer. See the second constructor. + // By default, the incremeent value equals (1 << S), i.e., the block size. + //------------------------------------------------------------------------ + template class pod_bvector + { + public: + enum block_scale_e + { + block_shift = S, + block_size = 1 << block_shift, + block_mask = block_size - 1 + }; + + typedef T value_type; + + ~pod_bvector(); + pod_bvector(); + pod_bvector(unsigned block_ptr_inc); + + // Copying + pod_bvector(const pod_bvector& v); + const pod_bvector& operator = (const pod_bvector& v); + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void free_all() { free_tail(0); } + void free_tail(unsigned size); + void add(const T& val); + void push_back(const T& val) { add(val); } + void modify_last(const T& val); + void remove_last(); + + int allocate_continuous_block(unsigned num_elements); + + void add_array(const T* ptr, unsigned num_elem) + { + while(num_elem--) + { + add(*ptr++); + } + } + + template void add_data(DataAccessor& data) + { + while(data.size()) + { + add(*data); + ++data; + } + } + + void cut_at(unsigned size) + { + if(size < m_size) m_size = size; + } + + unsigned size() const { return m_size; } + + const T& operator [] (unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T& operator [] (unsigned i) + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + const T& at(unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T& at(unsigned i) + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T value_at(unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + const T& curr(unsigned idx) const + { + return (*this)[idx]; + } + + T& curr(unsigned idx) + { + return (*this)[idx]; + } + + const T& prev(unsigned idx) const + { + return (*this)[(idx + m_size - 1) % m_size]; + } + + T& prev(unsigned idx) + { + return (*this)[(idx + m_size - 1) % m_size]; + } + + const T& next(unsigned idx) const + { + return (*this)[(idx + 1) % m_size]; + } + + T& next(unsigned idx) + { + return (*this)[(idx + 1) % m_size]; + } + + const T& last() const + { + return (*this)[m_size - 1]; + } + + T& last() + { + return (*this)[m_size - 1]; + } + + unsigned byte_size() const; + void serialize(int8u* ptr) const; + void deserialize(const int8u* data, unsigned byte_size); + void deserialize(unsigned start, const T& empty_val, + const int8u* data, unsigned byte_size); + + template + void deserialize(ByteAccessor data) + { + remove_all(); + unsigned elem_size = data.size() / sizeof(T); + + for(unsigned i = 0; i < elem_size; ++i) + { + int8u* ptr = (int8u*)data_ptr(); + for(unsigned j = 0; j < sizeof(T); ++j) + { + *ptr++ = *data; + ++data; + } + ++m_size; + } + } + + template + void deserialize(unsigned start, const T& empty_val, ByteAccessor data) + { + while(m_size < start) + { + add(empty_val); + } + + unsigned elem_size = data.size() / sizeof(T); + for(unsigned i = 0; i < elem_size; ++i) + { + int8u* ptr; + if(start + i < m_size) + { + ptr = (int8u*)(&((*this)[start + i])); + } + else + { + ptr = (int8u*)data_ptr(); + ++m_size; + } + for(unsigned j = 0; j < sizeof(T); ++j) + { + *ptr++ = *data; + ++data; + } + } + } + + const T* block(unsigned nb) const { return m_blocks[nb]; } + + private: + void allocate_block(unsigned nb); + T* data_ptr(); + + unsigned m_size; + unsigned m_num_blocks; + unsigned m_max_blocks; + T** m_blocks; + unsigned m_block_ptr_inc; + }; + + + //------------------------------------------------------------------------ + template pod_bvector::~pod_bvector() + { + if(m_num_blocks) + { + T** blk = m_blocks + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(*blk, block_size); + --blk; + } + } + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::free_tail(unsigned size) + { + if(size < m_size) + { + unsigned nb = (size + block_mask) >> block_shift; + while(m_num_blocks > nb) + { + pod_allocator::deallocate(m_blocks[--m_num_blocks], block_size); + } + if(m_num_blocks == 0) + { + pod_allocator::deallocate(m_blocks, m_max_blocks); + m_blocks = 0; + m_max_blocks = 0; + } + m_size = size; + } + } + + + //------------------------------------------------------------------------ + template pod_bvector::pod_bvector() : + m_size(0), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_block_ptr_inc(block_size) + { + } + + + //------------------------------------------------------------------------ + template + pod_bvector::pod_bvector(unsigned block_ptr_inc) : + m_size(0), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_block_ptr_inc(block_ptr_inc) + { + } + + + //------------------------------------------------------------------------ + template + pod_bvector::pod_bvector(const pod_bvector& v) : + m_size(v.m_size), + m_num_blocks(v.m_num_blocks), + m_max_blocks(v.m_max_blocks), + m_blocks(v.m_max_blocks ? + pod_allocator::allocate(v.m_max_blocks) : + 0), + m_block_ptr_inc(v.m_block_ptr_inc) + { + unsigned i; + for(i = 0; i < v.m_num_blocks; ++i) + { + m_blocks[i] = pod_allocator::allocate(block_size); + memcpy(m_blocks[i], v.m_blocks[i], block_size * sizeof(T)); + } + } + + + //------------------------------------------------------------------------ + template + const pod_bvector& + pod_bvector::operator = (const pod_bvector& v) + { + unsigned i; + for(i = m_num_blocks; i < v.m_num_blocks; ++i) + { + allocate_block(i); + } + for(i = 0; i < v.m_num_blocks; ++i) + { + memcpy(m_blocks[i], v.m_blocks[i], block_size * sizeof(T)); + } + m_size = v.m_size; + return *this; + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::allocate_block(unsigned nb) + { + if(nb >= m_max_blocks) + { + T** new_blocks = pod_allocator::allocate(m_max_blocks + m_block_ptr_inc); + + if(m_blocks) + { + memcpy(new_blocks, + m_blocks, + m_num_blocks * sizeof(T*)); + + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_blocks = new_blocks; + m_max_blocks += m_block_ptr_inc; + } + m_blocks[nb] = pod_allocator::allocate(block_size); + m_num_blocks++; + } + + + + //------------------------------------------------------------------------ + template + inline T* pod_bvector::data_ptr() + { + unsigned nb = m_size >> block_shift; + if(nb >= m_num_blocks) + { + allocate_block(nb); + } + return m_blocks[nb] + (m_size & block_mask); + } + + + + //------------------------------------------------------------------------ + template + inline void pod_bvector::add(const T& val) + { + *data_ptr() = val; + ++m_size; + } + + + //------------------------------------------------------------------------ + template + inline void pod_bvector::remove_last() + { + if(m_size) --m_size; + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::modify_last(const T& val) + { + remove_last(); + add(val); + } + + + //------------------------------------------------------------------------ + template + int pod_bvector::allocate_continuous_block(unsigned num_elements) + { + if(num_elements < block_size) + { + data_ptr(); // Allocate initial block if necessary + unsigned rest = block_size - (m_size & block_mask); + unsigned index; + if(num_elements <= rest) + { + // The rest of the block is good, we can use it + //----------------- + index = m_size; + m_size += num_elements; + return index; + } + + // New block + //--------------- + m_size += rest; + data_ptr(); + index = m_size; + m_size += num_elements; + return index; + } + return -1; // Impossible to allocate + } + + + //------------------------------------------------------------------------ + template + unsigned pod_bvector::byte_size() const + { + return m_size * sizeof(T); + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::serialize(int8u* ptr) const + { + unsigned i; + for(i = 0; i < m_size; i++) + { + memcpy(ptr, &(*this)[i], sizeof(T)); + ptr += sizeof(T); + } + } + + //------------------------------------------------------------------------ + template + void pod_bvector::deserialize(const int8u* data, unsigned byte_size) + { + remove_all(); + byte_size /= sizeof(T); + for(unsigned i = 0; i < byte_size; ++i) + { + T* ptr = data_ptr(); + memcpy(ptr, data, sizeof(T)); + ++m_size; + data += sizeof(T); + } + } + + + // Replace or add a number of elements starting from "start" position + //------------------------------------------------------------------------ + template + void pod_bvector::deserialize(unsigned start, const T& empty_val, + const int8u* data, unsigned byte_size) + { + while(m_size < start) + { + add(empty_val); + } + + byte_size /= sizeof(T); + for(unsigned i = 0; i < byte_size; ++i) + { + if(start + i < m_size) + { + memcpy(&((*this)[start + i]), data, sizeof(T)); + } + else + { + T* ptr = data_ptr(); + memcpy(ptr, data, sizeof(T)); + ++m_size; + } + data += sizeof(T); + } + } + + + //---------------------------------------------------------block_allocator + // Allocator for arbitrary POD data. Most usable in different cache + // systems for efficient memory allocations. + // Memory is allocated with blocks of fixed size ("block_size" in + // the constructor). If required size exceeds the block size the allocator + // creates a new block of the required size. However, the most efficient + // use is when the average reqired size is much less than the block size. + //------------------------------------------------------------------------ + class block_allocator + { + struct block_type + { + int8u* data; + unsigned size; + }; + + public: + void remove_all() + { + if(m_num_blocks) + { + block_type* blk = m_blocks + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(blk->data, blk->size); + --blk; + } + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_num_blocks = 0; + m_max_blocks = 0; + m_blocks = 0; + m_buf_ptr = 0; + m_rest = 0; + } + + ~block_allocator() + { + remove_all(); + } + + block_allocator(unsigned block_size, unsigned block_ptr_inc=256-8) : + m_block_size(block_size), + m_block_ptr_inc(block_ptr_inc), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_buf_ptr(0), + m_rest(0) + { + } + + + int8u* allocate(unsigned size, unsigned alignment=1) + { + if(size == 0) return 0; + if(size <= m_rest) + { + int8u* ptr = m_buf_ptr; + if(alignment > 1) + { + unsigned align = + (alignment - unsigned((size_t)ptr) % alignment) % alignment; + + size += align; + ptr += align; + if(size <= m_rest) + { + m_rest -= size; + m_buf_ptr += size; + return ptr; + } + allocate_block(size); + return allocate(size - align, alignment); + } + m_rest -= size; + m_buf_ptr += size; + return ptr; + } + allocate_block(size + alignment - 1); + return allocate(size, alignment); + } + + + private: + void allocate_block(unsigned size) + { + if(size < m_block_size) size = m_block_size; + if(m_num_blocks >= m_max_blocks) + { + block_type* new_blocks = + pod_allocator::allocate(m_max_blocks + m_block_ptr_inc); + + if(m_blocks) + { + memcpy(new_blocks, + m_blocks, + m_num_blocks * sizeof(block_type)); + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_blocks = new_blocks; + m_max_blocks += m_block_ptr_inc; + } + + m_blocks[m_num_blocks].size = size; + m_blocks[m_num_blocks].data = + m_buf_ptr = + pod_allocator::allocate(size); + + m_num_blocks++; + m_rest = size; + } + + unsigned m_block_size; + unsigned m_block_ptr_inc; + unsigned m_num_blocks; + unsigned m_max_blocks; + block_type* m_blocks; + int8u* m_buf_ptr; + unsigned m_rest; + }; + + + + + + + + + //------------------------------------------------------------------------ + enum quick_sort_threshold_e + { + quick_sort_threshold = 9 + }; + + + //-----------------------------------------------------------swap_elements + template inline void swap_elements(T& a, T& b) + { + T temp = a; + a = b; + b = temp; + } + + + //--------------------------------------------------------------quick_sort + template + void quick_sort(Array& arr, Less less) + { + if(arr.size() < 2) return; + + typename Array::value_type* e1; + typename Array::value_type* e2; + + int stack[80]; + int* top = stack; + int limit = arr.size(); + int base = 0; + + for(;;) + { + int len = limit - base; + + int i; + int j; + int pivot; + + if(len > quick_sort_threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + swap_elements(arr[base], arr[pivot]); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + e1 = &(arr[j]); + e2 = &(arr[i]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + e1 = &(arr[base]); + e2 = &(arr[i]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + e1 = &(arr[j]); + e2 = &(arr[base]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + for(;;) + { + do i++; while( less(arr[i], arr[base]) ); + do j--; while( less(arr[base], arr[j]) ); + + if( i > j ) + { + break; + } + + swap_elements(arr[i], arr[j]); + } + + swap_elements(arr[base], arr[j]); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; less(*(e1 = &(arr[j + 1])), *(e2 = &(arr[j]))); j--) + { + swap_elements(*e1, *e2); + if(j == base) + { + break; + } + } + } + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } + } + + + + + //------------------------------------------------------remove_duplicates + // Remove duplicates from a sorted array. It doesn't cut the + // tail of the array, it just returns the number of remaining elements. + //----------------------------------------------------------------------- + template + unsigned remove_duplicates(Array& arr, Equal equal) + { + if(arr.size() < 2) return arr.size(); + + unsigned i, j; + for(i = 1, j = 1; i < arr.size(); i++) + { + typename Array::value_type& e = arr[i]; + if(!equal(e, arr[i - 1])) + { + arr[j++] = e; + } + } + return j; + } + + //--------------------------------------------------------invert_container + template void invert_container(Array& arr) + { + int i = 0; + int j = arr.size() - 1; + while(i < j) + { + swap_elements(arr[i++], arr[j--]); + } + } + + //------------------------------------------------------binary_search_pos + template + unsigned binary_search_pos(const Array& arr, const Value& val, Less less) + { + if(arr.size() == 0) return 0; + + unsigned beg = 0; + unsigned end = arr.size() - 1; + + if(less(val, arr[0])) return 0; + if(less(arr[end], val)) return end + 1; + + while(end - beg > 1) + { + unsigned mid = (end + beg) >> 1; + if(less(val, arr[mid])) end = mid; + else beg = mid; + } + + //if(beg <= 0 && less(val, arr[0])) return 0; + //if(end >= arr.size() - 1 && less(arr[end], val)) ++end; + + return end; + } + + //----------------------------------------------------------range_adaptor + template class range_adaptor + { + public: + typedef typename Array::value_type value_type; + + range_adaptor(Array& array, unsigned start, unsigned size) : + m_array(array), m_start(start), m_size(size) + {} + + unsigned size() const { return m_size; } + const value_type& operator [] (unsigned i) const { return m_array[m_start + i]; } + value_type& operator [] (unsigned i) { return m_array[m_start + i]; } + const value_type& at(unsigned i) const { return m_array[m_start + i]; } + value_type& at(unsigned i) { return m_array[m_start + i]; } + value_type value_at(unsigned i) const { return m_array[m_start + i]; } + + private: + Array& m_array; + unsigned m_start; + unsigned m_size; + }; + + //---------------------------------------------------------------int_less + inline bool int_less(int a, int b) { return a < b; } + + //------------------------------------------------------------int_greater + inline bool int_greater(int a, int b) { return a > b; } + + //----------------------------------------------------------unsigned_less + inline bool unsigned_less(unsigned a, unsigned b) { return a < b; } + + //-------------------------------------------------------unsigned_greater + inline bool unsigned_greater(unsigned a, unsigned b) { return a > b; } +} + +#endif diff --git a/kiva/gl/src/agg/agg_basics.h b/kiva/gl/src/agg/agg_basics.h new file mode 100644 index 000000000..88930fd47 --- /dev/null +++ b/kiva/gl/src/agg/agg_basics.h @@ -0,0 +1,560 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_BASICS_INCLUDED +#define AGG_BASICS_INCLUDED + +#include +#include "agg_config.h" + +//---------------------------------------------------------AGG_CUSTOM_ALLOCATOR +#ifdef AGG_CUSTOM_ALLOCATOR +#include "agg_allocator.h" +#else +namespace kiva_gl_agg +{ + // The policy of all AGG containers and memory allocation strategy + // in general is that no allocated data requires explicit construction. + // It means that the allocator can be really simple; you can even + // replace new/delete to malloc/free. The constructors and destructors + // won't be called in this case, however everything will remain working. + // The second argument of deallocate() is the size of the allocated + // block. You can use this information if you wish. + //------------------------------------------------------------pod_allocator + template struct pod_allocator + { + static T* allocate(unsigned num) { return new T [num]; } + static void deallocate(T* ptr, unsigned) { delete [] ptr; } + }; + + // Single object allocator. It's also can be replaced with your custom + // allocator. The difference is that it can only allocate a single + // object and the constructor and destructor must be called. + // In AGG there is no need to allocate an array of objects with + // calling their constructors (only single ones). So that, if you + // replace these new/delete to malloc/free make sure that the in-place + // new is called and take care of calling the destructor too. + //------------------------------------------------------------obj_allocator + template struct obj_allocator + { + static T* allocate() { return new T; } + static void deallocate(T* ptr) { delete ptr; } + }; +} +#endif + + +//-------------------------------------------------------- Default basic types +// +// If the compiler has different capacity of the basic types you can redefine +// them via the compiler command line or by generating agg_config.h that is +// empty by default. +// +#ifndef AGG_INT8 +#define AGG_INT8 signed char +#endif + +#ifndef AGG_INT8U +#define AGG_INT8U unsigned char +#endif + +#ifndef AGG_INT16 +#define AGG_INT16 short +#endif + +#ifndef AGG_INT16U +#define AGG_INT16U unsigned short +#endif + +#ifndef AGG_INT32 +#define AGG_INT32 int +#endif + +#ifndef AGG_INT32U +#define AGG_INT32U unsigned +#endif + +#ifndef AGG_INT64 +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define AGG_INT64 signed __int64 +#else +#define AGG_INT64 signed long long +#endif +#endif + +#ifndef AGG_INT64U +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define AGG_INT64U unsigned __int64 +#else +#define AGG_INT64U unsigned long long +#endif +#endif + +//------------------------------------------------ Some fixes for MS Visual C++ +#if defined(_MSC_VER) +#pragma warning(disable:4786) // Identifier was truncated... +#endif + +#if defined(_MSC_VER) +#define AGG_INLINE __forceinline +#else +#define AGG_INLINE inline +#endif + +namespace kiva_gl_agg +{ + //------------------------------------------------------------------------- + typedef AGG_INT8 int8; //----int8 + typedef AGG_INT8U int8u; //----int8u + typedef AGG_INT16 int16; //----int16 + typedef AGG_INT16U int16u; //----int16u + typedef AGG_INT32 int32; //----int32 + typedef AGG_INT32U int32u; //----int32u + typedef AGG_INT64 int64; //----int64 + typedef AGG_INT64U int64u; //----int64u + +#if defined(AGG_FISTP) +#pragma warning(push) +#pragma warning(disable : 4035) //Disable warning "no return value" + AGG_INLINE int iround(double v) //-------iround + { + int t; + __asm fld qword ptr [v] + __asm fistp dword ptr [t] + __asm mov eax, dword ptr [t] + } + AGG_INLINE unsigned uround(double v) //-------uround + { + unsigned t; + __asm fld qword ptr [v] + __asm fistp dword ptr [t] + __asm mov eax, dword ptr [t] + } +#pragma warning(pop) + AGG_INLINE int ifloor(double v) + { + return int(floor(v)); + } + AGG_INLINE unsigned ufloor(double v) //-------ufloor + { + return unsigned(floor(v)); + } + AGG_INLINE int iceil(double v) + { + return int(ceil(v)); + } + AGG_INLINE unsigned uceil(double v) //--------uceil + { + return unsigned(ceil(v)); + } +#elif defined(AGG_QIFIST) + AGG_INLINE int iround(double v) + { + return int(v); + } + AGG_INLINE int uround(double v) + { + return unsigned(v); + } + AGG_INLINE int ifloor(double v) + { + return int(floor(v)); + } + AGG_INLINE unsigned ufloor(double v) + { + return unsigned(floor(v)); + } + AGG_INLINE int iceil(double v) + { + return int(ceil(v)); + } + AGG_INLINE unsigned uceil(double v) + { + return unsigned(ceil(v)); + } +#else + AGG_INLINE int iround(double v) + { + return int((v < 0.0) ? v - 0.5 : v + 0.5); + } + AGG_INLINE int uround(double v) + { + return unsigned(v + 0.5); + } + AGG_INLINE int ifloor(double v) + { + int i = int(v); + return i - (i > v); + } + AGG_INLINE unsigned ufloor(double v) + { + return unsigned(v); + } + AGG_INLINE int iceil(double v) + { + return int(ceil(v)); + } + AGG_INLINE unsigned uceil(double v) + { + return unsigned(ceil(v)); + } +#endif + + //---------------------------------------------------------------saturation + template struct saturation + { + AGG_INLINE static int iround(double v) + { + if(v < double(-Limit)) return -Limit; + if(v > double( Limit)) return Limit; + return kiva_gl_agg::iround(v); + } + }; + + //------------------------------------------------------------------mul_one + template struct mul_one + { + AGG_INLINE static unsigned mul(unsigned a, unsigned b) + { + register unsigned q = a * b + (1 << (Shift-1)); + return (q + (q >> Shift)) >> Shift; + } + }; + + //------------------------------------------------------------------------- + typedef unsigned char cover_type; //----cover_type + enum cover_scale_e + { + cover_shift = 8, //----cover_shift + cover_size = 1 << cover_shift, //----cover_size + cover_mask = cover_size - 1, //----cover_mask + cover_none = 0, //----cover_none + cover_full = cover_mask //----cover_full + }; + + //----------------------------------------------------poly_subpixel_scale_e + // These constants determine the subpixel accuracy, to be more precise, + // the number of bits of the fractional part of the coordinates. + // The possible coordinate capacity in bits can be calculated by formula: + // sizeof(int) * 8 - poly_subpixel_shift, i.e, for 32-bit integers and + // 8-bits fractional part the capacity is 24 bits. + enum poly_subpixel_scale_e + { + poly_subpixel_shift = 8, //----poly_subpixel_shift + poly_subpixel_scale = 1< struct rect_base + { + typedef T value_type; + typedef rect_base self_type; + T x1, y1, x2, y2; + + rect_base() {} + rect_base(T x1_, T y1_, T x2_, T y2_) : + x1(x1_), y1(y1_), x2(x2_), y2(y2_) {} + + void init(T x1_, T y1_, T x2_, T y2_) + { + x1 = x1_; y1 = y1_; x2 = x2_; y2 = y2_; + } + + const self_type& normalize() + { + T t; + if(x1 > x2) { t = x1; x1 = x2; x2 = t; } + if(y1 > y2) { t = y1; y1 = y2; y2 = t; } + return *this; + } + + bool clip(const self_type& r) + { + if(x2 > r.x2) x2 = r.x2; + if(y2 > r.y2) y2 = r.y2; + if(x1 < r.x1) x1 = r.x1; + if(y1 < r.y1) y1 = r.y1; + return x1 <= x2 && y1 <= y2; + } + + bool is_valid() const + { + return x1 <= x2 && y1 <= y2; + } + + bool hit_test(T x, T y) const + { + return (x >= x1 && x <= x2 && y >= y1 && y <= y2); + } + + bool overlaps(const self_type& r) const + { + return !(r.x1 > x2 || r.x2 < x1 + || r.y1 > y2 || r.y2 < y1); + } + }; + + //-----------------------------------------------------intersect_rectangles + template + inline Rect intersect_rectangles(const Rect& r1, const Rect& r2) + { + Rect r = r1; + + // First process x2,y2 because the other order + // results in Internal Compiler Error under + // Microsoft Visual C++ .NET 2003 69462-335-0000007-18038 in + // case of "Maximize Speed" optimization option. + //----------------- + if(r.x2 > r2.x2) r.x2 = r2.x2; + if(r.y2 > r2.y2) r.y2 = r2.y2; + if(r.x1 < r2.x1) r.x1 = r2.x1; + if(r.y1 < r2.y1) r.y1 = r2.y1; + return r; + } + + + //---------------------------------------------------------unite_rectangles + template + inline Rect unite_rectangles(const Rect& r1, const Rect& r2) + { + Rect r = r1; + if(r.x2 < r2.x2) r.x2 = r2.x2; + if(r.y2 < r2.y2) r.y2 = r2.y2; + if(r.x1 > r2.x1) r.x1 = r2.x1; + if(r.y1 > r2.y1) r.y1 = r2.y1; + return r; + } + + typedef rect_base rect_i; //----rect_i + typedef rect_base rect_f; //----rect_f + typedef rect_base rect_d; //----rect_d + + //---------------------------------------------------------path_commands_e + enum path_commands_e + { + path_cmd_stop = 0, //----path_cmd_stop + path_cmd_move_to = 1, //----path_cmd_move_to + path_cmd_line_to = 2, //----path_cmd_line_to + path_cmd_curve3 = 3, //----path_cmd_curve3 + path_cmd_curve4 = 4, //----path_cmd_curve4 + path_cmd_curveN = 5, //----path_cmd_curveN + path_cmd_catrom = 6, //----path_cmd_catrom + path_cmd_ubspline = 7, //----path_cmd_ubspline + path_cmd_end_poly = 0x0F, //----path_cmd_end_poly + path_cmd_mask = 0x0F //----path_cmd_mask + }; + + //------------------------------------------------------------path_flags_e + enum path_flags_e + { + path_flags_none = 0, //----path_flags_none + path_flags_ccw = 0x10, //----path_flags_ccw + path_flags_cw = 0x20, //----path_flags_cw + path_flags_close = 0x40, //----path_flags_close + path_flags_mask = 0xF0 //----path_flags_mask + }; + + //---------------------------------------------------------------is_vertex + inline bool is_vertex(unsigned c) + { + return c >= path_cmd_move_to && c < path_cmd_end_poly; + } + + //--------------------------------------------------------------is_drawing + inline bool is_drawing(unsigned c) + { + return c >= path_cmd_line_to && c < path_cmd_end_poly; + } + + //-----------------------------------------------------------------is_stop + inline bool is_stop(unsigned c) + { + return c == path_cmd_stop; + } + + //--------------------------------------------------------------is_move_to + inline bool is_move_to(unsigned c) + { + return c == path_cmd_move_to; + } + + //--------------------------------------------------------------is_line_to + inline bool is_line_to(unsigned c) + { + return c == path_cmd_line_to; + } + + //----------------------------------------------------------------is_curve + inline bool is_curve(unsigned c) + { + return c == path_cmd_curve3 || c == path_cmd_curve4; + } + + //---------------------------------------------------------------is_curve3 + inline bool is_curve3(unsigned c) + { + return c == path_cmd_curve3; + } + + //---------------------------------------------------------------is_curve4 + inline bool is_curve4(unsigned c) + { + return c == path_cmd_curve4; + } + + //-------------------------------------------------------------is_end_poly + inline bool is_end_poly(unsigned c) + { + return (c & path_cmd_mask) == path_cmd_end_poly; + } + + //----------------------------------------------------------------is_close + inline bool is_close(unsigned c) + { + return (c & ~(path_flags_cw | path_flags_ccw)) == + (path_cmd_end_poly | path_flags_close); + } + + //------------------------------------------------------------is_next_poly + inline bool is_next_poly(unsigned c) + { + return is_stop(c) || is_move_to(c) || is_end_poly(c); + } + + //-------------------------------------------------------------------is_cw + inline bool is_cw(unsigned c) + { + return (c & path_flags_cw) != 0; + } + + //------------------------------------------------------------------is_ccw + inline bool is_ccw(unsigned c) + { + return (c & path_flags_ccw) != 0; + } + + //-------------------------------------------------------------is_oriented + inline bool is_oriented(unsigned c) + { + return (c & (path_flags_cw | path_flags_ccw)) != 0; + } + + //---------------------------------------------------------------is_closed + inline bool is_closed(unsigned c) + { + return (c & path_flags_close) != 0; + } + + //----------------------------------------------------------get_close_flag + inline unsigned get_close_flag(unsigned c) + { + return c & path_flags_close; + } + + //-------------------------------------------------------clear_orientation + inline unsigned clear_orientation(unsigned c) + { + return c & ~(path_flags_cw | path_flags_ccw); + } + + //---------------------------------------------------------get_orientation + inline unsigned get_orientation(unsigned c) + { + return c & (path_flags_cw | path_flags_ccw); + } + + //---------------------------------------------------------set_orientation + inline unsigned set_orientation(unsigned c, unsigned o) + { + return clear_orientation(c) | o; + } + + //--------------------------------------------------------------point_base + template struct point_base + { + typedef T value_type; + T x,y; + point_base() {} + point_base(T x_, T y_) : x(x_), y(y_) {} + }; + typedef point_base point_i; //-----point_i + typedef point_base point_f; //-----point_f + typedef point_base point_d; //-----point_d + + //-------------------------------------------------------------vertex_base + template struct vertex_base + { + typedef T value_type; + T x,y; + unsigned cmd; + vertex_base() {} + vertex_base(T x_, T y_, unsigned cmd_) : x(x_), y(y_), cmd(cmd_) {} + }; + typedef vertex_base vertex_i; //-----vertex_i + typedef vertex_base vertex_f; //-----vertex_f + typedef vertex_base vertex_d; //-----vertex_d + + //----------------------------------------------------------------row_info + template struct row_info + { + int x1, x2; + T* ptr; + row_info() {} + row_info(int x1_, int x2_, T* ptr_) : x1(x1_), x2(x2_), ptr(ptr_) {} + }; + + //----------------------------------------------------------const_row_info + template struct const_row_info + { + int x1, x2; + const T* ptr; + const_row_info() {} + const_row_info(int x1_, int x2_, const T* ptr_) : + x1(x1_), x2(x2_), ptr(ptr_) {} + }; + + //------------------------------------------------------------is_equal_eps + template inline bool is_equal_eps(T v1, T v2, T epsilon) + { + return fabs(v1 - v2) <= double(epsilon); + } +} + + +#endif + diff --git a/kiva/gl/src/agg/agg_bezier_arc.cpp b/kiva/gl/src/agg/agg_bezier_arc.cpp new file mode 100644 index 000000000..c499c23a8 --- /dev/null +++ b/kiva/gl/src/agg/agg_bezier_arc.cpp @@ -0,0 +1,255 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e., +// 4, 7, 10, or 13 vertices. +// +//---------------------------------------------------------------------------- + +#include +#include "agg_bezier_arc.h" + +namespace kiva_gl_agg +{ + + // This epsilon is used to prevent us from adding degenerate curves + // (converging to a single point). + // The value isn't very critical. Function arc_to_bezier() has a limit + // of the sweep_angle. If fabs(sweep_angle) exceeds pi/2 the curve + // becomes inaccurate. But slight exceeding is quite appropriate. + //-------------------------------------------------bezier_arc_angle_epsilon + const double bezier_arc_angle_epsilon = 0.01; + + //------------------------------------------------------------arc_to_bezier + void arc_to_bezier(double cx, double cy, double rx, double ry, + double start_angle, double sweep_angle, + double* curve) + { + double x0 = cos(sweep_angle / 2.0); + double y0 = sin(sweep_angle / 2.0); + double tx = (1.0 - x0) * 4.0 / 3.0; + double ty = y0 - tx * x0 / y0; + double px[4]; + double py[4]; + px[0] = x0; + py[0] = -y0; + px[1] = x0 + tx; + py[1] = -ty; + px[2] = x0 + tx; + py[2] = ty; + px[3] = x0; + py[3] = y0; + + double sn = sin(start_angle + sweep_angle / 2.0); + double cs = cos(start_angle + sweep_angle / 2.0); + + unsigned i; + for(i = 0; i < 4; i++) + { + curve[i * 2] = cx + rx * (px[i] * cs - py[i] * sn); + curve[i * 2 + 1] = cy + ry * (px[i] * sn + py[i] * cs); + } + } + + + + //------------------------------------------------------------------------ + void bezier_arc::init(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle) + { + start_angle = fmod(start_angle, 2.0 * pi); + if(sweep_angle >= 2.0 * pi) sweep_angle = 2.0 * pi; + if(sweep_angle <= -2.0 * pi) sweep_angle = -2.0 * pi; + + if(fabs(sweep_angle) < 1e-10) + { + m_num_vertices = 4; + m_cmd = path_cmd_line_to; + m_vertices[0] = x + rx * cos(start_angle); + m_vertices[1] = y + ry * sin(start_angle); + m_vertices[2] = x + rx * cos(start_angle + sweep_angle); + m_vertices[3] = y + ry * sin(start_angle + sweep_angle); + return; + } + + double total_sweep = 0.0; + double local_sweep = 0.0; + double prev_sweep; + m_num_vertices = 2; + m_cmd = path_cmd_curve4; + bool done = false; + do + { + if(sweep_angle < 0.0) + { + prev_sweep = total_sweep; + local_sweep = -pi * 0.5; + total_sweep -= pi * 0.5; + if(total_sweep <= sweep_angle + bezier_arc_angle_epsilon) + { + local_sweep = sweep_angle - prev_sweep; + done = true; + } + } + else + { + prev_sweep = total_sweep; + local_sweep = pi * 0.5; + total_sweep += pi * 0.5; + if(total_sweep >= sweep_angle - bezier_arc_angle_epsilon) + { + local_sweep = sweep_angle - prev_sweep; + done = true; + } + } + + arc_to_bezier(x, y, rx, ry, + start_angle, + local_sweep, + m_vertices + m_num_vertices - 2); + + m_num_vertices += 6; + start_angle += local_sweep; + } + while(!done && m_num_vertices < 26); + } + + + + + //-------------------------------------------------------------------- + void bezier_arc_svg::init(double x0, double y0, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2) + { + m_radii_ok = true; + + if(rx < 0.0) rx = -rx; + if(ry < 0.0) ry = -rx; + + // Calculate the middle point between + // the current and the final points + //------------------------ + double dx2 = (x0 - x2) / 2.0; + double dy2 = (y0 - y2) / 2.0; + + double cos_a = cos(angle); + double sin_a = sin(angle); + + // Calculate (x1, y1) + //------------------------ + double x1 = cos_a * dx2 + sin_a * dy2; + double y1 = -sin_a * dx2 + cos_a * dy2; + + // Ensure radii are large enough + //------------------------ + double prx = rx * rx; + double pry = ry * ry; + double px1 = x1 * x1; + double py1 = y1 * y1; + + // Check that radii are large enough + //------------------------ + double radii_check = px1/prx + py1/pry; + if(radii_check > 1.0) + { + rx = sqrt(radii_check) * rx; + ry = sqrt(radii_check) * ry; + prx = rx * rx; + pry = ry * ry; + if(radii_check > 10.0) m_radii_ok = false; + } + + // Calculate (cx1, cy1) + //------------------------ + double sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0; + double sq = (prx*pry - prx*py1 - pry*px1) / (prx*py1 + pry*px1); + double coef = sign * sqrt((sq < 0) ? 0 : sq); + double cx1 = coef * ((rx * y1) / ry); + double cy1 = coef * -((ry * x1) / rx); + + // + // Calculate (cx, cy) from (cx1, cy1) + //------------------------ + double sx2 = (x0 + x2) / 2.0; + double sy2 = (y0 + y2) / 2.0; + double cx = sx2 + (cos_a * cx1 - sin_a * cy1); + double cy = sy2 + (sin_a * cx1 + cos_a * cy1); + + // Calculate the start_angle (angle1) and the sweep_angle (dangle) + //------------------------ + double ux = (x1 - cx1) / rx; + double uy = (y1 - cy1) / ry; + double vx = (-x1 - cx1) / rx; + double vy = (-y1 - cy1) / ry; + double p, n; + + // Calculate the angle start + //------------------------ + n = sqrt(ux*ux + uy*uy); + p = ux; // (1 * ux) + (0 * uy) + sign = (uy < 0) ? -1.0 : 1.0; + double v = p / n; + if(v < -1.0) v = -1.0; + if(v > 1.0) v = 1.0; + double start_angle = sign * acos(v); + + // Calculate the sweep angle + //------------------------ + n = sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)); + p = ux * vx + uy * vy; + sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0; + v = p / n; + if(v < -1.0) v = -1.0; + if(v > 1.0) v = 1.0; + double sweep_angle = sign * acos(v); + if(!sweep_flag && sweep_angle > 0) + { + sweep_angle -= pi * 2.0; + } + else + if (sweep_flag && sweep_angle < 0) + { + sweep_angle += pi * 2.0; + } + + // We can now build and transform the resulting arc + //------------------------ + m_arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle); + trans_affine mtx = trans_affine_rotation(angle); + mtx *= trans_affine_translation(cx, cy); + + for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2) + { + mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1); + } + + // We must make sure that the starting and ending points + // exactly coincide with the initial (x0,y0) and (x2,y2) + m_arc.vertices()[0] = x0; + m_arc.vertices()[1] = y0; + if(m_arc.num_vertices() > 2) + { + m_arc.vertices()[m_arc.num_vertices() - 2] = x2; + m_arc.vertices()[m_arc.num_vertices() - 1] = y2; + } + } + +} diff --git a/kiva/gl/src/agg/agg_bezier_arc.h b/kiva/gl/src/agg/agg_bezier_arc.h new file mode 100644 index 000000000..85fba1c0e --- /dev/null +++ b/kiva/gl/src/agg/agg_bezier_arc.h @@ -0,0 +1,154 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e., +// 4, 7, 10, or 13 vertices. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_BEZIER_ARC_INCLUDED +#define AGG_BEZIER_ARC_INCLUDED + +#include "agg_conv_transform.h" + +namespace kiva_gl_agg +{ + + //----------------------------------------------------------------------- + void arc_to_bezier(double cx, double cy, double rx, double ry, + double start_angle, double sweep_angle, + double* curve); + + //==============================================================bezier_arc + // + // See implemantaion agg_bezier_arc.cpp + // + class bezier_arc + { + public: + //-------------------------------------------------------------------- + bezier_arc() : m_vertex(26), m_num_vertices(0), m_cmd(path_cmd_line_to) {} + bezier_arc(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle) + { + init(x, y, rx, ry, start_angle, sweep_angle); + } + + //-------------------------------------------------------------------- + void init(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle); + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_vertex = 0; + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + if(m_vertex >= m_num_vertices) return path_cmd_stop; + *x = m_vertices[m_vertex]; + *y = m_vertices[m_vertex + 1]; + m_vertex += 2; + return (m_vertex == 2) ? path_cmd_move_to : m_cmd; + } + + // Supplemantary functions. num_vertices() actually returns doubled + // number of vertices. That is, for 1 vertex it returns 2. + //-------------------------------------------------------------------- + unsigned num_vertices() const { return m_num_vertices; } + const double* vertices() const { return m_vertices; } + double* vertices() { return m_vertices; } + + private: + unsigned m_vertex; + unsigned m_num_vertices; + double m_vertices[26]; + unsigned m_cmd; + }; + + + + //==========================================================bezier_arc_svg + // Compute an SVG-style bezier arc. + // + // Computes an elliptical arc from (x1, y1) to (x2, y2). The size and + // orientation of the ellipse are defined by two radii (rx, ry) + // and an x-axis-rotation, which indicates how the ellipse as a whole + // is rotated relative to the current coordinate system. The center + // (cx, cy) of the ellipse is calculated automatically to satisfy the + // constraints imposed by the other parameters. + // large-arc-flag and sweep-flag contribute to the automatic calculations + // and help determine how the arc is drawn. + class bezier_arc_svg + { + public: + //-------------------------------------------------------------------- + bezier_arc_svg() : m_arc(), m_radii_ok(false) {} + + bezier_arc_svg(double x1, double y1, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2) : + m_arc(), m_radii_ok(false) + { + init(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2); + } + + //-------------------------------------------------------------------- + void init(double x1, double y1, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2); + + //-------------------------------------------------------------------- + bool radii_ok() const { return m_radii_ok; } + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_arc.rewind(0); + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + return m_arc.vertex(x, y); + } + + // Supplemantary functions. num_vertices() actually returns doubled + // number of vertices. That is, for 1 vertex it returns 2. + //-------------------------------------------------------------------- + unsigned num_vertices() const { return m_arc.num_vertices(); } + const double* vertices() const { return m_arc.vertices(); } + double* vertices() { return m_arc.vertices(); } + + private: + bezier_arc m_arc; + bool m_radii_ok; + }; + +} + +#endif diff --git a/kiva/gl/src/agg/agg_color_rgba.h b/kiva/gl/src/agg/agg_color_rgba.h new file mode 100644 index 000000000..f2987884a --- /dev/null +++ b/kiva/gl/src/agg/agg_color_rgba.h @@ -0,0 +1,1351 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_RGBA_INCLUDED +#define AGG_COLOR_RGBA_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_gamma_lut.h" + +namespace kiva_gl_agg +{ + // Supported component orders for RGB and RGBA pixel formats + //======================================================================= + struct order_rgb { enum rgb_e { R=0, G=1, B=2, N=3 }; }; + struct order_bgr { enum bgr_e { B=0, G=1, R=2, N=3 }; }; + struct order_rgba { enum rgba_e { R=0, G=1, B=2, A=3, N=4 }; }; + struct order_argb { enum argb_e { A=0, R=1, G=2, B=3, N=4 }; }; + struct order_abgr { enum abgr_e { A=0, B=1, G=2, R=3, N=4 }; }; + struct order_bgra { enum bgra_e { B=0, G=1, R=2, A=3, N=4 }; }; + + // Colorspace tag types. + struct linear {}; + struct sRGB {}; + + //====================================================================rgba + struct rgba + { + typedef double value_type; + + double r; + double g; + double b; + double a; + + //-------------------------------------------------------------------- + rgba() {} + + //-------------------------------------------------------------------- + rgba(double r_, double g_, double b_, double a_=1.0) : + r(r_), g(g_), b(b_), a(a_) {} + + //-------------------------------------------------------------------- + rgba(const rgba& c, double a_) : r(c.r), g(c.g), b(c.b), a(a_) {} + + //-------------------------------------------------------------------- + rgba& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + rgba& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + rgba& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = a_; + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return a; + } + + //-------------------------------------------------------------------- + rgba& premultiply() + { + r *= a; + g *= a; + b *= a; + return *this; + } + + //-------------------------------------------------------------------- + rgba& premultiply(double a_) + { + if (a <= 0 || a_ <= 0) + { + r = g = b = a = 0; + } + else + { + a_ /= a; + r *= a_; + g *= a_; + b *= a_; + a = a_; + } + return *this; + } + + //-------------------------------------------------------------------- + rgba& demultiply() + { + if (a == 0) + { + r = g = b = 0; + } + else + { + double a_ = 1.0 / a; + r *= a_; + g *= a_; + b *= a_; + } + return *this; + } + + + //-------------------------------------------------------------------- + rgba gradient(rgba c, double k) const + { + rgba ret; + ret.r = r + (c.r - r) * k; + ret.g = g + (c.g - g) * k; + ret.b = b + (c.b - b) * k; + ret.a = a + (c.a - a) * k; + return ret; + } + + rgba& operator+=(const rgba& c) + { + r += c.r; + g += c.g; + b += c.b; + a += c.a; + return *this; + } + + rgba& operator*=(double k) + { + r *= k; + g *= k; + b *= k; + a *= k; + return *this; + } + + //-------------------------------------------------------------------- + static rgba no_color() { return rgba(0,0,0,0); } + + //-------------------------------------------------------------------- + static rgba from_wavelength(double wl, double gamma = 1.0); + + //-------------------------------------------------------------------- + explicit rgba(double wavelen, double gamma=1.0) + { + *this = from_wavelength(wavelen, gamma); + } + + }; + + inline rgba operator+(const rgba& a, const rgba& b) + { + return rgba(a) += b; + } + + inline rgba operator*(const rgba& a, double b) + { + return rgba(a) *= b; + } + + //------------------------------------------------------------------------ + inline rgba rgba::from_wavelength(double wl, double gamma) + { + rgba t(0.0, 0.0, 0.0); + + if (wl >= 380.0 && wl <= 440.0) + { + t.r = -1.0 * (wl - 440.0) / (440.0 - 380.0); + t.b = 1.0; + } + else if (wl >= 440.0 && wl <= 490.0) + { + t.g = (wl - 440.0) / (490.0 - 440.0); + t.b = 1.0; + } + else if (wl >= 490.0 && wl <= 510.0) + { + t.g = 1.0; + t.b = -1.0 * (wl - 510.0) / (510.0 - 490.0); + } + else if (wl >= 510.0 && wl <= 580.0) + { + t.r = (wl - 510.0) / (580.0 - 510.0); + t.g = 1.0; + } + else if (wl >= 580.0 && wl <= 645.0) + { + t.r = 1.0; + t.g = -1.0 * (wl - 645.0) / (645.0 - 580.0); + } + else if (wl >= 645.0 && wl <= 780.0) + { + t.r = 1.0; + } + + double s = 1.0; + if (wl > 700.0) s = 0.3 + 0.7 * (780.0 - wl) / (780.0 - 700.0); + else if (wl < 420.0) s = 0.3 + 0.7 * (wl - 380.0) / (420.0 - 380.0); + + t.r = pow(t.r * s, gamma); + t.g = pow(t.g * s, gamma); + t.b = pow(t.b * s, gamma); + return t; + } + + inline rgba rgba_pre(double r, double g, double b, double a) + { + return rgba(r, g, b, a).premultiply(); + } + + + //===================================================================rgba8 + template + struct rgba8T + { + typedef int8u value_type; + typedef int32u calc_type; + typedef int32 long_type; + enum base_scale_e + { + base_shift = 8, + base_scale = 1 << base_shift, + base_mask = base_scale - 1, + base_MSB = 1 << (base_shift - 1) + }; + typedef rgba8T self_type; + + + value_type r; + value_type g; + value_type b; + value_type a; + + static void convert(rgba8T& dst, const rgba8T& src) + { + dst.r = sRGB_conv::rgb_from_sRGB(src.r); + dst.g = sRGB_conv::rgb_from_sRGB(src.g); + dst.b = sRGB_conv::rgb_from_sRGB(src.b); + dst.a = src.a; + } + + static void convert(rgba8T& dst, const rgba8T& src) + { + dst.r = sRGB_conv::rgb_to_sRGB(src.r); + dst.g = sRGB_conv::rgb_to_sRGB(src.g); + dst.b = sRGB_conv::rgb_to_sRGB(src.b); + dst.a = src.a; + } + + static void convert(rgba8T& dst, const rgba& src) + { + dst.r = value_type(uround(src.r * base_mask)); + dst.g = value_type(uround(src.g * base_mask)); + dst.b = value_type(uround(src.b * base_mask)); + dst.a = value_type(uround(src.a * base_mask)); + } + + static void convert(rgba8T& dst, const rgba& src) + { + // Use the "float" table. + dst.r = sRGB_conv::rgb_to_sRGB(float(src.r)); + dst.g = sRGB_conv::rgb_to_sRGB(float(src.g)); + dst.b = sRGB_conv::rgb_to_sRGB(float(src.b)); + dst.a = sRGB_conv::alpha_to_sRGB(float(src.a)); + } + + static void convert(rgba& dst, const rgba8T& src) + { + dst.r = src.r / 255.0; + dst.g = src.g / 255.0; + dst.b = src.b / 255.0; + dst.a = src.a / 255.0; + } + + static void convert(rgba& dst, const rgba8T& src) + { + // Use the "float" table. + dst.r = sRGB_conv::rgb_from_sRGB(src.r); + dst.g = sRGB_conv::rgb_from_sRGB(src.g); + dst.b = sRGB_conv::rgb_from_sRGB(src.b); + dst.a = sRGB_conv::alpha_from_sRGB(src.a); + } + + //-------------------------------------------------------------------- + rgba8T() {} + + //-------------------------------------------------------------------- + rgba8T(unsigned r_, unsigned g_, unsigned b_, unsigned a_ = base_mask) : + r(value_type(r_)), + g(value_type(g_)), + b(value_type(b_)), + a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba8T(const rgba& c) + { + convert(*this, c); + } + + //-------------------------------------------------------------------- + rgba8T(const self_type& c, unsigned a_) : + r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + template + rgba8T(const rgba8T& c) + { + convert(*this, c); + } + + //-------------------------------------------------------------------- + operator rgba() const + { + rgba c; + convert(c, *this); + return c; + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return double(a) / base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(uround(a * base_mask)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return base_mask; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a == 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a == base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type invert(value_type x) + { + return base_mask - x; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int8u. + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + calc_type t = a * b + base_MSB; + return value_type(((t >> base_shift) + t) >> base_shift); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + if (a * b == 0) + { + return 0; + } + else if (a >= b) + { + return base_mask; + } + else return value_type((a * base_mask + (b >> 1)) / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a >> base_shift; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return a >> n; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int8u. + // Specifically for multiplying a color component by a cover. + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return multiply(a, b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return multiply(b, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return p + q - multiply(p, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + int t = (q - p) * a + base_MSB - (p > q); + return value_type(p + (((t >> base_shift) + t) >> base_shift)); + } + + //-------------------------------------------------------------------- + self_type& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = (value_type)uround(a_ * double(base_mask)); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply() + { + if (a != base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + r = multiply(r, a); + g = multiply(g, a); + b = multiply(b, a); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply(unsigned a_) + { + if (a != base_mask || a_ < base_mask) + { + if (a == 0 || a_ == 0) + { + r = g = b = a = 0; + } + else + { + calc_type r_ = (calc_type(r) * a_) / a; + calc_type g_ = (calc_type(g) * a_) / a; + calc_type b_ = (calc_type(b) * a_) / a; + r = value_type((r_ > a_) ? a_ : r_); + g = value_type((g_ > a_) ? a_ : g_); + b = value_type((b_ > a_) ? a_ : b_); + a = value_type(a_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& demultiply() + { + if (a < base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + calc_type r_ = (calc_type(r) * base_mask) / a; + calc_type g_ = (calc_type(g) * base_mask) / a; + calc_type b_ = (calc_type(b) * base_mask) / a; + r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); + g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); + b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_mask); + ret.r = lerp(r, c.r, ik); + ret.g = lerp(g, c.g, ik); + ret.b = lerp(b, c.b, ik); + ret.a = lerp(a, c.a, ik); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cr, cg, cb, ca; + if (cover == cover_mask) + { + if (c.a == base_mask) + { + *this = c; + return; + } + else + { + cr = r + c.r; + cg = g + c.g; + cb = b + c.b; + ca = a + c.a; + } + } + else + { + cr = r + mult_cover(c.r, cover); + cg = g + mult_cover(c.g, cover); + cb = b + mult_cover(c.b, cover); + ca = a + mult_cover(c.a, cover); + } + r = (value_type)((cr > calc_type(base_mask)) ? calc_type(base_mask) : cr); + g = (value_type)((cg > calc_type(base_mask)) ? calc_type(base_mask) : cg); + b = (value_type)((cb > calc_type(base_mask)) ? calc_type(base_mask) : cb); + a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1.0) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; + + typedef rgba8T rgba8; + typedef rgba8T srgba8; + + + //-------------------------------------------------------------rgb8_packed + inline rgba8 rgb8_packed(unsigned v) + { + return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); + } + + //-------------------------------------------------------------bgr8_packed + inline rgba8 bgr8_packed(unsigned v) + { + return rgba8(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF); + } + + //------------------------------------------------------------argb8_packed + inline rgba8 argb8_packed(unsigned v) + { + return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF, v >> 24); + } + + //---------------------------------------------------------rgba8_gamma_dir + template + rgba8 rgba8_gamma_dir(rgba8 c, const GammaLUT& gamma) + { + return rgba8(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); + } + + //---------------------------------------------------------rgba8_gamma_inv + template + rgba8 rgba8_gamma_inv(rgba8 c, const GammaLUT& gamma) + { + return rgba8(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); + } + + + + //==================================================================rgba16 + struct rgba16 + { + typedef int16u value_type; + typedef int32u calc_type; + typedef int64 long_type; + enum base_scale_e + { + base_shift = 16, + base_scale = 1 << base_shift, + base_mask = base_scale - 1, + base_MSB = 1 << (base_shift - 1) + }; + typedef rgba16 self_type; + + value_type r; + value_type g; + value_type b; + value_type a; + + //-------------------------------------------------------------------- + rgba16() {} + + //-------------------------------------------------------------------- + rgba16(unsigned r_, unsigned g_, unsigned b_, unsigned a_=base_mask) : + r(value_type(r_)), + g(value_type(g_)), + b(value_type(b_)), + a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba16(const self_type& c, unsigned a_) : + r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba16(const rgba& c) : + r((value_type)uround(c.r * double(base_mask))), + g((value_type)uround(c.g * double(base_mask))), + b((value_type)uround(c.b * double(base_mask))), + a((value_type)uround(c.a * double(base_mask))) {} + + //-------------------------------------------------------------------- + rgba16(const rgba8& c) : + r(value_type((value_type(c.r) << 8) | c.r)), + g(value_type((value_type(c.g) << 8) | c.g)), + b(value_type((value_type(c.b) << 8) | c.b)), + a(value_type((value_type(c.a) << 8) | c.a)) {} + + //-------------------------------------------------------------------- + rgba16(const srgba8& c) : + r(sRGB_conv::rgb_from_sRGB(c.r)), + g(sRGB_conv::rgb_from_sRGB(c.g)), + b(sRGB_conv::rgb_from_sRGB(c.b)), + a(sRGB_conv::alpha_from_sRGB(c.a)) {} + + //-------------------------------------------------------------------- + operator rgba() const + { + return rgba( + r / 65535.0, + g / 65535.0, + b / 65535.0, + a / 65535.0); + } + + //-------------------------------------------------------------------- + operator rgba8() const + { + return rgba8(r >> 8, g >> 8, b >> 8, a >> 8); + } + + //-------------------------------------------------------------------- + operator srgba8() const + { + // Return (non-premultiplied) sRGB values. + return srgba8( + sRGB_conv::rgb_to_sRGB(r), + sRGB_conv::rgb_to_sRGB(g), + sRGB_conv::rgb_to_sRGB(b), + sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return double(a) / base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(uround(a * base_mask)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return base_mask; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a == 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a == base_mask; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type invert(value_type x) + { + return base_mask - x; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, exact over int16u. + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + calc_type t = a * b + base_MSB; + return value_type(((t >> base_shift) + t) >> base_shift); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + if (a * b == 0) + { + return 0; + } + else if (a >= b) + { + return base_mask; + } + else return value_type((a * base_mask + (b >> 1)) / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a >> base_shift; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return a >> n; + } + + //-------------------------------------------------------------------- + // Fixed-point multiply, almost exact over int16u. + // Specifically for multiplying a color component by a cover. + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return multiply(a, (b << 8) | b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return multiply((a << 8) | a, b) >> 8; + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return p + q - multiply(p, a); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + int t = (q - p) * a + base_MSB - (p > q); + return value_type(p + (((t >> base_shift) + t) >> base_shift)); + } + + //-------------------------------------------------------------------- + self_type& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + if (a_ > 1) a = 1; + a = value_type(uround(a_ * double(base_mask))); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply() + { + if (a != base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + r = multiply(r, a); + g = multiply(g, a); + b = multiply(b, a); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply(unsigned a_) + { + if (a < base_mask || a_ < base_mask) + { + if (a == 0 || a_ == 0) + { + r = g = b = a = 0; + } + else + { + calc_type r_ = (calc_type(r) * a_) / a; + calc_type g_ = (calc_type(g) * a_) / a; + calc_type b_ = (calc_type(b) * a_) / a; + r = value_type((r_ > a_) ? a_ : r_); + g = value_type((g_ > a_) ? a_ : g_); + b = value_type((b_ > a_) ? a_ : b_); + a = value_type(a_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& demultiply() + { + if (a < base_mask) + { + if (a == 0) + { + r = g = b = 0; + } + else + { + calc_type r_ = (calc_type(r) * base_mask) / a; + calc_type g_ = (calc_type(g) * base_mask) / a; + calc_type b_ = (calc_type(b) * base_mask) / a; + r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); + g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); + b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_mask); + ret.r = lerp(r, c.r, ik); + ret.g = lerp(g, c.g, ik); + ret.b = lerp(b, c.b, ik); + ret.a = lerp(a, c.a, ik); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cr, cg, cb, ca; + if (cover == cover_mask) + { + if (c.a == base_mask) + { + *this = c; + return; + } + else + { + cr = r + c.r; + cg = g + c.g; + cb = b + c.b; + ca = a + c.a; + } + } + else + { + cr = r + mult_cover(c.r, cover); + cg = g + mult_cover(c.g, cover); + cb = b + mult_cover(c.b, cover); + ca = a + mult_cover(c.a, cover); + } + r = (value_type)((cr > calc_type(base_mask)) ? calc_type(base_mask) : cr); + g = (value_type)((cg > calc_type(base_mask)) ? calc_type(base_mask) : cg); + b = (value_type)((cb > calc_type(base_mask)) ? calc_type(base_mask) : cb); + a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1.0) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; + + + //------------------------------------------------------rgba16_gamma_dir + template + rgba16 rgba16_gamma_dir(rgba16 c, const GammaLUT& gamma) + { + return rgba16(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); + } + + //------------------------------------------------------rgba16_gamma_inv + template + rgba16 rgba16_gamma_inv(rgba16 c, const GammaLUT& gamma) + { + return rgba16(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); + } + + //====================================================================rgba32 + struct rgba32 + { + typedef float value_type; + typedef double calc_type; + typedef double long_type; + typedef rgba32 self_type; + + value_type r; + value_type g; + value_type b; + value_type a; + + //-------------------------------------------------------------------- + rgba32() {} + + //-------------------------------------------------------------------- + rgba32(value_type r_, value_type g_, value_type b_, value_type a_= 1) : + r(r_), g(g_), b(b_), a(a_) {} + + //-------------------------------------------------------------------- + rgba32(const self_type& c, float a_) : + r(c.r), g(c.g), b(c.b), a(a_) {} + + //-------------------------------------------------------------------- + rgba32(const rgba& c) : + r(value_type(c.r)), g(value_type(c.g)), b(value_type(c.b)), a(value_type(c.a)) {} + + //-------------------------------------------------------------------- + rgba32(const rgba8& c) : + r(value_type(c.r / 255.0)), + g(value_type(c.g / 255.0)), + b(value_type(c.b / 255.0)), + a(value_type(c.a / 255.0)) {} + + //-------------------------------------------------------------------- + rgba32(const srgba8& c) : + r(sRGB_conv::rgb_from_sRGB(c.r)), + g(sRGB_conv::rgb_from_sRGB(c.g)), + b(sRGB_conv::rgb_from_sRGB(c.b)), + a(sRGB_conv::alpha_from_sRGB(c.a)) {} + + //-------------------------------------------------------------------- + rgba32(const rgba16& c) : + r(value_type(c.r / 65535.0)), + g(value_type(c.g / 65535.0)), + b(value_type(c.b / 65535.0)), + a(value_type(c.a / 65535.0)) {} + + //-------------------------------------------------------------------- + operator rgba() const + { + return rgba(r, g, b, a); + } + + //-------------------------------------------------------------------- + operator rgba8() const + { + return rgba8( + uround(r * 255.0), + uround(g * 255.0), + uround(b * 255.0), + uround(a * 255.0)); + } + + //-------------------------------------------------------------------- + operator srgba8() const + { + return srgba8( + sRGB_conv::rgb_to_sRGB(r), + sRGB_conv::rgb_to_sRGB(g), + sRGB_conv::rgb_to_sRGB(b), + sRGB_conv::alpha_to_sRGB(a)); + } + + //-------------------------------------------------------------------- + operator rgba16() const + { + return rgba8( + uround(r * 65535.0), + uround(g * 65535.0), + uround(b * 65535.0), + uround(a * 65535.0)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE double to_double(value_type a) + { + return a; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type from_double(double a) + { + return value_type(a); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type empty_value() + { + return 0; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type full_value() + { + return 1; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_transparent() const + { + return a <= 0; + } + + //-------------------------------------------------------------------- + AGG_INLINE bool is_opaque() const + { + return a >= 1; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type invert(value_type x) + { + return 1 - x; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type multiply(value_type a, value_type b) + { + return value_type(a * b); + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type demultiply(value_type a, value_type b) + { + return (b == 0) ? 0 : value_type(a / b); + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downscale(T a) + { + return a; + } + + //-------------------------------------------------------------------- + template + static AGG_INLINE T downshift(T a, unsigned n) + { + return n > 0 ? a / (1 << n) : a; + } + + //-------------------------------------------------------------------- + static AGG_INLINE value_type mult_cover(value_type a, cover_type b) + { + return value_type(a * b / cover_mask); + } + + //-------------------------------------------------------------------- + static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) + { + return cover_type(uround(a * b)); + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a, assuming q is premultiplied by a. + static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) + { + return (1 - a) * p + q; // more accurate than "p + q - p * a" + } + + //-------------------------------------------------------------------- + // Interpolate p to q by a. + static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) + { + // The form "p + a * (q - p)" avoids a multiplication, but may produce an + // inaccurate result. For example, "p + (q - p)" may not be exactly equal + // to q. Therefore, stick to the basic expression, which at least produces + // the correct result at either extreme. + return (1 - a) * p + a * q; + } + + //-------------------------------------------------------------------- + self_type& clear() + { + r = g = b = a = 0; + return *this; + } + + //-------------------------------------------------------------------- + self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& opacity(double a_) + { + if (a_ < 0) a = 0; + else if (a_ > 1) a = 1; + else a = value_type(a_); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return a; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& premultiply() + { + if (a < 1) + { + if (a <= 0) + { + r = g = b = 0; + } + else + { + r *= a; + g *= a; + b *= a; + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type& demultiply() + { + if (a < 1) + { + if (a <= 0) + { + r = g = b = 0; + } + else + { + r /= a; + g /= a; + b /= a; + } + } + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + ret.r = value_type(r + (c.r - r) * k); + ret.g = value_type(g + (c.g - g) * k); + ret.b = value_type(b + (c.b - b) * k); + ret.a = value_type(a + (c.a - a) * k); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + if (cover == cover_mask) + { + if (c.is_opaque()) + { + *this = c; + return; + } + else + { + r += c.r; + g += c.g; + b += c.b; + a += c.a; + } + } + else + { + r += mult_cover(c.r, cover); + g += mult_cover(c.g, cover); + b += mult_cover(c.b, cover); + a += mult_cover(c.a, cover); + } + if (a > 1) a = 1; + if (r > a) r = a; + if (g > a) g = a; + if (b > a) b = a; + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; +} + +#endif diff --git a/kiva/gl/src/agg/agg_config.h b/kiva/gl/src/agg/agg_config.h new file mode 100644 index 000000000..40205e827 --- /dev/null +++ b/kiva/gl/src/agg/agg_config.h @@ -0,0 +1,44 @@ +#ifndef AGG_CONFIG_INCLUDED +#define AGG_CONFIG_INCLUDED + +// This file can be used to redefine certain data types. + +//--------------------------------------- +// 1. Default basic types such as: +// +// AGG_INT8 +// AGG_INT8U +// AGG_INT16 +// AGG_INT16U +// AGG_INT32 +// AGG_INT32U +// AGG_INT64 +// AGG_INT64U +// +// Just replace this file with new defines if necessary. +// For example, if your compiler doesn't have a 64 bit integer type +// you can still use AGG if you define the follows: +// +// #define AGG_INT64 int +// #define AGG_INT64U unsigned +// +// It will result in overflow in 16 bit-per-component image/pattern resampling +// but it won't result any crash and the rest of the library will remain +// fully functional. + + +//--------------------------------------- +// 2. Default rendering_buffer type. Can be: +// +// Provides faster access for massive pixel operations, +// such as blur, image filtering: +// #define AGG_RENDERING_BUFFER row_ptr_cache +// +// Provides cheaper creation and destruction (no mem allocs): +// #define AGG_RENDERING_BUFFER row_accessor +// +// You can still use both of them simultaneously in your applications +// This #define is used only for default rendering_buffer type, +// in short hand typedefs like pixfmt_rgba32. + +#endif diff --git a/kiva/gl/src/agg/agg_conv_transform.h b/kiva/gl/src/agg/agg_conv_transform.h new file mode 100644 index 000000000..b3fda33f3 --- /dev/null +++ b/kiva/gl/src/agg/agg_conv_transform.h @@ -0,0 +1,67 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// class conv_transform +// +//---------------------------------------------------------------------------- +#ifndef AGG_CONV_TRANSFORM_INCLUDED +#define AGG_CONV_TRANSFORM_INCLUDED + +#include "agg_basics.h" +#include "agg_trans_affine.h" + +namespace kiva_gl_agg +{ + + //----------------------------------------------------------conv_transform + template class conv_transform + { + public: + conv_transform(VertexSource& source, Transformer& tr) : + m_source(&source), m_trans(&tr) {} + void attach(VertexSource& source) { m_source = &source; } + + void rewind(unsigned path_id) + { + m_source->rewind(path_id); + } + + unsigned vertex(double* x, double* y) + { + unsigned cmd = m_source->vertex(x, y); + if(is_vertex(cmd)) + { + m_trans->transform(x, y); + } + return cmd; + } + + void transformer(Transformer& tr) + { + m_trans = &tr; + } + + private: + conv_transform(const conv_transform&); + const conv_transform& + operator = (const conv_transform&); + + VertexSource* m_source; + Transformer* m_trans; + }; + +} + +#endif diff --git a/kiva/gl/src/agg/agg_gamma_functions.h b/kiva/gl/src/agg/agg_gamma_functions.h new file mode 100644 index 000000000..36676e00f --- /dev/null +++ b/kiva/gl/src/agg/agg_gamma_functions.h @@ -0,0 +1,128 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_FUNCTIONS_INCLUDED +#define AGG_GAMMA_FUNCTIONS_INCLUDED + +#include +#include "agg_basics.h" + +namespace kiva_gl_agg +{ + //===============================================================gamma_none + struct gamma_none + { + double operator()(double x) const { return x; } + }; + + //==============================================================gamma_power + class gamma_power + { + public: + gamma_power() : m_gamma(1.0) {} + gamma_power(double g) : m_gamma(g) {} + + void gamma(double g) { m_gamma = g; } + double gamma() const { return m_gamma; } + + double operator() (double x) const + { + return pow(x, m_gamma); + } + + private: + double m_gamma; + }; + + + //==========================================================gamma_threshold + class gamma_threshold + { + public: + gamma_threshold() : m_threshold(0.5) {} + gamma_threshold(double t) : m_threshold(t) {} + + void threshold(double t) { m_threshold = t; } + double threshold() const { return m_threshold; } + + double operator() (double x) const + { + return (x < m_threshold) ? 0.0 : 1.0; + } + + private: + double m_threshold; + }; + + + //============================================================gamma_linear + class gamma_linear + { + public: + gamma_linear() : m_start(0.0), m_end(1.0) {} + gamma_linear(double s, double e) : m_start(s), m_end(e) {} + + void set(double s, double e) { m_start = s; m_end = e; } + void start(double s) { m_start = s; } + void end(double e) { m_end = e; } + double start() const { return m_start; } + double end() const { return m_end; } + + double operator() (double x) const + { + if(x < m_start) return 0.0; + if(x > m_end) return 1.0; + return (x - m_start) / (m_end - m_start); + } + + private: + double m_start; + double m_end; + }; + + + //==========================================================gamma_multiply + class gamma_multiply + { + public: + gamma_multiply() : m_mul(1.0) {} + gamma_multiply(double v) : m_mul(v) {} + + void value(double v) { m_mul = v; } + double value() const { return m_mul; } + + double operator() (double x) const + { + double y = x * m_mul; + if(y > 1.0) y = 1.0; + return y; + } + + private: + double m_mul; + }; + + inline double sRGB_to_linear(double x) + { + return (x <= 0.04045) ? (x / 12.92) : pow((x + 0.055) / (1.055), 2.4); + } + + inline double linear_to_sRGB(double x) + { + return (x <= 0.0031308) ? (x * 12.92) : (1.055 * pow(x, 1 / 2.4) - 0.055); + } +} + +#endif diff --git a/kiva/gl/src/agg/agg_gamma_lut.h b/kiva/gl/src/agg/agg_gamma_lut.h new file mode 100644 index 000000000..25570fef2 --- /dev/null +++ b/kiva/gl/src/agg/agg_gamma_lut.h @@ -0,0 +1,305 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_LUT_INCLUDED +#define AGG_GAMMA_LUT_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_gamma_functions.h" + +namespace kiva_gl_agg +{ + template class gamma_lut + { + public: + typedef gamma_lut self_type; + + enum gamma_scale_e + { + gamma_shift = GammaShift, + gamma_size = 1 << gamma_shift, + gamma_mask = gamma_size - 1 + }; + + enum hi_res_scale_e + { + hi_res_shift = HiResShift, + hi_res_size = 1 << hi_res_shift, + hi_res_mask = hi_res_size - 1 + }; + + ~gamma_lut() + { + pod_allocator::deallocate(m_inv_gamma, hi_res_size); + pod_allocator::deallocate(m_dir_gamma, gamma_size); + } + + gamma_lut() : + m_gamma(1.0), + m_dir_gamma(pod_allocator::allocate(gamma_size)), + m_inv_gamma(pod_allocator::allocate(hi_res_size)) + { + unsigned i; + for(i = 0; i < gamma_size; i++) + { + m_dir_gamma[i] = HiResT(i << (hi_res_shift - gamma_shift)); + } + + for(i = 0; i < hi_res_size; i++) + { + m_inv_gamma[i] = LoResT(i >> (hi_res_shift - gamma_shift)); + } + } + + gamma_lut(double g) : + m_gamma(1.0), + m_dir_gamma(pod_allocator::allocate(gamma_size)), + m_inv_gamma(pod_allocator::allocate(hi_res_size)) + { + gamma(g); + } + + void gamma(double g) + { + m_gamma = g; + + unsigned i; + for(i = 0; i < gamma_size; i++) + { + m_dir_gamma[i] = (HiResT) + uround(pow(i / double(gamma_mask), m_gamma) * double(hi_res_mask)); + } + + double inv_g = 1.0 / g; + for(i = 0; i < hi_res_size; i++) + { + m_inv_gamma[i] = (LoResT) + uround(pow(i / double(hi_res_mask), inv_g) * double(gamma_mask)); + } + } + + double gamma() const + { + return m_gamma; + } + + HiResT dir(LoResT v) const + { + return m_dir_gamma[unsigned(v)]; + } + + LoResT inv(HiResT v) const + { + return m_inv_gamma[unsigned(v)]; + } + + private: + gamma_lut(const self_type&); + const self_type& operator = (const self_type&); + + double m_gamma; + HiResT* m_dir_gamma; + LoResT* m_inv_gamma; + }; + + // + // sRGB support classes + // + + // Optimized sRGB lookup table. The direct conversion (sRGB to linear) + // is a straightforward lookup. The inverse conversion (linear to sRGB) + // is implemented using binary search. + template + class sRGB_lut_base + { + public: + LinearType dir(int8u v) const + { + return m_dir_table[v]; + } + + int8u inv(LinearType v) const + { + // Unrolled binary search. + int8u x = 0; + if (v > m_inv_table[128]) x = 128; + if (v > m_inv_table[x + 64]) x += 64; + if (v > m_inv_table[x + 32]) x += 32; + if (v > m_inv_table[x + 16]) x += 16; + if (v > m_inv_table[x + 8]) x += 8; + if (v > m_inv_table[x + 4]) x += 4; + if (v > m_inv_table[x + 2]) x += 2; + if (v > m_inv_table[x + 1]) x += 1; + return x; + } + + protected: + LinearType m_dir_table[256]; + LinearType m_inv_table[256]; + + // Only derived classes may instantiate. + sRGB_lut_base() + { + } + }; + + // sRGB_lut - implements sRGB conversion for the various types. + // Base template is undefined, specializations are provided below. + template + class sRGB_lut; + + template<> + class sRGB_lut : public sRGB_lut_base + { + public: + sRGB_lut() + { + // Generate lookup tables. + m_dir_table[0] = 0; + m_inv_table[0] = 0; + for (unsigned i = 1; i <= 255; ++i) + { + // Floating-point RGB is in range [0,1]. + m_dir_table[i] = float(sRGB_to_linear(i / 255.0)); + m_inv_table[i] = float(sRGB_to_linear((i - 0.5) / 255.0)); + } + } + }; + + template<> + class sRGB_lut : public sRGB_lut_base + { + public: + sRGB_lut() + { + // Generate lookup tables. + m_dir_table[0] = 0; + m_inv_table[0] = 0; + for (unsigned i = 1; i <= 255; ++i) + { + // 16-bit RGB is in range [0,65535]. + m_dir_table[i] = uround(65535.0 * sRGB_to_linear(i / 255.0)); + m_inv_table[i] = uround(65535.0 * sRGB_to_linear((i - 0.5) / 255.0)); + } + } + }; + + template<> + class sRGB_lut : public sRGB_lut_base + { + public: + sRGB_lut() + { + // Generate lookup tables. + m_dir_table[0] = 0; + m_inv_table[0] = 0; + for (unsigned i = 1; i <= 255; ++i) + { + // 8-bit RGB is handled with simple bidirectional lookup tables. + m_dir_table[i] = uround(255.0 * sRGB_to_linear(i / 255.0)); + m_inv_table[i] = uround(255.0 * linear_to_sRGB(i / 255.0)); + } + } + + int8u inv(int8u v) const + { + // In this case, the inverse transform is a simple lookup. + return m_inv_table[v]; + } + }; + + // Common base class for sRGB_conv objects. Defines an internal + // sRGB_lut object so that users don't have to. + template + class sRGB_conv_base + { + public: + static T rgb_from_sRGB(int8u x) + { + return lut.dir(x); + } + + static int8u rgb_to_sRGB(T x) + { + return lut.inv(x); + } + + private: + static sRGB_lut lut; + }; + + // Definition of sRGB_conv_base::lut. Due to the fact that this a template, + // we don't need to place the definition in a cpp file. Hurrah. + template + sRGB_lut sRGB_conv_base::lut; + + // Wrapper for sRGB-linear conversion. + // Base template is undefined, specializations are provided below. + template + class sRGB_conv; + + template<> + class sRGB_conv : public sRGB_conv_base + { + public: + static float alpha_from_sRGB(int8u x) + { + return float(x / 255.0); + } + + static int8u alpha_to_sRGB(float x) + { + if (x <= 0) return 0; + else if (x >= 1) return 255; + else return int8u(0.5 + x * 255); + } + }; + + template<> + class sRGB_conv : public sRGB_conv_base + { + public: + static int16u alpha_from_sRGB(int8u x) + { + return (x << 8) | x; + } + + static int8u alpha_to_sRGB(int16u x) + { + return x >> 8; + } + }; + + template<> + class sRGB_conv : public sRGB_conv_base + { + public: + static int8u alpha_from_sRGB(int8u x) + { + return x; + } + + static int8u alpha_to_sRGB(int8u x) + { + return x; + } + }; +} + +#endif diff --git a/kiva/gl/src/agg/agg_math.h b/kiva/gl/src/agg/agg_math.h new file mode 100644 index 000000000..6336c6f9a --- /dev/null +++ b/kiva/gl/src/agg/agg_math.h @@ -0,0 +1,437 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// Bessel function (besj) was adapted for use in AGG library by Andy Wilk +// Contact: castor.vulgaris@gmail.com +//---------------------------------------------------------------------------- + +#ifndef AGG_MATH_INCLUDED +#define AGG_MATH_INCLUDED + +#include +#include "agg_basics.h" + +namespace kiva_gl_agg +{ + + //------------------------------------------------------vertex_dist_epsilon + // Coinciding points maximal distance (Epsilon) + const double vertex_dist_epsilon = 1e-14; + + //-----------------------------------------------------intersection_epsilon + // See calc_intersection + const double intersection_epsilon = 1.0e-30; + + //------------------------------------------------------------cross_product + AGG_INLINE double cross_product(double x1, double y1, + double x2, double y2, + double x, double y) + { + return (x - x2) * (y2 - y1) - (y - y2) * (x2 - x1); + } + + //--------------------------------------------------------point_in_triangle + AGG_INLINE bool point_in_triangle(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x, double y) + { + bool cp1 = cross_product(x1, y1, x2, y2, x, y) < 0.0; + bool cp2 = cross_product(x2, y2, x3, y3, x, y) < 0.0; + bool cp3 = cross_product(x3, y3, x1, y1, x, y) < 0.0; + return cp1 == cp2 && cp2 == cp3 && cp3 == cp1; + } + + //-----------------------------------------------------------calc_distance + AGG_INLINE double calc_distance(double x1, double y1, double x2, double y2) + { + double dx = x2-x1; + double dy = y2-y1; + return sqrt(dx * dx + dy * dy); + } + + //--------------------------------------------------------calc_sq_distance + AGG_INLINE double calc_sq_distance(double x1, double y1, double x2, double y2) + { + double dx = x2-x1; + double dy = y2-y1; + return dx * dx + dy * dy; + } + + //------------------------------------------------calc_line_point_distance + AGG_INLINE double calc_line_point_distance(double x1, double y1, + double x2, double y2, + double x, double y) + { + double dx = x2-x1; + double dy = y2-y1; + double d = sqrt(dx * dx + dy * dy); + if(d < vertex_dist_epsilon) + { + return calc_distance(x1, y1, x, y); + } + return ((x - x2) * dy - (y - y2) * dx) / d; + } + + //-------------------------------------------------------calc_line_point_u + AGG_INLINE double calc_segment_point_u(double x1, double y1, + double x2, double y2, + double x, double y) + { + double dx = x2 - x1; + double dy = y2 - y1; + + if(dx == 0 && dy == 0) + { + return 0; + } + + double pdx = x - x1; + double pdy = y - y1; + + return (pdx * dx + pdy * dy) / (dx * dx + dy * dy); + } + + //---------------------------------------------calc_line_point_sq_distance + AGG_INLINE double calc_segment_point_sq_distance(double x1, double y1, + double x2, double y2, + double x, double y, + double u) + { + if(u <= 0) + { + return calc_sq_distance(x, y, x1, y1); + } + else + if(u >= 1) + { + return calc_sq_distance(x, y, x2, y2); + } + return calc_sq_distance(x, y, x1 + u * (x2 - x1), y1 + u * (y2 - y1)); + } + + //---------------------------------------------calc_line_point_sq_distance + AGG_INLINE double calc_segment_point_sq_distance(double x1, double y1, + double x2, double y2, + double x, double y) + { + return + calc_segment_point_sq_distance( + x1, y1, x2, y2, x, y, + calc_segment_point_u(x1, y1, x2, y2, x, y)); + } + + //-------------------------------------------------------calc_intersection + AGG_INLINE bool calc_intersection(double ax, double ay, double bx, double by, + double cx, double cy, double dx, double dy, + double* x, double* y) + { + double num = (ay-cy) * (dx-cx) - (ax-cx) * (dy-cy); + double den = (bx-ax) * (dy-cy) - (by-ay) * (dx-cx); + if(fabs(den) < intersection_epsilon) return false; + double r = num / den; + *x = ax + r * (bx-ax); + *y = ay + r * (by-ay); + return true; + } + + //-----------------------------------------------------intersection_exists + AGG_INLINE bool intersection_exists(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4) + { + // It's less expensive but you can't control the + // boundary conditions: Less or LessEqual + double dx1 = x2 - x1; + double dy1 = y2 - y1; + double dx2 = x4 - x3; + double dy2 = y4 - y3; + return ((x3 - x2) * dy1 - (y3 - y2) * dx1 < 0.0) != + ((x4 - x2) * dy1 - (y4 - y2) * dx1 < 0.0) && + ((x1 - x4) * dy2 - (y1 - y4) * dx2 < 0.0) != + ((x2 - x4) * dy2 - (y2 - y4) * dx2 < 0.0); + + // It's is more expensive but more flexible + // in terms of boundary conditions. + //-------------------- + //double den = (x2-x1) * (y4-y3) - (y2-y1) * (x4-x3); + //if(fabs(den) < intersection_epsilon) return false; + //double nom1 = (x4-x3) * (y1-y3) - (y4-y3) * (x1-x3); + //double nom2 = (x2-x1) * (y1-y3) - (y2-y1) * (x1-x3); + //double ua = nom1 / den; + //double ub = nom2 / den; + //return ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0; + } + + //--------------------------------------------------------calc_orthogonal + AGG_INLINE void calc_orthogonal(double thickness, + double x1, double y1, + double x2, double y2, + double* x, double* y) + { + double dx = x2 - x1; + double dy = y2 - y1; + double d = sqrt(dx*dx + dy*dy); + *x = thickness * dy / d; + *y = -thickness * dx / d; + } + + //--------------------------------------------------------dilate_triangle + AGG_INLINE void dilate_triangle(double x1, double y1, + double x2, double y2, + double x3, double y3, + double *x, double* y, + double d) + { + double dx1=0.0; + double dy1=0.0; + double dx2=0.0; + double dy2=0.0; + double dx3=0.0; + double dy3=0.0; + double loc = cross_product(x1, y1, x2, y2, x3, y3); + if(fabs(loc) > intersection_epsilon) + { + if(cross_product(x1, y1, x2, y2, x3, y3) > 0.0) + { + d = -d; + } + calc_orthogonal(d, x1, y1, x2, y2, &dx1, &dy1); + calc_orthogonal(d, x2, y2, x3, y3, &dx2, &dy2); + calc_orthogonal(d, x3, y3, x1, y1, &dx3, &dy3); + } + *x++ = x1 + dx1; *y++ = y1 + dy1; + *x++ = x2 + dx1; *y++ = y2 + dy1; + *x++ = x2 + dx2; *y++ = y2 + dy2; + *x++ = x3 + dx2; *y++ = y3 + dy2; + *x++ = x3 + dx3; *y++ = y3 + dy3; + *x++ = x1 + dx3; *y++ = y1 + dy3; + } + + //------------------------------------------------------calc_triangle_area + AGG_INLINE double calc_triangle_area(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + return (x1*y2 - x2*y1 + x2*y3 - x3*y2 + x3*y1 - x1*y3) * 0.5; + } + + //-------------------------------------------------------calc_polygon_area + template double calc_polygon_area(const Storage& st) + { + unsigned i; + double sum = 0.0; + double x = st[0].x; + double y = st[0].y; + double xs = x; + double ys = y; + + for(i = 1; i < st.size(); i++) + { + const typename Storage::value_type& v = st[i]; + sum += x * v.y - y * v.x; + x = v.x; + y = v.y; + } + return (sum + x * ys - y * xs) * 0.5; + } + + //------------------------------------------------------------------------ + // Tables for fast sqrt + extern int16u g_sqrt_table[1024]; + extern int8 g_elder_bit_table[256]; + + + //---------------------------------------------------------------fast_sqrt + //Fast integer Sqrt - really fast: no cycles, divisions or multiplications + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 4035) //Disable warning "no return value" + #endif + AGG_INLINE unsigned fast_sqrt(unsigned val) + { + #if defined(_M_IX86) && defined(_MSC_VER) && !defined(AGG_NO_ASM) + //For Ix86 family processors this assembler code is used. + //The key command here is bsr - determination the number of the most + //significant bit of the value. For other processors + //(and maybe compilers) the pure C "#else" section is used. + __asm + { + mov ebx, val + mov edx, 11 + bsr ecx, ebx + sub ecx, 9 + jle less_than_9_bits + shr ecx, 1 + adc ecx, 0 + sub edx, ecx + shl ecx, 1 + shr ebx, cl + less_than_9_bits: + xor eax, eax + mov ax, g_sqrt_table[ebx*2] + mov ecx, edx + shr eax, cl + } + #else + + //This code is actually pure C and portable to most + //arcitectures including 64bit ones. + unsigned t = val; + int bit=0; + unsigned shift = 11; + + //The following piece of code is just an emulation of the + //Ix86 assembler command "bsr" (see above). However on old + //Intels (like Intel MMX 233MHz) this code is about twice + //faster (sic!) then just one "bsr". On PIII and PIV the + //bsr is optimized quite well. + bit = t >> 24; + if(bit) + { + bit = g_elder_bit_table[bit] + 24; + } + else + { + bit = (t >> 16) & 0xFF; + if(bit) + { + bit = g_elder_bit_table[bit] + 16; + } + else + { + bit = (t >> 8) & 0xFF; + if(bit) + { + bit = g_elder_bit_table[bit] + 8; + } + else + { + bit = g_elder_bit_table[t]; + } + } + } + + //This code calculates the sqrt. + bit -= 9; + if(bit > 0) + { + bit = (bit >> 1) + (bit & 1); + shift -= bit; + val >>= (bit << 1); + } + return g_sqrt_table[val] >> shift; + #endif + } + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + + + + + //--------------------------------------------------------------------besj + // Function BESJ calculates Bessel function of first kind of order n + // Arguments: + // n - an integer (>=0), the order + // x - value at which the Bessel function is required + //-------------------- + // C++ Mathematical Library + // Convereted from equivalent FORTRAN library + // Converetd by Gareth Walker for use by course 392 computational project + // All functions tested and yield the same results as the corresponding + // FORTRAN versions. + // + // If you have any problems using these functions please report them to + // M.Muldoon@UMIST.ac.uk + // + // Documentation available on the web + // http://www.ma.umist.ac.uk/mrm/Teaching/392/libs/392.html + // Version 1.0 8/98 + // 29 October, 1999 + //-------------------- + // Adapted for use in AGG library by Andy Wilk (castor.vulgaris@gmail.com) + //------------------------------------------------------------------------ + inline double besj(double x, int n) + { + if(n < 0) + { + return 0; + } + double d = 1E-6; + double b = 0; + if(fabs(x) <= d) + { + if(n != 0) return 0; + return 1; + } + double b1 = 0; // b1 is the value from the previous iteration + // Set up a starting order for recurrence + int m1 = (int)fabs(x) + 6; + if(fabs(x) > 5) + { + m1 = (int)(fabs(1.4 * x + 60 / x)); + } + int m2 = (int)(n + 2 + fabs(x) / 4); + if (m1 > m2) + { + m2 = m1; + } + + // Apply recurrence down from curent max order + for(;;) + { + double c3 = 0; + double c2 = 1E-30; + double c4 = 0; + int m8 = 1; + if (m2 / 2 * 2 == m2) + { + m8 = -1; + } + int imax = m2 - 2; + for (int i = 1; i <= imax; i++) + { + double c6 = 2 * (m2 - i) * c2 / x - c3; + c3 = c2; + c2 = c6; + if(m2 - i - 1 == n) + { + b = c6; + } + m8 = -1 * m8; + if (m8 > 0) + { + c4 = c4 + 2 * c6; + } + } + double c6 = 2 * c2 / x - c3; + if(n == 0) + { + b = c6; + } + c4 += c6; + b /= c4; + if(fabs(b - b1) < d) + { + return b; + } + b1 = b; + m2 += 3; + } + } + +} + + +#endif diff --git a/kiva/gl/src/agg/agg_path_storage.h b/kiva/gl/src/agg/agg_path_storage.h new file mode 100644 index 000000000..f908c1626 --- /dev/null +++ b/kiva/gl/src/agg/agg_path_storage.h @@ -0,0 +1,1539 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_PATH_STORAGE_INCLUDED +#define AGG_PATH_STORAGE_INCLUDED + +#include +#include +#include "agg_math.h" +#include "agg_array.h" +#include "agg_bezier_arc.h" + +namespace kiva_gl_agg +{ + + //----------------------------------------------------vertex_block_storage + template + class vertex_block_storage + { + public: + // Allocation parameters + enum block_scale_e + { + block_shift = BlockShift, + block_size = 1 << block_shift, + block_mask = block_size - 1, + block_pool = BlockPool + }; + + typedef T value_type; + typedef vertex_block_storage self_type; + + ~vertex_block_storage(); + vertex_block_storage(); + vertex_block_storage(const self_type& v); + const self_type& operator = (const self_type& ps); + + void remove_all(); + void free_all(); + + void add_vertex(double x, double y, unsigned cmd); + void modify_vertex(unsigned idx, double x, double y); + void modify_vertex(unsigned idx, double x, double y, unsigned cmd); + void modify_command(unsigned idx, unsigned cmd); + void swap_vertices(unsigned v1, unsigned v2); + + unsigned last_command() const; + unsigned last_vertex(double* x, double* y) const; + unsigned prev_vertex(double* x, double* y) const; + + double last_x() const; + double last_y() const; + + unsigned total_vertices() const; + unsigned vertex(unsigned idx, double* x, double* y) const; + unsigned command(unsigned idx) const; + + private: + void allocate_block(unsigned nb); + int8u* storage_ptrs(T** xy_ptr); + + private: + unsigned m_total_vertices; + unsigned m_total_blocks; + unsigned m_max_blocks; + T** m_coord_blocks; + int8u** m_cmd_blocks; + }; + + + //------------------------------------------------------------------------ + template + void vertex_block_storage::free_all() + { + if(m_total_blocks) + { + T** coord_blk = m_coord_blocks + m_total_blocks - 1; + while(m_total_blocks--) + { + pod_allocator::deallocate( + *coord_blk, + block_size * 2 + + block_size / (sizeof(T) / sizeof(unsigned char))); + --coord_blk; + } + pod_allocator::deallocate(m_coord_blocks, m_max_blocks * 2); + m_total_blocks = 0; + m_max_blocks = 0; + m_coord_blocks = 0; + m_cmd_blocks = 0; + m_total_vertices = 0; + } + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::~vertex_block_storage() + { + free_all(); + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::vertex_block_storage() : + m_total_vertices(0), + m_total_blocks(0), + m_max_blocks(0), + m_coord_blocks(0), + m_cmd_blocks(0) + { + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::vertex_block_storage(const vertex_block_storage& v) : + m_total_vertices(0), + m_total_blocks(0), + m_max_blocks(0), + m_coord_blocks(0), + m_cmd_blocks(0) + { + *this = v; + } + + //------------------------------------------------------------------------ + template + const vertex_block_storage& + vertex_block_storage::operator = (const vertex_block_storage& v) + { + remove_all(); + unsigned i; + for(i = 0; i < v.total_vertices(); ++i) + { + double x, y; + unsigned cmd = v.vertex(i, &x, &y); + add_vertex(x, y, cmd); + } + return *this; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::remove_all() + { + m_total_vertices = 0; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::add_vertex(double x, double y, + unsigned cmd) + { + T* coord_ptr = 0; + *storage_ptrs(&coord_ptr) = (int8u)cmd; + coord_ptr[0] = T(x); + coord_ptr[1] = T(y); + m_total_vertices++; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_vertex(unsigned idx, + double x, double y) + { + T* pv = m_coord_blocks[idx >> block_shift] + ((idx & block_mask) << 1); + pv[0] = T(x); + pv[1] = T(y); + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_vertex(unsigned idx, + double x, double y, + unsigned cmd) + { + unsigned block = idx >> block_shift; + unsigned offset = idx & block_mask; + T* pv = m_coord_blocks[block] + (offset << 1); + pv[0] = T(x); + pv[1] = T(y); + m_cmd_blocks[block][offset] = (int8u)cmd; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_command(unsigned idx, + unsigned cmd) + { + m_cmd_blocks[idx >> block_shift][idx & block_mask] = (int8u)cmd; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::swap_vertices(unsigned v1, unsigned v2) + { + unsigned b1 = v1 >> block_shift; + unsigned b2 = v2 >> block_shift; + unsigned o1 = v1 & block_mask; + unsigned o2 = v2 & block_mask; + T* pv1 = m_coord_blocks[b1] + (o1 << 1); + T* pv2 = m_coord_blocks[b2] + (o2 << 1); + T val; + val = pv1[0]; pv1[0] = pv2[0]; pv2[0] = val; + val = pv1[1]; pv1[1] = pv2[1]; pv2[1] = val; + int8u cmd = m_cmd_blocks[b1][o1]; + m_cmd_blocks[b1][o1] = m_cmd_blocks[b2][o2]; + m_cmd_blocks[b2][o2] = cmd; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::last_command() const + { + if(m_total_vertices) return command(m_total_vertices - 1); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::last_vertex(double* x, double* y) const + { + if(m_total_vertices) return vertex(m_total_vertices - 1, x, y); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::prev_vertex(double* x, double* y) const + { + if(m_total_vertices > 1) return vertex(m_total_vertices - 2, x, y); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline double vertex_block_storage::last_x() const + { + if(m_total_vertices) + { + unsigned idx = m_total_vertices - 1; + return m_coord_blocks[idx >> block_shift][(idx & block_mask) << 1]; + } + return 0.0; + } + + //------------------------------------------------------------------------ + template + inline double vertex_block_storage::last_y() const + { + if(m_total_vertices) + { + unsigned idx = m_total_vertices - 1; + return m_coord_blocks[idx >> block_shift][((idx & block_mask) << 1) + 1]; + } + return 0.0; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::total_vertices() const + { + return m_total_vertices; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::vertex(unsigned idx, + double* x, double* y) const + { + unsigned nb = idx >> block_shift; + const T* pv = m_coord_blocks[nb] + ((idx & block_mask) << 1); + *x = pv[0]; + *y = pv[1]; + return m_cmd_blocks[nb][idx & block_mask]; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::command(unsigned idx) const + { + return m_cmd_blocks[idx >> block_shift][idx & block_mask]; + } + + //------------------------------------------------------------------------ + template + void vertex_block_storage::allocate_block(unsigned nb) + { + if(nb >= m_max_blocks) + { + T** new_coords = + pod_allocator::allocate((m_max_blocks + block_pool) * 2); + + unsigned char** new_cmds = + (unsigned char**)(new_coords + m_max_blocks + block_pool); + + if(m_coord_blocks) + { + memcpy(new_coords, + m_coord_blocks, + m_max_blocks * sizeof(T*)); + + memcpy(new_cmds, + m_cmd_blocks, + m_max_blocks * sizeof(unsigned char*)); + + pod_allocator::deallocate(m_coord_blocks, m_max_blocks * 2); + } + m_coord_blocks = new_coords; + m_cmd_blocks = new_cmds; + m_max_blocks += block_pool; + } + m_coord_blocks[nb] = + pod_allocator::allocate(block_size * 2 + + block_size / (sizeof(T) / sizeof(unsigned char))); + + m_cmd_blocks[nb] = + (unsigned char*)(m_coord_blocks[nb] + block_size * 2); + + m_total_blocks++; + } + + //------------------------------------------------------------------------ + template + int8u* vertex_block_storage::storage_ptrs(T** xy_ptr) + { + unsigned nb = m_total_vertices >> block_shift; + if(nb >= m_total_blocks) + { + allocate_block(nb); + } + *xy_ptr = m_coord_blocks[nb] + ((m_total_vertices & block_mask) << 1); + return m_cmd_blocks[nb] + (m_total_vertices & block_mask); + } + + + + + //-----------------------------------------------------poly_plain_adaptor + template class poly_plain_adaptor + { + public: + typedef T value_type; + + poly_plain_adaptor() : + m_data(0), + m_ptr(0), + m_end(0), + m_closed(false), + m_stop(false) + {} + + poly_plain_adaptor(const T* data, unsigned num_points, bool closed) : + m_data(data), + m_ptr(data), + m_end(data + num_points * 2), + m_closed(closed), + m_stop(false) + {} + + void init(const T* data, unsigned num_points, bool closed) + { + m_data = data; + m_ptr = data; + m_end = data + num_points * 2; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_ptr = m_data; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_ptr < m_end) + { + bool first = m_ptr == m_data; + *x = *m_ptr++; + *y = *m_ptr++; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + const T* m_data; + const T* m_ptr; + const T* m_end; + bool m_closed; + bool m_stop; + }; + + + + + + //-------------------------------------------------poly_container_adaptor + template class poly_container_adaptor + { + public: + typedef typename Container::value_type vertex_type; + + poly_container_adaptor() : + m_container(0), + m_index(0), + m_closed(false), + m_stop(false) + {} + + poly_container_adaptor(const Container& data, bool closed) : + m_container(&data), + m_index(0), + m_closed(closed), + m_stop(false) + {} + + void init(const Container& data, bool closed) + { + m_container = &data; + m_index = 0; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_index = 0; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_index < m_container->size()) + { + bool first = m_index == 0; + const vertex_type& v = (*m_container)[m_index++]; + *x = v.x; + *y = v.y; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + const Container* m_container; + unsigned m_index; + bool m_closed; + bool m_stop; + }; + + + + //-----------------------------------------poly_container_reverse_adaptor + template class poly_container_reverse_adaptor + { + public: + typedef typename Container::value_type vertex_type; + + poly_container_reverse_adaptor() : + m_container(0), + m_index(-1), + m_closed(false), + m_stop(false) + {} + + poly_container_reverse_adaptor(Container& data, bool closed) : + m_container(&data), + m_index(-1), + m_closed(closed), + m_stop(false) + {} + + void init(Container& data, bool closed) + { + m_container = &data; + m_index = m_container->size() - 1; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_index = m_container->size() - 1; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_index >= 0) + { + bool first = m_index == int(m_container->size() - 1); + const vertex_type& v = (*m_container)[m_index--]; + *x = v.x; + *y = v.y; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + Container* m_container; + int m_index; + bool m_closed; + bool m_stop; + }; + + + + + + //--------------------------------------------------------line_adaptor + class line_adaptor + { + public: + typedef double value_type; + + line_adaptor() : m_line(m_coord, 2, false) {} + line_adaptor(double x1, double y1, double x2, double y2) : + m_line(m_coord, 2, false) + { + m_coord[0] = x1; + m_coord[1] = y1; + m_coord[2] = x2; + m_coord[3] = y2; + } + + void init(double x1, double y1, double x2, double y2) + { + m_coord[0] = x1; + m_coord[1] = y1; + m_coord[2] = x2; + m_coord[3] = y2; + m_line.rewind(0); + } + + void rewind(unsigned) + { + m_line.rewind(0); + } + + unsigned vertex(double* x, double* y) + { + return m_line.vertex(x, y); + } + + private: + double m_coord[4]; + poly_plain_adaptor m_line; + }; + + + + + + + + + + + + + + //---------------------------------------------------------------path_base + // A container to store vertices with their flags. + // A path consists of a number of contours separated with "move_to" + // commands. The path storage can keep and maintain more than one + // path. + // To navigate to the beginning of a particular path, use rewind(path_id); + // Where path_id is what start_new_path() returns. So, when you call + // start_new_path() you need to store its return value somewhere else + // to navigate to the path afterwards. + // + // See also: vertex_source concept + //------------------------------------------------------------------------ + template class path_base + { + public: + typedef VertexContainer container_type; + typedef path_base self_type; + + //-------------------------------------------------------------------- + path_base() : m_vertices(), m_iterator(0) {} + void remove_all() { m_vertices.remove_all(); m_iterator = 0; } + void free_all() { m_vertices.free_all(); m_iterator = 0; } + + // Make path functions + //-------------------------------------------------------------------- + unsigned start_new_path(); + + void move_to(double x, double y); + void move_rel(double dx, double dy); + + void line_to(double x, double y); + void line_rel(double dx, double dy); + + void hline_to(double x); + void hline_rel(double dx); + + void vline_to(double y); + void vline_rel(double dy); + + void arc_to(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x, double y); + + void arc_rel(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double dx, double dy); + + void curve3(double x_ctrl, double y_ctrl, + double x_to, double y_to); + + void curve3_rel(double dx_ctrl, double dy_ctrl, + double dx_to, double dy_to); + + void curve3(double x_to, double y_to); + + void curve3_rel(double dx_to, double dy_to); + + void curve4(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + void curve4_rel(double dx_ctrl1, double dy_ctrl1, + double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to); + + void curve4(double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + void curve4_rel(double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + + void end_poly(unsigned flags = path_flags_close); + void close_polygon(unsigned flags = path_flags_none); + + // Accessors + //-------------------------------------------------------------------- + const container_type& vertices() const { return m_vertices; } + container_type& vertices() { return m_vertices; } + + unsigned total_vertices() const; + + void rel_to_abs(double* x, double* y) const; + + unsigned last_vertex(double* x, double* y) const; + unsigned prev_vertex(double* x, double* y) const; + + double last_x() const; + double last_y() const; + + unsigned vertex(unsigned idx, double* x, double* y) const; + unsigned command(unsigned idx) const; + + void modify_vertex(unsigned idx, double x, double y); + void modify_vertex(unsigned idx, double x, double y, unsigned cmd); + void modify_command(unsigned idx, unsigned cmd); + + // VertexSource interface + //-------------------------------------------------------------------- + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + // Arrange the orientation of a polygon, all polygons in a path, + // or in all paths. After calling arrange_orientations() or + // arrange_orientations_all_paths(), all the polygons will have + // the same orientation, i.e. path_flags_cw or path_flags_ccw + //-------------------------------------------------------------------- + unsigned arrange_polygon_orientation(unsigned start, path_flags_e orientation); + unsigned arrange_orientations(unsigned path_id, path_flags_e orientation); + void arrange_orientations_all_paths(path_flags_e orientation); + void invert_polygon(unsigned start); + + // Flip all vertices horizontally or vertically, + // between x1 and x2, or between y1 and y2 respectively + //-------------------------------------------------------------------- + void flip_x(double x1, double x2); + void flip_y(double y1, double y2); + + // Concatenate path. The path is added as is. + //-------------------------------------------------------------------- + template + void concat_path(VertexSource& vs, unsigned path_id = 0) + { + double x, y; + unsigned cmd; + vs.rewind(path_id); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + m_vertices.add_vertex(x, y, cmd); + } + } + + //-------------------------------------------------------------------- + // Join path. The path is joined with the existing one, that is, + // it behaves as if the pen of a plotter was always down (drawing) + template + void join_path(VertexSource& vs, unsigned path_id = 0) + { + double x, y; + unsigned cmd; + vs.rewind(path_id); + cmd = vs.vertex(&x, &y); + if(!is_stop(cmd)) + { + if(is_vertex(cmd)) + { + double x0, y0; + unsigned cmd0 = last_vertex(&x0, &y0); + if(is_vertex(cmd0)) + { + if(calc_distance(x, y, x0, y0) > vertex_dist_epsilon) + { + if(is_move_to(cmd)) cmd = path_cmd_line_to; + m_vertices.add_vertex(x, y, cmd); + } + } + else + { + if(is_stop(cmd0)) + { + cmd = path_cmd_move_to; + } + else + { + if(is_move_to(cmd)) cmd = path_cmd_line_to; + } + m_vertices.add_vertex(x, y, cmd); + } + } + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + m_vertices.add_vertex(x, y, is_move_to(cmd) ? + unsigned(path_cmd_line_to) : + cmd); + } + } + } + + // Concatenate polygon/polyline. + //-------------------------------------------------------------------- + template void concat_poly(const T* data, + unsigned num_points, + bool closed) + { + poly_plain_adaptor poly(data, num_points, closed); + concat_path(poly); + } + + // Join polygon/polyline continuously. + //-------------------------------------------------------------------- + template void join_poly(const T* data, + unsigned num_points, + bool closed) + { + poly_plain_adaptor poly(data, num_points, closed); + join_path(poly); + } + + //-------------------------------------------------------------------- + void translate(double dx, double dy, unsigned path_id=0); + void translate_all_paths(double dx, double dy); + + //-------------------------------------------------------------------- + template + void transform(const Trans& trans, unsigned path_id=0) + { + unsigned num_ver = m_vertices.total_vertices(); + for(; path_id < num_ver; path_id++) + { + double x, y; + unsigned cmd = m_vertices.vertex(path_id, &x, &y); + if(is_stop(cmd)) break; + if(is_vertex(cmd)) + { + trans.transform(&x, &y); + m_vertices.modify_vertex(path_id, x, y); + } + } + } + + //-------------------------------------------------------------------- + template + void transform_all_paths(const Trans& trans) + { + unsigned idx; + unsigned num_ver = m_vertices.total_vertices(); + for(idx = 0; idx < num_ver; idx++) + { + double x, y; + if(is_vertex(m_vertices.vertex(idx, &x, &y))) + { + trans.transform(&x, &y); + m_vertices.modify_vertex(idx, x, y); + } + } + } + + + + private: + unsigned perceive_polygon_orientation(unsigned start, unsigned end); + void invert_polygon(unsigned start, unsigned end); + + VertexContainer m_vertices; + unsigned m_iterator; + }; + + //------------------------------------------------------------------------ + template + unsigned path_base::start_new_path() + { + if(!is_stop(m_vertices.last_command())) + { + m_vertices.add_vertex(0.0, 0.0, path_cmd_stop); + } + return m_vertices.total_vertices(); + } + + + //------------------------------------------------------------------------ + template + inline void path_base::rel_to_abs(double* x, double* y) const + { + if(m_vertices.total_vertices()) + { + double x2; + double y2; + if(is_vertex(m_vertices.last_vertex(&x2, &y2))) + { + *x += x2; + *y += y2; + } + } + } + + //------------------------------------------------------------------------ + template + inline void path_base::move_to(double x, double y) + { + m_vertices.add_vertex(x, y, path_cmd_move_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::move_rel(double dx, double dy) + { + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_move_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::line_to(double x, double y) + { + m_vertices.add_vertex(x, y, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::line_rel(double dx, double dy) + { + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::hline_to(double x) + { + m_vertices.add_vertex(x, last_y(), path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::hline_rel(double dx) + { + double dy = 0; + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::vline_to(double y) + { + m_vertices.add_vertex(last_x(), y, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::vline_rel(double dy) + { + double dx = 0; + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + void path_base::arc_to(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x, double y) + { + if(m_vertices.total_vertices() && is_vertex(m_vertices.last_command())) + { + const double epsilon = 1e-30; + double x0 = 0.0; + double y0 = 0.0; + m_vertices.last_vertex(&x0, &y0); + + rx = fabs(rx); + ry = fabs(ry); + + // Ensure radii are valid + //------------------------- + if(rx < epsilon || ry < epsilon) + { + line_to(x, y); + return; + } + + if(calc_distance(x0, y0, x, y) < epsilon) + { + // If the endpoints (x, y) and (x0, y0) are identical, then this + // is equivalent to omitting the elliptical arc segment entirely. + return; + } + bezier_arc_svg a(x0, y0, rx, ry, angle, large_arc_flag, sweep_flag, x, y); + if(a.radii_ok()) + { + join_path(a); + } + else + { + line_to(x, y); + } + } + else + { + move_to(x, y); + } + } + + //------------------------------------------------------------------------ + template + void path_base::arc_rel(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double dx, double dy) + { + rel_to_abs(&dx, &dy); + arc_to(rx, ry, angle, large_arc_flag, sweep_flag, dx, dy); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3(double x_ctrl, double y_ctrl, + double x_to, double y_to) + { + m_vertices.add_vertex(x_ctrl, y_ctrl, path_cmd_curve3); + m_vertices.add_vertex(x_to, y_to, path_cmd_curve3); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3_rel(double dx_ctrl, double dy_ctrl, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl, &dy_ctrl); + rel_to_abs(&dx_to, &dy_to); + m_vertices.add_vertex(dx_ctrl, dy_ctrl, path_cmd_curve3); + m_vertices.add_vertex(dx_to, dy_to, path_cmd_curve3); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3(double x_to, double y_to) + { + double x0; + double y0; + if(is_vertex(m_vertices.last_vertex(&x0, &y0))) + { + double x_ctrl; + double y_ctrl; + unsigned cmd = m_vertices.prev_vertex(&x_ctrl, &y_ctrl); + if(is_curve(cmd)) + { + x_ctrl = x0 + x0 - x_ctrl; + y_ctrl = y0 + y0 - y_ctrl; + } + else + { + x_ctrl = x0; + y_ctrl = y0; + } + curve3(x_ctrl, y_ctrl, x_to, y_to); + } + } + + //------------------------------------------------------------------------ + template + void path_base::curve3_rel(double dx_to, double dy_to) + { + rel_to_abs(&dx_to, &dy_to); + curve3(dx_to, dy_to); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to) + { + m_vertices.add_vertex(x_ctrl1, y_ctrl1, path_cmd_curve4); + m_vertices.add_vertex(x_ctrl2, y_ctrl2, path_cmd_curve4); + m_vertices.add_vertex(x_to, y_to, path_cmd_curve4); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4_rel(double dx_ctrl1, double dy_ctrl1, + double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl1, &dy_ctrl1); + rel_to_abs(&dx_ctrl2, &dy_ctrl2); + rel_to_abs(&dx_to, &dy_to); + m_vertices.add_vertex(dx_ctrl1, dy_ctrl1, path_cmd_curve4); + m_vertices.add_vertex(dx_ctrl2, dy_ctrl2, path_cmd_curve4); + m_vertices.add_vertex(dx_to, dy_to, path_cmd_curve4); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4(double x_ctrl2, double y_ctrl2, + double x_to, double y_to) + { + double x0; + double y0; + if(is_vertex(last_vertex(&x0, &y0))) + { + double x_ctrl1; + double y_ctrl1; + unsigned cmd = prev_vertex(&x_ctrl1, &y_ctrl1); + if(is_curve(cmd)) + { + x_ctrl1 = x0 + x0 - x_ctrl1; + y_ctrl1 = y0 + y0 - y_ctrl1; + } + else + { + x_ctrl1 = x0; + y_ctrl1 = y0; + } + curve4(x_ctrl1, y_ctrl1, x_ctrl2, y_ctrl2, x_to, y_to); + } + } + + //------------------------------------------------------------------------ + template + void path_base::curve4_rel(double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl2, &dy_ctrl2); + rel_to_abs(&dx_to, &dy_to); + curve4(dx_ctrl2, dy_ctrl2, dx_to, dy_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::end_poly(unsigned flags) + { + if(is_vertex(m_vertices.last_command())) + { + m_vertices.add_vertex(0.0, 0.0, path_cmd_end_poly | flags); + } + } + + //------------------------------------------------------------------------ + template + inline void path_base::close_polygon(unsigned flags) + { + end_poly(path_flags_close | flags); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::total_vertices() const + { + return m_vertices.total_vertices(); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::last_vertex(double* x, double* y) const + { + return m_vertices.last_vertex(x, y); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::prev_vertex(double* x, double* y) const + { + return m_vertices.prev_vertex(x, y); + } + + //------------------------------------------------------------------------ + template + inline double path_base::last_x() const + { + return m_vertices.last_x(); + } + + //------------------------------------------------------------------------ + template + inline double path_base::last_y() const + { + return m_vertices.last_y(); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::vertex(unsigned idx, double* x, double* y) const + { + return m_vertices.vertex(idx, x, y); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::command(unsigned idx) const + { + return m_vertices.command(idx); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_vertex(unsigned idx, double x, double y) + { + m_vertices.modify_vertex(idx, x, y); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_vertex(unsigned idx, double x, double y, unsigned cmd) + { + m_vertices.modify_vertex(idx, x, y, cmd); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_command(unsigned idx, unsigned cmd) + { + m_vertices.modify_command(idx, cmd); + } + + //------------------------------------------------------------------------ + template + inline void path_base::rewind(unsigned path_id) + { + m_iterator = path_id; + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::vertex(double* x, double* y) + { + if(m_iterator >= m_vertices.total_vertices()) return path_cmd_stop; + return m_vertices.vertex(m_iterator++, x, y); + } + + //------------------------------------------------------------------------ + template + unsigned path_base::perceive_polygon_orientation(unsigned start, + unsigned end) + { + // Calculate signed area (double area to be exact) + //--------------------- + unsigned np = end - start; + double area = 0.0; + unsigned i; + for(i = 0; i < np; ++i) + { + double x1, y1, x2, y2; + m_vertices.vertex(start + i, &x1, &y1); + m_vertices.vertex(start + (i + 1) % np, &x2, &y2); + area += x1 * y2 - y1 * x2; + } + return (area < 0.0) ? path_flags_cw : path_flags_ccw; + } + + //------------------------------------------------------------------------ + template + void path_base::invert_polygon(unsigned start, unsigned end) + { + unsigned i; + unsigned tmp_cmd = m_vertices.command(start); + + --end; // Make "end" inclusive + + // Shift all commands to one position + for(i = start; i < end; ++i) + { + m_vertices.modify_command(i, m_vertices.command(i + 1)); + } + + // Assign starting command to the ending command + m_vertices.modify_command(end, tmp_cmd); + + // Reverse the polygon + while(end > start) + { + m_vertices.swap_vertices(start++, end--); + } + } + + //------------------------------------------------------------------------ + template + void path_base::invert_polygon(unsigned start) + { + // Skip all non-vertices at the beginning + while(start < m_vertices.total_vertices() && + !is_vertex(m_vertices.command(start))) ++start; + + // Skip all insignificant move_to + while(start+1 < m_vertices.total_vertices() && + is_move_to(m_vertices.command(start)) && + is_move_to(m_vertices.command(start+1))) ++start; + + // Find the last vertex + unsigned end = start + 1; + while(end < m_vertices.total_vertices() && + !is_next_poly(m_vertices.command(end))) ++end; + + invert_polygon(start, end); + } + + //------------------------------------------------------------------------ + template + unsigned path_base::arrange_polygon_orientation(unsigned start, + path_flags_e orientation) + { + if(orientation == path_flags_none) return start; + + // Skip all non-vertices at the beginning + while(start < m_vertices.total_vertices() && + !is_vertex(m_vertices.command(start))) ++start; + + // Skip all insignificant move_to + while(start+1 < m_vertices.total_vertices() && + is_move_to(m_vertices.command(start)) && + is_move_to(m_vertices.command(start+1))) ++start; + + // Find the last vertex + unsigned end = start + 1; + while(end < m_vertices.total_vertices() && + !is_next_poly(m_vertices.command(end))) ++end; + + if(end - start > 2) + { + if(perceive_polygon_orientation(start, end) != unsigned(orientation)) + { + // Invert polygon, set orientation flag, and skip all end_poly + invert_polygon(start, end); + unsigned cmd; + while(end < m_vertices.total_vertices() && + is_end_poly(cmd = m_vertices.command(end))) + { + m_vertices.modify_command(end++, set_orientation(cmd, orientation)); + } + } + } + return end; + } + + //------------------------------------------------------------------------ + template + unsigned path_base::arrange_orientations(unsigned start, + path_flags_e orientation) + { + if(orientation != path_flags_none) + { + while(start < m_vertices.total_vertices()) + { + start = arrange_polygon_orientation(start, orientation); + if(is_stop(m_vertices.command(start))) + { + ++start; + break; + } + } + } + return start; + } + + //------------------------------------------------------------------------ + template + void path_base::arrange_orientations_all_paths(path_flags_e orientation) + { + if(orientation != path_flags_none) + { + unsigned start = 0; + while(start < m_vertices.total_vertices()) + { + start = arrange_orientations(start, orientation); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::flip_x(double x1, double x2) + { + unsigned i; + double x, y; + for(i = 0; i < m_vertices.total_vertices(); ++i) + { + unsigned cmd = m_vertices.vertex(i, &x, &y); + if(is_vertex(cmd)) + { + m_vertices.modify_vertex(i, x2 - x + x1, y); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::flip_y(double y1, double y2) + { + unsigned i; + double x, y; + for(i = 0; i < m_vertices.total_vertices(); ++i) + { + unsigned cmd = m_vertices.vertex(i, &x, &y); + if(is_vertex(cmd)) + { + m_vertices.modify_vertex(i, x, y2 - y + y1); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::translate(double dx, double dy, unsigned path_id) + { + unsigned num_ver = m_vertices.total_vertices(); + for(; path_id < num_ver; path_id++) + { + double x, y; + unsigned cmd = m_vertices.vertex(path_id, &x, &y); + if(is_stop(cmd)) break; + if(is_vertex(cmd)) + { + x += dx; + y += dy; + m_vertices.modify_vertex(path_id, x, y); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::translate_all_paths(double dx, double dy) + { + unsigned idx; + unsigned num_ver = m_vertices.total_vertices(); + for(idx = 0; idx < num_ver; idx++) + { + double x, y; + if(is_vertex(m_vertices.vertex(idx, &x, &y))) + { + x += dx; + y += dy; + m_vertices.modify_vertex(idx, x, y); + } + } + } + + //-----------------------------------------------------vertex_stl_storage + template class vertex_stl_storage + { + public: + typedef typename Container::value_type vertex_type; + typedef typename vertex_type::value_type value_type; + + void remove_all() { m_vertices.clear(); } + void free_all() { m_vertices.clear(); } + + void add_vertex(double x, double y, unsigned cmd) + { + m_vertices.push_back(vertex_type(value_type(x), + value_type(y), + int8u(cmd))); + } + + void modify_vertex(unsigned idx, double x, double y) + { + vertex_type& v = m_vertices[idx]; + v.x = value_type(x); + v.y = value_type(y); + } + + void modify_vertex(unsigned idx, double x, double y, unsigned cmd) + { + vertex_type& v = m_vertices[idx]; + v.x = value_type(x); + v.y = value_type(y); + v.cmd = int8u(cmd); + } + + void modify_command(unsigned idx, unsigned cmd) + { + m_vertices[idx].cmd = int8u(cmd); + } + + void swap_vertices(unsigned v1, unsigned v2) + { + vertex_type t = m_vertices[v1]; + m_vertices[v1] = m_vertices[v2]; + m_vertices[v2] = t; + } + + unsigned last_command() const + { + return m_vertices.size() ? + m_vertices[m_vertices.size() - 1].cmd : + path_cmd_stop; + } + + unsigned last_vertex(double* x, double* y) const + { + if(m_vertices.size() == 0) + { + *x = *y = 0.0; + return path_cmd_stop; + } + return vertex(m_vertices.size() - 1, x, y); + } + + unsigned prev_vertex(double* x, double* y) const + { + if(m_vertices.size() < 2) + { + *x = *y = 0.0; + return path_cmd_stop; + } + return vertex(m_vertices.size() - 2, x, y); + } + + double last_x() const + { + return m_vertices.size() ? m_vertices[m_vertices.size() - 1].x : 0.0; + } + + double last_y() const + { + return m_vertices.size() ? m_vertices[m_vertices.size() - 1].y : 0.0; + } + + unsigned total_vertices() const + { + return m_vertices.size(); + } + + unsigned vertex(unsigned idx, double* x, double* y) const + { + const vertex_type& v = m_vertices[idx]; + *x = v.x; + *y = v.y; + return v.cmd; + } + + unsigned command(unsigned idx) const + { + return m_vertices[idx].cmd; + } + + private: + Container m_vertices; + }; + + //-----------------------------------------------------------path_storage + typedef path_base > path_storage; + + // Example of declarations path_storage with pod_bvector as a container + //----------------------------------------------------------------------- + //typedef path_base > > path_storage; + +} + +// Example of declarations path_storage with std::vector as a container +//--------------------------------------------------------------------------- +//#include +//namespace kiva_gl_agg +//{ +// typedef path_base > > stl_path_storage; +//} + +#endif diff --git a/kiva/gl/src/agg/agg_sqrt_tables.cpp b/kiva/gl/src/agg/agg_sqrt_tables.cpp new file mode 100644 index 000000000..6811448e8 --- /dev/null +++ b/kiva/gl/src/agg/agg_sqrt_tables.cpp @@ -0,0 +1,115 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// static tables for fast integer sqrt +// +//---------------------------------------------------------------------------- + +#include "agg_basics.h" + +namespace kiva_gl_agg +{ + int16u g_sqrt_table[1024] = //----------g_sqrt_table + { + 0, + 2048,2896,3547,4096,4579,5017,5418,5793,6144,6476,6792,7094,7384,7663,7932,8192,8444, + 8689,8927,9159,9385,9606,9822,10033,10240,10443,10642,10837,11029,11217,11403,11585, + 11765,11942,12116,12288,12457,12625,12790,12953,13114,13273,13430,13585,13738,13890, + 14040,14189,14336,14482,14626,14768,14910,15050,15188,15326,15462,15597,15731,15864, + 15995,16126,16255,16384,16512,16638,16764,16888,17012,17135,17257,17378,17498,17618, + 17736,17854,17971,18087,18203,18318,18432,18545,18658,18770,18882,18992,19102,19212, + 19321,19429,19537,19644,19750,19856,19961,20066,20170,20274,20377,20480,20582,20684, + 20785,20886,20986,21085,21185,21283,21382,21480,21577,21674,21771,21867,21962,22058, + 22153,22247,22341,22435,22528,22621,22713,22806,22897,22989,23080,23170,23261,23351, + 23440,23530,23619,23707,23796,23884,23971,24059,24146,24232,24319,24405,24491,24576, + 24661,24746,24831,24915,24999,25083,25166,25249,25332,25415,25497,25580,25661,25743, + 25824,25905,25986,26067,26147,26227,26307,26387,26466,26545,26624,26703,26781,26859, + 26937,27015,27092,27170,27247,27324,27400,27477,27553,27629,27705,27780,27856,27931, + 28006,28081,28155,28230,28304,28378,28452,28525,28599,28672,28745,28818,28891,28963, + 29035,29108,29180,29251,29323,29394,29466,29537,29608,29678,29749,29819,29890,29960, + 30030,30099,30169,30238,30308,30377,30446,30515,30583,30652,30720,30788,30856,30924, + 30992,31059,31127,31194,31261,31328,31395,31462,31529,31595,31661,31727,31794,31859, + 31925,31991,32056,32122,32187,32252,32317,32382,32446,32511,32575,32640,32704,32768, + 32832,32896,32959,33023,33086,33150,33213,33276,33339,33402,33465,33527,33590,33652, + 33714,33776,33839,33900,33962,34024,34086,34147,34208,34270,34331,34392,34453,34514, + 34574,34635,34695,34756,34816,34876,34936,34996,35056,35116,35176,35235,35295,35354, + 35413,35472,35531,35590,35649,35708,35767,35825,35884,35942,36001,36059,36117,36175, + 36233,36291,36348,36406,36464,36521,36578,36636,36693,36750,36807,36864,36921,36978, + 37034,37091,37147,37204,37260,37316,37372,37429,37485,37540,37596,37652,37708,37763, + 37819,37874,37929,37985,38040,38095,38150,38205,38260,38315,38369,38424,38478,38533, + 38587,38642,38696,38750,38804,38858,38912,38966,39020,39073,39127,39181,39234,39287, + 39341,39394,39447,39500,39553,39606,39659,39712,39765,39818,39870,39923,39975,40028, + 40080,40132,40185,40237,40289,40341,40393,40445,40497,40548,40600,40652,40703,40755, + 40806,40857,40909,40960,41011,41062,41113,41164,41215,41266,41317,41368,41418,41469, + 41519,41570,41620,41671,41721,41771,41821,41871,41922,41972,42021,42071,42121,42171, + 42221,42270,42320,42369,42419,42468,42518,42567,42616,42665,42714,42763,42813,42861, + 42910,42959,43008,43057,43105,43154,43203,43251,43300,43348,43396,43445,43493,43541, + 43589,43637,43685,43733,43781,43829,43877,43925,43972,44020,44068,44115,44163,44210, + 44258,44305,44352,44400,44447,44494,44541,44588,44635,44682,44729,44776,44823,44869, + 44916,44963,45009,45056,45103,45149,45195,45242,45288,45334,45381,45427,45473,45519, + 45565,45611,45657,45703,45749,45795,45840,45886,45932,45977,46023,46069,46114,46160, + 46205,46250,46296,46341,46386,46431,46477,46522,46567,46612,46657,46702,46746,46791, + 46836,46881,46926,46970,47015,47059,47104,47149,47193,47237,47282,47326,47370,47415, + 47459,47503,47547,47591,47635,47679,47723,47767,47811,47855,47899,47942,47986,48030, + 48074,48117,48161,48204,48248,48291,48335,48378,48421,48465,48508,48551,48594,48637, + 48680,48723,48766,48809,48852,48895,48938,48981,49024,49067,49109,49152,49195,49237, + 49280,49322,49365,49407,49450,49492,49535,49577,49619,49661,49704,49746,49788,49830, + 49872,49914,49956,49998,50040,50082,50124,50166,50207,50249,50291,50332,50374,50416, + 50457,50499,50540,50582,50623,50665,50706,50747,50789,50830,50871,50912,50954,50995, + 51036,51077,51118,51159,51200,51241,51282,51323,51364,51404,51445,51486,51527,51567, + 51608,51649,51689,51730,51770,51811,51851,51892,51932,51972,52013,52053,52093,52134, + 52174,52214,52254,52294,52334,52374,52414,52454,52494,52534,52574,52614,52654,52694, + 52734,52773,52813,52853,52892,52932,52972,53011,53051,53090,53130,53169,53209,53248, + 53287,53327,53366,53405,53445,53484,53523,53562,53601,53640,53679,53719,53758,53797, + 53836,53874,53913,53952,53991,54030,54069,54108,54146,54185,54224,54262,54301,54340, + 54378,54417,54455,54494,54532,54571,54609,54647,54686,54724,54762,54801,54839,54877, + 54915,54954,54992,55030,55068,55106,55144,55182,55220,55258,55296,55334,55372,55410, + 55447,55485,55523,55561,55599,55636,55674,55712,55749,55787,55824,55862,55900,55937, + 55975,56012,56049,56087,56124,56162,56199,56236,56273,56311,56348,56385,56422,56459, + 56497,56534,56571,56608,56645,56682,56719,56756,56793,56830,56867,56903,56940,56977, + 57014,57051,57087,57124,57161,57198,57234,57271,57307,57344,57381,57417,57454,57490, + 57527,57563,57599,57636,57672,57709,57745,57781,57817,57854,57890,57926,57962,57999, + 58035,58071,58107,58143,58179,58215,58251,58287,58323,58359,58395,58431,58467,58503, + 58538,58574,58610,58646,58682,58717,58753,58789,58824,58860,58896,58931,58967,59002, + 59038,59073,59109,59144,59180,59215,59251,59286,59321,59357,59392,59427,59463,59498, + 59533,59568,59603,59639,59674,59709,59744,59779,59814,59849,59884,59919,59954,59989, + 60024,60059,60094,60129,60164,60199,60233,60268,60303,60338,60373,60407,60442,60477, + 60511,60546,60581,60615,60650,60684,60719,60753,60788,60822,60857,60891,60926,60960, + 60995,61029,61063,61098,61132,61166,61201,61235,61269,61303,61338,61372,61406,61440, + 61474,61508,61542,61576,61610,61644,61678,61712,61746,61780,61814,61848,61882,61916, + 61950,61984,62018,62051,62085,62119,62153,62186,62220,62254,62287,62321,62355,62388, + 62422,62456,62489,62523,62556,62590,62623,62657,62690,62724,62757,62790,62824,62857, + 62891,62924,62957,62991,63024,63057,63090,63124,63157,63190,63223,63256,63289,63323, + 63356,63389,63422,63455,63488,63521,63554,63587,63620,63653,63686,63719,63752,63785, + 63817,63850,63883,63916,63949,63982,64014,64047,64080,64113,64145,64178,64211,64243, + 64276,64309,64341,64374,64406,64439,64471,64504,64536,64569,64601,64634,64666,64699, + 64731,64763,64796,64828,64861,64893,64925,64957,64990,65022,65054,65086,65119,65151, + 65183,65215,65247,65279,65312,65344,65376,65408,65440,65472,65504 + }; + + + int8 g_elder_bit_table[256] = //---------g_elder_bit_table + { + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + }; + +} diff --git a/kiva/gl/src/agg/agg_trans_affine.cpp b/kiva/gl/src/agg/agg_trans_affine.cpp new file mode 100644 index 000000000..d9f6d9888 --- /dev/null +++ b/kiva/gl/src/agg/agg_trans_affine.cpp @@ -0,0 +1,190 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Affine transformations +// +//---------------------------------------------------------------------------- +#include "agg_trans_affine.h" + +namespace kiva_gl_agg +{ + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::parl_to_parl(const double* src, + const double* dst) + { + sx = src[2] - src[0]; + shy = src[3] - src[1]; + shx = src[4] - src[0]; + sy = src[5] - src[1]; + tx = src[0]; + ty = src[1]; + invert(); + multiply(trans_affine(dst[2] - dst[0], dst[3] - dst[1], + dst[4] - dst[0], dst[5] - dst[1], + dst[0], dst[1])); + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::rect_to_parl(double x1, double y1, + double x2, double y2, + const double* parl) + { + double src[6]; + src[0] = x1; src[1] = y1; + src[2] = x2; src[3] = y1; + src[4] = x2; src[5] = y2; + parl_to_parl(src, parl); + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::parl_to_rect(const double* parl, + double x1, double y1, + double x2, double y2) + { + double dst[6]; + dst[0] = x1; dst[1] = y1; + dst[2] = x2; dst[3] = y1; + dst[4] = x2; dst[5] = y2; + parl_to_parl(parl, dst); + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::multiply(const trans_affine& m) + { + double t0 = sx * m.sx + shy * m.shx; + double t2 = shx * m.sx + sy * m.shx; + double t4 = tx * m.sx + ty * m.shx + m.tx; + shy = sx * m.shy + shy * m.sy; + sy = shx * m.shy + sy * m.sy; + ty = tx * m.shy + ty * m.sy + m.ty; + sx = t0; + shx = t2; + tx = t4; + return *this; + } + + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::invert() + { + double d = determinant_reciprocal(); + + double t0 = sy * d; + sy = sx * d; + shy = -shy * d; + shx = -shx * d; + + double t4 = -tx * t0 - ty * shx; + ty = -tx * shy - ty * sy; + + sx = t0; + tx = t4; + return *this; + } + + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::flip_x() + { + sx = -sx; + shy = -shy; + tx = -tx; + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::flip_y() + { + shx = -shx; + sy = -sy; + ty = -ty; + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::reset() + { + sx = sy = 1.0; + shy = shx = tx = ty = 0.0; + return *this; + } + + //------------------------------------------------------------------------ + bool trans_affine::is_identity(double epsilon) const + { + return is_equal_eps(sx, 1.0, epsilon) && + is_equal_eps(shy, 0.0, epsilon) && + is_equal_eps(shx, 0.0, epsilon) && + is_equal_eps(sy, 1.0, epsilon) && + is_equal_eps(tx, 0.0, epsilon) && + is_equal_eps(ty, 0.0, epsilon); + } + + //------------------------------------------------------------------------ + bool trans_affine::is_valid(double epsilon) const + { + return fabs(sx) > epsilon && fabs(sy) > epsilon; + } + + //------------------------------------------------------------------------ + bool trans_affine::is_equal(const trans_affine& m, double epsilon) const + { + return is_equal_eps(sx, m.sx, epsilon) && + is_equal_eps(shy, m.shy, epsilon) && + is_equal_eps(shx, m.shx, epsilon) && + is_equal_eps(sy, m.sy, epsilon) && + is_equal_eps(tx, m.tx, epsilon) && + is_equal_eps(ty, m.ty, epsilon); + } + + //------------------------------------------------------------------------ + double trans_affine::rotation() const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 0.0; + transform(&x1, &y1); + transform(&x2, &y2); + return atan2(y2-y1, x2-x1); + } + + //------------------------------------------------------------------------ + void trans_affine::translation(double* dx, double* dy) const + { + *dx = tx; + *dy = ty; + } + + //------------------------------------------------------------------------ + void trans_affine::scaling(double* x, double* y) const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 1.0; + trans_affine t(*this); + t *= trans_affine_rotation(-rotation()); + t.transform(&x1, &y1); + t.transform(&x2, &y2); + *x = x2 - x1; + *y = y2 - y1; + } + +} diff --git a/kiva/gl/src/agg/agg_trans_affine.h b/kiva/gl/src/agg/agg_trans_affine.h new file mode 100644 index 000000000..13bb8af0f --- /dev/null +++ b/kiva/gl/src/agg/agg_trans_affine.h @@ -0,0 +1,518 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Affine transformation classes. +// +//---------------------------------------------------------------------------- +#ifndef AGG_TRANS_AFFINE_INCLUDED +#define AGG_TRANS_AFFINE_INCLUDED + +#include +#include "agg_basics.h" + +namespace kiva_gl_agg +{ + const double affine_epsilon = 1e-14; + + //============================================================trans_affine + // + // See Implementation agg_trans_affine.cpp + // + // Affine transformation are linear transformations in Cartesian coordinates + // (strictly speaking not only in Cartesian, but for the beginning we will + // think so). They are rotation, scaling, translation and skewing. + // After any affine transformation a line segment remains a line segment + // and it will never become a curve. + // + // There will be no math about matrix calculations, since it has been + // described many times. Ask yourself a very simple question: + // "why do we need to understand and use some matrix stuff instead of just + // rotating, scaling and so on". The answers are: + // + // 1. Any combination of transformations can be done by only 4 multiplications + // and 4 additions in floating point. + // 2. One matrix transformation is equivalent to the number of consecutive + // discrete transformations, i.e. the matrix "accumulates" all transformations + // in the order of their settings. Suppose we have 4 transformations: + // * rotate by 30 degrees, + // * scale X to 2.0, + // * scale Y to 1.5, + // * move to (100, 100). + // The result will depend on the order of these transformations, + // and the advantage of matrix is that the sequence of discret calls: + // rotate(30), scaleX(2.0), scaleY(1.5), move(100,100) + // will have exactly the same result as the following matrix transformations: + // + // affine_matrix m; + // m *= rotate_matrix(30); + // m *= scaleX_matrix(2.0); + // m *= scaleY_matrix(1.5); + // m *= move_matrix(100,100); + // + // m.transform_my_point_at_last(x, y); + // + // What is the good of it? In real life we will set-up the matrix only once + // and then transform many points, let alone the convenience to set any + // combination of transformations. + // + // So, how to use it? Very easy - literally as it's shown above. Not quite, + // let us write a correct example: + // + // kiva_gl_agg::trans_affine m; + // m *= kiva_gl_agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); + // m *= kiva_gl_agg::trans_affine_scaling(2.0, 1.5); + // m *= kiva_gl_agg::trans_affine_translation(100.0, 100.0); + // m.transform(&x, &y); + // + // The affine matrix is all you need to perform any linear transformation, + // but all transformations have origin point (0,0). It means that we need to + // use 2 translations if we want to rotate someting around (100,100): + // + // m *= kiva_gl_agg::trans_affine_translation(-100.0, -100.0); // move to (0,0) + // m *= kiva_gl_agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); // rotate + // m *= kiva_gl_agg::trans_affine_translation(100.0, 100.0); // move back to (100,100) + //---------------------------------------------------------------------- + struct trans_affine + { + double sx, shy, shx, sy, tx, ty; + + //------------------------------------------ Construction + // Identity matrix + trans_affine() : + sx(1.0), shy(0.0), shx(0.0), sy(1.0), tx(0.0), ty(0.0) + {} + + // Custom matrix. Usually used in derived classes + trans_affine(double v0, double v1, double v2, + double v3, double v4, double v5) : + sx(v0), shy(v1), shx(v2), sy(v3), tx(v4), ty(v5) + {} + + // Custom matrix from m[6] + explicit trans_affine(const double* m) : + sx(m[0]), shy(m[1]), shx(m[2]), sy(m[3]), tx(m[4]), ty(m[5]) + {} + + // Rectangle to a parallelogram. + trans_affine(double x1, double y1, double x2, double y2, + const double* parl) + { + rect_to_parl(x1, y1, x2, y2, parl); + } + + // Parallelogram to a rectangle. + trans_affine(const double* parl, + double x1, double y1, double x2, double y2) + { + parl_to_rect(parl, x1, y1, x2, y2); + } + + // Arbitrary parallelogram transformation. + trans_affine(const double* src, const double* dst) + { + parl_to_parl(src, dst); + } + + //---------------------------------- Parellelogram transformations + // transform a parallelogram to another one. Src and dst are + // pointers to arrays of three points (double[6], x1,y1,...) that + // identify three corners of the parallelograms assuming implicit + // fourth point. The arguments are arrays of double[6] mapped + // to x1,y1, x2,y2, x3,y3 where the coordinates are: + // *-----------------* + // / (x3,y3)/ + // / / + // /(x1,y1) (x2,y2)/ + // *-----------------* + const trans_affine& parl_to_parl(const double* src, + const double* dst); + + const trans_affine& rect_to_parl(double x1, double y1, + double x2, double y2, + const double* parl); + + const trans_affine& parl_to_rect(const double* parl, + double x1, double y1, + double x2, double y2); + + + //------------------------------------------ Operations + // Reset - load an identity matrix + const trans_affine& reset(); + + // Direct transformations operations + const trans_affine& translate(double x, double y); + const trans_affine& rotate(double a); + const trans_affine& scale(double s); + const trans_affine& scale(double x, double y); + + // Multiply matrix to another one + const trans_affine& multiply(const trans_affine& m); + + // Multiply "m" to "this" and assign the result to "this" + const trans_affine& premultiply(const trans_affine& m); + + // Multiply matrix to inverse of another one + const trans_affine& multiply_inv(const trans_affine& m); + + // Multiply inverse of "m" to "this" and assign the result to "this" + const trans_affine& premultiply_inv(const trans_affine& m); + + // Invert matrix. Do not try to invert degenerate matrices, + // there's no check for validity. If you set scale to 0 and + // then try to invert matrix, expect unpredictable result. + const trans_affine& invert(); + + // Mirroring around X + const trans_affine& flip_x(); + + // Mirroring around Y + const trans_affine& flip_y(); + + //------------------------------------------- Load/Store + // Store matrix to an array [6] of double + void store_to(double* m) const + { + *m++ = sx; *m++ = shy; *m++ = shx; *m++ = sy; *m++ = tx; *m++ = ty; + } + + // Load matrix from an array [6] of double + const trans_affine& load_from(const double* m) + { + sx = *m++; shy = *m++; shx = *m++; sy = *m++; tx = *m++; ty = *m++; + return *this; + } + + //------------------------------------------- Operators + + // Multiply the matrix by another one + const trans_affine& operator *= (const trans_affine& m) + { + return multiply(m); + } + + // Multiply the matrix by inverse of another one + const trans_affine& operator /= (const trans_affine& m) + { + return multiply_inv(m); + } + + // Multiply the matrix by another one and return + // the result in a separete matrix. + trans_affine operator * (const trans_affine& m) const + { + return trans_affine(*this).multiply(m); + } + + // Multiply the matrix by inverse of another one + // and return the result in a separete matrix. + trans_affine operator / (const trans_affine& m) const + { + return trans_affine(*this).multiply_inv(m); + } + + // Calculate and return the inverse matrix + trans_affine operator ~ () const + { + trans_affine ret = *this; + return ret.invert(); + } + + // Equal operator with default epsilon + bool operator == (const trans_affine& m) const + { + return is_equal(m, affine_epsilon); + } + + // Not Equal operator with default epsilon + bool operator != (const trans_affine& m) const + { + return !is_equal(m, affine_epsilon); + } + + //-------------------------------------------- Transformations + // Direct transformation of x and y + void transform(double* x, double* y) const; + + // Direct transformation of x and y, 2x2 matrix only, no translation + void transform_2x2(double* x, double* y) const; + + // Inverse transformation of x and y. It works slower than the + // direct transformation. For massive operations it's better to + // invert() the matrix and then use direct transformations. + void inverse_transform(double* x, double* y) const; + + //-------------------------------------------- Auxiliary + // Calculate the determinant of matrix + double determinant() const + { + return sx * sy - shy * shx; + } + + // Calculate the reciprocal of the determinant + double determinant_reciprocal() const + { + return 1.0 / (sx * sy - shy * shx); + } + + // Get the average scale (by X and Y). + // Basically used to calculate the approximation_scale when + // decomposinting curves into line segments. + double scale() const; + + // Check to see if the matrix is not degenerate + bool is_valid(double epsilon = affine_epsilon) const; + + // Check to see if it's an identity matrix + bool is_identity(double epsilon = affine_epsilon) const; + + // Check to see if two matrices are equal + bool is_equal(const trans_affine& m, double epsilon = affine_epsilon) const; + + // Determine the major parameters. Use with caution considering + // possible degenerate cases. + double rotation() const; + void translation(double* dx, double* dy) const; + void scaling(double* x, double* y) const; + void scaling_abs(double* x, double* y) const; + }; + + //------------------------------------------------------------------------ + inline void trans_affine::transform(double* x, double* y) const + { + register double tmp = *x; + *x = tmp * sx + *y * shx + tx; + *y = tmp * shy + *y * sy + ty; + } + + //------------------------------------------------------------------------ + inline void trans_affine::transform_2x2(double* x, double* y) const + { + register double tmp = *x; + *x = tmp * sx + *y * shx; + *y = tmp * shy + *y * sy; + } + + //------------------------------------------------------------------------ + inline void trans_affine::inverse_transform(double* x, double* y) const + { + register double d = determinant_reciprocal(); + register double a = (*x - tx) * d; + register double b = (*y - ty) * d; + *x = a * sy - b * shx; + *y = b * sx - a * shy; + } + + //------------------------------------------------------------------------ + inline double trans_affine::scale() const + { + double x = 0.707106781 * sx + 0.707106781 * shx; + double y = 0.707106781 * shy + 0.707106781 * sy; + return sqrt(x*x + y*y); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::translate(double x, double y) + { + tx += x; + ty += y; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::rotate(double a) + { + double ca = cos(a); + double sa = sin(a); + double t0 = sx * ca - shy * sa; + double t2 = shx * ca - sy * sa; + double t4 = tx * ca - ty * sa; + shy = sx * sa + shy * ca; + sy = shx * sa + sy * ca; + ty = tx * sa + ty * ca; + sx = t0; + shx = t2; + tx = t4; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double x, double y) + { + double mm0 = x; // Possible hint for the optimizer + double mm3 = y; + sx *= mm0; + shx *= mm0; + tx *= mm0; + shy *= mm3; + sy *= mm3; + ty *= mm3; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double s) + { + double m = s; // Possible hint for the optimizer + sx *= m; + shx *= m; + tx *= m; + shy *= m; + sy *= m; + ty *= m; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::premultiply(const trans_affine& m) + { + trans_affine t = m; + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::multiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return multiply(t); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::premultiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline void trans_affine::scaling_abs(double* x, double* y) const + { + // Used to calculate scaling coefficients in image resampling. + // When there is considerable shear this method gives us much + // better estimation than just sx, sy. + *x = sqrt(sx * sx + shx * shx); + *y = sqrt(shy * shy + sy * sy); + } + + //====================================================trans_affine_rotation + // Rotation matrix. sin() and cos() are calculated twice for the same angle. + // There's no harm because the performance of sin()/cos() is very good on all + // modern processors. Besides, this operation is not going to be invoked too + // often. + class trans_affine_rotation : public trans_affine + { + public: + trans_affine_rotation(double a) : + trans_affine(cos(a), sin(a), -sin(a), cos(a), 0.0, 0.0) + {} + }; + + //====================================================trans_affine_scaling + // Scaling matrix. x, y - scale coefficients by X and Y respectively + class trans_affine_scaling : public trans_affine + { + public: + trans_affine_scaling(double x, double y) : + trans_affine(x, 0.0, 0.0, y, 0.0, 0.0) + {} + + trans_affine_scaling(double s) : + trans_affine(s, 0.0, 0.0, s, 0.0, 0.0) + {} + }; + + //================================================trans_affine_translation + // Translation matrix + class trans_affine_translation : public trans_affine + { + public: + trans_affine_translation(double x, double y) : + trans_affine(1.0, 0.0, 0.0, 1.0, x, y) + {} + }; + + //====================================================trans_affine_skewing + // Sckewing (shear) matrix + class trans_affine_skewing : public trans_affine + { + public: + trans_affine_skewing(double x, double y) : + trans_affine(1.0, tan(y), tan(x), 1.0, 0.0, 0.0) + {} + }; + + + //===============================================trans_affine_line_segment + // Rotate, Scale and Translate, associating 0...dist with line segment + // x1,y1,x2,y2 + class trans_affine_line_segment : public trans_affine + { + public: + trans_affine_line_segment(double x1, double y1, double x2, double y2, + double dist) + { + double dx = x2 - x1; + double dy = y2 - y1; + if(dist > 0.0) + { + multiply(trans_affine_scaling(sqrt(dx * dx + dy * dy) / dist)); + } + multiply(trans_affine_rotation(atan2(dy, dx))); + multiply(trans_affine_translation(x1, y1)); + } + }; + + + //============================================trans_affine_reflection_unit + // Reflection matrix. Reflect coordinates across the line through + // the origin containing the unit vector (ux, uy). + // Contributed by John Horigan + class trans_affine_reflection_unit : public trans_affine + { + public: + trans_affine_reflection_unit(double ux, double uy) : + trans_affine(2.0 * ux * ux - 1.0, + 2.0 * ux * uy, + 2.0 * ux * uy, + 2.0 * uy * uy - 1.0, + 0.0, 0.0) + {} + }; + + + //=================================================trans_affine_reflection + // Reflection matrix. Reflect coordinates across the line through + // the origin at the angle a or containing the non-unit vector (x, y). + // Contributed by John Horigan + class trans_affine_reflection : public trans_affine_reflection_unit + { + public: + trans_affine_reflection(double a) : + trans_affine_reflection_unit(cos(a), sin(a)) + {} + + + trans_affine_reflection(double x, double y) : + trans_affine_reflection_unit(x / sqrt(x * x + y * y), y / sqrt(x * x + y * y)) + {} + }; + +} + + +#endif + diff --git a/kiva/gl/src/kiva_gl_affine_helpers.cpp b/kiva/gl/src/kiva_gl_affine_helpers.cpp new file mode 100644 index 000000000..c3a72ecd7 --- /dev/null +++ b/kiva/gl/src/kiva_gl_affine_helpers.cpp @@ -0,0 +1,55 @@ +#include "kiva_gl_affine_helpers.h" +#include + +#define f_eq(a,b) (fabs((a)-(b)) < epsilon) + +namespace kiva_gl +{ + bool + is_identity(kiva_gl_agg::trans_affine& mat, double epsilon) + { + double temp[6]; + mat.store_to(temp); + return (f_eq(temp[0], 1.0) && f_eq(temp[1], 0.0) && + f_eq(temp[2], 0.0) && f_eq(temp[3], 1.0) && + f_eq(temp[4], 0.0) && f_eq(temp[5], 0.0)); + } + + bool + only_translation(kiva_gl_agg::trans_affine& mat, double epsilon) + { + double temp[6]; + mat.store_to(temp); + return (f_eq(temp[0], 1.0) && f_eq(temp[1], 0.0) && + f_eq(temp[2], 0.0) && f_eq(temp[3], 1.0)); + } + + bool + only_scale_and_translation(kiva_gl_agg::trans_affine& mat, double epsilon) + { + double temp[6]; + mat.store_to(temp); + return (f_eq(temp[1], 0.0) && f_eq(temp[2], 0.0)); + } + + void + get_translation(kiva_gl_agg::trans_affine& m, double* tx, double* ty) + { + double temp[6]; + m.store_to(temp); + *tx = temp[4]; + *ty = temp[5]; + } + + void + get_scale(kiva_gl_agg::trans_affine& m, double* dx, double* dy) + { + { + double temp[6]; + m.store_to(temp); + *dx = temp[0]; + *dy = temp[3]; + } + } + +} diff --git a/kiva/gl/src/kiva_gl_affine_helpers.h b/kiva/gl/src/kiva_gl_affine_helpers.h new file mode 100644 index 000000000..fb295e4fb --- /dev/null +++ b/kiva/gl/src/kiva_gl_affine_helpers.h @@ -0,0 +1,15 @@ +#ifndef KIVA_GL_AFFINE_MATRIX_H +#define KIVA_GL_AFFINE_MATRIX_H + +#include "agg_trans_affine.h" + +namespace kiva_gl +{ + bool is_identity(kiva_gl_agg::trans_affine& mat, double epsilon=1e-3); + bool only_translation(kiva_gl_agg::trans_affine& mat, double epsilon=1e-3); + bool only_scale_and_translation(kiva_gl_agg::trans_affine& mat, double epsilon=1e-3); + void get_translation(kiva_gl_agg::trans_affine& m, double* tx, double* ty); + void get_scale(kiva_gl_agg::trans_affine& m, double* dx, double* dy); +} + +#endif diff --git a/kiva/gl/src/kiva_gl_basics.h b/kiva/gl/src/kiva_gl_basics.h new file mode 100644 index 000000000..23ec736aa --- /dev/null +++ b/kiva/gl/src/kiva_gl_basics.h @@ -0,0 +1,125 @@ +#ifndef KIVA_GL_BASICS_H +#define KIVA_GL_BASICS_H + +#include "kiva_gl_constants.h" + +namespace kiva_gl +{ + +#ifdef _MSC_VER + #if _MSC_VER => 1300 + typedef signed __int64 INT64, *PINT64; + #else + #ifndef INT64 + #define INT64 __int64 + #endif + #endif +#endif +#ifdef __GNUC__ + typedef long long INT64; +#endif + +#ifdef max + #undef max +#endif +#ifdef min + #undef min +#endif + + + inline double max(double a, double b) + { + if (a>b) return a; + else return b; + } + + inline double min(double a, double b) + { + if (a=0) + { + if (y>=0) return 1; + else return 4; + } + else + { + if (y>=0) return 2; + else return 3; + } + } + + + // Determines whether or not two floating point numbers are + // essentially equal. This uses an aspect of the IEEE floating- + // point spec described in + // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + // The code for both functions are from this same page. + // + // The basic idea is to cast the floats to ints and look at then + // number of floating point numbers between them. This take advantage + // of the fact that the IEEE representation puts the mantissa on the right, + // meaning that incrementing an int representation of a float yields the + // next representable float. The two's complement stuff is to ensure + // that comparisons across the 0 boundary work. + + // For floating point, a maxUlps (ULP=units in last place) is roughly + // equivalent to a precision of 1/8,000,000 to 1/16,000,000 + inline bool almost_equal(float A, float B, int maxUlps = 100) + { + if (A==B) return true; + // Make sure maxUlps is non-negative and small enough that the + // default NAN won't compare as equal to anything. + //assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); + + int aInt = *(int*)&A; + // Make aInt lexicographically ordered as a twos-complement int + if (aInt < 0) + aInt = 0x80000000 - aInt; + + // Make bInt lexicographically ordered as a twos-complement int + int bInt = *(int*)&B; + if (bInt < 0) + bInt = 0x80000000 - bInt; + int intDiff = aInt - bInt; + if (intDiff < 0) + intDiff = -intDiff; + if (intDiff <= maxUlps) + return true; + return false; + } + + // For double, a maxUlps (ULP=units in last place) is roughly + // equivalent to a precision of 1/4e15 to 1/8e15. + inline bool almost_equal(double A, double B, int maxUlps = 10000) + { + // Make sure maxUlps is non-negative and small enough that the + // default NAN won't compare as equal to anything. + //assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); + + if (A==B) return true; + + INT64 aInt = *(INT64*)&A; + // Make aInt lexicographically ordered as a twos-complement int + if (aInt < 0) + aInt = 0x80000000 - aInt; + + // Make bInt lexicographically ordered as a twos-complement int + INT64 bInt = *(INT64*)&B; + if (bInt < 0) + bInt = 0x80000000 - bInt; + INT64 intDiff = aInt - bInt; + if (intDiff < 0) + intDiff = -intDiff; + if (intDiff <= maxUlps) + return true; + return false; + } + +} + +#endif /* KIVA_GL_BASICS_H */ diff --git a/kiva/gl/src/kiva_gl_compiled_path.cpp b/kiva/gl/src/kiva_gl_compiled_path.cpp new file mode 100644 index 000000000..83e63a602 --- /dev/null +++ b/kiva/gl/src/kiva_gl_compiled_path.cpp @@ -0,0 +1,330 @@ +#include "agg_bezier_arc.h" + +#include "kiva_gl_compiled_path.h" +#include "kiva_gl_basics.h" + +#include +#include + +using namespace kiva_gl; + +void +compiled_path::remove_all() +{ + //kiva_gl_agg::path_storage::remove_all(); + // fix me: call to base:: to appease VC++6.0 + this->base::remove_all(); + this->_has_curves = false; + //ptm = kiva_gl_agg::trans_affine(); +} + +void +compiled_path::begin_path() +{ + this->remove_all(); +} + +void +compiled_path::close_path() +{ + this->close_polygon(); +} + +void +compiled_path::move_to(double x, double y) +{ + this->ptm.transform(&x, &y); + // fix me: call to base:: to appease VC++6.0 + this->base::move_to(x, y); +} + +void +compiled_path::line_to(double x, double y) +{ + this->ptm.transform(&x, &y); + // fix me: call to base:: to appease VC++6.0 + this->base::line_to(x, y); +} + +void +compiled_path::quad_curve_to(double x_ctrl, double y_ctrl, + double x_to, double y_to) +{ + this->ptm.transform(&x_ctrl, &y_ctrl); + this->ptm.transform(&x_to, &y_to); + // fix me: call to base:: to appease VC++6.0 + this->base::curve3(x_ctrl, y_ctrl, x_to, y_to); + this->_has_curves = true; +} + +void +compiled_path::curve_to(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to) +{ + this->ptm.transform(&x_ctrl1, &y_ctrl1); + this->ptm.transform(&x_ctrl2, &y_ctrl2); + this->ptm.transform(&x_to, &y_to); + // fix me: call to base:: to appease VC++6.0 + this->base::curve4(x_ctrl1, y_ctrl1, + x_ctrl2, y_ctrl2, + x_to, y_to); + this->_has_curves = true; +} + +void +compiled_path::arc(double x, double y, double radius, double start_angle, + double end_angle, bool cw) +{ + // Rather than try to transform the center and scale the axes correctly, + // we'll just create an untransformed agg curve, grab its Bezier control + // points, transform them, and manually add them to the path. + double sweep_angle = end_angle - start_angle; + if (cw) + { + sweep_angle = -(2*kiva_gl_agg::pi - sweep_angle); + } + kiva_gl_agg::bezier_arc aggarc(x, y, radius, radius, start_angle, sweep_angle); + + // Now manually transform each vertex and add it. For some reason, trying + // to transform aggarc in place and then using this->base::add_path() + // causes an access violation if cw=true (but works fine if cw=false). + int numverts = aggarc.num_vertices(); + container_type& vertices = this->vertices(); + double vx, vy; + unsigned int cmd; + aggarc.rewind(0); + for (int i = 0; i <= numverts/2; ++i) + { + cmd = aggarc.vertex(&vx, &vy); + if (!kiva_gl_agg::is_stop(cmd)) + { + this->ptm.transform(&vx, &vy); + vertices.add_vertex(vx, vy, cmd); + } + } + + this->_has_curves = true; +} + +void +compiled_path::arc_to(double x1, double y1, double x2, double y2, + double radius) +{ + // We have to do some work above and beyond what Agg offers. The Agg + // arc_to() happily creates rotated elliptical arcs, but to match the + // DisplayPDF spec, we need to compute the correct tangent points on + // the tangent lines defined by (cur_x,cur_y), (x1,y1), and (x2,y2) such + // that a circular arc of the given radius will be created. + + // The general approach is to transform the coordinates of the three + // points so that x1,y1 is at the origin, x0,y0 is to the right of x1,y1, + // and y0==y1. This should be just a translation followed by a rotation. + // We then compute the relative position of the circle's center as well + // as the start angle and then inverse transform these back. (The angular + // sweep of the arc is unchanged.) + + double x0=0, y0=0; + this->last_vertex(&x0, &y0); + this->ptm.inverse_transform(&x0, &y0); + + // Calculate the offset and rotation so that x1,y1, is at the origin (0,0), + // and x0, y0 sites on the positive x axis (right side of x1,y1). + kiva_gl_agg::trans_affine_translation xform(-x1, -y1); + double xform_angle = -atan2(y0-y1, x0-x1); + if (!kiva_gl::almost_equal(fmod(xform_angle, 2*kiva_gl_agg::pi), 0.0)) + { + xform *= kiva_gl_agg::trans_affine_rotation(xform_angle); + } + + // Transform and rotate the points. + xform.transform(&x0, &y0); + xform.transform(&x1, &y1); + xform.transform(&x2, &y2); + + assert(kiva_gl::almost_equal(y1, 0.0)); + assert(kiva_gl::almost_equal(x1, 0.0)); + + double cx, cy; // location of circle's center + double center_angle = atan2(y2, x2) / 2; + bool sweep_flag = (center_angle >= 0) ? false : true; + double hypotenuse = fabs(radius / sin(center_angle)); + cx = hypotenuse * cos(center_angle); + cy = hypotenuse * sin(center_angle); + + // determine if we need to draw a line to the first tangent point + // from the current pen position. + if (!kiva_gl::almost_equal(x0, cx)) + { + x0 = cx; + xform.inverse_transform(&x0, &y0); + this->line_to(x0, y0); + } + else + { + xform.inverse_transform(&x0, &y0); + } + + // determine the second tangent point + double point2_scale = cx / sqrt(x2*x2 + y2*y2); + x2 *= point2_scale; + y2 *= point2_scale; + xform.inverse_transform(&x2, &y2); + kiva_gl_agg::bezier_arc_svg aggarc(x0, y0, radius, radius, 0.0, false, sweep_flag, x2, y2); + + int numverts = aggarc.num_vertices(); + double *vertices = aggarc.vertices(); + double *v = NULL; + for (int i = 0; i <= numverts/2; ++i) + { + v = vertices + i*2; + this->ptm.transform(v, v+1); + } + + // I believe join_path is equivalent to the old add_path() with solid_path=true + this->join_path(aggarc, 0); + // This is the alternative call. + //this->concat_path(aggarc, 0); + + this->_has_curves = true; +} + +void +compiled_path::add_path(compiled_path& other_path) +{ + container_type& vertices = this->vertices(); + double x=0.0; + double y=0.0; + unsigned cmd; + + other_path.rewind(0); + cmd = other_path.vertex(&x, &y); + while (!kiva_gl_agg::is_stop(cmd)) + { + this->_has_curves |= kiva_gl_agg::is_curve(cmd); + this->ptm.transform(&x, &y); + vertices.add_vertex(x, y, cmd); + cmd = other_path.vertex(&x, &y); + } + this->concat_ctm(other_path.ptm); +} + +void +compiled_path::lines(double* pts, int Npts) +{ + this->move_to(pts[0], pts[1]); + for (int i=2; i < Npts*2; i+=2) + { + this->line_to(pts[i], pts[i+1]); + } +} + +void +compiled_path::line_set(double* start, int Nstart, double* end, int Nend) +{ + int num_pts = (Nstart > Nend) ? Nend : Nstart; + for (int i=0; i < num_pts*2; i += 2) + { + this->move_to(start[i], start[i+1]); + this->line_to(end[i], end[i+1]); + } +} + +void +compiled_path::rect(double x, double y, double sx, double sy) +{ + this->move_to(x, y); + this->line_to(x, y+sy); + this->line_to(x+sx, y+sy); + this->line_to(x+sx, y); + this->close_path(); +} + +void +compiled_path::rect(kiva_gl::rect_type &r) +{ + this->rect(r.x, r.y, r.w, r.h); +} + +void +compiled_path::rects(double* all_rects, int Nrects) +{ + double *tmp; + for (int i = 0; i < Nrects*4; i+=4) + { + tmp = &all_rects[i]; + this->rect(tmp[0], tmp[1], tmp[2], tmp[3]); + } +} + +void +compiled_path::rects(kiva_gl::rect_list_type &rectlist) +{ + for (kiva_gl::rect_list_type::iterator it=rectlist.begin(); it != rectlist.end(); ++it) + { + this->rect(it->x, it->y, it->w, it->h); + } +} + +void +compiled_path::_transform_ctm(kiva_gl_agg::trans_affine& m) +{ + this->ptm.premultiply(m); +} + +void +compiled_path::translate_ctm(double x, double y) +{ + kiva_gl_agg::trans_affine_translation m(x, y); + this->_transform_ctm(m); +} + +void +compiled_path::rotate_ctm(double angle) +{ + kiva_gl_agg::trans_affine_rotation m(angle); + this->_transform_ctm(m); +} + +void +compiled_path::scale_ctm(double sx, double sy) +{ + kiva_gl_agg::trans_affine_scaling m(sx, sy); + this->_transform_ctm(m); +} + +void +compiled_path::concat_ctm(kiva_gl_agg::trans_affine& m) +{ + kiva_gl_agg::trans_affine m_copy(m); + this->_transform_ctm(m_copy); +} + +void +compiled_path::set_ctm(kiva_gl_agg::trans_affine& m) +{ + this->ptm = kiva_gl_agg::trans_affine(m); +} + +kiva_gl_agg::trans_affine +compiled_path::get_ctm() +{ + return this->ptm; +} + +void +compiled_path::save_ctm() +{ + this->ptm_stack.push(this->ptm); +} + +void +compiled_path::restore_ctm() +{ + // !! need to check what error should be on empty stack. + if (!this->ptm_stack.empty()) + { + this->ptm = this->ptm_stack.top(); + this->ptm_stack.pop(); + } +} diff --git a/kiva/gl/src/kiva_gl_compiled_path.h b/kiva/gl/src/kiva_gl_compiled_path.h new file mode 100644 index 000000000..f41f62a92 --- /dev/null +++ b/kiva/gl/src/kiva_gl_compiled_path.h @@ -0,0 +1,129 @@ +#ifndef KIVA_GL_COMPILED_PATH_H +#define KIVA_GL_COMPILED_PATH_H + +#include +#include + +#include "agg_basics.h" +#include "agg_path_storage.h" +#include "agg_trans_affine.h" + +#include "kiva_gl_rect.h" + + +namespace kiva_gl +{ + + class compiled_path : public kiva_gl_agg::path_storage + { + + /*------------------------------------------------------------------- + This extends the standard kiva_gl_agg::path_storage class to include + matrix transforms within the path definition. Doing so requires + overriding a number of methods to apply the matrix transformation + to vertices added to the path. + + The overridden methods are: + move_to + line_to + add_path + curve3 + curve4 + add_path + + There are a few others that need to be looked at also... + + In addition, we need to add several methods: + translate_ctm + rotate_ctm + scale_ctm + concat_ctm + set_ctm + get_ctm + save_ctm + restore_ctm + -------------------------------------------------------------------*/ + + // hack to get VC++ 6.0 to compile correctly + typedef kiva_gl_agg::path_storage base; + + /*------------------------------------------------------------------- + ptm -- path transform matrix. + + This is used to transform each point added to the path. It begins + as an identity matrix and accumulates every transform made during the + path formation. At the end of the path creation, the ptm holds + the total transformation seen during the path formation. It + can thus be multiplied with the ctm to determine what the ctm + should be after the compiled_path has been drawn. + + Todo: Should this default to the identity matrix or the current ctm? + -------------------------------------------------------------------*/ + kiva_gl_agg::trans_affine ptm; + + // ptm_stack is used for save/restore of the ptm + std::stack ptm_stack; + + // If the path contains curves, this value is true; + bool _has_curves; + + public: + // constructor + compiled_path() : base(), ptm(kiva_gl_agg::trans_affine()) + {} + + //--------------------------------------------------------------- + // path_storage interface + //--------------------------------------------------------------- + void remove_all(); + + void begin_path(); + void close_path(); + void move_to(double x, double y); + void line_to(double x, double y); + void quad_curve_to(double x_ctrl, double y_ctrl, + double x_to, double y_to); + void curve_to(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + // see graphics_context_base for descriptions of these functions + void arc(double x, double y, double radius, double start_angle, + double end_angle, bool cw=false); + void arc_to(double x1, double y1, double x2, double y2, + double radius); + + void add_path(compiled_path& other_path); + void lines(double* pts, int Npts); + void line_set(double* start, int Nstart, double* end, int Nend); + void rect(double x, double y, double sx, double sy); + void rect(kiva_gl::rect_type &rect); + void rects(double* all_rects, int Nrects); + void rects(kiva_gl::rect_list_type &rectlist); + + //--------------------------------------------------------------- + // compiled_path interface + //--------------------------------------------------------------- + + void _transform_ctm(kiva_gl_agg::trans_affine& m); + void translate_ctm(double x, double y); + void rotate_ctm(double angle); + void scale_ctm(double sx, double sy); + void concat_ctm(kiva_gl_agg::trans_affine& m); + void set_ctm(kiva_gl_agg::trans_affine& m); + kiva_gl_agg::trans_affine get_ctm(); + + //--------------------------------------------------------------- + // save/restore ptm methods + //--------------------------------------------------------------- + void save_ctm(); + void restore_ctm(); + + //--------------------------------------------------------------- + // Test whether curves exist in path. + //--------------------------------------------------------------- + inline bool has_curves() { return this->_has_curves;} + }; +} + +#endif diff --git a/kiva/gl/src/kiva_gl_constants.h b/kiva/gl/src/kiva_gl_constants.h new file mode 100644 index 000000000..d1d461789 --- /dev/null +++ b/kiva/gl/src/kiva_gl_constants.h @@ -0,0 +1,143 @@ +#ifndef KIVA_GL_CONSTANTS_H +#define KIVA_GL_CONSTANTS_H + +namespace kiva_gl +{ + + //----------------------------------------------------------------------- + // Line Cap Constants + //----------------------------------------------------------------------- + + enum line_cap_e + { + CAP_ROUND = 0, + CAP_BUTT = 1, + CAP_SQUARE = 2 + }; + + //----------------------------------------------------------------------- + // Line Join Constants + //----------------------------------------------------------------------- + + enum line_join_e + { + JOIN_ROUND = 0, + JOIN_BEVEL = 1, + JOIN_MITER = 2 + }; + + //----------------------------------------------------------------------- + // Path Drawing Mode Constants + // + // Path drawing modes for path drawing methods. + // The values are chosen so that bit flags can be checked in a later + // C version. + //----------------------------------------------------------------------- + + enum draw_mode_e + { + FILL = 1, + EOF_FILL = 2, + STROKE = 4, + FILL_STROKE = 5, + EOF_FILL_STROKE = 6 + }; + + //----------------------------------------------------------------------- + // The following enums are Agg-specific, and might not be applicable + // to other backends. + //----------------------------------------------------------------------- + + enum interpolation_e + { + nearest = 0, + bilinear = 1, + bicubic = 2, + spline16 = 3, + spline36 = 4, + sinc64 = 5, + sinc144 = 6, + sinc256 = 7, + blackman64 = 8, + blackman100 = 9, + blackman256 = 10 + }; + + enum pix_format_e + { + pix_format_undefined = 0, // By default. No conversions are applied + pix_format_gray8, // Simple 256 level grayscale + pix_format_rgb555, // 15 bit rgb. Depends on the byte ordering! + pix_format_rgb565, // 16 bit rgb. Depends on the byte ordering! + pix_format_rgb24, // R-G-B, one byte per color component + pix_format_bgr24, // B-G-R, native win32 BMP format. + pix_format_rgba32, // R-G-B-A, one byte per color component + pix_format_argb32, // A-R-G-B, native MAC format + pix_format_abgr32, // A-B-G-R, one byte per color component + pix_format_bgra32, // B-G-R-A, native win32 BMP format + + end_of_pix_formats + }; + + enum blend_mode_e + { + blend_normal, // pdf nrmal blending mode. + blend_copy, // overright destination with src ignoring any alpha setting. + /* + // these are copies from agg -- but not yet supported. + blend_clear, //----clear + blend_src, //----src + blend_dst, //----dst + blend_src_over, //----src_over + blend_dst_over, //----dst_over + blend_src_in, //----src_in + blend_dst_in, //----dst_in + blend_src_out, //----src_out + blend_dst_out, //----dst_out + blend_src_atop, //----src_atop + blend_dst_atop, //----dst_atop + blend_xor, //----xor + blend_plus, //----plus + blend_minus, //----minus + blend_multiply, //----multiply + blend_screen, //----screen + blend_overlay, //----overlay + blend_darken, //----darken + blend_lighten, //----lighten + blend_color_dodge, //----color_dodge + blend_color_burn, //----color_burn + blend_hard_light, //----hard_light + blend_soft_light, //----soft_light + blend_difference, //----difference + blend_exclusion, //----exclusion + blend_contrast, //----contrast + */ + end_of_e + }; + + enum marker_e + { + marker_square, + marker_diamond, + marker_circle, + marker_crossed_circle, + marker_semiellipse_left, + marker_semiellipse_right, + marker_semiellipse_up, + marker_semiellipse_down, + marker_triangle_left, + marker_triangle_right, + marker_triangle_up, + marker_triangle_down, + marker_four_rays, + marker_cross, + marker_x, + marker_dash, + marker_dot, + marker_pixel, + + end_of_markers + }; + +} +#endif diff --git a/kiva/gl/src/kiva_gl_dash_type.h b/kiva/gl/src/kiva_gl_dash_type.h new file mode 100644 index 000000000..317adc55f --- /dev/null +++ b/kiva/gl/src/kiva_gl_dash_type.h @@ -0,0 +1,54 @@ +#ifndef KIVA_GL_DASH_TYPE_H +#define KIVA_GL_DASH_TYPE_H + +#include + +namespace kiva_gl +{ + //----------------------------------------------------------------------- + // line dash type + //----------------------------------------------------------------------- + + class dash_type + { + public: + double phase; + std::vector pattern; + + // constructor + dash_type() + : phase(0) + , pattern(2, 0) + { + } + + // this forces even length of pattern + dash_type(double _phase, double* _pattern, int n) + : phase(_phase) + , pattern(n % 2 ? n+1 : n) + { + for (int i = 0; i < n; ++i) + { + pattern[i] = _pattern[i]; + } + + // for odd length patterns, use the first entry as the + // last gap size. (this is arbitrary) + if (n % 2) + { + pattern[n] = _pattern[0]; + } + } + + ~dash_type() {} + + bool is_solid() + { + return (pattern.size() == 2 && pattern[0] == 0.0); + } + + // TODO-PZW: define a copy constructor + }; +} + +#endif diff --git a/kiva/gl/src/kiva_gl_exceptions.h b/kiva/gl/src/kiva_gl_exceptions.h new file mode 100644 index 000000000..8ce6ab36f --- /dev/null +++ b/kiva/gl/src/kiva_gl_exceptions.h @@ -0,0 +1,16 @@ +#ifndef KIVA_GL_EXCEPTIONS_H +#define KIVA_GL_EXCEPTIONS_H + +namespace kiva_gl +{ + // exception codes used in graphics_context + enum { + not_implemented_error = 0, + ctm_rotation_error, + bad_clip_state_error, + even_odd_clip_error, + clipping_path_unsupported + }; +} + +#endif diff --git a/kiva/gl/src/kiva_gl_font_type.cpp b/kiva/gl/src/kiva_gl_font_type.cpp new file mode 100755 index 000000000..71cd5cae6 --- /dev/null +++ b/kiva/gl/src/kiva_gl_font_type.cpp @@ -0,0 +1,121 @@ +#include + +#include "kiva_gl_font_type.h" + +// In the python layer, the enthought.freetype library is used for font lookup. +// Since we can't use that, we emulate the functionality here. +#ifdef _WIN32 + const char* font_dirs[] = { + "c:/windows/fonts/", + "./", + "c:/winnt/fonts/", + "c:/windows/system32/fonts/", + }; +#elif defined SUNOS + const char* font_dirs[] = { + "/usr/openwin/lib/X11/fonts", + }; +#elif defined DARWIN + const char* font_dirs[] = { + "/Library/Fonts/", + }; +#else + const char* font_dirs[] = { + "./", + "/usr/lib/X11/fonts/", + "/usr/share/fonts/truetype/", + "/usr/share/fonts/msttcorefonts/", + "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType/", + "/usr/share/fonts/truetype/msttcorefonts/", +}; +#endif + +const char* freetype_suffixes[] = { ".ttf", ".pfa", ".pfb" }; + +// This really only for testing purposes. Font searching is superceded by the code borrowed from +// matplotlib, however, since that is in python, we can't load a font from C++ for C++ tests. +// Therefore this simple function is left in. +kiva_gl::font_type::font_type(std::string _name, int _size, int _family, + int _style, int _encoding, bool validate) +: name(_name) +, size(_size) +, family(_family) +, style(_style) +, encoding(_encoding) +, _is_loaded(false) +{ + std::string full_file_name; + if (validate) + { + if (this->name == "") + { + this->_is_loaded = false; + } + else + { + for (unsigned int d=0; d < sizeof(font_dirs) / sizeof(char*); d++) + { + for (unsigned int e=0; e < sizeof(freetype_suffixes) / sizeof(char*); e++) + { + full_file_name = font_dirs[d]; + full_file_name.append(this->name); + full_file_name.append(freetype_suffixes[e]); + FILE *f = fopen(full_file_name.c_str(), "rb"); + if (f != NULL) + { + fclose(f); + this->filename = full_file_name; + this->_is_loaded = true; + break; + } + } + } + } + this->filename = ""; + this->name = ""; + this->_is_loaded = false; + } + else + { + this->filename = this->name; + this->_is_loaded = true; + } +} + +kiva_gl::font_type::font_type(const kiva_gl::font_type &font) +: name(font.name) +, filename(font.filename) +, size(font.size) +, _is_loaded(font.is_loaded()) +{ + this->family = font.family; + this->style = font.style; +} + +kiva_gl::font_type& +kiva_gl::font_type::operator=(const kiva_gl::font_type& font) +{ + this->size = font.size; + this->family = font.family; + this->style = font.style; + this->encoding = font.encoding; + this->name = font.name; + this->filename = font.filename; + this->_is_loaded = font.is_loaded(); + return *this; +} + +int +kiva_gl::font_type::change_filename(std::string _filename) +{ + FILE *f = fopen(_filename.c_str(), "rb"); + if (f != NULL) + { + fclose(f); + this->filename = _filename; + this->_is_loaded = true; + return 1; + } + + return 0; +} diff --git a/kiva/gl/src/kiva_gl_font_type.h b/kiva/gl/src/kiva_gl_font_type.h new file mode 100644 index 000000000..60ce12d6f --- /dev/null +++ b/kiva/gl/src/kiva_gl_font_type.h @@ -0,0 +1,56 @@ +#ifndef KIVA_GL_FONT_TYPE_H +#define KIVA_GL_FONT_TYPE_H + +#include + +#ifdef _MSC_VER +// Turn off MSDEV warning about truncated long identifiers +#pragma warning(disable:4786) +#endif + +namespace kiva_gl +{ + class font_type + { + public: + std::string name; + std::string filename; + int size; + int family; + int style; + int encoding; + + // Constructors + + // Creates a font object. By default, searches the hardcoded + // font paths for a file named like the face name; to override + // this, set validate=false. + font_type(std::string _name="Arial", + int _size=12, + int _family=0, + int _style=0, + int _encoding=0, + bool validate=true); + + font_type(const font_type &font); + font_type &operator=(const font_type& font); + + int change_filename(std::string _filename); + + // Is the font loaded properly? + inline bool is_loaded() const { return _is_loaded; } + + private: + bool _is_loaded; + }; + + inline bool operator==(font_type &a, font_type &b) + { + return (a.size == b.size) && (a.name == b.name) && + (a.style == b.style) && (a.encoding == b.encoding) && + (a.family == b.family); + } + +} + +#endif diff --git a/kiva/agg/src/gl_graphics_context.cpp b/kiva/gl/src/kiva_gl_graphics_context.cpp similarity index 70% rename from kiva/agg/src/gl_graphics_context.cpp rename to kiva/gl/src/kiva_gl_graphics_context.cpp index c1a2a30f4..e63efbea9 100644 --- a/kiva/agg/src/gl_graphics_context.cpp +++ b/kiva/gl/src/kiva_gl_graphics_context.cpp @@ -1,18 +1,17 @@ - - - // #ifndef MULTI_DRAW_ELEMENTS // #define MULTI_DRAW_ELEMENTS glMultiDrawElements // #endif #include +#include +#include -#include "kiva_affine_helpers.h" -#include "kiva_exceptions.h" -#include "kiva_rect.h" -#include "gl_graphics_context.h" +#include "kiva_gl_affine_helpers.h" +#include "kiva_gl_exceptions.h" +#include "kiva_gl_rect.h" +#include "kiva_gl_graphics_context.h" -using namespace kiva; +using namespace kiva_gl; #ifndef CALLBACK #define CALLBACK @@ -23,20 +22,19 @@ using namespace kiva; #define EXPAND_COLOR(c) c->r, c->g, c->b, (c->a * this->state.alpha) -// This should be just double, but as long as we're using C++... -typedef agg24::path_storage::container_type::value_type VertexType; +typedef double VertexType; struct PointType { VertexType x,y,z; }; typedef std::vector PointListType; static void _submit_path_points(PointListType const & points, bool polygon, bool fill); static void CALLBACK _combine_callback(GLdouble coords[3], GLdouble *vert_data[4], - GLfloat weight[4], GLdouble **dataOut); + GLfloat weight[4], GLdouble **dataOut); static void CALLBACK _vertex_callback(GLvoid *vertex); gl_graphics_context::gl_graphics_context(int width, int height, - kiva::pix_format_e format) -: graphics_context_base(NULL, width, height, 1, kiva::nearest) + kiva_gl::pix_format_e format) +: graphics_context_base(kiva_gl::nearest) , m_width(width) , m_height(height) , m_gl_initialized(false) @@ -44,7 +42,6 @@ gl_graphics_context::gl_graphics_context(int width, int height, { } - gl_graphics_context::~gl_graphics_context() { if (m_gl_initialized) @@ -53,9 +50,27 @@ gl_graphics_context::~gl_graphics_context() } } -void gl_graphics_context::gl_init() +int +gl_graphics_context::width() +{ + return m_width; +} + +int +gl_graphics_context::height() +{ + return m_height; +} + +int +gl_graphics_context::stride() { + return 1; +} +void +gl_graphics_context::gl_init() +{ glViewport(0, 0, m_width, m_height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -80,27 +95,29 @@ void gl_graphics_context::gl_init() clip_to_rect(0, 0, m_width, m_height); } -void gl_graphics_context::gl_cleanup() +void +gl_graphics_context::gl_cleanup() { //glMatrixMode(GL_MODELVIEW); //glPopMatrix(); } - -kiva::pix_format_e gl_graphics_context::format() +kiva_gl::pix_format_e +gl_graphics_context::format() { - return m_pixfmt; + return m_pixfmt; } - -void gl_graphics_context::save_state() +void +gl_graphics_context::save_state() { graphics_context_base::save_state(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); } -void gl_graphics_context::restore_state() +void +gl_graphics_context::restore_state() { if (this->state_stack.size() == 0) { @@ -118,14 +135,14 @@ void gl_graphics_context::restore_state() { if (this->state.device_space_clip_rects.size() > 0) { - kiva::rect_list_type rects = disjoint_intersect(this->state.device_space_clip_rects); + kiva_gl::rect_list_type rects = disjoint_intersect(this->state.device_space_clip_rects); // XXX: Right now we don't support disjoint clip rects. To implement // this, we would probably want to use a mask or stencil, or just // re-render with each clip rect set as the scissor. // XXX: figure out better way to round out the floating-point // dimensions for kiva_rect than just casting to int(). - kiva::rect_iterator it = rects.begin(); + kiva_gl::rect_iterator it = rects.begin(); glScissor(int(it->x), int(it->y), int(it->w), int(it->h)); } } @@ -140,29 +157,34 @@ void gl_graphics_context::restore_state() } -void gl_graphics_context::begin_page() +void +gl_graphics_context::begin_page() { - glClearColor( 1.f, 1.f, 1.f, 0.f ); - glClear( GL_COLOR_BUFFER_BIT ); + glClearColor(1.f, 1.f, 1.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); } -void gl_graphics_context::clip() +void +gl_graphics_context::clip() { - throw kiva::not_implemented_error; + throw kiva_gl::not_implemented_error; } -void gl_graphics_context::even_odd_clip() +void +gl_graphics_context::even_odd_clip() { - throw kiva::not_implemented_error; + throw kiva_gl::not_implemented_error; } -void gl_graphics_context::clip_to_rect(double x, double y, double sx, double sy) +void +gl_graphics_context::clip_to_rect(double x, double y, double sx, double sy) { - kiva::rect_type tmp(x, y, sx, sy); + kiva_gl::rect_type tmp(x, y, sx, sy); clip_to_rect(tmp); } -void gl_graphics_context::clip_to_rect(kiva::rect_type &rect) +void +gl_graphics_context::clip_to_rect(kiva_gl::rect_type &rect) { this->path.remove_all(); if (!this->state.use_rect_clipping()) @@ -170,19 +192,19 @@ void gl_graphics_context::clip_to_rect(kiva::rect_type &rect) throw clipping_path_unsupported; } - kiva::rect_type device_rect(transform_clip_rectangle(rect)); + kiva_gl::rect_type device_rect(transform_clip_rectangle(rect)); if (this->state.device_space_clip_rects.size() == 1) { - kiva::rect_type old(this->state.device_space_clip_rects.back()); + kiva_gl::rect_type old(this->state.device_space_clip_rects.back()); this->state.device_space_clip_rects.pop_back(); - kiva::rect_type newrect(kiva::disjoint_intersect(old, device_rect)); + kiva_gl::rect_type newrect(kiva_gl::disjoint_intersect(old, device_rect)); if ((newrect.w < 0) || (newrect.h < 0)) { // new clip rectangle doesn't intersect anything, so we push on // an empty rect as the new clipping region. - glScissor(0,0,0,0); + glScissor(0, 0, 0, 0); //printf("NULL intersection area in clip_to_rect\n"); - this->state.device_space_clip_rects.push_back(kiva::rect_type(0, 0, -1, -1)); + this->state.device_space_clip_rects.push_back(kiva_gl::rect_type(0, 0, -1, -1)); } else { @@ -196,22 +218,22 @@ void gl_graphics_context::clip_to_rect(kiva::rect_type &rect) // we need to compute the intersection of the new rectangle with // the current set of clip rectangles. we assume that the existing // clip_rects are a disjoint set. - this->state.device_space_clip_rects = kiva::disjoint_intersect( + this->state.device_space_clip_rects = kiva_gl::disjoint_intersect( this->state.device_space_clip_rects, device_rect); if (this->state.device_space_clip_rects.size() == 0) { - glScissor(0,0,0,0); + glScissor(0, 0, 0, 0); //printf("NULL intersection area in clip_to_rect\n"); - this->state.device_space_clip_rects.push_back(kiva::rect_type(0, 0, -1, -1)); + this->state.device_space_clip_rects.push_back(kiva_gl::rect_type(0, 0, -1, -1)); } else { - kiva::rect_list_type rects = disjoint_intersect(this->state.device_space_clip_rects); + kiva_gl::rect_list_type rects = disjoint_intersect(this->state.device_space_clip_rects); // XXX: Right now we don't support disjoint clip rects. // (same problem as in restore_state()) - kiva::rect_iterator it = rects.begin(); + kiva_gl::rect_iterator it = rects.begin(); glScissor(int(it->x), int(it->y), int(it->w), int(it->h)); if (rects.size() > 1) { @@ -221,17 +243,20 @@ void gl_graphics_context::clip_to_rect(kiva::rect_type &rect) } } -void gl_graphics_context::clip_to_rects(double* new_rects, int Nrects) +void +gl_graphics_context::clip_to_rects(double* new_rects, int Nrects) { printf("Clip to rects() unsupported\n"); } -void gl_graphics_context::clip_to_rects(kiva::rect_list_type &rects) +void +gl_graphics_context::clip_to_rects(kiva_gl::rect_list_type &rects) { printf("Clip to rects() unsupported\n"); } -void gl_graphics_context::clear_clip_path() +void +gl_graphics_context::clear_clip_path() { // clear the existing clipping paths this->state.clipping_path.remove_all(); @@ -242,20 +267,20 @@ void gl_graphics_context::clear_clip_path() // store the new clipping rectangle back into the first // rectangle of the graphics state clipping rects. - this->state.device_space_clip_rects.push_back(kiva::rect_type(0, 0, m_width, m_height)); + this->state.device_space_clip_rects.push_back(kiva_gl::rect_type(0, 0, m_width, m_height)); } - // XXX: This is cut and paste from graphics_context.h; refactor into base // class. -kiva::rect_type gl_graphics_context::transform_clip_rectangle(const kiva::rect_type &rect) +kiva_gl::rect_type +gl_graphics_context::transform_clip_rectangle(const kiva_gl::rect_type &rect) { // This only works if the ctm doesn't have any rotation. // otherwise, we need to use a clipping path. Test for this. - agg24::trans_affine tmp(this->path.get_ctm()); - if ( !only_scale_and_translation(tmp)) + kiva_gl_agg::trans_affine tmp(this->path.get_ctm()); + if (!only_scale_and_translation(tmp)) { - throw kiva::ctm_rotation_error; + throw kiva_gl::ctm_rotation_error; } double x = rect.x; @@ -274,49 +299,54 @@ kiva::rect_type gl_graphics_context::transform_clip_rectangle(const kiva::rect_t // subtract 1 to account for agg (inclusive) vs. kiva (exclusive) clipping x2 = int(floor(x2+0.5))-1; y2 = int(floor(y2+0.5))-1; - //x2 = int(floor(x2+0.5)); - //y2 = int(floor(y2+0.5)); - return kiva::rect_type(x, y, x2-x, y2-y); + return kiva_gl::rect_type(x, y, x2-x, y2-y); } -int gl_graphics_context::get_num_clip_regions() +int +gl_graphics_context::get_num_clip_regions() { return this->state.device_space_clip_rects.size(); } -kiva::rect_type gl_graphics_context::get_clip_region(unsigned int i) +kiva_gl::rect_type +gl_graphics_context::get_clip_region(unsigned int i) { - throw kiva::not_implemented_error; + throw kiva_gl::not_implemented_error; } - -void gl_graphics_context::clear(agg24::rgba value) +void +gl_graphics_context::clear(kiva_gl_agg::rgba value) { glClearColor(float(value.r), float(value.g), float(value.b), float(value.a)); glClear(GL_COLOR_BUFFER_BIT); } -void gl_graphics_context::fill_path() +void +gl_graphics_context::fill_path() { draw_path(FILL); } -void gl_graphics_context::eof_fill_path() +void +gl_graphics_context::eof_fill_path() { draw_path(EOF_FILL); } -void gl_graphics_context::stroke_path() +void +gl_graphics_context::stroke_path() { draw_path(STROKE); } - -void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon, bool fill) +void +gl_graphics_context::gl_render_path(kiva_gl::compiled_path *path, bool polygon, bool fill) { if ((path == NULL) || (path->total_vertices() == 0)) + { return; + } unsigned command = 0; PointListType pointList; @@ -339,13 +369,12 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon // make space for points pointList.reserve(path->total_vertices()); - for (unsigned int i=0; i < path->total_vertices(); i++) + for (unsigned int i=0; i < path->total_vertices(); ++i) { command = path->vertex(i, &v.x, &v.y); - - switch (command & agg24::path_cmd_mask) + switch (command & kiva_gl_agg::path_cmd_mask) { - case agg24::path_cmd_line_to: + case kiva_gl_agg::path_cmd_line_to: if (!first_vertex_drawn) { pointList.push_back(v0); @@ -354,14 +383,14 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon pointList.push_back(v); break; - case agg24::path_cmd_end_poly: + case kiva_gl_agg::path_cmd_end_poly: // We shouldn't need to do anything because if this is a closed path // - //if (command & agg24::path_flags_close) - // glVertex2f(x0, y0); + //if (command & kiva_gl_agg::path_flags_close) + // glVertex2f(x0, y0); break; - case agg24::path_cmd_curve3: + case kiva_gl_agg::path_cmd_curve3: // FIXME: refactor! if (!first_vertex_drawn) { @@ -375,7 +404,7 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon c1y = (v.y + ccy + ccy) / 3.0; c2x = (c3x + ccx + ccx) / 3.0; c2y = (c3y + ccy + ccy) / 3.0; - for (j=1; j<=_Npoints; j++) + for (j=1; j<=_Npoints; ++j) { t = ((VertexType)j) / _Npoints; t2 = t*t; @@ -389,7 +418,7 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon } break; - case agg24::path_cmd_curve4: + case kiva_gl_agg::path_cmd_curve4: if (!first_vertex_drawn) { pointList.push_back(v0); @@ -402,7 +431,7 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon path->vertex(i+1, &c2x, &c2y); path->vertex(i+2, &c3x, &c3y); i += 2; - for (j=1; j<=_Npoints; j++) + for (j=1; j<=_Npoints; ++j) { t = ((VertexType)j) / _Npoints; t2 = t*t; @@ -417,7 +446,7 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon break; // The following commands are ignored. - case agg24::path_cmd_move_to: + case kiva_gl_agg::path_cmd_move_to: if (!pointList.empty()) { // do a full glBegin/glEnd sequence for the points in the buffer @@ -430,18 +459,18 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon first_vertex_drawn = false; break; - case agg24::path_cmd_ubspline: + case kiva_gl_agg::path_cmd_ubspline: break; // XXX: This case number is already used?? - //case agg24::path_cmd_mask: - // break; + //case kiva_gl_agg::path_cmd_mask: + // break; // Unsupported // XXX: We need to have better error handling/reporting from the C++ // layer up to the Python layer. - case agg24::path_cmd_catrom: - case agg24::path_cmd_curveN: + case kiva_gl_agg::path_cmd_catrom: + case kiva_gl_agg::path_cmd_curveN: break; } @@ -449,24 +478,27 @@ void gl_graphics_context::gl_render_path(kiva::compiled_path *path, bool polygon // submit the points if (!pointList.empty()) + { _submit_path_points(pointList, polygon, fill); + } } -void gl_graphics_context::gl_render_points(double** points, bool polygon, - bool fill, kiva::draw_mode_e mode) +void +gl_graphics_context::gl_render_points(double** points, bool polygon, + bool fill, kiva_gl::draw_mode_e mode) { } - -void gl_graphics_context::draw_path(draw_mode_e mode) +void +gl_graphics_context::draw_path(draw_mode_e mode) { // XXX: This is a direct transcription from basecore2d. The algorithm // and approach can probably be improved tremendously for OpenGL. - agg24::rgba *line_color = &this->state.line_color; - agg24::rgba *fill_color = &this->state.fill_color; + kiva_gl_agg::rgba *line_color = &this->state.line_color; + kiva_gl_agg::rgba *fill_color = &this->state.fill_color; -// CNP + // CNP if (this->state.should_antialias) { glEnable(GL_LINE_SMOOTH); @@ -479,7 +511,7 @@ void gl_graphics_context::draw_path(draw_mode_e mode) } // Check to see if we have closed polygons - typedef agg24::path_storage::container_type::value_type VertexType; + typedef kiva_gl_agg::path_storage::container_type::value_type VertexType; unsigned numvertices = this->path.total_vertices(); bool polygon = false; if (numvertices > 1) @@ -490,19 +522,21 @@ void gl_graphics_context::draw_path(draw_mode_e mode) // Go backwards from the last vertex until we find an actual line_to // or curve3 or curve4 comand. - for (int i=numvertices-1; i>0; i--) + for (int i=numvertices-1; i>0; --i) { unsigned cmd = this->path.vertex(i, &xf, &yf); - if (((cmd & agg24::path_cmd_mask) == agg24::path_cmd_curve3) || - ((cmd & agg24::path_cmd_mask) == agg24::path_cmd_curve4) || - ((cmd & agg24::path_cmd_mask) == agg24::path_cmd_line_to)) + if (((cmd & kiva_gl_agg::path_cmd_mask) == kiva_gl_agg::path_cmd_curve3) || + ((cmd & kiva_gl_agg::path_cmd_mask) == kiva_gl_agg::path_cmd_curve4) || + ((cmd & kiva_gl_agg::path_cmd_mask) == kiva_gl_agg::path_cmd_line_to)) { if ((x0 == xf) && (y0 == yf)) + { polygon = true; + } break; } - if ((cmd & agg24::path_cmd_mask) == agg24::path_cmd_end_poly) + if ((cmd & kiva_gl_agg::path_cmd_mask) == kiva_gl_agg::path_cmd_end_poly) { polygon = true; break; @@ -514,7 +548,6 @@ void gl_graphics_context::draw_path(draw_mode_e mode) if (mode != STROKE) { // device_update_fill_state - //glColor4f(fill_color->r, fill_color->g, fill_color->b, fill_color->a); glColor4f(EXPAND_COLOR(fill_color)); // call gl_render_path() @@ -526,7 +559,6 @@ void gl_graphics_context::draw_path(draw_mode_e mode) { // CNP // device_update_line_state - //glColor4f(line_color->r, line_color->g, line_color->b, line_color->a); glColor4f(EXPAND_COLOR(line_color)); glLineWidth(this->state.line_width); @@ -545,11 +577,11 @@ void gl_graphics_context::draw_path(draw_mode_e mode) this->path.remove_all(); } - -void gl_graphics_context::draw_rect(double rect[4], draw_mode_e mode) +void +gl_graphics_context::draw_rect(double rect[4], draw_mode_e mode) { - agg24::rgba *line_color = &this->state.line_color; - agg24::rgba *fill_color = &this->state.fill_color; + kiva_gl_agg::rgba *line_color = &this->state.line_color; + kiva_gl_agg::rgba *fill_color = &this->state.fill_color; // CNP if (this->state.should_antialias) @@ -598,75 +630,91 @@ void gl_graphics_context::draw_rect(double rect[4], draw_mode_e mode) this->path.remove_all(); } - -int gl_graphics_context::draw_marker_at_points(double *pts, int Npts, - int size, agg24::marker_e type) +int +gl_graphics_context::draw_marker_at_points(double *pts, int Npts, + int size, kiva_gl::marker_e type) { - agg24::rgba *line_color = &this->state.line_color; - agg24::rgba *fill_color = &this->state.fill_color; + kiva_gl_agg::rgba *line_color = &this->state.line_color; + kiva_gl_agg::rgba *fill_color = &this->state.fill_color; bool do_fill = (fill_color->a != 0); bool do_stroke = ((line_color->a != 0) && (this->state.line_width > 0.0)); if (do_stroke) + { glLineWidth(this->state.line_width); + } // Get the current origin double x0=0.0, y0=0.0; this->path.get_ctm().translation(&x0, &y0); - kiva::draw_mode_e draw_mode = FILL; + kiva_gl::draw_mode_e draw_mode = FILL; if (do_fill & !do_stroke) + { draw_mode = FILL; + } else if (do_stroke & !do_fill) + { draw_mode = STROKE; + } else if (do_fill & do_stroke) + { draw_mode = FILL_STROKE; - + } GLuint fill_list, stroke_list; bool list_created = false; switch (type) { - // Simple paths that only need to be stroked - case agg24::marker_x: - draw_x_marker(pts, Npts, size, draw_mode, x0, y0); break; - case agg24::marker_cross: - draw_cross(pts, Npts, size, draw_mode, x0, y0); break; - case agg24::marker_dot: - draw_dot(pts, Npts, size, draw_mode, x0, y0); break; - case agg24::marker_pixel: - draw_pixel(pts, Npts, size, draw_mode, x0, y0); break; + // Simple paths that only need to be stroked + case kiva_gl::marker_x: + draw_x_marker(pts, Npts, size, draw_mode, x0, y0); + break; + case kiva_gl::marker_cross: + draw_cross(pts, Npts, size, draw_mode, x0, y0); + break; + + case kiva_gl::marker_dot: + draw_dot(pts, Npts, size, draw_mode, x0, y0); + break; + + case kiva_gl::marker_pixel: + draw_pixel(pts, Npts, size, draw_mode, x0, y0); + break; // Paths that need to be filled and stroked // There are experimental approaches taken for drawing squares and // diamonds, so they are in their own block here. There's no reason // why they cannot be treated in the same way as the circle and // triangle markers. - case agg24::marker_square: - draw_square(pts, Npts, size, draw_mode, x0, y0); break; - case agg24::marker_diamond: - draw_diamond(pts, Npts, size, draw_mode, x0, y0); break; + case kiva_gl::marker_square: + draw_square(pts, Npts, size, draw_mode, x0, y0); + break; + case kiva_gl::marker_diamond: + draw_diamond(pts, Npts, size, draw_mode, x0, y0); + break; - case agg24::marker_crossed_circle: - draw_crossed_circle(pts, Npts, size, draw_mode, x0, y0); break; + case kiva_gl::marker_crossed_circle: + draw_crossed_circle(pts, Npts, size, draw_mode, x0, y0); + break; - case agg24::marker_circle: - fill_list = make_marker_lists(&kiva::gl_graphics_context::circle_path_func, draw_mode, size); + case kiva_gl::marker_circle: + fill_list = make_marker_lists(&kiva_gl::gl_graphics_context::circle_path_func, draw_mode, size); list_created = true; // Fall through to next case - case agg24::marker_triangle_up: + case kiva_gl::marker_triangle_up: if (!list_created) { - fill_list = make_marker_lists(&kiva::gl_graphics_context::triangle_up_func, draw_mode, size); + fill_list = make_marker_lists(&kiva_gl::gl_graphics_context::triangle_up_func, draw_mode, size); list_created = true; } // Fall through to next case - case agg24::marker_triangle_down: + case kiva_gl::marker_triangle_down: if (!list_created) { - fill_list = make_marker_lists(&kiva::gl_graphics_context::triangle_down_func, draw_mode, size); + fill_list = make_marker_lists(&kiva_gl::gl_graphics_context::triangle_down_func, draw_mode, size); list_created = true; } stroke_list = fill_list + 1; @@ -681,55 +729,54 @@ int gl_graphics_context::draw_marker_at_points(double *pts, int Npts, return 1; } -void gl_graphics_context::draw_path_at_points(double *pts, int Npts, - kiva::compiled_path &marker, - draw_mode_e mode) +void +gl_graphics_context::draw_path_at_points(double *pts, int Npts, + kiva_gl::compiled_path &marker, + draw_mode_e mode) { return; } - -void gl_graphics_context::draw_glyphs(kiva::graphics_context_base* img, - double tx, double ty) -{ -} - -int gl_graphics_context::draw_image(kiva::graphics_context_base* img, - double rect[4], bool force_copy) +int +gl_graphics_context::draw_image(kiva_gl::graphics_context_base* img, + double rect[4], bool force_copy) { return 0; } -int gl_graphics_context::draw_image(kiva::graphics_context_base* img) +int gl_graphics_context::draw_image(kiva_gl::graphics_context_base* img) { return 0; } - //--------------------------------------------------------------------------- // Marker drawing methods //--------------------------------------------------------------------------- -void gl_graphics_context::draw_display_list_at_pts(GLuint list, double *pts, int Npts, - kiva::draw_mode_e mode, double x0, double y0) +void +gl_graphics_context::draw_display_list_at_pts(GLuint list, double *pts, int Npts, + kiva_gl::draw_mode_e mode, + double x0, double y0) { draw_display_list_at_pts(list, list, pts, Npts, mode, x0, y0); } -void gl_graphics_context::draw_display_list_at_pts(GLuint fill_list, GLuint stroke_list, - double *pts, int Npts, - kiva::draw_mode_e mode, double x0, double y0) +void +gl_graphics_context::draw_display_list_at_pts(GLuint fill_list, GLuint stroke_list, + double *pts, int Npts, + kiva_gl::draw_mode_e mode, + double x0, double y0) { - agg24::rgba *colors[2] = { &this->state.fill_color, &this->state.line_color }; + kiva_gl_agg::rgba *colors[2] = { &this->state.fill_color, &this->state.line_color }; GLuint lists[2] = { fill_list, stroke_list }; float x = 0.f, y = 0.f; - for (int pass=0; pass < 2; pass++) + for (int pass=0; pass < 2; ++pass) { if (((pass == 0) && ((mode == FILL) || (mode == FILL_STROKE))) || ((pass == 1) && ((mode == STROKE) || (mode == FILL_STROKE)))) { glColor4f(EXPAND_COLOR(colors[pass])); - for (int i=0; i < Npts; i++) + for (int i=0; i < Npts; ++i) { x = pts[i*2] + x0; y = pts[i*2 + 1] + y0; @@ -744,7 +791,7 @@ void gl_graphics_context::draw_display_list_at_pts(GLuint fill_list, GLuint stro if ((mode == FILL) || (mode == FILL_STROKE)) { glColor4f(EXPAND_COLOR(fill_color)); - for (int i=0; i < Npts; i++) + for (int i=0; i < Npts; ++i) { x = pts[i*2] + x0; y = pts[i*2 + 1] + y0; @@ -756,7 +803,7 @@ void gl_graphics_context::draw_display_list_at_pts(GLuint fill_list, GLuint stro if ((mode == STROKE) || (mode == FILL_STROKE)) { glColor4f(EXPAND_COLOR(line_color)); - for (int i=0; i < Npts; i++) + for (int i=0; i < Npts; ++i) { x = pts[i*2] + x0; y = pts[i*2 + 1] + y0; @@ -768,12 +815,14 @@ void gl_graphics_context::draw_display_list_at_pts(GLuint fill_list, GLuint stro #endif } -GLuint gl_graphics_context::make_marker_lists(PathDefinitionFunc path_func, - kiva::draw_mode_e mode, int size) +GLuint +gl_graphics_context::make_marker_lists(PathDefinitionFunc path_func, + kiva_gl::draw_mode_e mode, + int size) { GLuint fill_list = glGenLists(2); GLuint stroke_list = fill_list + 1; - for (int dummy=0; dummy < 2; dummy++) + for (int dummy=0; dummy < 2; ++dummy) { if (dummy == 0) { @@ -793,11 +842,13 @@ GLuint gl_graphics_context::make_marker_lists(PathDefinitionFunc path_func, return fill_list; } -void gl_graphics_context::draw_square(double *pts, int Npts, int size, - kiva::draw_mode_e mode, double x0, double y0) +void +gl_graphics_context::draw_square(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, + double x0, double y0) { - agg24::rgba *line_color = &this->state.line_color; - agg24::rgba *fill_color = &this->state.fill_color; + kiva_gl_agg::rgba *line_color = &this->state.line_color; + kiva_gl_agg::rgba *fill_color = &this->state.fill_color; // We build up a VertexArray of the vertices of all the squares. // We then use glDrawElements with GL_QUADS or GL_LINE_LOOP to fill @@ -810,7 +861,7 @@ void gl_graphics_context::draw_square(double *pts, int Npts, int size, glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_DOUBLE, 0, vertices); - for (int i=0; istate.line_color; - agg24::rgba *fill_color = &this->state.fill_color; + kiva_gl_agg::rgba *line_color = &this->state.line_color; + kiva_gl_agg::rgba *fill_color = &this->state.fill_color; // Each marker consists of four vertices in this order: left, top, right, bottom. GLdouble *vertices = new GLdouble[Npts * 4 * 2]; @@ -898,7 +952,7 @@ void gl_graphics_context::draw_diamond(double *pts, int Npts, int size, glVertexPointer(2, GL_DOUBLE, 0, vertices); float s = size / 2.0; - for (int i=0; istate.line_color; + kiva_gl_agg::rgba *line_color = &this->state.line_color; glColor4f(EXPAND_COLOR(line_color)); glBegin(GL_POINTS); - for (int i=0; i < Npts; i++) + for (int i=0; i < Npts; ++i) { glVertex2f(pts[i*2] + x0, pts[i*2+1] + y0); } glEnd(); } - -void _submit_path_points(PointListType const & points, bool polygon, bool fill) +void +_submit_path_points(PointListType const & points, bool polygon, bool fill) { // Uncomment this when we turn the glPolygonMode calls back on (below) //glPushAttrib(GL_POLYGON_BIT); @@ -1102,7 +1172,9 @@ void _submit_path_points(PointListType const & points, bool polygon, bool fill) //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (int i=0; i < points.size(); ++i) + { glVertex2dv((VertexType *)&points[i]); + } glEnd(); } @@ -1112,7 +1184,9 @@ void _submit_path_points(PointListType const & points, bool polygon, bool fill) glBegin(GL_LINE_STRIP); for (int i=0; i < points.size(); ++i) + { glVertex2dv((VertexType *)&points[i]); + } glEnd(); } @@ -1120,9 +1194,10 @@ void _submit_path_points(PointListType const & points, bool polygon, bool fill) //glPopAttrib(); } - -void CALLBACK _combine_callback(GLdouble coords[3], GLdouble *vert_data[4], - GLfloat weight[4], GLdouble **dataOut) +void +CALLBACK +_combine_callback(GLdouble coords[3], GLdouble *vert_data[4], + GLfloat weight[4], GLdouble **dataOut) { GLdouble *vertex = (GLdouble *)malloc(3 * sizeof(GLdouble)); vertex[0] = coords[0]; @@ -1132,8 +1207,9 @@ void CALLBACK _combine_callback(GLdouble coords[3], GLdouble *vert_data[4], *dataOut = vertex; } - -void CALLBACK _vertex_callback(GLvoid *vertex) +void +CALLBACK +_vertex_callback(GLvoid *vertex) { GLdouble *ptr = (GLdouble *)vertex; glVertex3dv(ptr); diff --git a/kiva/gl/src/kiva_gl_graphics_context.h b/kiva/gl/src/kiva_gl_graphics_context.h new file mode 100644 index 000000000..f13938ff4 --- /dev/null +++ b/kiva/gl/src/kiva_gl_graphics_context.h @@ -0,0 +1,155 @@ +#ifndef KIVA_GL_GRAPHICS_CONTEXT_H +#define KIVA_GL_GRAPHICS_CONTEXT_H + +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif + +#ifdef __DARWIN__ + #include + #include +#else + #ifdef _MSC_VER + #include + #endif + #include + #include + + // The following mechanism is necessary in order to use MultiDrawElements + // on Windows with mingw. + // 11/24/2010: glMultiDrawElements is not being used right now in the GL + // backend, so comment this out for the time being, especially since it + // causes build problems with 64-bit mingw. + //#ifdef __MINGW32__ + // #define GL_GLEXT_PROTOTYPES 1 + // #include + // #define MULTI_DRAW_ELEMENTS glMultiDrawElementsEXT + //#endif +#endif + +#include "agg_basics.h" + +#include "kiva_gl_compiled_path.h" +#include "kiva_gl_graphics_context_base.h" + + +namespace kiva_gl +{ + // This function pointer is used by various draw_marker functions + class gl_graphics_context; + typedef void(gl_graphics_context::* PathDefinitionFunc)(int); + + class gl_graphics_context : public graphics_context_base + { + public: + + gl_graphics_context(int width, int height, + kiva_gl::pix_format_e format=kiva_gl::pix_format_rgb24); + ~gl_graphics_context(); + + int width(); + int height(); + int stride(); + + //--------------------------------------------------------------- + // GL-specific methods + //--------------------------------------------------------------- + void gl_init(); + void gl_cleanup(); + void begin_page(); + void gl_render_path(kiva_gl::compiled_path *path, bool polygon=false, bool fill=false); + void gl_render_points(double** points, bool polygon, bool fill, + kiva_gl::draw_mode_e mode = FILL); + + //--------------------------------------------------------------- + // GraphicsContextBase interface + //--------------------------------------------------------------- + + kiva_gl::pix_format_e format(); + void save_state(); + void restore_state(); + + //--------------------------------------------------------------- + // Clipping path manipulation + //--------------------------------------------------------------- + void clip(); + void even_odd_clip(); + void clip_to_rect(double x, double y, double sx, double sy); + void clip_to_rect(kiva_gl::rect_type &rect); + void clip_to_rects(double* new_rects, int Nrects); + void clip_to_rects(kiva_gl::rect_list_type &rects); + kiva_gl::rect_type transform_clip_rectangle(const kiva_gl::rect_type &rect); + void clear_clip_path(); + + int get_num_clip_regions(); + kiva_gl::rect_type get_clip_region(unsigned int i); + + //--------------------------------------------------------------- + // Painting paths (drawing and filling contours) + //--------------------------------------------------------------- + void clear(kiva_gl_agg::rgba value=kiva_gl_agg::rgba(1, 1, 1, 1)); + void fill_path(); + void eof_fill_path(); + void stroke_path(); + // empty function; for some reason this is abstract in the base class + inline void _stroke_path() {} + + void draw_path(draw_mode_e mode=FILL_STROKE); + void draw_rect(double rect[4], draw_mode_e mode=FILL_STROKE); + + int draw_marker_at_points(double* pts,int Npts,int size, + kiva_gl::marker_e type=kiva_gl::marker_square); + + void draw_path_at_points(double* pts,int Npts, + kiva_gl::compiled_path& marker, + draw_mode_e mode); + + int draw_image(kiva_gl::graphics_context_base* img, double rect[4], bool force_copy=false); + int draw_image(kiva_gl::graphics_context_base* img); + + protected: + + void draw_display_list_at_pts(GLuint list, double *pts, int Npts, + kiva_gl::draw_mode_e mode, + double x0, double y0); + void draw_display_list_at_pts(GLuint fill_list, GLuint stroke_list, + double *pts, int Npts, + kiva_gl::draw_mode_e mode, + double x0, double y0); + + // Given a path function, returns two OpenGL display lists representing + // the list to fill and the list to stroke. The caller is responsible + // for calling glDeleteLists on the two. + // Only the list name of the first list (fill list) will be returned; + // the stroke list can be accessed by just adding 1. + GLuint make_marker_lists(kiva_gl::PathDefinitionFunc path_func, + kiva_gl::draw_mode_e mode, int size); + + void circle_path_func(int size); + void triangle_up_func(int size); + void triangle_down_func(int size); + + void draw_square(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, double x0, double y0); + void draw_diamond(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, double x0, double y0); + void draw_crossed_circle(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, double x0, double y0); + void draw_x_marker(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, double x0, double y0); + void draw_cross(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, double x0, double y0); + void draw_dot(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, double x0, double y0); + void draw_pixel(double *pts, int Npts, int size, + kiva_gl::draw_mode_e mode, double x0, double y0); + + private: + int m_width; + int m_height; + bool m_gl_initialized; + kiva_gl::pix_format_e m_pixfmt; + }; +} + +#endif /* KIVA_GL_GRAPHICS_CONTEXT_H */ diff --git a/kiva/gl/src/kiva_gl_graphics_context_base.cpp b/kiva/gl/src/kiva_gl_graphics_context_base.cpp new file mode 100755 index 000000000..9e8e82bc5 --- /dev/null +++ b/kiva/gl/src/kiva_gl_graphics_context_base.cpp @@ -0,0 +1,426 @@ + +#include + +#include "agg_path_storage.h" + +#include "kiva_gl_exceptions.h" +#include "kiva_gl_graphics_context_base.h" + +using namespace kiva_gl; + +graphics_context_base::graphics_context_base(interpolation_e interp) +: _image_interpolation(interp) +{ +} + +graphics_context_base::~graphics_context_base() +{ +} + +int +graphics_context_base::width() +{ + return 0; +} + +int +graphics_context_base::height() +{ + return 0; +} + +int +graphics_context_base::stride() +{ + return 1; +} + +int +graphics_context_base::bottom_up() +{ + return (this->stride() > 0 ? 0 : 1); +} + +kiva_gl::interpolation_e +graphics_context_base::get_image_interpolation() +{ + return this->_image_interpolation; +} + +void +graphics_context_base::set_image_interpolation(kiva_gl::interpolation_e interpolation) +{ + this->_image_interpolation = interpolation; +} + +//--------------------------------------------------------------- +// set graphics_state values +//--------------------------------------------------------------- + +void +graphics_context_base::set_stroke_color(kiva_gl_agg::rgba& value) +{ + this->state.line_color = value; +} + +kiva_gl_agg::rgba& +graphics_context_base::get_stroke_color() +{ + return this->state.line_color; +} + +void +graphics_context_base::set_line_width(double value) +{ + this->state.line_width = value; +} + +void +graphics_context_base::set_line_join(kiva_gl::line_join_e value) +{ + this->state.line_join = value; +} + +void +graphics_context_base::set_line_cap(kiva_gl::line_cap_e value) +{ + this->state.line_cap = value; +} + +void +graphics_context_base::set_line_dash(double* pattern, int n, double phase) +{ + this->state.line_dash = kiva_gl::dash_type(phase, pattern, n); +} + +void +graphics_context_base::set_blend_mode(kiva_gl::blend_mode_e value) +{ + this->state.blend_mode = value; +} + +kiva_gl::blend_mode_e +graphics_context_base::get_blend_mode() +{ + return this->state.blend_mode; +} + +void +graphics_context_base::set_fill_color(kiva_gl_agg::rgba& value) +{ + this->state.fill_color = value; +} + +kiva_gl_agg::rgba& +graphics_context_base::get_fill_color() +{ + return this->state.fill_color; +} + +void +graphics_context_base::set_alpha(double value) +{ + // alpha should be between 0 and 1, so clamp: + if (value < 0.0) + { + value = 0.0; + } + else if (value > 1.0) + { + value = 1.0; + } + this->state.alpha = value; +} + +double +graphics_context_base::get_alpha() +{ + return this->state.alpha; +} + +void +graphics_context_base::set_antialias(int value) +{ + this->state.should_antialias = value; +} + +int +graphics_context_base::get_antialias() +{ + return this->state.should_antialias; +} + +void +graphics_context_base::set_miter_limit(double value) +{ + this->state.miter_limit = value; +} + +void +graphics_context_base::set_flatness(double value) +{ + this->state.flatness = value; +} + +//--------------------------------------------------------------- +// save/restore graphics state +//--------------------------------------------------------------- + +void +graphics_context_base::save_state() +{ + this->state_stack.push(this->state); + this->path.save_ctm(); +} + +//--------------------------------------------------------------- +// coordinate transform matrix transforms +//--------------------------------------------------------------- + +void +graphics_context_base::translate_ctm(double x, double y) +{ + this->path.translate_ctm(x, y); +} + +void +graphics_context_base::rotate_ctm(double angle) +{ + this->path.rotate_ctm(angle); +} + +void +graphics_context_base::scale_ctm(double sx, double sy) +{ + this->path.scale_ctm(sx, sy); +} + +void +graphics_context_base::concat_ctm(kiva_gl_agg::trans_affine& m) +{ + this->path.concat_ctm(m); +} + +void +graphics_context_base::set_ctm(kiva_gl_agg::trans_affine& m) +{ + this->path.set_ctm(m); +} + +kiva_gl_agg::trans_affine +graphics_context_base::get_ctm() +{ + return this->path.get_ctm(); +} + +//--------------------------------------------------------------- +// Sending drawing data to a device +//--------------------------------------------------------------- + +void +graphics_context_base::flush() +{ + // TODO-PZW: clarify this and other "not sure if anything is needed" functions + // not sure if anything is needed. +} + +void +graphics_context_base::synchronize() +{ + // not sure if anything is needed. +} + +//--------------------------------------------------------------- +// Page Definitions +//--------------------------------------------------------------- + +void +graphics_context_base::begin_page() +{ + // not sure if anything is needed. +} + +void +graphics_context_base::end_page() +{ + // not sure if anything is needed. +} + +//--------------------------------------------------------------- +// Path operations +//--------------------------------------------------------------- + +void +graphics_context_base::begin_path() +{ + this->path.begin_path(); +} + +void +graphics_context_base::move_to(double x, double y) +{ + this->path.move_to(x, y); +} + +void +graphics_context_base::line_to( double x, double y) +{ + this->path.line_to(x, y); +} + +void +graphics_context_base::curve_to(double cpx1, double cpy1, + double cpx2, double cpy2, + double x, double y) +{ + this->path.curve_to(cpx1, cpy1, cpx2, cpy2, x, y); +} + +void +graphics_context_base::quad_curve_to(double cpx, double cpy, double x, double y) +{ + this->path.quad_curve_to(cpx, cpy, x, y); +} + +void +graphics_context_base::arc(double x, double y, double radius, + double start_angle, double end_angle, + bool cw) +{ + this->path.arc(x, y, radius, start_angle, end_angle, cw); +} + +void +graphics_context_base::arc_to(double x1, double y1, double x2, double y2, + double radius) +{ + this->path.arc_to(x1, y1, x2, y2, radius); +} + +void +graphics_context_base::close_path() +{ + this->path.close_polygon(); +} + +void +graphics_context_base::add_path(kiva_gl::compiled_path& other_path) +{ + this->path.add_path(other_path); +} + +void +graphics_context_base::lines(double* pts, int Npts) +{ + this->path.lines(pts, Npts); +} + +void +graphics_context_base::line_set(double* start, int Nstart, double* end, int Nend) +{ + this->path.line_set(start, Nstart, end, Nend); +} + +void +graphics_context_base::rect(double x, double y, double sx, double sy) +{ + this->path.rect(x, y, sx, sy); +} + +void +graphics_context_base::rect(kiva_gl::rect_type &rect) +{ + this->path.rect(rect); +} + +void +graphics_context_base::rects(double* all_rects, int Nrects) +{ + this->path.rects(all_rects, Nrects); +} + +void +graphics_context_base::rects(kiva_gl::rect_list_type &rectlist) +{ + this->path.rects(rectlist); +} + +kiva_gl::compiled_path +graphics_context_base::_get_path() +{ + return this->path; +} + +kiva_gl::rect_type +graphics_context_base::_get_path_bounds() +{ + double xmin = 0., ymin = 0., xmax = 0., ymax = 0.; + double x = 0., y = 0.; + + for (unsigned i = 0; i < this->path.total_vertices(); ++i) + { + this->path.vertex(i, &x, &y); + + if (i == 0) + { + xmin = xmax = x; + ymin = ymax = y; + continue; + } + + if (x < xmin) + { + xmin = x; + } + else if (xmax < x) + { + xmax = x; + } + if (y < ymin) + { + ymin = y; + } + else if (ymax < y) + { + ymax = y; + } + } + + return kiva_gl::rect_type(xmin, ymin, xmax-xmin, ymax-ymin); +} + +kiva_gl_agg::path_storage +graphics_context_base::boundary_path(kiva_gl_agg::trans_affine& affine_mtx) +{ + // Return the path that outlines the image in device space + // This is used in _draw to specify the device area + // that should be rendered. + kiva_gl_agg::path_storage clip_path; + double p0x = 0; + double p0y = 0; + double p1x = this->width(); + double p1y = 0; + double p2x = this->width(); + double p2y = this->height(); + double p3x = 0; + double p3y = this->height(); + + affine_mtx.transform(&p0x, &p0y); + affine_mtx.transform(&p1x, &p1y); + affine_mtx.transform(&p2x, &p2y); + affine_mtx.transform(&p3x, &p3y); + + clip_path.move_to(p0x, p0y); + clip_path.line_to(p1x, p1y); + clip_path.line_to(p2x, p2y); + clip_path.line_to(p3x, p3y); + clip_path.close_polygon(); + return clip_path; +} + +int +graphics_context_base::draw_image(kiva_gl::graphics_context_base* img) +{ + double tmp[] = {0, 0, img->width(), img->height()}; + return this->draw_image(img, tmp); +} diff --git a/kiva/gl/src/kiva_gl_graphics_context_base.h b/kiva/gl/src/kiva_gl_graphics_context_base.h new file mode 100644 index 000000000..e8ab0a01e --- /dev/null +++ b/kiva/gl/src/kiva_gl_graphics_context_base.h @@ -0,0 +1,239 @@ +#ifndef KIVA_GL_GRAPHICS_CONTEXT_BASE_H +#define KIVA_GL_GRAPHICS_CONTEXT_BASE_H + +#include +#include + +#include "agg_basics.h" +#include "agg_color_rgba.h" + +#include "kiva_gl_constants.h" +#include "kiva_gl_rect.h" +#include "kiva_gl_graphics_state.h" +#include "kiva_gl_affine_helpers.h" + +namespace kiva_gl +{ + class graphics_context_base + { + public: + // The current path. This also includes the ctm. + kiva_gl::compiled_path path; + + kiva_gl::graphics_state state; + std::stack state_stack; + + // fix me: Not sure this should be here, but, putting it here completely + // unifies images and graphics contexts. + // (TODO-PZW: revisit this) + kiva_gl::interpolation_e _image_interpolation; + + graphics_context_base(kiva_gl::interpolation_e interp); + virtual ~graphics_context_base(); + + int width(); + int height(); + int stride(); + int bottom_up(); + + virtual kiva_gl::pix_format_e format() = 0; + + kiva_gl::interpolation_e get_image_interpolation(); + void set_image_interpolation(interpolation_e interpolation); + + //--------------------------------------------------------------- + // set graphics_state values + //--------------------------------------------------------------- + + void set_stroke_color(kiva_gl_agg::rgba& value); + kiva_gl_agg::rgba& get_stroke_color(); + + // TODO-PZW: do we need corresponding get() functions for + // all of the following? + + void set_line_width(double value); + void set_line_join(line_join_e value); + void set_line_cap(line_cap_e value); + void set_line_dash(double* pattern, int n, double phase=0); + + // fix me: Blend mode is *barely* supported and + // probably abused (my copy setting). + void set_blend_mode(blend_mode_e value); + kiva_gl::blend_mode_e get_blend_mode(); + + void set_fill_color(kiva_gl_agg::rgba& value); + + // need get method for freetype renderer. + // should I return a reference?? + kiva_gl_agg::rgba& get_fill_color(); + + // need get method for freetype renderer. + // fix me: Is the get method still needed? + void set_alpha(double value); + double get_alpha(); + + // need get method for freetype renderer. + // fix me: Is the get method still needed? + void set_antialias(int value); + int get_antialias(); + + // TODO-PZW: get() functions needed? + void set_miter_limit(double value); + void set_flatness(double value); + + //--------------------------------------------------------------- + // save/restore graphics state + //--------------------------------------------------------------- + + void save_state(); + virtual void restore_state() = 0; + + //--------------------------------------------------------------- + // coordinate transform matrix transforms + //--------------------------------------------------------------- + + void translate_ctm(double x, double y); + void rotate_ctm(double angle); + void scale_ctm(double sx, double sy); + void concat_ctm(kiva_gl_agg::trans_affine& m); + void set_ctm(kiva_gl_agg::trans_affine& m); + kiva_gl_agg::trans_affine get_ctm(); + + //--------------------------------------------------------------- + // Sending drawing data to a device + //--------------------------------------------------------------- + + void flush(); + void synchronize(); + + //--------------------------------------------------------------- + // Page Definitions + //--------------------------------------------------------------- + + void begin_page(); + void end_page(); + + //--------------------------------------------------------------- + // Path operations + //--------------------------------------------------------------- + + void begin_path(); + void move_to(double x, double y); + void line_to( double x, double y); + void curve_to(double cpx1, double cpy1, + double cpx2, double cpy2, + double x, double y); + + void quad_curve_to(double cpx, double cpy, + double x, double y); + + // arc() and arc_to() draw circular segments. When the arc + // is added to the current path, it may become an elliptical + // arc depending on the CTM. + + // Draws a circular segment centered at the point (x,y) with the + // given radius. + void arc(double x, double y, double radius, double start_angle, + double end_angle, bool cw=false); + + // Sweeps a circular arc from the pen position to a point on the + // line from (x1,y1) to (x2,y2). + // The arc is tangent to the line from the current pen position + // to (x1,y1), and it is also tangent to the line from (x1,y1) + // to (x2,y2). (x1,y1) is the imaginary intersection point of + // the two lines tangent to the arc at the current point and + // at (x2,y2). + // If the tangent point on the line from the current pen position + // to (x1,y1) is not equal to the current pen position, a line is + // drawn to it. Depending on the supplied radius, the tangent + // point on the line fron (x1,y1) to (x2,y2) may or may not be + // (x2,y2). In either case, the arc is drawn to the point of + // tangency, which is also the new pen position. + // + // Consider the common case of rounding a rectangle's upper left + // corner. Let "r" be the radius of rounding. Let the current + // pen position be (x_left + r, y_top). Then (x2,y2) would be + // (x_left, y_top - radius), and (x1,y1) would be (x_left, y_top). + void arc_to(double x1, double y1, double x2, double y2, double radius); + + void close_path(); + void add_path(kiva_gl::compiled_path& other_path); + compiled_path _get_path(); + kiva_gl::rect_type _get_path_bounds(); + + void lines(double* pts, int Npts); + void line_set(double* start, int Nstart, double* end, int Nend); + + void rect(double x, double y, double sx, double sy); + void rect(kiva_gl::rect_type &rect); + void rects(double* all_rects, int Nrects); + void rects(kiva_gl::rect_list_type &rectlist); + + kiva_gl_agg::path_storage boundary_path(kiva_gl_agg::trans_affine& affine_mtx); + + //--------------------------------------------------------------- + // Clipping path manipulation + //--------------------------------------------------------------- + virtual void clip() = 0; + virtual void even_odd_clip() = 0; + virtual void clip_to_rect(double x, double y, double sx, double sy) = 0; + virtual void clip_to_rect(kiva_gl::rect_type &rect) = 0; + virtual void clip_to_rects(double* new_rects, int Nrects) = 0; + virtual void clip_to_rects(kiva_gl::rect_list_type &rects) = 0; + virtual void clear_clip_path() = 0; + + // The following two are meant for debugging purposes, and are not part + // of the formal interface for GraphicsContexts. + virtual int get_num_clip_regions() = 0; + virtual kiva_gl::rect_type get_clip_region(unsigned int i) = 0; + + //--------------------------------------------------------------- + // Painting paths (drawing and filling contours) + //--------------------------------------------------------------- + virtual void clear(kiva_gl_agg::rgba value=kiva_gl_agg::rgba(1, 1, 1, 1)) = 0; + + virtual void fill_path() = 0; + virtual void eof_fill_path() = 0; + + virtual void stroke_path() = 0; + virtual void _stroke_path() = 0; + + virtual void draw_path(draw_mode_e mode=FILL_STROKE) = 0; + virtual void draw_rect(double rect[4], + draw_mode_e mode=FILL_STROKE) = 0; + + // Draw a marker at all the points in the list. This is a + // very fast function that only works in special cases. + // The succeeds if the line_width != 0.0 or 1.0, the line_join + // is set to JOIN_MITER (!! NOT CURRENTLY ENFORCED), and the + // ctm only has translational components. + // + // Typically this is called before trying the more general + // draw_path_at_points() command. It is typically 5-10 times + // faster. + // + // Returns: int + // 0 on failure + // 1 on success + virtual int draw_marker_at_points(double* pts,int Npts,int size, + kiva_gl::marker_e type=kiva_gl::marker_square) = 0; + + virtual void draw_path_at_points(double* pts,int Npts, + kiva_gl::compiled_path& marker, + draw_mode_e mode) = 0; + + //--------------------------------------------------------------- + // Image handling + //--------------------------------------------------------------- + + // Draws an image into the rectangle specified as (x, y, width, height); + // The image is scaled and/or stretched to fit inside the rectangle area + // specified. + virtual int draw_image(kiva_gl::graphics_context_base* img, double rect[4], bool force_copy=false) = 0; + int draw_image(kiva_gl::graphics_context_base* img); + + }; + +} + +#endif /* KIVA_GL_GRAPHICS_CONTEXT_BASE_H */ diff --git a/kiva/gl/src/kiva_gl_graphics_state.h b/kiva/gl/src/kiva_gl_graphics_state.h new file mode 100644 index 000000000..63b194c0d --- /dev/null +++ b/kiva/gl/src/kiva_gl_graphics_state.h @@ -0,0 +1,97 @@ +#ifndef KIVA_GL_GRAPHICS_STATE_H +#define KIVA_GL_GRAPHICS_STATE_H + +#include +#include "agg_trans_affine.h" + +#include "kiva_gl_constants.h" +#include "kiva_gl_dash_type.h" +#include "kiva_gl_compiled_path.h" + +#include + +namespace kiva_gl +{ + //----------------------------------------------------------------------- + // graphics_state class + //----------------------------------------------------------------------- + + class graphics_state + { + public: + + // line attributes + kiva_gl_agg::rgba line_color; + double line_width; + kiva_gl::line_cap_e line_cap; + kiva_gl::line_join_e line_join; + kiva_gl::dash_type line_dash; + + // other attributes + kiva_gl::blend_mode_e blend_mode; + kiva_gl_agg::rgba fill_color; + double alpha; + + // clipping path + // In general, we need a path to store the clipping region. + // However, in most cases, the clipping region can be represented + // by a list of rectangles. The graphics state can support one or + // the other, but not both. By default, device_space_clip_rects is + // used; but as soon as a non-rectangular clip path is added to + // the graphics state or the rectangular region is rotated, then + // it becomes an arbitrary clipping path. + // + // device_space_clip_rects always contains at least one rectangle. + // In the event that everything is clipped out, the clip rectangle + // will have dimensions (0,0). + // + // The function use_rect_clipping is used to determine whether or + // not to use device_space_clip_rects. 'true' means to use it, 'false' + // means ot use clipping_path; + kiva_gl::compiled_path clipping_path; + std::vector device_space_clip_rects; + inline bool use_rect_clipping(); + + double current_point[2]; // !! not sure about this. + int should_antialias; + double miter_limit; + double flatness; // !! not sure about this type. + + // double rendering_intent; // !! I know this type is wrong... + + graphics_state() + : line_color(kiva_gl_agg::rgba(0.0, 0.0, 0.0)) + , line_width(1.0) + , line_cap(kiva_gl::CAP_BUTT) + , line_join(kiva_gl::JOIN_MITER) + , blend_mode(kiva_gl::blend_normal) + , fill_color(kiva_gl_agg::rgba(0.0, 0.0, 0.0)) + , alpha(1.0) + , should_antialias(1) + { + } + + ~graphics_state() + { + } + + inline bool is_singleclip() + { + return (device_space_clip_rects.size() <= 1 ? true : false); + } + }; + + inline bool graphics_state::use_rect_clipping() + { + if (clipping_path.total_vertices() > 0) + { + std::cout << "clipping path has vertices" << std::endl; + return false; + } + + return true; + } + +} + +#endif diff --git a/kiva/gl/src/kiva_gl_rect.cpp b/kiva/gl/src/kiva_gl_rect.cpp new file mode 100755 index 000000000..299e83d3c --- /dev/null +++ b/kiva/gl/src/kiva_gl_rect.cpp @@ -0,0 +1,292 @@ +#include "kiva_gl_rect.h" +#include +#include +#include +#include + +namespace kiva_gl +{ + + rect_type + disjoint_intersect(const rect_type &a, const rect_type &b) + { + double xl = max(a.x, b.x); + double yb = max(a.y, b.y); + double xr = min(a.x2(), b.x2()); + double yt = min(a.y2(), b.y2()); + if ((xr >= xl) && (yt >= yb)) + { + return rect_type(xl, yb, xr-xl, yt-yb); + } + else + { + return rect_type(xl, yb, -1, -1); + } + } + + rect_list_type + disjoint_intersect(const rect_list_type &rects) + { + if (rects.size() < 2) + { + return rects; + } + + rect_list_type result_list; + result_list.push_back(rects[0]); + for (unsigned int i=1; i= 0) && (result_rect.h >= 0)) + { + result_list.push_back(result_rect); + } + } + + return result_list; + } + + + rect_list_type + disjoint_union(const rect_type &a, const rect_type &b) + { + rect_list_type rlist; + rlist.push_back(a); + return disjoint_union(rlist, b); + } + + rect_list_type + disjoint_union(const rect_list_type &rects) + { + if (rects.size() < 2) + { + return rects; + } + + rect_list_type rlist; + rlist.push_back(rects[0]); + for (unsigned int i=1; ix; + double yb1 = cur_todo->y; + double xr1 = cur_todo->x2(); + double yt1 = cur_todo->y2(); + + double xl2, yb2, xr2, yt2; + + unsigned int orig_count = 0; + while (orig_count < original_list.size()) + { + rect_type *cur_orig = &original_list[orig_count]; + xl2 = cur_orig->x; + yb2 = cur_orig->y; + xr2 = cur_orig->x2(); + yt2 = cur_orig->y2(); + + // Test for non-overlapping + if ((xl1 >= xr2) || (xr1 <= xl2) || (yb1 >= yt2) || (yt1 <= yb2)) + { + orig_count++; + continue; + } + + // Test for new rect being wholly contained in an existing one + bool x1inx2 = ((xl1 >= xl2) && (xr1 <= xr2)); + bool y1iny2 = ((yb1 >= yb2) && (yt1 <= yt2)); + if (x1inx2 && y1iny2) + { + use_leftover = false; + break; + } + + // Test for existing rect being wholly contained in new rect + bool x2inx1 = ((xl2 >= xl1) && (xr2 <= xr1)); + bool y2iny1 = ((yb2 >= yb1) && (yt2 <= yt1)); + if (x2inx1 && y2iny1) + { + // Erase the existing rectangle from the original_list + // and set the iterator to the next one. + original_list.erase(original_list.begin() + orig_count); + continue; + } + + // Test for rect 1 being within rect 2 along the x-axis: + if (x1inx2) + { + if (yb1 < yb2) + { + if (yt1 > yt2) + { + todo.push_back(rect_type(xl1, yt2, xr1-xl1, yt1-yt2)); + } + yt1 = yb2; + } + else + { + yb1 = yt2; + } + orig_count++; + continue; + } + + // Test for rect 2 being within rect 1 along the x-axis: + if (x2inx1) + { + if (yb2 < yb1) + { + if (yt2 > yt1) + { + original_list.insert(original_list.begin() + orig_count, + rect_type(xl2, yt1, xr2-xl2, yt2-yt1)); + orig_count++; + } + original_list[orig_count] = rect_type(xl2, yb2, xr2-xl2, yb1-yb2); + } + else + { + original_list[orig_count] = rect_type(xl2, yt1, xr2-xl2, yt2-yt1); + } + orig_count++; + continue; + } + + // Test for rect 1 being within rect 2 along the y-axis: + if (y1iny2) + { + if (xl1 < xl2) + { + if (xr1 > xr2) + { + todo.push_back(rect_type(xr2, yb1, xr1-xr2, yt1-yb1)); + } + xr1 = xl2; + } + else + { + xl1 = xr2; + } + orig_count++; + continue; + } + + // Test for rect 2 being within rect 1 along the y-axis: + if (y2iny1) + { + if (xl2 < xl1) + { + if (xr2 > xr1) + { + original_list.insert(original_list.begin() + orig_count, + rect_type(xr1, yb2, xr2-xr1, yt2-yb2)); + orig_count++; + } + original_list[orig_count] = rect_type(xl2, yb2, xl1-xl2, yt2-yb2); + } + else + { + original_list[orig_count] = rect_type(xr1, yb2, xr2-xr1, yt2-yb2); + } + orig_count++; + continue; + } + + // Handle a 'corner' overlap of rect 1 and rect 2: + double xl, yb, xr, yt; + if (xl1 < xl2) + { + xl = xl1; + xr = xl2; + } + else + { + xl = xr2; + xr = xr1; + } + + if (yb1 < yb2) + { + yb = yb2; + yt = yt1; + yt1 = yb2; + } + else + { + yb = yb1; + yt = yt2; + yb1 = yt2; + } + + todo.push_back(rect_type(xl, yb, xr-xl, yt-yb)); + + orig_count++; + } + + if (use_leftover) + { + additional_rects.push_back(rect_type(xl1, yb1, xr1-xl1, yt1-yb1)); + } + todo_count++; + } + + for (rect_list_type::iterator it = additional_rects.begin(); it != additional_rects.end(); ++it) + { + original_list.push_back(*it); + } + + return original_list; + } + + bool + rect_list_contains(rect_list_type &l, rect_type &r) + { + return (std::find(l.begin(), l.end(), r) != l.end()); + } +} diff --git a/kiva/gl/src/kiva_gl_rect.h b/kiva/gl/src/kiva_gl_rect.h new file mode 100644 index 000000000..dfd8d7d9a --- /dev/null +++ b/kiva/gl/src/kiva_gl_rect.h @@ -0,0 +1,112 @@ +#ifndef KIVA_GL_RECT_H +#define KIVA_GL_RECT_H + +#include + +#include "agg_basics.h" +#include "kiva_gl_basics.h" + +namespace kiva_gl +{ + + //----------------------------------------------------------------------- + // graphics_state class + //----------------------------------------------------------------------- + + class rect_type { + public: + // constructors + inline rect_type(): x(0), y(0), w(-1), h(-1) { } + inline rect_type(double newx, double newy, double neww, double newh) + : x(newx), y(newy), w(neww), h(newh) { } + inline rect_type(kiva_gl_agg::rect_i r) { *this = r; } + inline rect_type(kiva_gl_agg::rect_d r) { *this = r; } + + // conversion from kiva_gl_agg::rect + inline rect_type& operator=(kiva_gl_agg::rect_i &r) + { + x = int(r.x1); + y = int(r.y1); + w = int(r.x2 - r.x1); + h = int(r.y2 - r.y1); + return *this; + } + + inline rect_type& operator=(kiva_gl_agg::rect_d &r) + { + x = r.x1; + y = r.y1; + w = r.x2 - r.x1; + h = r.y2 - r.y1; + return *this; + } + + inline bool operator==(rect_type& other) + { + return ((x == other.x) && (y == other.y) && (w == other.w) && (h == other.h)); + } + + inline bool operator!=(rect_type& other) + { + return !(*this == other); + } + + // conversion to kiva_gl_agg::rect + inline operator kiva_gl_agg::rect_i() const { return kiva_gl_agg::rect_i(int(x), int(y), int(w), int(h)); } + inline operator kiva_gl_agg::rect_d() const { return kiva_gl_agg::rect_d(x, y, w, h); } + + // conversion to double[4] + inline double operator[](unsigned int ndx) const + { + switch (ndx) + { + case 0: return x; + case 1: return y; + case 2: return w; + case 3: return h; + } + } + + // comparison + inline bool operator==(const rect_type &b) const + { + return ((this->x == b.x) && (this->y == b.y) && (this->w == b.w) && (this->h == b.h)); + } + + // utility functions: + inline double x2() const { return x+w; } + inline double y2() const { return y+h; } + + double x, y, w, h; + }; + + typedef std::vector rect_list_type; + typedef rect_list_type::iterator rect_iterator; + + // This returns the rectangle representing the overlapping area between + // rectangles a and b. If they do not overlap, the returned rectangle + // will have width and height -1. + // + // (We use -1 instead of 0 because Agg will accept clip rectangles of + // size 0.) + rect_type disjoint_intersect(const rect_type &a, const rect_type &b); + + // Returns a list of rectangles resulting from the intersection of the + // input list of rectangles. If there are no intersection regions, + // returns an empty list. + rect_list_type disjoint_intersect(const rect_list_type &rects); + + // Intersects a single rectangle against a list of existing, non- + // intersecting rectangles, and returns a list of the intersection regions. + // If there are no intersection regions, returns an empty list. + rect_list_type disjoint_intersect(const rect_list_type &original_list, + const rect_type &new_rect); + + rect_list_type disjoint_union(const rect_type &a, const rect_type &b); + rect_list_type disjoint_union(const rect_list_type &rects); + rect_list_type disjoint_union(rect_list_type original_list, + const rect_type &new_rect); + +} + +#endif diff --git a/kiva/gl/src/swig/affine_matrix.i b/kiva/gl/src/swig/affine_matrix.i new file mode 100644 index 000000000..d71196dc4 --- /dev/null +++ b/kiva/gl/src/swig/affine_matrix.i @@ -0,0 +1,218 @@ +/* -*- c -*- */ +/* AffineMatrix class wrapper + + 1. C++ class 'trans_affine' is renamed to python '_AffineMatrix' + + 2. All methods accept 'transform' and 'inverse_transform' are + wrapped. + + 3. __repr__ and __str__ methods are added to print out an + _AffineMatrix object as: "AffineMatrix(a,b,c,d,tx,ty)" + + 4. A subclass called 'AffineMatrix' is derived from '_AffineMatrix' + using a %pythoncode directive. This is so that __init__ can be + overloadeded to convert a sequence into the appropriate argument + convention for the _AffineMatrix constructor. + + 5. Classes such as trans_affine_rotation were converted to factory + functions so that they return an trans_affine class instead of + having a new class type (such as RotationMatrix). + + Notes: + !! 1. + !! (4) is a hack to get around the fact that I couldn't + !! figure out how to get the overloaded constructor for trans_affine + !! to accept a numpy array as input -- even if I added a function + !! new_AffineMatrix(double ary[6]); and then put the + !! trans_affine(double ary[6]) signature in the class interface. It + !! appears that SWIG is a little overzealous in its type checking + !! in the constructor call, only allowing double* pointers through + !! instead of allowing any sequence through. This is the correct + !! default behavior, but I couldn't figure out how to overload it with + !! my own test. + !! + !! 2. + !! The C++ operator *= is definitely broken -- probably not setting the + !! thisown property correctly on returned pointers. It is currently + !! set to return void so that it can't cause any mischief, but it also + !! breaks its functionality. + !! FIX: I have just created this function in Python and call the + !! C++ multiply() method. +*/ + +%include "numpy.i" +%include "sequence_to_array.i" + + +%{ +#include "numpy/arrayobject.h" +#include "agg_trans_affine.h" + +// These factories mimic the functionality of like-named classes in agg. +// Making them functions that return trans_affine types leads to a cleaner +// and easier to maintain Python interface. + + +kiva_gl_agg::trans_affine* trans_affine_rotation(double a) +{ + return new kiva_gl_agg::trans_affine(cos(a), sin(a), -sin(a), cos(a), 0.0, 0.0); +} + +kiva_gl_agg::trans_affine* trans_affine_scaling(double sx, double sy) +{ + return new kiva_gl_agg::trans_affine(sx, 0.0, 0.0, sy, 0.0, 0.0); +} + +kiva_gl_agg::trans_affine* trans_affine_translation(double tx, double ty) +{ + return new kiva_gl_agg::trans_affine(1.0, 0.0, 0.0, 1.0, tx, ty); +} + +kiva_gl_agg::trans_affine* trans_affine_skewing(double sx, double sy) +{ + return new kiva_gl_agg::trans_affine(1.0, tan(sy), tan(sx), 1.0, 0.0, 0.0); +} + +%} + +%newobject trans_affine_rotation; +%rename(rotation_matrix) trans_affine_rotation(double); +kiva_gl_agg::trans_affine* trans_affine_rotation(double a); + +%newobject trans_affine_scaling; +%rename(scaling_matrix) trans_affine_scaling(double, double); +kiva_gl_agg::trans_affine* trans_affine_scaling(double sx, double sy); + +%newobject trans_affine_translation; +%rename(translation_matrix) trans_affine_translation(double, double); +kiva_gl_agg::trans_affine* trans_affine_translation(double tx, double ty); + +%newobject trans_affine_skewing; +%rename(skewing_matrix) trans_affine_skewing(double, double); +kiva_gl_agg::trans_affine* trans_affine_skewing(double sx, double sy); + + +%include "agg_typemaps.i" +%apply (double* array6) {(double* out)}; + +// used by __getitem__ +%typemap(check) (int affine_index) +{ + if ($1 < 0 || $1 > 5) + { + PyErr_Format(PyExc_IndexError, + "affine matrices are indexed 0 to 5. Received %d", $1); + return NULL; + } +} + +%apply owned_pointer { kiva_gl_agg::trans_affine* }; + + +namespace kiva_gl_agg +{ + %rename(_AffineMatrix) trans_affine; + %rename(asarray) trans_affine::store_to(double*) const; + + class trans_affine + { + public: + trans_affine(); + trans_affine(const trans_affine& m); + trans_affine(double v0, double v1, double v2, double v3, + double v4, double v5); + trans_affine operator ~ () const; + // I added this to trans_affine -- it really isn't there. + // trans_affine operator *(const trans_affine& m); + + // Returning trans_affine& causes problems, so these are all + // changed to void. + //const trans_affine& operator *= (const trans_affine& m); + //const trans_affine& reset(); + // const trans_affine& multiply(const trans_affine& m); + // const trans_affine& invert(); + // const trans_affine& flip_x(); + // const trans_affine& flip_y(); + //void operator *= (const trans_affine& m); + void reset(); + void multiply(const trans_affine& m); + void invert(); + void flip_x(); + void flip_y(); + + double scale() const; + double determinant() const; + + void store_to(double* out) const; + //const trans_affine& load_from(double ary[6]); + void load_from(double ary[6]); + + // !! omitted + //void transform(double* x, double* y) const; + //void inverse_transform(double* x, double* y) const; + }; +}; + +%pythoncode %{ +def is_sequence(arg): + try: + len(arg) + return 1 + except: + return 0 + +# AffineMatrix sub-class to get around problems with adding +# a AffineMatrix constructor that accepts a numpy array +# as input. +class AffineMatrix(_AffineMatrix): + def __init__(self, *args): + if len(args) == 1 and is_sequence(args[0]): + args = tuple(args[0]) + if len(args) != 6: + raise ValueError("array argument must be 1x6") + _AffineMatrix.__init__(self,*args) + + def __imul__(self, other): + """ inplace multiply + + We don't use the C++ version of this because it ends up + deleting the object out from under itself. + """ + self.multiply(other) + return self +%} + +%extend kiva_gl_agg::trans_affine +{ + char *__repr__() + { + // Write out elements of trans_affine in a, b, c, d, tx, ty order + // !! We should work to make output formatting conform to + // !! whatever it numpy does (which needs to be cleaned up also). + static char tmp[1024]; + double m[6]; + self->store_to(m); + sprintf(tmp,"AffineMatrix(%g, %g, %g, %g, %g, %g)", m[0], m[1], m[2], + m[3], m[4], m[5]); + return tmp; + } + + double __getitem__(int affine_index) + { + double ary[6]; + self->store_to(ary); + return ary[affine_index]; + } + + int __eq__(kiva_gl_agg::trans_affine& other) + { + double ary1[6], ary2[6]; + self->store_to(ary1); + other.store_to(ary2); + int eq = 1; + for (int i = 0; i < 6; i++) + eq &= (ary1[i] == ary2[i]); + return eq; + } +} + diff --git a/kiva/gl/src/swig/agg_std_string.i b/kiva/gl/src/swig/agg_std_string.i new file mode 100644 index 000000000..886f80953 --- /dev/null +++ b/kiva/gl/src/swig/agg_std_string.i @@ -0,0 +1,34 @@ +%include "std_string.i" + +// These typemaps are needed to handle member access to font_type.name +// and friends. They really should by part of std_string.i, shouldn't +// they? +#ifdef SWIGPYTHON +%typemap(in) std::string * { + if (PyBytes_Check ($input)) + { + $1 = new std::string((char *)PyBytes_AsString($input)); + } +#if PY_VERSION_HEX >= 0x03030000 + else if (PyUnicode_Check($input)) + { + $1 = new std::string((char *)PyUnicode_AsUTF8($input)); + } +#endif + else + { + PyErr_SetString (PyExc_TypeError, "not a String"); + return NULL; + } +} +%typemap(out) std::string * { + $result = SWIG_Python_str_FromChar((const char *)$1->c_str()); +} +%typemap(freearg) std::string * { + if ($1) + { + delete $1; + } +} + +#endif /* SWIGPYTHON */ diff --git a/kiva/gl/src/swig/agg_typemaps.i b/kiva/gl/src/swig/agg_typemaps.i new file mode 100644 index 000000000..e63a6be36 --- /dev/null +++ b/kiva/gl/src/swig/agg_typemaps.i @@ -0,0 +1,313 @@ +// -------------------------------------------------------------------------- +// Generic typemap to handle enumerated types. +// +// Both agg and kiva have quite a few enumerated types. SWIG will wrap +// functions that use these as arguments to require a pointer to an +// enumerated type object. This isn't very convenient. It is much nicer +// to just pass an integer. this generic converter can be used in to +// allow this. +// +// To apply it to a type, for example kiva_gl::marker_e, do the following +// +// %apply(kiva_enum_typemap) { kiva_gl::marker_e } +// +// Now any function that expects a marker_e will accept integer values as +// input. +// -------------------------------------------------------------------------- + +%include "numpy.i" + +%typemap(in) kiva_enum_typemap type { + + int temp = PyInt_AsLong($input); + if (PyErr_Occurred()) SWIG_fail; + $1 = $1_ltype(temp); +} + + +// -------------------------------------------------------------------------- +// Typemaps for (double x, double y) points +// +// For: * +// +// This is useful for places where ints may be passed in and need to +// be converted. Python 2.6 requires this +// -------------------------------------------------------------------------- +%typemap(in) double x, double y +{ + if (PyNumber_Check($input)) + { + $1 = static_cast(PyFloat_AsDouble($input)); + } + else + { + SWIG_exception(SWIG_TypeError, "Expected argument $argnum of type '$1_type'"); + } +} + +// -------------------------------------------------------------------------- +// Typemaps for (double* pts, int Npts) used in lines() +// +// For: compiled_path and graphics_context +// +// This typemap takes any Nx2 input (nested sequences or an Nx2 array). If +// the input has the wrong shape or can't be converted to a double, an +// exception is raised. It is more efficient if the input passed in is a +// contiguous array, so if you're calling lines(pts) a lot of times, make +// pts an array. +// +// -------------------------------------------------------------------------- + +%typemap(in) (double* point_array, int point_count) (PyArrayObject* ary=NULL, + int is_new_object=0) +{ + ary = obj_to_array_contiguous_allow_conversion($input, PyArray_DOUBLE, + is_new_object); + int size[2] = {-1,2}; + if (!ary || + !require_dimensions(ary,2) || + !require_size(ary,size,2)) + { + goto fail; + } + $1 = (double*) ary->data; + $2 = ary->dimensions[0]; +} + +%typemap(freearg) (double* point_array, int point_count) +{ + if (is_new_object$argnum) + { + Py_XDECREF(ary$argnum); + } +} + +// -------------------------------------------------------------------------- +// Typemaps for (unsigned char* results, int Nresults) +// +// For: points_in_polygon +// +// This typemap takes any N input. +// +// -------------------------------------------------------------------------- + +%typemap(in) (unsigned char* results, int Nresults) (PyArrayObject* ary=NULL, + int is_new_object=0) +{ + ary = obj_to_array_contiguous_allow_conversion($input, PyArray_BOOL, + is_new_object); + int size[1] = {-1}; + if (!ary || + !require_dimensions(ary,1) || + !require_size(ary,size,1)) + { + goto fail; + } + $1 = (unsigned char*) ary->data; + $2 = ary->dimensions[0]; +} + +%typemap(freearg) (unsigned char* results, int Nresults) +{ + if (is_new_object$argnum) + { + Py_XDECREF(ary$argnum); + } +} + + +/* Typemaps for rects(double* all_rects, int Nrects) + + For: compiled_path and graphics_context + + This typemap takes any Nx4 input (nested sequences or an Nx4 array). If + the input has the wrong shape or can't be converted to a double, an + exception is raised. It is more efficient if the input passed in is a + contiguous array, so if you're calling rects(all_rects) a lot of times, + make all_rects an array. +*/ +%typemap(in) (double* rect_array, int rect_count) (PyArrayObject* ary=NULL, + int is_new_object=0) +{ + ary = obj_to_array_contiguous_allow_conversion($input, PyArray_DOUBLE, + is_new_object); + int size[2] = {-1,4}; + if (!ary || + !require_dimensions(ary,2) || + !require_size(ary,size,2)) + { + goto fail; + } + $1 = (double*) ary->data; + $2 = ary->dimensions[0]; +} + +%typemap(freearg) (double* rect_array, int rect_count) +{ + if (is_new_object$argnum) + { + Py_XDECREF(ary$argnum); + } +} + +// -------------------------------------------------------------------------- +// +// vertex() returns ( pt, cmd) where pt is a tuple (x,y) +// +// This tells SWIG to treat an double * argument with name 'x' as +// an output value. We'll append the value to the current result which +// is guaranteed to be a List object by SWIG. +// -------------------------------------------------------------------------- +%typemap(in,numinputs=0) (double *vertex_x, double* vertex_y)(double temp1, + double temp2) +{ + temp1 = 0; $1 = &temp1; + temp2 = 0; $2 = &temp2; +} + +%typemap(argout) (double *vertex_x, double* vertex_y) +{ + PyObject *px = PyFloat_FromDouble(*$1); + PyObject *py = PyFloat_FromDouble(*$2); + PyObject *pt = PyTuple_New(2); + PyTuple_SetItem(pt,0,px); + PyTuple_SetItem(pt,1,py); + PyObject *return_val = PyTuple_New(2); + PyTuple_SetItem(return_val,0,pt); + // result is what was returned from vertex + PyTuple_SetItem(return_val,1,$result); + //Py_DECREF($result); + $result = return_val; +} + +// -------------------------------------------------------------------------- +// map to output arguments into a 2-tuple +// -------------------------------------------------------------------------- +%typemap(in,numinputs=0) (double *pt_x, double* pt_y)(double temp1, + double temp2) +{ + temp1 = 0; $1 = &temp1; + temp2 = 0; $2 = &temp2; +} +%typemap(argout) (double *pt_x, double *pt_y) +{ + PyObject *px = PyFloat_FromDouble(*$1); + PyObject *py = PyFloat_FromDouble(*$2); + PyObject *pt = PyTuple_New(2); + PyTuple_SetItem(pt,0,px); + PyTuple_SetItem(pt,1,py); + //Py_DECREF($result); + $result = pt; +} + +// -------------------------------------------------------------------------- +// map an 6 element double* output into a numpy array. +// -------------------------------------------------------------------------- +%typemap(in, numinputs=0) double *array6 (double temp[6]) { + $1 = temp; +} + +%typemap(argout) double *array6 { + // Append output value $1 to $result + npy_intp dims = 6; + PyArrayObject* ary_obj = (PyArrayObject*) PyArray_SimpleNew(1,&dims,PyArray_DOUBLE); + if( ary_obj == NULL ) + return NULL; + double* data = (double*)ary_obj->data; + for (int i=0; i < 6;i++) + data[i] = $1[i]; + Py_DECREF($result); + $result = PyArray_Return(ary_obj); +} + +// -------------------------------------------------------------------------- +// Typemaps for graphics_context.set_line_dash() +// +// For: +// +// This typemap takes None or any N element input (sequence or array). If +// the input is None, it passes a 2 element array of zeros to in as the +// pattern. If the input is a sequence and isn't 1D or can't be converted +// to a double, an exception is raised. +// -------------------------------------------------------------------------- + +%typemap(in) (double* dash_pattern, int n) (PyArrayObject* ary=NULL, + int is_new_object=0, + double temp[2]) +{ + is_new_object = 0; + if ($input == Py_None) + { + temp[0] = 0.0; + temp[1] = 0.0; + $1 = temp; + $2 = 2; + } + else + { + ary = obj_to_array_contiguous_allow_conversion($input, PyArray_DOUBLE, + is_new_object); + if (!ary || + !require_dimensions(ary,1)) + { + goto fail; + } + $1 = (double*) ary->data; + $2 = ary->dimensions[0]; + } +} + +%typemap(freearg) (double* dash_pattern, int n) +{ + if (is_new_object$argnum) + { + Py_XDECREF(ary$argnum); + } +} + +// -------------------------------------------------------------------------- +// Image typemaps +// +// Currently, this requires a contiguous array. It should be fixed to +// allow arrays that are only contiguous along the last two dimensions. +// This is because the windows bitmap format requires that each row of +// pixels (scan line) is word aligned (16 bit boundaries). As a result, rgb +// images compatible with this format potentially +// need a pad byte at the end of each scanline. +// -------------------------------------------------------------------------- + +%typemap(in) (unsigned char *image_data=NULL, int width, int height, int stride) +{ + PyArrayObject* ary = obj_to_array_no_conversion($input, PyArray_UBYTE); + int dimensions[2] = {2,3}; +// !! No longer requiring contiguity because some bitmaps are padded at the +// !! end (i.e. Windows). We should probably special case that one though, +// !! and re-instate the contiguous policy... +// if (!ary || +// !require_dimensions(ary,dimensions,2) || +// !require_contiguous(ary)) + if (!ary || + !require_dimensions(ary,dimensions,2)) + { + goto fail; + } + $1 = (unsigned char*) ary->data; + // notice reversed orders... + $2 = ary->dimensions[1]; + $3 = ary->dimensions[0]; + $4 = ary->strides[0]; +} + +// -------------------------------------------------------------------------- +// Some functions create new objects and return these to python. By +// default, SWIG sets these objects as "unowned" by the shadow class +// created to represent them in python. The result is that these objects +// are not freed when the shadow object calls its __del__ method. Here +// the thisown flag is set to 1 so that the object will be destroyed on +// destruction. +// -------------------------------------------------------------------------- + +%typemap(out) owned_pointer +{ + $result = SWIG_NewPointerObj((void *) $1, $1_descriptor, 1); +} diff --git a/kiva/gl/src/swig/compiled_path.i b/kiva/gl/src/swig/compiled_path.i new file mode 100644 index 000000000..03e2cd678 --- /dev/null +++ b/kiva/gl/src/swig/compiled_path.i @@ -0,0 +1,122 @@ +%{ + #include "kiva_gl_compiled_path.h" +%} + +// handle kiva_gl::rect declarations + +%include "rect.i" + +%include "agg_typemaps.i" +%apply (double* point_array, int point_count) {(double* pts, int Npts)}; +%apply (double* point_array, int point_count) {(double* start, int Nstart)}; +%apply (double* point_array, int point_count) {(double* end, int Nend)}; +%apply (double* rect_array, int rect_count) {(double* all_rects, int Nrects)}; +%apply (double *vertex_x, double* vertex_y) {(double* x, double *y)}; + + + +namespace kiva_gl +{ + %rename(CompiledPath) compiled_path; + + class compiled_path + { + public: + compiled_path(); + void remove_all(); + void begin_path(); + void close_path(); + void move_to(double x, double y); + void line_to(double x, double y); + void quad_curve_to(double x_ctrl, double y_ctrl, + double x_to, double y_to); + void curve_to(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + void arc(double x, double y, double radius, double start_angle, + double end_angle, bool cw=false); + void arc_to(double x1, double y1, double x2, double y2, double radius); + + void add_path(compiled_path& vs); + void lines(double* pts, int Npts); + void line_set(double* start, int Nstart, double* end, int Nend); + void rect(kiva_gl::rect_type &rect); + void rect(double x, double y, double sx, double sy); + void rects(double* all_rects, int Nrects); + void translate_ctm(double x, double y); + void rotate_ctm(double angle); + void scale_ctm(double sx, double sy); + %rename(concat_ctm_agg) concat_ctm(kiva_gl_agg::trans_affine&); + void concat_ctm(kiva_gl_agg::trans_affine& m); + %rename(set_ctm_agg) set_ctm(kiva_gl_agg::trans_affine&); + void set_ctm(kiva_gl_agg::trans_affine& m); + %pythoncode + %{ + def kivaaffine_to_aggaffine(self, ctm): + return AffineMatrix(ctm[0,0], ctm[0,1], ctm[1,0], ctm[1,1], + ctm[2,0], ctm[2,1]) + def concat_ctm(self, ctm): + # This is really tortured and may cause performance problems. + # Unfortunately I don't see a much better way right now. + if '__class__' in dir(ctm) and ctm.__class__.__name__.count('AffineMatrix'): + self.concat_ctm_agg(ctm) + else: + self.concat_ctm_agg(self.kivaaffine_to_aggaffine(ctm)) + def set_ctm(self, ctm): + if '__class__' in dir(ctm) and ctm.__class__.__name__.count('AffineMatrix'): + self.set_ctm_agg(ctm) + else: + self.set_ctm_agg(self.kivaaffine_to_aggaffine(ctm)) + %} + kiva_gl_agg::trans_affine get_ctm(); + void save_ctm(); + void restore_ctm(); + + // methods from kiva_gl_agg::path_storage that are used in testing + unsigned total_vertices() const; + %rename(_rewind) rewind(unsigned); + void rewind(unsigned start=0); + + %rename (_vertex) vertex(unsigned, double*, double*); + unsigned vertex(unsigned idx, double* x, double* y) const; + + %rename (_vertex) vertex(double*, double*); + unsigned vertex(double* x, double* y); + + }; +} + +%pythoncode { +from numpy import array, float64 +def _vertices(self): + """ This is only used for testing. It allows us to retrieve + all the vertices in the path at once. The vertices are + returned as an Nx4 array of the following format. + + x0, y0, cmd0, flag0 + x1, y1, cmd0, flag1 + ... + """ + vertices = [] + self._rewind() + cmd_flag = 1 + while cmd_flag != 0: + pt, cmd_flag = self._vertex() + cmd, flag = _gl.path_cmd(cmd_flag), _gl.path_flags(cmd_flag) + vertices.append((pt[0],pt[1], cmd, flag)) + return array(vertices) + +CompiledPath._vertices = _vertices + + +def get_kiva_ctm(self): + aff = self.get_ctm() + return array([[aff[0], aff[1], 0], + [aff[2], aff[3], 0], + [aff[4], aff[5], 1]], float64) + +CompiledPath.get_kiva_ctm = get_kiva_ctm + +} + +%clear (double *x, double *y); diff --git a/kiva/gl/src/swig/constants.i b/kiva/gl/src/swig/constants.i new file mode 100644 index 000000000..180ecc7d1 --- /dev/null +++ b/kiva/gl/src/swig/constants.i @@ -0,0 +1,142 @@ +///////////////////////////////////////////////////////////////////////////// +// +// 1) Wraps constants and enumerated types commonly used in agg and kiva. +// 2) Provides typemaps to accpet integer inputs for enumerated types. +// 3) Provides python dictionaries to map back and forth between enumerated +// types and more descriptive strings that can be used in python. +// +// A number of constants (and some functions and types) are defined in +// agg_basics.h and kiva_constants.h. +// +// agg_renderer_markers.h is used for rendering simple shapes at multiple +// data points. It is useful for generating scatter plots in simple cases. +// This wrapper is used to pick up the enumerated types for markers such +// as marker_square, marker_circle, etc. The only classes in the header are +// template definitions so they are all ignored by swig. +// +// +///////////////////////////////////////////////////////////////////////////// + +%{ +#include "agg_basics.h" +#include "kiva_gl_constants.h" +%} + +%include "agg_basics.h" +%include "kiva_gl_constants.h" + +%{ + inline unsigned path_cmd(unsigned c) { return c & kiva_gl_agg::path_cmd_mask; } + inline unsigned path_flags(unsigned c) { return c & kiva_gl_agg::path_flags_mask; } +%} + +%include "agg_typemaps.i" + +%apply(kiva_enum_typemap) { kiva_gl_agg::path_flags_e }; +%apply(kiva_enum_typemap) { kiva_gl::marker_e }; +%apply(kiva_enum_typemap) { kiva_gl::draw_mode_e mode, kiva_gl::line_join_e, + kiva_gl::line_cap_e, kiva_gl::blend_mode_e }; +%apply(kiva_enum_typemap) { kiva_gl::pix_format_e, kiva_gl::interpolation_e }; +%apply(kiva_enum_typemap) { kiva_gl::blend_mode_e mode}; + +unsigned path_cmd(unsigned c); +unsigned path_flags(unsigned c); + +%pythoncode %{ + +#---------------------------------------------------------------------------- +# +# map strings values to the marker enumerated values and back with: +# marker_string_map[string] = enum +# marker_enum_map[enum] = string +# +#---------------------------------------------------------------------------- + +kiva_marker_to_agg = {} +kiva_marker_to_agg[1] = marker_square +kiva_marker_to_agg[2] = marker_diamond +kiva_marker_to_agg[3] = marker_circle +kiva_marker_to_agg[4] = marker_crossed_circle +kiva_marker_to_agg[5] = marker_x +kiva_marker_to_agg[6] = marker_triangle_up +kiva_marker_to_agg[7] = marker_triangle_down +kiva_marker_to_agg[8] = marker_cross # "plus" sign; Agg calls this "cross" +kiva_marker_to_agg[9] = marker_dot +kiva_marker_to_agg[10] = marker_pixel + + +#---------------------------------------------------------------------------- +# +# Map strings values to the pix_format enumerated values and back with: +# pix_format_string_map[string] = enum +# pix_format_enum_map[enum] = string +# +#---------------------------------------------------------------------------- + +pix_format_string_map = {} +pix_format_string_map["gray8"] = pix_format_gray8 +pix_format_string_map["rgb555"] = pix_format_rgb555 +pix_format_string_map["rgb565"] = pix_format_rgb565 +pix_format_string_map["rgb24"] = pix_format_rgb24 +pix_format_string_map["bgr24"] = pix_format_bgr24 +pix_format_string_map["rgba32"] = pix_format_rgba32 +pix_format_string_map["argb32"] = pix_format_argb32 +pix_format_string_map["abgr32"] = pix_format_abgr32 +pix_format_string_map["bgra32"] = pix_format_bgra32 + +pix_format_enum_map = {} +for key,value in pix_format_string_map.items(): + pix_format_enum_map[value] = key + +#---------------------------------------------------------------------------- +# Map a pix format string value to the number of bytes per pixel +#---------------------------------------------------------------------------- + +pix_format_bytes = {} +pix_format_bytes["gray8"] = 1 +pix_format_bytes["rgb555"] = 2 +pix_format_bytes["rgb565"] = 2 +pix_format_bytes["rgb24"] = 3 +pix_format_bytes["bgr24"] = 3 +pix_format_bytes["rgba32"] = 4 +pix_format_bytes["argb32"] = 4 +pix_format_bytes["abgr32"] = 4 +pix_format_bytes["bgra32"] = 4 + +pix_format_bits = {} +pix_format_bits["gray8"] = 8 +pix_format_bits["rgb555"] = 15 +pix_format_bits["rgb565"] = 16 +pix_format_bits["rgb24"] = 24 +pix_format_bits["bgr24"] = 24 +pix_format_bits["rgba32"] = 32 +pix_format_bits["argb32"] = 32 +pix_format_bits["abgr32"] = 32 +pix_format_bits["bgra32"] = 32 + +#---------------------------------------------------------------------------- +# +# Map strings values to the interpolation enumerated values and back with: +# interp_string_map[string] = enum +# interp_enum_map[enum] = string +# +#---------------------------------------------------------------------------- + +interp_string_map = {} +interp_string_map["nearest"] = nearest +interp_string_map["bilinear"] = bilinear +interp_string_map["bicubic"] = bicubic +interp_string_map["spline16"] = spline16 +interp_string_map["spline36"] = spline36 +interp_string_map["sinc64"] = sinc64 +interp_string_map["sinc144"] = sinc144 +interp_string_map["sinc256"] = sinc256 +interp_string_map["blackman64"] = blackman64 +interp_string_map["blackman100"] = blackman100 +interp_string_map["blackman256"] = blackman256 + +interp_enum_map = {} +for key,value in interp_string_map.items(): + interp_enum_map[value] = key + +%} diff --git a/kiva/gl/src/swig/font_type.i b/kiva/gl/src/swig/font_type.i new file mode 100644 index 000000000..a8f78e9cc --- /dev/null +++ b/kiva/gl/src/swig/font_type.i @@ -0,0 +1,78 @@ +%{ + #include "kiva_gl_font_type.h" +%} + +%include "agg_std_string.i" + +namespace kiva_gl +{ + %rename(KivaGLFontType) font_type; + class font_type + { + public: + %mutable; + int size; + std::string name; + int family; + int style; + int encoding; + std::string filename; + + // constructor + font_type(std::string _name="Arial", + int _size=12, + int _family=0, + int _style=0, + int _encoding=0, + bool validate=true); + + int change_filename(std::string _filename); + + bool is_loaded(); + }; +} +%extend kiva_gl::font_type +{ + char *__repr__() + { + static char tmp[1024]; + // Write out elements of font_type in name, family, size, style, encoding order + // !! We should work to make output formatting conform to + // !! whatever it Numeric does (which needs to be cleaned up also). + sprintf(tmp, "Font(%s, %d, %d, %d, %d)", self->name.c_str(), self->family, + self->size, self->style, + self->encoding); + return tmp; + } + int __eq__(kiva_gl::font_type& other) + { + return (self->name == other.name && + self->family == other.family && + self->size == other.size && + self->style == other.style && + self->encoding == other.encoding); + } +} + +%pythoncode +%{ +def unicode_safe_init(self, _name="Arial", _size=12, _family=0, _style=0, + _encoding=0, validate=True): + ### HACK: C++ stuff expects a string (not unicode) for the face_name, so fix + ### if needed. + ### Only for python < 3 + if '' == b'': + if isinstance(_name, unicode): + _name = _name.encode("latin1") + else: + if isinstance(_name, bytes): + _name = _name.decode() + obj = _gl.new_KivaGLFontType(_name, _size, _family, _style, + _encoding, validate) + _swig_setattr(self, KivaGLFontType, "this", obj) + _swig_setattr(self, KivaGLFontType, "thisown", 1) + +# This is a crappy way of overriding the constructor +KivaGLFontType.__init__ = unicode_safe_init +%} + diff --git a/kiva/gl/src/swig/graphics_context.i b/kiva/gl/src/swig/graphics_context.i new file mode 100644 index 000000000..230dfd720 --- /dev/null +++ b/kiva/gl/src/swig/graphics_context.i @@ -0,0 +1,387 @@ + +// typemaps for many enumerated types used in graphics_contexts +%include "constants.i" + +// Language independent exception handler +%include exception.i + +// handle kiva_gl::rect declarations +%include "rect.i" +//%apply kiva_gl::rect_type {kiva_gl::rect_type}; + +%include "agg_typemaps.i" +%apply (double* point_array, int point_count) {(double* pts, int Npts)}; +%apply (double* point_array, int point_count) {(double* start, int Nstart)}; +%apply (double* point_array, int point_count) {(double* end, int Nend)}; +%apply (double* rect_array, int rect_count) {(double* rects, int Nrects)}; +%apply (double* pt_x, double* pt_y) {(double* tx, double* ty)}; +%apply (double* array6) {(double* out)}; +%apply (double* dash_pattern, int n) { (double* pattern, int n)}; +%apply (unsigned char *image_data, int width, int height, int stride) { + (unsigned char *data, int width, int height, int stride) }; +%apply (owned_pointer) { kiva_gl::graphics_context* }; + +// typemaps for double ary[] +%include "sequence_to_array.i" + +%include "rgba_array.i" +%apply rgba_as_array {kiva_gl_agg::rgba&}; +%{ + kiva_gl_agg::rgba _clear_color = kiva_gl_agg::rgba(1,1,1,1); +%} + +%typemap(out) PyObject* +{ + $result = $1; +} + +%{ +#include "kiva_gl_graphics_context.h" +%} + +namespace kiva_gl { + + %rename(GraphicsContextGL) gl_graphics_context; + + class gl_graphics_context : public graphics_context_base + { + public: + gl_graphics_context(int width, int height, + kiva_gl::pix_format_e format=kiva_gl::pix_format_rgb24); + + ~gl_graphics_context(); + + //--------------------------------------------------------------- + // GL-specific methods + //--------------------------------------------------------------- + void gl_init(); + void gl_cleanup(); + void gl_render_path(kiva_gl::compiled_path *path, bool polygon=false, bool fill=false); + void gl_render_points(double** points, bool polygon, bool fill, + kiva_gl::draw_mode_e mode = FILL); + + //--------------------------------------------------------------- + // GraphicsContextBase interface + //--------------------------------------------------------------- + + int bottom_up(); + int width(); + int height(); + int stride(); + + void save_state(); + void restore_state(); + + void flush(); + void synchronize(); + + void begin_page(); + void end_page(); + + void translate_ctm(double x, double y); + void rotate_ctm(double angle); + void scale_ctm(double sx, double sy); + + %feature("shadow") concat_ctm(kiva_gl_agg::trans_affine& m) + %{ + def concat_ctm(self, m): + if isinstance(m, tuple): + _gl.GraphicsContextGL_concat_ctm(self, _AffineMatrix(*m)) + else: + _gl.GraphicsContextGL_concat_ctm(self, m) + %} + void concat_ctm(kiva_gl_agg::trans_affine& m); + + %feature("shadow") set_ctm(kiva_gl_agg::trans_affine& m) + %{ + def set_ctm(self, m): + if isinstance(m, tuple): + _gl.GraphicsContextGL_set_ctm(self, _AffineMatrix(*m)) + else: + _gl.GraphicsContextGL_set_ctm(self, m) + %} + void set_ctm(kiva_gl_agg::trans_affine& m); + + %feature("shadow") get_ctm() + %{ + def get_ctm(self): + tmp = _gl.GraphicsContextGL_get_ctm(self) + return (tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]) + %} + kiva_gl_agg::trans_affine get_ctm(); + + %feature("shadow") format() + %{ + def format(self): + enum = _gl.GraphicsContextGL_format(self) + return pix_format_enum_map[enum] + %} + kiva_gl::pix_format_e format(); + + %feature("shadow") get_image_interpolation() + %{ + def get_image_interpolation(self): + enum = _gl.GraphicsContextGL_get_image_interpolation(self) + return interp_enum_map[enum] + %} + interpolation_e get_image_interpolation(); + + %feature("shadow") set_image_interpolation(kiva_gl::interpolation_e interpolation) + %{ + def set_image_interpolation(self,interp): + enum = interp_string_map[interp] + _gl.GraphicsContextGL_set_image_interpolation(self, enum) + %} + void set_image_interpolation(kiva_gl::interpolation_e interpolation); + + %feature("shadow") set_stroke_color(kiva_gl_agg::rgba& rgba_in) + %{ + def set_stroke_color(self, color): + if is_array(color) and len(color) == 3: + ary = color + r, g, b = ary + color = Rgba(r, g, b) + elif is_array(color) and len(color) == 4: + ary = color + r, g, b, a = ary + color = Rgba(r, g, b, a) + _gl.GraphicsContextGL_set_stroke_color(self, color) + %} + void set_stroke_color(kiva_gl_agg::rgba& rgba_in); + kiva_gl_agg::rgba& get_stroke_color(); + + %feature("shadow") set_fill_color(kiva_gl_agg::rgba& rgba_in) + %{ + def set_fill_color(self, color): + if is_array(color) and len(color) == 3: + ary = color + r, g, b = ary + color = Rgba(r, g, b) + elif is_array(color) and len(color) == 4: + ary = color + r, g, b, a = ary + color = Rgba(r, g, b, a) + _gl.GraphicsContextGL_set_fill_color(self, color) + %} + void set_fill_color(kiva_gl_agg::rgba& rgba_in); + kiva_gl_agg::rgba& get_fill_color(); + + void set_alpha(double value); + double get_alpha(); + void set_antialias(int value); + int get_antialias(); + void set_miter_limit(double value); + void set_flatness(double value); + void set_line_width(double value); + void set_line_join(kiva_gl::line_join_e value); + void set_line_cap(kiva_gl::line_cap_e value); + void set_line_dash(double* pattern, int n, double phase=0); + void set_blend_mode(kiva_gl::blend_mode_e value); + kiva_gl::blend_mode_e get_blend_mode(); + + //--------------------------------------------------------------- + // Path manipulation + //--------------------------------------------------------------- + + void begin_path(); + void move_to(double x, double y); + void line_to( double x, double y); + void curve_to(double cpx1, double cpy1, + double cpx2, double cpy2, + double x, double y); + void quad_curve_to(double cpx, double cpy, double x, double y); + + void arc(double x, double y, double radius, double start_angle, + double end_angle, bool cw=false); + void arc_to(double x1, double y1, double x2, double y2, double radius); + + void close_path(); + void add_path(kiva_gl::compiled_path& other_path); + void lines(double* pts, int Npts); + void line_set(double* start, int Nstart, double* end, int Nend); + void rect(kiva_gl::rect_type &rect); + void rect(double x, double y, double sx, double sy); + void rects(double* all_rects, int Nrects); + compiled_path _get_path(); + + //--------------------------------------------------------------- + // Clipping path manipulation + //--------------------------------------------------------------- + + void clip(); + void even_odd_clip(); + void clip_to_rect(double x, double y, double sx, double sy); + void clip_to_rect(kiva_gl::rect_type &rect); + void clip_to_rects(double* new_rects, int Nrects); + void clip_to_rects(kiva_gl::rect_list_type &rects); + kiva_gl::rect_type transform_clip_rectangle(const kiva_gl::rect_type &rect); + void clear_clip_path(); + + int get_num_clip_regions(); + kiva_gl::rect_type get_clip_region(unsigned int i); + + //--------------------------------------------------------------- + // Painting paths (drawing and filling contours) + //--------------------------------------------------------------- + + // Declare clear() to pass by reference so that the typemap applies, + // even though it is pass by value in the actual C++ class + void clear(kiva_gl_agg::rgba& value=_clear_color); + + void fill_path(); + void eof_fill_path(); + void stroke_path(); + // empty function; for some reason this is abstract in the base class + inline void _stroke_path() { } + + void draw_path(draw_mode_e mode=FILL_STROKE); + void draw_rect(double rect[4], draw_mode_e mode=FILL_STROKE); + + %feature("shadow") draw_marker_at_points(double* pts,int Npts, int size, + kiva_gl::marker_e type = kiva_gl::marker_square) + %{ + def draw_marker_at_points(self, pts, size, kiva_marker_type): + marker = kiva_marker_to_agg.get(kiva_marker_type, None) + if marker is None: + success = 0 + else: + args = (self, pts, int(size), marker) + success = _gl.GraphicsContextGL_draw_marker_at_points( + self, pts, int(size), marker + ) + return success + %} + int draw_marker_at_points(double* pts,int Npts,int size, + kiva_gl::marker_e type=kiva_gl::marker_square); + + void draw_path_at_points(double* pts,int Npts, + kiva_gl::compiled_path& marker, + draw_mode_e mode); + + //--------------------------------------------------------------- + // Image rendering + //--------------------------------------------------------------- + + int draw_image(kiva_gl::graphics_context_base* img, double rect[4], bool force_copy=false); + int draw_image(kiva_gl::graphics_context_base* img); + + //--------------------------------------------------------------- + // Text rendering (NOTE: disabled) + //--------------------------------------------------------------- + + %pythoncode + %{ + def show_text(self, text, point = None): + """Displays text at point, or at the current text pen position + if point is None. Returns true if text displayed properly, + false if there was a font issue or a glyph could not be + rendered. Will handle multi-line text separated by backslash-ns + """ + raise RuntimeError("Text is not supported by OpenGL.") + + def show_text_at_point(self, text, dx, dy): + raise RuntimeError("Text is not supported by OpenGL.") + + def get_text_extent(self, text): + raise RuntimeError("Text is not supported by OpenGL.") + + def get_full_text_extent(self, text): + raise RuntimeError("Text is not supported by OpenGL.") + + def get_font(self): + raise RuntimeError("Text is not supported by OpenGL.") + + def set_font(self, font): + raise RuntimeError("Unable to load font.") + + def is_font_initialized(self): + raise RuntimeError("Font not loaded/initialized.") + + def set_font_size(self, size): + raise RuntimeError("Font not loaded/initialized.") + + def get_freetype_text_matrix(self, *args): + raise RuntimeError("Text is not supported by OpenGL.") + + def get_text_matrix(self, matrix): + raise RuntimeError("Text is not supported by OpenGL.") + + def set_text_matrix(self, matrix): + raise RuntimeError("Text is not supported by OpenGL.") + + def set_text_position(self, tx, ty): + raise RuntimeError("Text is not supported by OpenGL") + + def get_text_position(self): + raise RuntimeError("Text is not supported by OpenGL") + + def set_character_spacing(self, value): + raise RuntimeError("Text is not supported by OpenGL.") + + def get_character_spacing(self): + raise RuntimeError("Text is not supported by OpenGL.") + + def set_text_drawing_mode(self, value): + raise RuntimeError("Text is not supported by OpenGL.") + + %} + + //--------------------------------------------------------------------- + // Gradient support (raises NotImplementedError) + //--------------------------------------------------------------------- + + %pythoncode + %{ + def linear_gradient(self, x1, y1, x2, y2, stops, spread_method, units="userSpaceOnUse"): + raise NotImplementedError("Gradient fills are not supported by OpenGL") + + def radial_gradient(self, cx, cy, r, fx, fy, stops, spread_method, units="userSpaceOnUse"): + raise NotImplementedError("Gradient fills are not supported by OpenGL") + + %} + + //--------------------------------------------------------------- + // Additional methods (added as pure python) + //--------------------------------------------------------------- + + %pythoncode + %{ + def get_empty_path(self): + return CompiledPath() + + def convert_pixel_format(self, pix_format, inplace=0): + """ Convert gc from one pixel format to another. + + NOTE: This has never worked on OpenGL, because draw_image has never + been implemented. It used to inherit the GraphicsContextArry + implementation, which relied on the context having a working + implementation of draw_image which would handle the pixel format + conversion. + """ + return self.__class__(self.width(), self.height(), pix_format=pix_format) + + def save(self, filename, file_format=None, pil_options=None): + """ Save the GraphicsContext to a file. + + NOTE: This has never worked on OpenGL, because it draws this + context into an image context by using the agg `rendering_buffer` + as a source of pixel data. The buffer is never used with OpenGL, + so it is always blank. + """ + raise RuntimeError("Saving is not supported by OpenGL.") + + #---------------------------------------------------------------- + # context manager interface + #---------------------------------------------------------------- + + def __enter__(self): + self.save_state() + + def __exit__(self, type, value, traceback): + self.restore_state() + + %} + + }; + +} diff --git a/kiva/gl/src/swig/numpy.i b/kiva/gl/src/swig/numpy.i new file mode 100644 index 000000000..dd023c5f0 --- /dev/null +++ b/kiva/gl/src/swig/numpy.i @@ -0,0 +1,351 @@ +/* -*- c -*- */ +/* Set the input argument to point to a temporary variable */ + +/* +Here are the typemap helper functions for numpy arrays: + + PyArrayObject* obj_to_array_no_conversion(PyObject* input, int typecode) + PyArrayObject* obj_to_array_allow_conversion(PyObject* input, int typecode, + int& is_new_object) + PyArrayObject* obj_to_array_contiguous_allow_conversion(PyObject* input, + int typecode, + int& is_new_object) + PyArrayObject* make_contiguous(PyArrayObject* ary, int& is_new_object, + int min_dims = 0, int max_dims = 0) + int require_contiguous(PyArrayObject* ary) + int require_last_dimensions_contiguous(PyArrayObject* ary, int dim_count) + int require_dimensions(PyArrayObject* ary, int exact_dimensions) + int require_dimensions(PyArrayObject* ary, int* exact_dimensions, int n) + int require_size(PyArrayObject* ary, int* size, int n) +*/ + +%{ +#include "numpy/arrayobject.h" +#include + +#define is_array(a) ((a) && PyArray_Check((PyArrayObject *)a)) +#define array_type(a) (int)(((PyArrayObject *)a)->descr->type_num) +#define array_dimensions(a) (((PyArrayObject *)a)->nd) +#define array_size(a,i) (((PyArrayObject *)a)->dimensions[i]) +#define array_is_contiguous(a) (PyArray_ISCONTIGUOUS(ary)) + +std::string pytype_string(PyObject* py_obj) +{ + if(py_obj == NULL) return "C NULL value"; + if(PyCallable_Check(py_obj)) return "callable"; + if(PyString_Check(py_obj)) return "string"; + if(PyInt_Check(py_obj)) return "int"; + if(PyFloat_Check(py_obj)) return "float"; + if(PyDict_Check(py_obj)) return "dict"; + if(PyList_Check(py_obj)) return "list"; + if(PyTuple_Check(py_obj)) return "tuple"; + /*if(PyFile_Check(py_obj)) return "file";*/ + if(PyModule_Check(py_obj)) return "module"; + + //should probably do more intergation (and thinking) on these. + /*if(PyCallable_Check(py_obj) && PyInstance_Check(py_obj)) return "callable"; + if(PyInstance_Check(py_obj)) return "instance";*/ + if(PyCallable_Check(py_obj)) return "callable"; + return "unkown type"; +} + +std::string typecode_string(int typecode) +{ + std::string type_names[20] = {"char", "unsigned byte", "byte", "short", + "unsigned short", "int", "unsigned int", + "long", "float", "double", "complex float", + "complex double", "object", "ntype", + "unknown"}; + return type_names[typecode]; +} + +int type_match(int actual_type, int desired_type) +{ + int match; + // Make sure input has correct numpy type. Allow character and byte to + // match also allow int and long to match. + if (actual_type != desired_type && + !(desired_type == PyArray_INT && actual_type == PyArray_LONG) && + !(desired_type == PyArray_LONG && actual_type == PyArray_INT)) + { + match = 0; + } + else + { + match = 1; + } + return match; +} + +PyArrayObject* obj_to_array_no_conversion(PyObject* input, int typecode) +{ + PyArrayObject* ary = NULL; + if (is_array(input) && array_type(input) == typecode) + { + ary = (PyArrayObject*) input; + } + else if is_array(input) + { + char msg[255] = "Array of type '%s' required. Array of type '%s' given"; + std::string desired_type = typecode_string(typecode); + std::string actual_type = typecode_string(array_type(input)); + PyErr_Format(PyExc_TypeError, msg, + desired_type.c_str(), actual_type.c_str()); + ary = NULL; + } + else + { + char msg[255] = "Array of type '%s' required. A %s was given"; + std::string desired_type = typecode_string(typecode); + std::string actual_type = pytype_string(input); + PyErr_Format(PyExc_TypeError, msg, + desired_type.c_str(), actual_type.c_str()); + ary = NULL; + } + return ary; +} + +PyArrayObject* obj_to_array_allow_conversion(PyObject* input, int typecode, + int& is_new_object) +{ + // Convert object to a numpy array with the given typecode. + // + // Return: + // On Success, return a valid PyArrayObject* with the correct type. + // On failure, return NULL. A python error will have been set. + + PyArrayObject* ary = NULL; + if (is_array(input) && type_match(array_type(input),typecode)) + { + ary = (PyArrayObject*) input; + is_new_object = 0; + } + else + { + PyObject* py_obj = PyArray_FromObject(input, typecode, 0, 0); + // If NULL, PyArray_FromObject will have set python error value. + ary = (PyArrayObject*) py_obj; + is_new_object = 1; + } + + return ary; +} + +PyArrayObject* make_contiguous(PyArrayObject* ary, int& is_new_object, + int min_dims = 0, int max_dims = 0) +{ + PyArrayObject* result; + if (array_is_contiguous(ary)) + { + result = ary; + is_new_object = 0; + } + else + { + result = (PyArrayObject*) PyArray_ContiguousFromObject( + (PyObject*)ary, + array_type(ary), + min_dims, + max_dims); + is_new_object = 1; + } + + return result; +} + +PyArrayObject* obj_to_array_contiguous_allow_conversion(PyObject* input, + int typecode, + int& is_new_object) +{ + int is_new1 = 0; + int is_new2 = 0; + PyArrayObject* ary1 = obj_to_array_allow_conversion(input, typecode, + is_new1); + if (ary1) + { + PyArrayObject* ary2 = make_contiguous(ary1, is_new2); + + if ( is_new1 && is_new2) + { + Py_DECREF(ary1); + } + ary1 = ary2; + } + + is_new_object = is_new1 || is_new2; + return ary1; +} + + +int require_contiguous(PyArrayObject* ary) +{ + // Test whether a python object is contiguous. + // + // Return: + // 1 if array is contiguous. + // Otherwise, return 0 and set python exception. + int contiguous = 1; + if (!array_is_contiguous(ary)) + { + char msg[255] = "Array must be contiguous. A discontiguous array was given"; + PyErr_SetString(PyExc_TypeError, msg); + contiguous = 0; + } + return contiguous; +} + +// Useful for allowing images with discontiguous first dimension. +// This sort of array is used for arrays mapped to Windows bitmaps. +/* +int require_last_dimensions_contiguous(PyArrayObject* ary, int dim_count) +{ + int contiguous = 1; + if (array_is_contiguous(ary)) + { + char msg[255] = "Array must be contiguous. A discontiguous array was given"; + PyErr_SetString(PyExc_TypeError, msg); + contiguous = 0; + } + return contiguous; +} +*/ + +int require_dimensions(PyArrayObject* ary, int exact_dimensions) +{ + int success = 1; + if (array_dimensions(ary) != exact_dimensions) + { + char msg[255] = "Array must be have %d dimensions. Given array has %d dimensions"; + PyErr_Format(PyExc_TypeError, msg, + exact_dimensions, array_dimensions(ary)); + success = 0; + } + return success; +} + +int require_dimensions(PyArrayObject* ary, int* exact_dimensions, int n) +{ + int success = 0; + int i; + for (i = 0; i < n && !success; i++) + { + if (array_dimensions(ary) == exact_dimensions[i]) + { + success = 1; + } + } + if (!success) + { + char dims_str[255] = ""; + char s[255]; + for (int i = 0; i < n-1; i++) + { + sprintf(s, "%d, ", exact_dimensions[i]); + strcat(dims_str,s); + } + sprintf(s, " or %d", exact_dimensions[n-1]); + strcat(dims_str,s); + char msg[255] = "Array must be have %s dimensions. Given array has %d dimensions"; + PyErr_Format(PyExc_TypeError, msg, dims_str, array_dimensions(ary)); + } + return success; +} + +int require_size(PyArrayObject* ary, int* size, int n) + +{ + int i; + int success = 1; + for(i=0; i < n;i++) + { + if (size[i] != -1 && size[i] != array_size(ary,i)) + { + success = 0; + } + } + + if (!success) + { + int len; + char desired_dims[255] = "["; + char s[255]; + for (i = 0; i < n; i++) + { + if (size[i] == -1) + { + sprintf(s, "*,"); + } + else + { + sprintf(s, "%d,", size[i]); + } + strcat(desired_dims,s); + } + len = strlen(desired_dims); + desired_dims[len-1] = ']'; + + char actual_dims[255] = "["; + for (i = 0; i < n; i++) + { + sprintf(s, "%d,", (int)array_size(ary,i)); + strcat(actual_dims,s); + } + len = strlen(actual_dims); + actual_dims[len-1] = ']'; + + char msg[255] = "Array must be have shape of %s. Given array has shape of %s"; + PyErr_Format(PyExc_TypeError, msg, desired_dims, actual_dims); + } + return success; +} + + +%} + +%pythoncode %{ +from numpy import ndarray + +def is_array(obj): + return type(obj) is ndarray + +def is_correct_type(obj, numpy_type): + return is_array(obj) and (obj.dtype == numpy_type) + +def numpy_check(obj, typecode, + exact_size = [], + must_be_contiguous = 1, + allow_coersion = 0): + + if is_correct_type(obj, typecode): + ary = obj + elif allow_coersion: + ary = asarray(obj,typecode) + else: + raise TypeError("input is not an array or the array has the wrong type") + + if must_be_contiguous and not ary.flags["CONTIGUOUS"]: + if allow_coersion: + ary = ary.copy() + else: + raise TypeError("input array must be contiguous") + + # check number of dimensions + required_dims = len(exact_size) + if required_dims and required_dims != len(ary.shape): + raise ValueError("The input array does not have the correct shape") + + # check exact shape of each dimension + cnt = 0 + for desired,actual in zip(exact_size,ary.shape): + if desired != -1 and desired != actual: + raise ValueError("The %d dimensions of the array has the wrong shape" % (cnt)) + cnt += 1 + + return ary +%} + +%init %{ + Py_Initialize(); + import_array(); + PyImport_ImportModule("numpy"); +%} diff --git a/kiva/gl/src/swig/rect.i b/kiva/gl/src/swig/rect.i new file mode 100644 index 000000000..c1ffde1c1 --- /dev/null +++ b/kiva/gl/src/swig/rect.i @@ -0,0 +1,38 @@ +%{ +#include "kiva_gl_rect.h" +%} + +%typemap(in) (kiva_gl::rect_type &rect) +{ + PyArrayObject* ary=NULL; + int is_new_object; + ary = obj_to_array_contiguous_allow_conversion($input, PyArray_DOUBLE, + is_new_object); + + int size[1] = {4}; + if (!ary || + !require_dimensions(ary, 1) || + !require_size(ary, size, 1)) + { + goto fail; + } + + double* data = (double*)(ary->data); + kiva_gl::rect_type rect(data[0], data[1], data[2], data[3]); + $1 = ▭ + + if (is_new_object) + { + Py_DECREF(ary); + } +} + +%typemap(out) kiva_gl::rect_type +{ + PyObject *pt = PyTuple_New(4); + PyTuple_SetItem(pt,0,PyFloat_FromDouble($1.x)); + PyTuple_SetItem(pt,1,PyFloat_FromDouble($1.y)); + PyTuple_SetItem(pt,2,PyFloat_FromDouble($1.w)); + PyTuple_SetItem(pt,3,PyFloat_FromDouble($1.h)); + $result = pt; +} diff --git a/kiva/gl/src/swig/rgba.i b/kiva/gl/src/swig/rgba.i new file mode 100644 index 000000000..910dc031e --- /dev/null +++ b/kiva/gl/src/swig/rgba.i @@ -0,0 +1,95 @@ +%{ +#include "agg_color_rgba.h" +%} + +%include "numpy.i" + +%typemap(in, numinputs=0) double* out (double temp[4]) { + $1 = temp; +} + +%typemap(argout) double *out { + // Append output value $1 to $result + npy_intp dims = 4; + PyArrayObject* ary_obj = (PyArrayObject*) PyArray_SimpleNew(1, &dims, PyArray_DOUBLE); + if( ary_obj == NULL ) + return NULL; + double* data = (double*)ary_obj->data; + for (int i=0; i < 4;i++) + data[i] = $1[i]; + Py_DECREF($result); + $result = PyArray_Return(ary_obj); +} + +%typemap(check) (double r) +{ + if ($1 < 0.0 || $1 > 1.0) + { + PyErr_Format(PyExc_ValueError, + "color values must be between 0.0 and 1.0, Got: %g", $1); + } +} + +%apply (double r) {double g, double b, double a}; + + +namespace kiva_gl_agg +{ + %rename(_Rgba) rgba; + struct rgba + { + double r; + double g; + double b; + double a; + + rgba(double r_=0.0, double g_=0.0, double b_=0.0, double a_=1.0); + //void opacity(double a_); + //double opacity() const; + rgba gradient(rgba c, double k) const; + const rgba &premultiply(); + }; +} + +%extend kiva_gl_agg::rgba +{ + char *__repr__() + { + static char tmp[1024]; + sprintf(tmp,"Rgba(%g, %g, %g, %g)", self->r, self->g, self->b, self->a); + return tmp; + } + int __eq__(kiva_gl_agg::rgba& o) + { + return (self->r == o.r && self->g == o.g && + self->b == o.b && self->a == o.a); + } + void asarray(double* out) + { + out[0] = self->r; + out[1] = self->g; + out[2] = self->b; + out[3] = self->a; + } +} + + +%pythoncode %{ +def is_sequence(arg): + try: + len(arg) + return 1 + except: + return 0 + +# Use sub-class to allow sequence as input +class Rgba(_Rgba): + def __init__(self,*args): + if len(args) == 1 and is_sequence(args[0]): + args = tuple(args[0]) + if len(args) not in [3,4]: + raise ValueError("array argument must be 1x3 or 1x4") + _Rgba.__init__(self,*args) +%} + +%clear double r, double g, double b, double a; diff --git a/kiva/gl/src/swig/rgba_array.i b/kiva/gl/src/swig/rgba_array.i new file mode 100644 index 000000000..b64a780d3 --- /dev/null +++ b/kiva/gl/src/swig/rgba_array.i @@ -0,0 +1,91 @@ +// -------------------------------------------------------------------------- +// +// Convert kiva_gl_agg::rgba types to/from Numeric arrays. The rgba_as_array +// typemap will accept any 3 or 4 element sequence of float compatible +// objects and convert them into an kiva_gl_agg::rgba object. +// +// The typemap also converts any rgba output value back to a numeric array +// in python. This is a more useful representation for numerical +// manipulation. +// +// -------------------------------------------------------------------------- + +%{ + #include "agg_color_rgba.h" +%} + +%include "numpy.i" + +#ifdef SWIGPYTHON + +%typemap(in) rgba_as_array (int must_free=0) +{ + must_free = 0; + if ((SWIG_ConvertPtr($input,(void **) &$1, SWIGTYPE_p_kiva_gl_agg__rgba, + SWIG_POINTER_EXCEPTION | 0 )) == -1) + { + PyErr_Clear(); + if (!PySequence_Check($input)) + { + PyErr_SetString(PyExc_TypeError,"Expecting a sequence"); + return NULL; + } + + int seq_len = PyObject_Length($input); + if (seq_len != 3 && seq_len != 4) + { + PyErr_SetString(PyExc_ValueError, + "Expecting a sequence with 3 or 4 elements"); + return NULL; + } + + double temp[4] = {0.0,0.0,0.0,1.0}; + for (int i =0; i < seq_len; i++) + { + PyObject *o = PySequence_GetItem($input,i); + if (PyFloat_Check(o)) + { + temp[i] = PyFloat_AsDouble(o); + } + else + { + PyObject* converted = PyNumber_Float(o); + if (!converted) + { + PyErr_SetString(PyExc_TypeError, + "Expecting a sequence of floats"); + return NULL; + } + temp[i] = PyFloat_AsDouble(converted); + Py_DECREF(converted); + } + if ((temp[i] < 0.0) || (temp [i] > 1.0)) + { + PyErr_SetString(PyExc_ValueError, + "Color values must be between 0.0 an 1.0"); + return NULL; + } + } + $1 = new kiva_gl_agg::rgba(temp[0],temp[1],temp[2],temp[3]); + must_free = 1; + } +} + +%typemap(freearg) rgba_as_array { + if (must_free$argnum) + delete $1; +} + +%typemap(out) rgba_as_array +{ + npy_intp size = 4; + $result = PyArray_SimpleNew(1, &size, PyArray_DOUBLE); + double* data = (double*)((PyArrayObject*)$result)->data; + data[0] = $1->r; + data[1] = $1->g; + data[2] = $1->b; + data[3] = $1->a; +} + +#endif + diff --git a/kiva/gl/src/swig/sequence_to_array.i b/kiva/gl/src/swig/sequence_to_array.i new file mode 100644 index 000000000..75ad1fbf9 --- /dev/null +++ b/kiva/gl/src/swig/sequence_to_array.i @@ -0,0 +1,34 @@ +// Map a Python sequence into any sized C double array +// This handles arrays and sequences with non-float values correctly. +// !! Optimize for array conversion?? + +#ifdef SWIGPYTHON + +%typemap(in) double[ANY](double temp[$1_dim0]) { + if (!PySequence_Check($input)) { + PyErr_SetString(PyExc_TypeError, "Expecting a sequence"); + return NULL; + } + if (PyObject_Length($input) != $1_dim0) { + PyErr_SetString(PyExc_ValueError, "Expecting a sequence with $1_dim0 elements"); + return NULL; + } + for (int i=0; i < $1_dim0; ++i) { + PyObject *o = PySequence_GetItem($input, i); + if (PyFloat_Check(o)) { + temp[i] = PyFloat_AsDouble(o); + } + else { + PyObject* converted = PyNumber_Float(o); + if (!converted) { + PyErr_SetString(PyExc_TypeError, "Expecting a sequence of floats"); + return NULL; + } + temp[i] = PyFloat_AsDouble(converted); + Py_DECREF(converted); + } + } + $1 = &temp[0]; +} + +#endif diff --git a/kiva/setup.py b/kiva/setup.py index 617f78cf2..8dd6b3800 100644 --- a/kiva/setup.py +++ b/kiva/setup.py @@ -28,6 +28,7 @@ def configuration(parent_package=None, top_path=None): config.add_subpackage('agg') config.add_subpackage('fonttools') config.add_subpackage('fonttools.tests') + config.add_subpackage('gl') config.add_subpackage('trait_defs') config.add_subpackage('trait_defs.ui') config.add_subpackage('trait_defs.ui.*') diff --git a/kiva/tests/test_gl_drawing.py b/kiva/tests/test_gl_drawing.py index 28b9831b4..a6a98245c 100644 --- a/kiva/tests/test_gl_drawing.py +++ b/kiva/tests/test_gl_drawing.py @@ -9,11 +9,11 @@ else: PYGLET_NOT_AVAILABLE = False - from kiva.tests.drawing_tester import DrawingImageTester is_windows = (sys.platform in ('win32', 'cygwin')) + @unittest.skipIf(is_windows, "Pyglet/GL backend issues on Windows") @unittest.skipIf(PYGLET_NOT_AVAILABLE, "Cannot import pyglet") class TestGLDrawing(DrawingImageTester, unittest.TestCase): diff --git a/kiva/tests/test_qpainter_drawing.py b/kiva/tests/test_qpainter_drawing.py index 4ce54abd6..4bf770b95 100644 --- a/kiva/tests/test_qpainter_drawing.py +++ b/kiva/tests/test_qpainter_drawing.py @@ -1,3 +1,4 @@ +import sys import unittest try: @@ -6,9 +7,15 @@ QT_NOT_AVAILABLE = True else: QT_NOT_AVAILABLE = False +try: + from pyface.qt import is_qt5 +except ImportError: + is_qt5 = False from kiva.tests.drawing_tester import DrawingImageTester +is_linux = sys.platform.startswith('linux') + @unittest.skipIf(QT_NOT_AVAILABLE, "Cannot import qt") class TestQPainterDrawing(DrawingImageTester, unittest.TestCase): @@ -26,6 +33,14 @@ def create_graphics_context(self, width, height): from kiva.qpainter import GraphicsContext return GraphicsContext((width, height)) + @unittest.skipIf(is_qt5 and is_linux, "Currently segfaulting") + def test_text(self): + super().test_text() + + @unittest.skipIf(is_qt5 and is_linux, "Currently segfaulting") + def test_text_clip(self): + super().test_text_clip() + if __name__ == "__main__": unittest.main() diff --git a/setup.py b/setup.py index c02072f72..e38891652 100644 --- a/setup.py +++ b/setup.py @@ -203,6 +203,10 @@ def run(self): join("agg", "plat_support.py"), join("agg", "agg_wrap.cpp"), + # Common GL + join("gl", "gl.py"), + join("gl", "gl_wrap.cpp"), + # Mac join("quartz", "ABCGI.so"), join("quartz", "ABCGI.c"), @@ -217,11 +221,17 @@ def run(self): join("agg", "src", "win32", "plat_support.pyd"), join("agg", "src", "win32", "plat_support_wrap.cpp"), + # Win32 GL + join("gl", "_gl.pyd"), + # *nix Agg join("agg", "_agg.so"), join("agg", "_plat_support.so"), join("agg", "src", "x11", "plat_support_wrap.cpp"), + # *nix GL + join("gl", "_gl.so"), + # Misc join("agg", "src", "gl", "plat_support_wrap.cpp"), join("agg", "src", "gl", "plat_support.py"),