diff --git a/src/Canvas.cc b/src/Canvas.cc index f7cfa11..8e45f20 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -166,7 +166,7 @@ Canvas::EIO_ToBuffer(eio_req *req) { #endif closure_t *closure = (closure_t *) req->data; - closure->status = cairo_surface_write_to_png_stream( + closure->status = canvas_write_to_png_stream( closure->canvas->surface() , toBuffer , closure); @@ -224,6 +224,7 @@ Canvas::EIO_AfterToBuffer(eio_req *req) { NAN_METHOD(Canvas::ToBuffer) { NanScope(); cairo_status_t status; + uint32_t compression_level = 6; Canvas *canvas = ObjectWrap::Unwrap(args.This()); // TODO: async / move this out @@ -235,10 +236,21 @@ NAN_METHOD(Canvas::ToBuffer) { NanReturnValue(buf); } + if (args.Length() == 2) { + if(args[1]->IsUint32()) { + compression_level = args[1]->Uint32Value(); + if (compression_level > 9) { + return NanThrowRangeError("Allowed compression levels lie in the range [0, 9]."); + } + } else { + return NanThrowTypeError("Compression level has to be an unsigned integer."); + } + } + // Async if (args[0]->IsFunction()) { closure_t *closure = (closure_t *) malloc(sizeof(closure_t)); - status = closure_init(closure, canvas); + status = closure_init(closure, canvas, compression_level); // ensure closure is ok if (status) { @@ -264,7 +276,7 @@ NAN_METHOD(Canvas::ToBuffer) { // Sync } else { closure_t closure; - status = closure_init(&closure, canvas); + status = closure_init(&closure, canvas, compression_level); // ensure closure is ok if (status) { @@ -384,7 +396,7 @@ Canvas::Canvas(int w, int h, canvas_type_t t): ObjectWrap() { if (CANVAS_TYPE_PDF == t) { _closure = malloc(sizeof(closure_t)); assert(_closure); - cairo_status_t status = closure_init((closure_t *) _closure, this); + cairo_status_t status = closure_init((closure_t *) _closure, this, 0); assert(status == CAIRO_STATUS_SUCCESS); _surface = cairo_pdf_surface_create_for_stream(toBuffer, _closure, w, h); } else { diff --git a/src/PNG.h b/src/PNG.h index ac2a56a..18e4ee1 100644 --- a/src/PNG.h +++ b/src/PNG.h @@ -4,8 +4,7 @@ #include #include #include - -#pragma once +#include "closure.h" #if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) #define likely(expr) (__builtin_expect (!!(expr), 1)) @@ -16,7 +15,139 @@ #endif + +typedef png_size_t png_alloc_size_t; + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * This piece of code can be compiled to validate max 64K allocations + * by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. + */ +typedef struct memory_information +{ + png_alloc_size_t size; + png_voidp pointer; + struct memory_information *next; +} memory_information; +typedef memory_information *memory_infop; + +static memory_infop pinformation = NULL; +static int current_allocation = 0; +static int maximum_allocation = 0; +static int total_allocation = 0; +static int num_allocations = 0; + +png_voidp png_debug_malloc PNGARG((png_structp png_ptr,png_alloc_size_t size)); +static void png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); + +png_voidp png_debug_malloc(png_structp png_ptr, png_alloc_size_t size) { + /* png_malloc has already tested for NULL; png_create_struct calls + * png_debug_malloc directly, with png_ptr == NULL which is OK + */ + + if (size == 0) + return (NULL); + + /* This calls the library allocator twice, once to get the requested + buffer and once to get a new free list entry. */ + { + /* Disable malloc_fn and free_fn */ + memory_infop pinfo; + png_set_mem_fn(png_ptr, NULL, NULL, NULL); + pinfo = (memory_infop)png_malloc(png_ptr, + (sizeof *pinfo)); + pinfo->size = size; + current_allocation += size; + total_allocation += size; + num_allocations ++; + + if (current_allocation > maximum_allocation) { + maximum_allocation = current_allocation; + } + + pinfo->pointer = png_malloc(png_ptr, size); + /* Restore malloc_fn and free_fn */ + + png_set_mem_fn(png_ptr, + NULL, png_debug_malloc, png_debug_free); + + if (size != 0 && pinfo->pointer == NULL) { + current_allocation -= size; + total_allocation -= size; + png_error(png_ptr, + "out of memory in pngtest->png_debug_malloc"); + } + + pinfo->next = pinformation; + pinformation = pinfo; + /* Make sure the caller isn't assuming zeroed memory. */ + memset(pinfo->pointer, 0xdd, pinfo->size); + + printf("png_malloc %lu bytes at %p\n", (unsigned long)size, + pinfo->pointer); + + return (png_voidp)(pinfo->pointer); + } +} + +/* Free a pointer. It is removed from the list at the same time. */ +static void png_debug_free(png_structp png_ptr, png_voidp ptr) { + if (png_ptr == NULL) + fprintf(stderr, "NULL pointer to png_debug_free.\n"); + + if (ptr == 0) + { +#if 0 /* This happens all the time. */ + fprintf(stderr, "WARNING: freeing NULL pointer\n"); +#endif + return; + } + + /* Unlink the element from the list. */ + { + memory_infop *ppinfo = &pinformation; + + for (;;) + { + memory_infop pinfo = *ppinfo; + + if (pinfo->pointer == ptr) + { + *ppinfo = pinfo->next; + current_allocation -= pinfo->size; + if (current_allocation < 0) + fprintf(stderr, "Duplicate free of memory\n"); + /* We must free the list element too, but first kill + the memory that is to be freed. */ + memset(ptr, 0x55, pinfo->size); + png_free_default(png_ptr, pinfo); + pinfo = NULL; + break; + } + + if (pinfo->next == NULL) + { + fprintf(stderr, "Pointer %p not found\n", ptr); + break; + } + + ppinfo = &pinfo->next; + } + } + + /* Finally free the data. */ + printf("Freeing %p\n", ptr); + + png_free_default(png_ptr, ptr); + ptr = NULL; +} + static void pngtest_flush(png_structp png_ptr) { +// fprintf(stderr, "Pngtest_flush called"); /* Do nothing; fflush() is said to be just a waste of energy. */ (void) png_ptr; /* Stifle compiler warning */ } @@ -60,6 +191,11 @@ static void unpremultiply_data(png_structp png, png_row_infop row_info, png_byte } } +struct png_write_closure_t { + cairo_write_func_t write_func; + void *closure; +}; + static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr write_func, void *closure) { unsigned int i; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -97,7 +233,8 @@ static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr writ } #ifdef PNG_USER_MEM_SUPPORTED - png = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, NULL, NULL); +// png = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, png_debug_malloc, png_debug_free); + png = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, NULL, NULL); #else png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); #endif @@ -126,6 +263,9 @@ static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr writ #endif png_set_write_fn(png, closure, write_func, pngtest_flush); +// png_set_flush(png, 100); +// png_set_compression_buffer_size(png, 1024*1024); + png_set_compression_level(png, ((closure_t *) ((png_write_closure_t *) closure)->closure)->compression_level); switch (cairo_image_surface_get_format(surface)) { case CAIRO_FORMAT_ARGB32: @@ -178,19 +318,14 @@ static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr writ png_set_filler(png, 0, PNG_FILLER_AFTER); } - png_write_image (png, rows); - png_write_end (png, info); + png_write_image(png, rows); + png_write_end(png, info); - png_destroy_write_struct (&png, &info); + png_destroy_write_struct(&png, &info); free(rows); return status; } -struct png_write_closure_t { - cairo_write_func_t write_func; - void *closure; -}; - static void stream_write_func(png_structp png, png_bytep data, png_size_t size) { cairo_status_t status; struct png_write_closure_t *png_closure; @@ -213,10 +348,6 @@ static cairo_status_t canvas_write_to_png_stream(cairo_surface_t *surface, cairo return cairo_surface_status(surface); } -// if (surface->finished) { -// return CAIRO_STATUS_SURFACE_FINISHED; -// } - png_closure.write_func = write_func; png_closure.closure = closure; diff --git a/src/closure.h b/src/closure.h index a94b206..9590425 100644 --- a/src/closure.h +++ b/src/closure.h @@ -30,6 +30,7 @@ typedef struct { uint8_t *data; Canvas *canvas; cairo_status_t status; + unsigned int compression_level; } closure_t; /* @@ -37,11 +38,12 @@ typedef struct { */ cairo_status_t -closure_init(closure_t *closure, Canvas *canvas) { +closure_init(closure_t *closure, Canvas *canvas, unsigned int compression_level) { closure->len = 0; closure->canvas = canvas; closure->data = (uint8_t *) malloc(closure->max_len = PAGE_SIZE); if (!closure->data) return CAIRO_STATUS_NO_MEMORY; + closure->compression_level = compression_level; return CAIRO_STATUS_SUCCESS; }