Skip to content

Commit 56f945f

Browse files
karwaKarthikRIyer
authored andcommitted
bfix: Fix base64 PNG rendering (#60)
* Fix base64 PNG rendering. Enable the full test, which now passes. * Fix some potential leaks in AGGRenderer * [AGG] Try removing the newlines from the base64 encoded string. * Tidy up CPPAggRenderer a little
1 parent f260201 commit 56f945f

File tree

6 files changed

+84
-80
lines changed

6 files changed

+84
-80
lines changed

Sources/AGGRenderer/AGGRenderer/AGGRenderer.swift

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ public class AGGRenderer: Renderer{
77
public var offset = zeroPoint
88
public var imageSize: Size {
99
willSet {
10-
agg_object = initializePlot(newValue.width, newValue.height, fontPath)
10+
delete_plot(agg_object);
11+
agg_object = initializePlot(newValue.width, newValue.height, fontPath)
1112
}
1213
}
13-
var agg_object: UnsafeRawPointer
14+
var agg_object: UnsafeMutableRawPointer
1415
var fontPath = ""
1516

1617
public init(width w: Float = 1000, height h: Float = 660, fontPath: String = "") {
@@ -239,25 +240,40 @@ public class AGGRenderer: Renderer{
239240
}
240241

241242
public func drawOutput(fileName name: String) throws {
242-
var errorDescPtr = UnsafePointer<Int8>(bitPattern: 0)
243+
var errorDescPtr: UnsafePointer<Int8>?
243244
let err = save_image(name, &errorDescPtr, agg_object)
244245
if err != 0, let errorDescPtr = errorDescPtr {
245246
throw DrawOutputError(errorCode: err, description: String(cString: errorDescPtr))
246247
}
247248
}
248249

249-
public func base64Png() -> String{
250-
let pngBufferPointer: UnsafePointer<UInt8> = get_png_buffer(agg_object)
251-
let bufferSize: Int = Int(get_png_buffer_size(agg_object))
252-
return Data(
253-
bytesNoCopy: UnsafeMutableRawPointer(mutating: pngBufferPointer),
254-
count: bufferSize,
255-
deallocator: .none
256-
).base64EncodedString(options: .lineLength64Characters)
250+
public func base64Png() -> String {
251+
var _bufferPtr: UnsafeMutablePointer<UInt8>?
252+
var errorDescPtr: UnsafePointer<Int8>?
253+
var bufferSize = 0
254+
255+
let err = create_png_buffer(&_bufferPtr, &bufferSize, &errorDescPtr, agg_object)
256+
guard let bufferPtr = _bufferPtr, err == 0 else {
257+
// We'll probably never make this throwing, but log the error all the same.
258+
print(
259+
errorDescPtr.map { "Error rendering image: \(String(cString: $0))"} ??
260+
"lodepng failed to render, but didn't produce an error."
261+
)
262+
return ""
263+
}
264+
265+
let base64String = Data(
266+
bytesNoCopy: UnsafeMutableRawPointer(mutating: bufferPtr),
267+
count: bufferSize,
268+
deallocator: .none
269+
).base64EncodedString()
270+
271+
free_png_buffer(&_bufferPtr)
272+
return base64String
257273
}
258274

259275
deinit {
260-
delete_buffer(agg_object)
276+
delete_plot(agg_object)
261277
}
262278

263279
}

Sources/AGGRenderer/CAGGRenderer/CAGGRenderer.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
#include "CPPAGGRenderer.h"
33
#include <iostream>
44

5-
const void * initializePlot(float w, float h, const char* fontPath){
5+
void * initializePlot(float w, float h, const char* fontPath){
66
return CPPAGGRenderer::initializePlot(w, h, fontPath);
77
}
88

9+
void delete_plot(void *object){
10+
CPPAGGRenderer::delete_plot(object);
11+
}
12+
913
void draw_rect(const float *x, const float *y, float thickness, float r, float g, float b, float a, const void *object){
1014
CPPAGGRenderer::draw_rect(x, y, thickness, r, g, b, a, object);
1115
}
@@ -46,14 +50,10 @@ unsigned save_image(const char *s, const char** errorDesc, const void *object){
4650
return CPPAGGRenderer::save_image(s, errorDesc, object);
4751
}
4852

49-
const unsigned char* get_png_buffer(const void *object){
50-
return CPPAGGRenderer::get_png_buffer(object);
51-
}
52-
53-
int get_png_buffer_size(const void *object){
54-
return CPPAGGRenderer::get_png_buffer_size(object);
53+
unsigned create_png_buffer(unsigned char** output, size_t *outputSize, const char** errorDesc, const void *object) {
54+
return CPPAGGRenderer::create_png_buffer(output, outputSize, errorDesc, object);
5555
}
5656

57-
void delete_buffer(const void *object){
58-
CPPAGGRenderer::delete_buffer(object);
57+
void free_png_buffer(unsigned char** output) {
58+
CPPAGGRenderer::free_png_buffer(output);
5959
}

Sources/AGGRenderer/CAGGRenderer/include/CAGGRenderer.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#include<stdbool.h>
2+
#include<stddef.h>
23
#ifdef __cplusplus
34
extern "C" {
45
#endif
56

6-
const void * initializePlot(float w, float h, const char* fontPath);
7+
void * initializePlot(float w, float h, const char* fontPath);
8+
9+
void delete_plot(void *object);
710

811
void draw_rect(const float *x, const float *y, float thickness, float r, float g, float b, float a, const void *object);
912

@@ -25,11 +28,9 @@ void get_text_size(const char *s, float size, float* outW, float* outH, const vo
2528

2629
unsigned save_image(const char *s, const char** errorDesc, const void *object);
2730

28-
const unsigned char* get_png_buffer(const void *object);
29-
30-
int get_png_buffer_size(const void *object);
31+
unsigned create_png_buffer(unsigned char** output, size_t *outputSize, const char** errorDesc, const void *object);
3132

32-
void delete_buffer(const void *object);
33+
void free_png_buffer(unsigned char** output);
3334

3435
#ifdef __cplusplus
3536
}

Sources/AGGRenderer/CPPAGGRenderer/CPPAGGRenderer.cpp

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -52,34 +52,30 @@ namespace CPPAGGRenderer{
5252
saveBMP(buf, width, height, file_name);
5353
}
5454

55-
unsigned write_png(std::vector<unsigned char>& image, unsigned width, unsigned height, const char* filename, const char** errorDesc) {
55+
unsigned write_png(const unsigned char* image, unsigned width, unsigned height, const char* filename, const char** errorDesc) {
5656
//Encode the image
5757
LodePNGColorType colorType = LCT_RGB;
5858
unsigned error = lodepng::encode(filename, image, width, height, colorType);
5959
if(error && errorDesc)
6060
*errorDesc = lodepng_error_text(error);
61-
return error;
61+
return error;
6262
}
6363

64-
std::vector<unsigned char> write_png_memory(const unsigned char* buf, unsigned width, unsigned height){
64+
unsigned write_png_memory(const unsigned char *image, unsigned width, unsigned height,
65+
unsigned char **output, size_t *outputSize, const char **errorDesc){
6566
//Encode the image
6667
LodePNGColorType colorType = LCT_RGB;
67-
std::vector<unsigned char> out;
68-
unsigned error = lodepng::encode(out, buf, width, height, colorType);
69-
70-
//if there's an error, display it
71-
if(error) std::cout << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl;
72-
return out;
68+
unsigned error = lodepng_encode_memory(output, outputSize, image, w, h, colorType, 8);
69+
if(error && errorDesc)
70+
*errorDesc = lodepng_error_text(error);
71+
return error;
7372
}
7473

7574
class Plot{
76-
77-
public:
7875
agg::rasterizer_scanline_aa<> m_ras;
7976
agg::scanline_p8 m_sl_p8;
8077
agg::line_cap_e buttCap = agg::butt_cap;
8178
renderer_aa ren_aa;
82-
int pngBufferSize = 0;
8379

8480
font_engine_type m_feng;
8581
font_manager_type m_fman;
@@ -101,7 +97,9 @@ namespace CPPAGGRenderer{
10197
agg::int8u* m_pattern;
10298
agg::rendering_buffer m_pattern_rbuf;
10399
renderer_base_pre rb_pre;
104-
100+
101+
public:
102+
105103
Plot(float width, float height, const char* fontPathPtr) :
106104
m_feng(),
107105
m_fman(m_feng),
@@ -110,10 +108,8 @@ namespace CPPAGGRenderer{
110108
frame_width(width),
111109
frame_height(height)
112110
{
113-
if (buffer != NULL) {
114-
delete[] buffer;
115-
}
116111
buffer = new unsigned char[frame_width*frame_height*3];
112+
memset(buffer, 255, frame_width*frame_height*3);
117113
m_curves.approximation_scale(2.0);
118114
m_contour.auto_detect_orientation(false);
119115
fontPath = fontPathPtr;
@@ -123,6 +119,10 @@ namespace CPPAGGRenderer{
123119
fontPath = dir_path.append("/Roboto-Regular.ttf");
124120
}
125121
}
122+
123+
~Plot() {
124+
delete [] buffer;
125+
}
126126

127127
void generate_pattern(float r, float g, float b, float a, int hatch_pattern){
128128
agg::path_storage m_ps;
@@ -466,34 +466,27 @@ namespace CPPAGGRenderer{
466466
char* file_png = (char *) malloc(1 + strlen(s)+ strlen(".png") );
467467
strcpy(file_png, s);
468468
strcat(file_png, ".png");
469-
std::vector<unsigned char> image(buffer, buffer + (frame_width*frame_height*3));
470-
unsigned err = write_png(image, frame_width, frame_height, file_png, errorDesc);
469+
unsigned err = write_png(buffer, frame_width, frame_height, file_png, errorDesc);
471470
free(file_png);
472471
return err;
473472
}
474473

475-
const unsigned char* getPngBuffer(){
476-
std::vector<unsigned char> outputImage = write_png_memory(buffer, frame_width, frame_height);
477-
pngBufferSize = outputImage.size();
478-
return outputImage.data();
479-
}
480-
481-
int getPngBufferSize(){
482-
return pngBufferSize;
474+
unsigned create_png_buffer(unsigned char** output, size_t *outputSize, const char** errorDesc) {
475+
return write_png_memory(buffer, frame_width, frame_height, output, outputSize, errorDesc);
483476
}
484-
485-
void delete_buffer(){
486-
delete[] buffer;
487-
}
488-
489477
};
490478

491-
const void * initializePlot(float w, float h, const char* fontPath){
479+
void * initializePlot(float w, float h, const char* fontPath){
492480
Plot *plot = new Plot(w, h, fontPath);
493-
memset(plot->buffer, 255, plot->frame_width*plot->frame_height*3);
494481
return (void *)plot;
495482
}
496483

484+
void delete_plot(void *object) {
485+
Plot *plot = (Plot *)object;
486+
delete plot;
487+
object = 0;
488+
}
489+
497490
void draw_rect(const float *x, const float *y, float thickness, float r, float g, float b, float a,
498491
const void *object){
499492
Plot *plot = (Plot *)object;
@@ -545,19 +538,13 @@ namespace CPPAGGRenderer{
545538
return plot -> save_image(s, errorDesc);
546539
}
547540

548-
const unsigned char* get_png_buffer(const void *object){
541+
unsigned create_png_buffer(unsigned char** output, size_t *outputSize, const char** errorDesc, const void *object) {
549542
Plot *plot = (Plot *)object;
550-
return plot -> getPngBuffer();
543+
return plot -> create_png_buffer(output, outputSize, errorDesc);
551544
}
552545

553-
int get_png_buffer_size(const void *object){
554-
Plot *plot = (Plot *)object;
555-
return plot -> getPngBufferSize();
546+
void free_png_buffer(unsigned char** buffer) {
547+
if (buffer) { free(*buffer); }
548+
*buffer = 0;
556549
}
557-
558-
void delete_buffer(const void *object){
559-
Plot *plot = (Plot *)object;
560-
plot -> delete_buffer();
561-
}
562-
563550
}

Sources/AGGRenderer/CPPAGGRenderer/include/CPPAGGRenderer.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
#include <stddef.h>
2+
13
namespace CPPAGGRenderer{
24

3-
const void * initializePlot(float w, float h, const char* fontPath);
5+
void * initializePlot(float w, float h, const char* fontPath);
6+
7+
void delete_plot(void *object);
48

59
void draw_rect(const float *x, const float *y, float thickness, float r, float g, float b, float a, const void *object);
610

@@ -22,10 +26,8 @@ namespace CPPAGGRenderer{
2226

2327
unsigned save_image(const char *s, const char** errorDesc, const void *object);
2428

25-
const unsigned char* get_png_buffer(const void *object);
26-
27-
int get_png_buffer_size(const void *object);
29+
unsigned create_png_buffer(unsigned char **output, size_t *outputSize, const char **errorDesc, const void *object);
2830

29-
void delete_buffer(const void *object);
31+
void free_png_buffer(unsigned char **buffer);
3032

3133
}

Tests/SwiftPlotTests/AGGRenderer/agg-png-output.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import AGGRenderer
55

66
extension AGGRendererTests {
77

8-
/// **XFAIL**
9-
///
108
/// Tests that base64 encoding is accurate by drawing a known graph directly in to
119
/// a PNG buffer (no files), then verifying the base64-encoded data matches that from
1210
/// the reference file.
@@ -22,7 +20,7 @@ extension AGGRendererTests {
2220
let renderer = AGGRenderer()
2321
barGraph.drawGraph(renderer: renderer)
2422
let outputBase64 = renderer.base64Png()
25-
XCTAssertEqual(outputBase64.count, 47397)
23+
XCTAssertEqual(outputBase64.count, 46668)
2624

2725
// First, sanity check: ensure *we* can decode the string.
2826
guard let _ = Data(base64Encoded: outputBase64, options: .ignoreUnknownCharacters) else {
@@ -36,9 +34,9 @@ extension AGGRendererTests {
3634
.appendingPathComponent(fileName)
3735
.appendingPathExtension(KnownRenderer.agg.fileExtension)
3836
let referenceBase64 = try Data(contentsOf: referenceFile)
39-
.base64EncodedString()//(options: .lineLength64Characters)
37+
.base64EncodedString()
4038

41-
//XCTAssertEqual(outputBase64, referenceBase64)
39+
XCTAssertEqual(outputBase64, referenceBase64)
4240
}
4341
}
4442

0 commit comments

Comments
 (0)