@@ -728,19 +728,23 @@ void GraphicsContextRenderer::draw_markers(
728
728
auto const & matrix =
729
729
matrix_from_transform (transform, get_additional_state ().height );
730
730
731
- auto const & fc_raw =
731
+ auto const & fc_raw_opt =
732
732
fc ? to_rgba (*fc, get_additional_state ().alpha ) : std::optional<rgba_t >{};
733
733
auto const & ec_raw = get_rgba ();
734
734
735
735
auto const & draw_one_marker = [&](cairo_t * cr, double x, double y) -> void {
736
736
auto const & m = cairo_matrix_t {
737
737
marker_matrix.xx , marker_matrix.yx , marker_matrix.xy , marker_matrix.yy ,
738
738
marker_matrix.x0 + x, marker_matrix.y0 + y};
739
- fill_and_stroke_exact (cr, marker_path, &m, fc_raw , ec_raw);
739
+ fill_and_stroke_exact (cr, marker_path, &m, fc_raw_opt , ec_raw);
740
740
};
741
741
742
+ // Pixel markers *must* be drawn snapped.
743
+ auto const & is_pixel_marker =
744
+ py_eq (marker_path, detail::PIXEL_MARKER.attr (" get_path" )())
745
+ && py_eq (marker_transform, detail::PIXEL_MARKER.attr (" get_transform" )());
742
746
auto const & simplify_threshold =
743
- has_vector_surface (cr_)
747
+ is_pixel_marker || has_vector_surface (cr_)
744
748
? 0 : rc_param (" path.simplify_threshold" ).cast <double >();
745
749
auto patterns = std::unique_ptr<cairo_pattern_t *[]>{};
746
750
auto const & n_subpix = // NOTE: Arbitrary limit of 1/16.
@@ -827,6 +831,25 @@ void GraphicsContextRenderer::draw_markers(
827
831
cairo_pattern_destroy (patterns[i]);
828
832
}
829
833
834
+ } else if (is_pixel_marker && !has_vector_surface (cr_)) {
835
+ auto const & surface = cairo_get_target (cr_);
836
+ auto const & raw = cairo_image_surface_get_data (surface);
837
+ auto const & stride = cairo_image_surface_get_stride (surface);
838
+ auto const & [r, g, b, a] = fc_raw_opt ? *fc_raw_opt : ec_raw;
839
+ auto const & fc_argb32 = uint32_t (
840
+ (uint8_t (255 * a) << 24 ) | (uint8_t (255 * a * r) << 16 )
841
+ | (uint8_t (255 * a * g) << 8 ) | (uint8_t (255 * a * b)));
842
+ cairo_surface_flush (surface);
843
+ for (auto i = 0 ; i < n_vertices; ++i) {
844
+ auto x = vertices (i, 0 ), y = vertices (i, 1 );
845
+ cairo_matrix_transform_point (&matrix, &x, &y);
846
+ if (!(std::isfinite (x) && std::isfinite (y))) {
847
+ continue ;
848
+ }
849
+ *reinterpret_cast <uint32_t *>(
850
+ raw + std::lround (y) * stride + 4 * std::lround (x)) = fc_argb32;
851
+ }
852
+ cairo_surface_mark_dirty (surface);
830
853
} else {
831
854
for (auto i = 0 ; i < n_vertices; ++i) {
832
855
cairo_save (cr_);
@@ -1200,7 +1223,7 @@ GraphicsContextRenderer::get_text_width_height_descent(
1200
1223
// - "ismath" can be True, False, "TeX" (i.e., usetex).
1201
1224
// FIXME[matplotlib]: RendererAgg relies on the text.usetex rcParam, whereas
1202
1225
// RendererBase relies (correctly?) on the value of ismath.
1203
- if (py::module::import ( " operator " ). attr ( " eq " )(ismath, " TeX" ). cast < bool >( )) {
1226
+ if (py_eq (ismath, py::cast ( " TeX" ))) {
1204
1227
return
1205
1228
py::module::import (" matplotlib.backend_bases" ).attr (" RendererBase" )
1206
1229
.attr (" get_text_width_height_descent" )(this , s, prop, ismath)
@@ -1273,7 +1296,7 @@ Region GraphicsContextRenderer::copy_from_bbox(py::object bbox)
1273
1296
throw std::invalid_argument (" Invalid bbox" );
1274
1297
}
1275
1298
auto const & width = x1 - x0, height = y1 - y0;
1276
- // 4 bytes per pixel throughout!
1299
+ // 4 bytes per pixel throughout.
1277
1300
auto buf = std::unique_ptr<uint8_t []>{new uint8_t [4 * width * height]};
1278
1301
auto const & surface = cairo_get_target (cr_);
1279
1302
if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE) {
@@ -1302,9 +1325,8 @@ void GraphicsContextRenderer::restore_region(Region& region)
1302
1325
auto const & raw = cairo_image_surface_get_data (surface);
1303
1326
auto const & stride = cairo_image_surface_get_stride (surface);
1304
1327
cairo_surface_flush (surface);
1305
- // 4 bytes per pixel!
1306
1328
for (auto y = y0; y < y1; ++y) {
1307
- std::memcpy (
1329
+ std::memcpy ( // 4 bytes per pixel.
1308
1330
raw + y * stride + 4 * x0, buf.get () + (y - y0) * 4 * width, 4 * width);
1309
1331
}
1310
1332
cairo_surface_mark_dirty_rectangle (surface, x0, y0, width, height);
@@ -1448,6 +1470,8 @@ PYBIND11_MODULE(_mplcairo, m)
1448
1470
1449
1471
detail::UNIT_CIRCLE =
1450
1472
py::module::import (" matplotlib.path" ).attr (" Path" ).attr (" unit_circle" )();
1473
+ detail::PIXEL_MARKER =
1474
+ py::module::import (" matplotlib.markers" ).attr (" MarkerStyle" )(" ," );
1451
1475
1452
1476
FT_CHECK (FT_Init_FreeType, &detail::ft_library);
1453
1477
auto ft_cleanup = py::cpp_function{
0 commit comments