-
Notifications
You must be signed in to change notification settings - Fork 45
Create an independent renderer for draw_marker_at_points #724
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # (C) Copyright 2005-2021 Enthought, Inc., Austin, TX | ||
| # All rights reserved. | ||
| # | ||
| # This software is provided without warranty under the terms of the BSD | ||
| # license included in LICENSE.txt and may be redistributed only 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 libcpp cimport bool | ||
|
|
||
| cdef extern from "marker_renderer.h" namespace "agg24markers": | ||
| cdef cppclass pixfmt_abgr32: | ||
| pass | ||
| cdef cppclass pixfmt_argb32: | ||
| pass | ||
| cdef cppclass pixfmt_bgra32: | ||
| pass | ||
| cdef cppclass pixfmt_rgba32: | ||
| pass | ||
| cdef cppclass pixfmt_bgr24: | ||
| pass | ||
| cdef cppclass pixfmt_rgb24: | ||
| pass | ||
|
|
||
|
|
||
| cdef extern from "marker_renderer.h" namespace "kiva_markers": | ||
| # This is just here for the type signature | ||
| cdef enum marker_type: | ||
| pass | ||
|
|
||
| # Abstract base class | ||
| cdef cppclass marker_renderer_base: | ||
| bool draw_markers(double* pts, unsigned Npts, | ||
| unsigned size, marker_type marker, | ||
| double* fill, double* stroke) | ||
| void transform(double sx, double sy, | ||
| double shx, double shy, | ||
| double tx, double ty) | ||
|
|
||
| # Template class | ||
| cdef cppclass marker_renderer[pixfmt_T]: | ||
| marker_renderer(unsigned char* buf, unsigned width, unsigned height, | ||
| int stride, bool bottom_up) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| # (C) Copyright 2005-2021 Enthought, Inc., Austin, TX | ||
| # All rights reserved. | ||
| # | ||
| # This software is provided without warranty under the terms of the BSD | ||
| # license included in LICENSE.txt and may be redistributed only 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! | ||
| import cython | ||
| import numpy as np | ||
| from numpy cimport uint8_t | ||
|
|
||
| cimport _marker_renderer | ||
|
|
||
| ctypedef _marker_renderer.marker_renderer_base renderer_base_t | ||
|
|
||
| @cython.internal | ||
| cdef class MarkerRendererBase: | ||
| cdef renderer_base_t* _this | ||
| cdef object py_array | ||
|
|
||
| def __dealloc__(self): | ||
| del self._this | ||
|
|
||
| cdef int base_init(self, image) except -1: | ||
| if image is None: | ||
| raise ValueError('image argument must not be None.') | ||
|
|
||
| # Retain a reference to the memory view supplied to the constructor | ||
| # so that it lives as long as this object | ||
| self.py_array = image | ||
|
|
||
| def draw_markers(self, points, size, marker, fill, stroke): | ||
| """draw_markers(points, size, marker, fill, stroke) | ||
| Draw markers at a collection of points. | ||
|
|
||
| :param points: An Nx2 iterable of (x, y) points for marker positions | ||
| :param size: An integer pixel size for each marker | ||
| :param marker: A Kiva marker enum integer | ||
| :param fill: Fill color given as an iterable of 4 numbers (R, G, B, A) | ||
| :param stroke: Line color given as an iterable of 4 numbers (R, G, B, A) | ||
|
|
||
| :returns: True if any markers were drawn, False otherwise | ||
| """ | ||
| cdef: | ||
| double[:,::1] _points = np.asarray(points, dtype=np.float64, order='c') | ||
| double[::1] _fill = np.asarray(fill, dtype=np.float64, order='c') | ||
| double[::1] _stroke = np.asarray(stroke, dtype=np.float64, order='c') | ||
| unsigned _size = <unsigned>size | ||
| _marker_renderer.marker_type _marker = <_marker_renderer.marker_type>marker | ||
|
jwiggins marked this conversation as resolved.
|
||
|
|
||
| if _points.shape[1] != 2: | ||
| msg = "points argument must be an iterable of (x, y) pairs." | ||
| raise ValueError(msg) | ||
|
jwiggins marked this conversation as resolved.
|
||
| if _stroke.shape[0] != 4: | ||
| msg = "stroke argument must be an iterable of 4 numbers." | ||
| raise ValueError(msg) | ||
| if _fill.shape[0] != 4: | ||
| msg = "fill argument must be an iterable of 4 numbers." | ||
| raise ValueError(msg) | ||
|
|
||
| return self._this.draw_markers( | ||
| &_points[0][0], _points.shape[0], _size, _marker, | ||
| &_fill[0], &_stroke[0] | ||
| ) | ||
|
|
||
| def transform(self, sx, sy, shx, shy, tx, ty): | ||
| """transform(sx, sy, shx, shy, tx, ty) | ||
| Set the transform to be applied to the marker points and size. | ||
|
|
||
| :param sx: Scale in X | ||
| :param sy: Scale in Y | ||
| :param shx: Shear in X | ||
| :param shy: Shear in Y | ||
| :param tx: Translation in X | ||
| :param ty: Translation in Y | ||
| """ | ||
| cdef: | ||
| double _sx = <double>sx | ||
| double _sy = <double>sy | ||
| double _shx = <double>shx | ||
| double _shy = <double>shy | ||
| double _tx = <double>tx | ||
| double _ty = <double>ty | ||
|
|
||
| self._this.transform(_sx, _sy, _shx, _shy, _tx, _ty) | ||
|
|
||
|
|
||
| # Template specializations | ||
| ctypedef _marker_renderer.marker_renderer[_marker_renderer.pixfmt_abgr32] renderer_abgr32_t | ||
| ctypedef _marker_renderer.marker_renderer[_marker_renderer.pixfmt_argb32] renderer_argb32_t | ||
| ctypedef _marker_renderer.marker_renderer[_marker_renderer.pixfmt_bgra32] renderer_bgra32_t | ||
| ctypedef _marker_renderer.marker_renderer[_marker_renderer.pixfmt_rgba32] renderer_rgba32_t | ||
| ctypedef _marker_renderer.marker_renderer[_marker_renderer.pixfmt_bgr24] renderer_bgr24_t | ||
| ctypedef _marker_renderer.marker_renderer[_marker_renderer.pixfmt_rgb24] renderer_rgb24_t | ||
|
|
||
| cdef class MarkerRendererABGR32(MarkerRendererBase): | ||
| def __cinit__(self, uint8_t[:,:,::1] image, bottom_up=True): | ||
| self.base_init(image) | ||
| self._this = <renderer_base_t*> new renderer_abgr32_t( | ||
| &image[0][0][0], image.shape[1], image.shape[0], image.strides[0], bottom_up | ||
| ) | ||
|
|
||
| cdef class MarkerRendererARGB32(MarkerRendererBase): | ||
| def __cinit__(self, uint8_t[:,:,::1] image, bottom_up=True): | ||
| self.base_init(image) | ||
| self._this = <renderer_base_t*> new renderer_argb32_t( | ||
| &image[0][0][0], image.shape[1], image.shape[0], image.strides[0], bottom_up | ||
| ) | ||
|
|
||
| cdef class MarkerRendererBGRA32(MarkerRendererBase): | ||
| def __cinit__(self, uint8_t[:,:,::1] image, bottom_up=True): | ||
| self.base_init(image) | ||
| self._this = <renderer_base_t*> new renderer_bgra32_t( | ||
| &image[0][0][0], image.shape[1], image.shape[0], image.strides[0], bottom_up | ||
| ) | ||
|
|
||
| cdef class MarkerRendererRGBA32(MarkerRendererBase): | ||
| def __cinit__(self, uint8_t[:,:,::1] image, bottom_up=True): | ||
| self.base_init(image) | ||
| self._this = <renderer_base_t*> new renderer_rgba32_t( | ||
| &image[0][0][0], image.shape[1], image.shape[0], image.strides[0], bottom_up | ||
| ) | ||
|
|
||
| cdef class MarkerRendererBGR24(MarkerRendererBase): | ||
| def __cinit__(self, uint8_t[:,:,::1] image, bottom_up=True): | ||
| self.base_init(image) | ||
| self._this = <renderer_base_t*> new renderer_bgr24_t( | ||
| &image[0][0][0], image.shape[1], image.shape[0], image.strides[0], bottom_up | ||
| ) | ||
|
|
||
| cdef class MarkerRendererRGB24(MarkerRendererBase): | ||
| def __cinit__(self, uint8_t[:,:,::1] image, bottom_up=True): | ||
| self.base_init(image) | ||
| self._this = <renderer_base_t*> new renderer_rgb24_t( | ||
| &image[0][0][0], image.shape[1], image.shape[0], image.strides[0], bottom_up | ||
| ) | ||
|
Comment on lines
+98
to
+138
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You think there's a nicer way to do these specializations?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. has no understanding whatsoever about templates/specializations so cannot provide constructive comments Should we ask internally if anyone knows better?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| from kiva.abstract_graphics_context import AbstractGraphicsContext | ||
| import kiva.constants as constants | ||
| from kiva.fonttools import Font | ||
| from kiva.marker_renderer import MarkerRenderer | ||
|
|
||
| # These are the symbols that a backend has to define. | ||
| __all__ = ["CompiledPath", "Font", "font_metrics_provider", "GraphicsContext"] | ||
|
|
@@ -89,8 +90,12 @@ def __init__(self, size, *args, **kwargs): | |
| self.pix_format = kwargs.get('pix_format', 'rgba32') | ||
|
|
||
| shape = (self._height, self._width, 4) | ||
| buffer = np.zeros(shape, dtype=np.uint8) | ||
| canvas_klass = pix_format_canvases[self.pix_format] | ||
| self.gc = canvas_klass(np.zeros(shape, dtype=np.uint8), bottom_up=True) | ||
| self.gc = canvas_klass(buffer, bottom_up=True) | ||
| self.marker_gc = MarkerRenderer( | ||
| buffer, pix_format=self.pix_format, bottom_up=True | ||
| ) | ||
|
|
||
| # init the state variables | ||
| clip = agg.Rect(0, 0, self._width, self._height) | ||
|
|
@@ -826,6 +831,33 @@ def draw_path_at_points(self, points, path, mode=constants.FILL_STROKE): | |
| fill=self.fill_paint, | ||
| ) | ||
|
|
||
| def draw_marker_at_points(self, points_array, size, | ||
| marker=constants.SQUARE_MARKER): | ||
| """ Draw a marker at a collection of points | ||
| """ | ||
| # Apply the current transform | ||
| ctm = self.transform | ||
| self.marker_gc.transform( | ||
| ctm.sx, ctm.sy, | ||
| ctm.shx, ctm.shy, | ||
| ctm.tx, ctm.ty, | ||
| ) | ||
|
|
||
| # Grab the fill and stroke colors (where possible) | ||
| fill = (0.0, 0.0, 0.0, 0.0) | ||
| stroke = (0.0, 0.0, 0.0, 1.0) | ||
| if isinstance(self.fill_paint, agg.SolidPaint): | ||
| fp = self.fill_paint | ||
| fill = (fp.r, fp.g, fp.b, fp.a) | ||
| if isinstance(self.stroke_paint, agg.SolidPaint): | ||
| sp = self.stroke_paint | ||
| stroke = (sp.r, sp.g, sp.b, sp.a) | ||
|
Comment on lines
+846
to
+854
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it would be better to add
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm not sure i fully understand what you're recommending here.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I won't doing anything about it in this PR |
||
|
|
||
| # Draw using the marker renderer | ||
| return self.marker_gc.draw_markers( | ||
| points_array, size, marker, fill, stroke | ||
| ) | ||
|
|
||
| def save(self, filename, file_format=None, pil_options=None): | ||
| """ Save the contents of the context to a file | ||
| """ | ||
|
|
@@ -841,7 +873,7 @@ def save(self, filename, file_format=None, pil_options=None): | |
| os.path.splitext(filename)[1][1:] if isinstance(filename, str) | ||
| else '' | ||
| ) | ||
|
|
||
| # Check the output format to see if it can handle an alpha channel. | ||
| no_alpha_formats = ('jpg', 'bmp', 'eps', 'jpeg') | ||
| if ext in no_alpha_formats or file_format.lower() in no_alpha_formats: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # (C) Copyright 2005-2021 Enthought, Inc., Austin, TX | ||
| # All rights reserved. | ||
| # | ||
| # This software is provided without warranty under the terms of the BSD | ||
| # license included in LICENSE.txt and may be redistributed only 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 kiva._marker_renderer import ( | ||
| MarkerRendererABGR32, MarkerRendererARGB32, MarkerRendererBGR24, | ||
| MarkerRendererBGRA32, MarkerRendererRGB24, MarkerRendererRGBA32, | ||
| ) | ||
|
|
||
| __all__ = ["MarkerRenderer"] | ||
|
|
||
| _renderers = { | ||
| "abgr32": (MarkerRendererABGR32, 4), | ||
| "argb32": (MarkerRendererARGB32, 4), | ||
| "bgra32": (MarkerRendererBGRA32, 4), | ||
| "rgba32": (MarkerRendererRGBA32, 4), | ||
| "bgr24": (MarkerRendererBGR24, 3), | ||
| "rgb24": (MarkerRendererRGB24, 3), | ||
| } | ||
|
|
||
|
|
||
| def MarkerRenderer(buffer, pix_format="bgra32", bottom_up=True): | ||
| """ MarkerRenderer(buffer, pix_format="bgra32", bottom_up=True) | ||
| Create a specialized renderer for implementing ``draw_marker_at_points``. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| buffer : ndarray | ||
| A MxNx{3,4} numpy array of uint8 to be used as the backing pixel store | ||
| pix_format : str | ||
| A string specifying the pixel format. Same as what it passed to | ||
| ``GraphicsContext``. | ||
| bottom_up : bool [optional, defaults to True] | ||
| If True, the origin is bottom-left instead of top-left. | ||
|
|
||
| Returns | ||
| ------- | ||
| renderer : A new MarkerRenderer instance. | ||
| """ | ||
| klass, components = _renderers.get(pix_format, (None, 0)) | ||
| if klass is None: | ||
| raise ValueError(f"{pix_format} is not a supported pixel format") | ||
|
|
||
| if (str(buffer.dtype) != "uint8" or buffer.ndim != 3 | ||
| or buffer.shape[2] != components): | ||
| raise ValueError(f"Pixel buffer must be MxNx{components} and uint8") | ||
|
|
||
| return klass(buffer, bottom_up=bottom_up) |
Uh oh!
There was an error while loading. Please reload this page.