Skip to content

Commit ff86c19

Browse files
committed
Agg: Use 32-bit scan line classes
When rendering objects, Agg rasterizes them into scan line objects (an x/y point, horizontal length, and colour), and the renderer class writes those to the pixels in the final buffer. Though we have determined that Agg buffers cannot be larger than 2**16, the scan line classes that we use contain 16-bit _signed_ integers internally, cutting off positive values at half the maximum. Since the renderer uses 32-bit integers, this can cause odd behaviour where any horizontal span that _starts_ before 2**15 (such as a horizontal spine) is rendered correctly even if it cross that point, but those that start after (such as a vertical spine or any portion of an angled line) end up clipped. For example, see how the spines and lines break in #28893. A similar problem occurs for resampled images, which also uses Agg scanlines internally. See the breakage in #26368 for an example. The example in that issue also contains horizontal spines that are wider than 2**15, which also exhibit strange behaviour. By moving to 32-bit scan lines, positions and lengths of the lines will no longer be clipped, and this fixes rendering on very large figures. Fixes #23826 Fixes #26368 Fixes #28893
1 parent e90952f commit ff86c19

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

src/_backend_agg.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,10 @@ class RendererAgg
117117
typedef agg::renderer_scanline_bin_solid<renderer_base> renderer_bin;
118118
typedef agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> rasterizer;
119119

120-
typedef agg::scanline_p8 scanline_p8;
121-
typedef agg::scanline_bin scanline_bin;
120+
typedef agg::scanline32_p8 scanline_p8;
121+
typedef agg::scanline32_bin scanline_bin;
122122
typedef agg::amask_no_clip_gray8 alpha_mask_type;
123-
typedef agg::scanline_u8_am<alpha_mask_type> scanline_am;
123+
typedef agg::scanline32_u8_am<alpha_mask_type> scanline_am;
124124

125125
typedef agg::renderer_base<agg::pixfmt_gray8> renderer_base_alpha_mask_type;
126126
typedef agg::renderer_scanline_aa_solid<renderer_base_alpha_mask_type> renderer_alpha_mask_type;

src/_image_resample.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
#define MPL_DISABLE_AGG_GRAY_CLIPPING
77

8+
#include <limits>
9+
#include <type_traits>
10+
811
#include "agg_image_accessors.h"
912
#include "agg_path_storage.h"
1013
#include "agg_pixfmt_gray.h"
@@ -699,8 +702,8 @@ static void get_filter(const resample_params_t &params,
699702
}
700703

701704

702-
template<typename color_type>
703-
void resample(
705+
template<typename color_type, bool is_large = false>
706+
void resample_scanline(
704707
const void *input, int in_width, int in_height,
705708
void *output, int out_width, int out_height,
706709
resample_params_t &params)
@@ -712,6 +715,7 @@ void resample(
712715

713716
using renderer_t = agg::renderer_base<output_pixfmt_t>;
714717
using rasterizer_t = agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl>;
718+
using scanline_t = std::conditional_t<is_large, agg::scanline32_u8, agg::scanline_u8>;
715719

716720
using reflect_t = agg::wrap_mode_reflect;
717721
using image_accessor_t = agg::image_accessor_wrap<input_pixfmt_t, reflect_t, reflect_t>;
@@ -739,7 +743,7 @@ void resample(
739743

740744
span_alloc_t span_alloc;
741745
rasterizer_t rasterizer;
742-
agg::scanline_u8 scanline;
746+
scanline_t scanline;
743747

744748
span_conv_alpha_t conv_alpha(params.alpha);
745749

@@ -828,4 +832,25 @@ void resample(
828832
}
829833
}
830834

835+
template<typename color_type>
836+
void resample(
837+
const void *input, int in_width, int in_height,
838+
void *output, int out_width, int out_height,
839+
resample_params_t &params)
840+
{
841+
// The smaller scanline classes use int16, so use the larger class for larger output.
842+
constexpr int cutoff = std::numeric_limits<agg::int16>::max();
843+
if (out_width >= cutoff || out_height >= cutoff) {
844+
resample_scanline<color_type, true>(
845+
input, in_width, in_height,
846+
output, out_width, out_height,
847+
params);
848+
} else {
849+
resample_scanline<color_type, false>(
850+
input, in_width, in_height,
851+
output, out_width, out_height,
852+
params);
853+
}
854+
}
855+
831856
#endif /* MPL_RESAMPLE_H */

0 commit comments

Comments
 (0)