-
Notifications
You must be signed in to change notification settings - Fork 44
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
Create an independent renderer for draw_marker_at_points #724
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
I ran the benchmark and the celiagg draw_marker_at_points
results look correct.
I did not look through most of the changes line by line as you suggested (skipped bulk of the .h files). I looked through celiagg implementation, test, etc. and everything made sense. I walked through the cython code and it seemed to all make sense, but given my limited exposure it might be good to get another pair of eyes on those changes specifically
// (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! | ||
#ifndef KIVA_MARKER_RENDERER_H | ||
#define KIVA_MARKER_RENDERER_H |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is new code. Only the code in kiva/markers/agg
is copied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several additional markers
methods in AGG that we could use to speed up various code paths in chaco's scatterplot (size and color variations). With the marker rendering existing independently of the kiva backend, we can even entertain such an idea...
template<class T> | ||
void markers(int n, const T* x, const T* y, const T* r, marker_e type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using this method would give us a draw_marker_at_points
where size
could be an array (it's r
in the signature).
template<class T> | ||
void markers(int n, const T* x, const T* y, const T* r, const color_type* fc, marker_e type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this version would give per-marker fill color selection (in addition to per-marker sizes).
template<class T> | ||
void markers(int n, const T* x, const T* y, const T* r, const color_type* fc, const color_type* lc, marker_e type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable fill and stroke colors.
Absolute +1 for the ability to provide arrays of sizes, markers shapes and colors. That would permit huge simplification of scatterplot/cmap_scatterplot code and potentially speed things up as well, since currently differences in color/marker/size mean rendering calls in Python loops. |
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 | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You think there's a nicer way to do these specializations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to add set_stroke_color
and set_fill_color
methods to MarkerRenderer*
so that we can just pass through colors from GraphicsContext.set_*_color
methods? This particular block of code is just here in case a gradient fill color was set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not sure i fully understand what you're recommending here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add set_*_color
methods to MarkerRenderer
, then in the set_*_color
methods of GraphicsContext
call self.marker_gc.set_*_color(color)
so that the marker renderer stays in sync with the simple fill and stroke colors. If you set a gradient on the celiagg backend and then call draw_marker_at_points
, the fill color of the markers will be transparent. One could argue that it's a bug in the code doing the drawing, but it's a silent failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't doing anything about it in this PR
Something I left out here: I didn't commit the 30k lines of Cython-generated C++ code like what was done for |
With the code copied from Agg, was this from the version in Kiva, or from the updated non-GPL Agg repo? |
From Kiva, but we updated that version from the upstream SVN repo in #288 |
QPainter prefers premultiplied ARGB32 for its internal buffer format, I think - is that going to make using this for the QPainter backend difficult? Don't have the insight into Agg's internal buffer formats to know if there is a mismatch here. |
AGG has pixel formats for everything, basically: enable/kiva/markers/agg/agg_pixfmt_rgba.h Lines 2750 to 2753 in cd06311
We can make more versions of MarkerRenderer as needed. I believe Quartz uses premultiplied alpha as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add kiva/_marker_renderer.cpp
to the git ignore list given that we don't want to include it in the git repo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with a couple of comments/questions (some nitpicky). I too ran the benchmark and things looked good on windows.
Also, I don't think we want to implement |
You mean in this PR, right? No complaints from my side there. Adding this to an AGG backend is fairly trivial because we're already in control of the backing store there. It might be a bit more challenging for the Quartz and QPainter backends. |
I'm testing this branch (and enable main branch) against an internal application - and there's one problem i'm running into when I use the celiagg backend. The internal application uses the Lines 486 to 489 in 2ce5387
left_triangle , right_triangle , pentagon , hexagon , hexagon2 , star , cross_plus .
|
Also, there are a ton of log messages now from
|
These markers don't have native agg versions, and so are rendered by paths only; so |
But this worked fine with the agg backend, which is why im slightly concerned |
Additionally, some of these correspond to agg markers, so they could be supported: Agg markers are (checked if it is exposed by Kiva):
So left and right triangles would be available out of the box. |
For the record here, issue was that Chaco code expects |
Thanks for all the great feedback and testing. This should be good to go once CI is happy and y'all have taken a look at my new changes. |
Not going to have enough time to do this myself today, but if we haven't we should make sure that the various Chaco scatterplot types work with this as expected. They are the primary users. For fun it might be also worth comparing how fast rendering large numbers of "pixel" markers is on agg and celiagg. Based on really old numbers I would expect ~100k to 1m points to be reasonably performant. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closes #708
This was surprisingly straightforward. Basically, since marker rendering is a much more primitive form of rendering, it's not too bad to just copy the subset of AGG which implements it.
As was the case with #392, there's a bunch of code from AGG here which is just copied in. Well, copied and given a new
agg24markers
namespace so that there's no danger of symbol collisions. (also LF line endings and stripped whitespace) You can basically ignore it for the purpose of reviewing and concentrate on the Python and Cython code.I've added
draw_marker_at_points
to the celiagg backend to show what an implementation might look like.