diff --git a/src/Canvas.cc b/src/Canvas.cc index 8e45f20..27d2c2c 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -236,15 +236,32 @@ 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."); - } + if (args.Length() == 2 && !args[1]->StrictEquals(Undefined())) { + bool good = true; + if (args[1]->IsNumber()) { + compression_level = args[1]->Uint32Value(); + } else if (args[1]->IsString()) { + if (args[1]->StrictEquals(String::New("0"))) { + compression_level = 0; + } else { + uint32_t tmp = args[1]->Uint32Value(); + if (tmp == 0) { + good = false; + } else { + compression_level = tmp; + } + } + } else { + good = false; + } + + if (good) { + if (compression_level > 9) { + return NanThrowRangeError("Allowed compression levels lie in the range [0, 9]."); + } + } else { + return NanThrowTypeError("Compression level must be a number."); + } } // Async @@ -324,17 +341,47 @@ streamPNG(void *c, const uint8_t *data, unsigned len) { NAN_METHOD(Canvas::StreamPNGSync) { NanScope(); + uint32_t compression_level = 6; // TODO: async as well if (!args[0]->IsFunction()) return NanThrowTypeError("callback function required"); + if (args.Length() == 2 && !args[1]->StrictEquals(Undefined())) { + bool good = true; + if (args[1]->IsNumber()) { + compression_level = args[1]->Uint32Value(); + } else if (args[1]->IsString()) { + if (args[1]->StrictEquals(String::New("0"))) { + compression_level = 0; + } else { + uint32_t tmp = args[1]->Uint32Value(); + if (tmp == 0) { + good = false; + } else { + compression_level = tmp; + } + } + } else { + good = false; + } + + if (good) { + if (compression_level > 9) { + return NanThrowRangeError("Allowed compression levels lie in the range [0, 9]."); + } + } else { + return NanThrowTypeError("Compression level must be a number."); + } + } + + Canvas *canvas = ObjectWrap::Unwrap(args.This()); closure_t closure; closure.fn = Handle::Cast(args[0]); + closure.compression_level = compression_level; TryCatch try_catch; - //TODO: use libpng directly cairo_status_t status = canvas_write_to_png_stream(canvas->surface(), streamPNG, &closure); if (try_catch.HasCaught()) { diff --git a/src/PNG.h b/src/PNG.h index 18e4ee1..2100f38 100644 --- a/src/PNG.h +++ b/src/PNG.h @@ -16,144 +16,14 @@ -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) { +static void canvas_png_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 */ } /* Converts native endian xRGB => RGBx bytes */ -static void convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) { +static void canvas_convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { @@ -170,7 +40,7 @@ static void convert_data_to_bytes (png_structp png, png_row_infop row_info, png_ } /* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ -static void unpremultiply_data(png_structp png, png_row_infop row_info, png_bytep data) { +static void canvas_unpremultiply_data(png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { @@ -191,7 +61,7 @@ static void unpremultiply_data(png_structp png, png_row_infop row_info, png_byte } } -struct png_write_closure_t { +struct canvas_png_write_closure_t { cairo_write_func_t write_func; void *closure; }; @@ -233,7 +103,6 @@ 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, 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); @@ -262,10 +131,8 @@ 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); + png_set_write_fn(png, closure, write_func, canvas_png_flush); + png_set_compression_level(png, ((closure_t *) ((canvas_png_write_closure_t *) closure)->closure)->compression_level); switch (cairo_image_surface_get_format(surface)) { case CAIRO_FORMAT_ARGB32: @@ -312,9 +179,9 @@ static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr writ */ png_write_info(png, info); if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - png_set_write_user_transform_fn(png, unpremultiply_data); + png_set_write_user_transform_fn(png, canvas_unpremultiply_data); } else if (png_color_type == PNG_COLOR_TYPE_RGB) { - png_set_write_user_transform_fn(png, convert_data_to_bytes); + png_set_write_user_transform_fn(png, canvas_convert_data_to_bytes); png_set_filler(png, 0, PNG_FILLER_AFTER); } @@ -326,11 +193,11 @@ static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr writ return status; } -static void stream_write_func(png_structp png, png_bytep data, png_size_t size) { +static void canvas_stream_write_func(png_structp png, png_bytep data, png_size_t size) { cairo_status_t status; - struct png_write_closure_t *png_closure; + struct canvas_png_write_closure_t *png_closure; - png_closure = (struct png_write_closure_t *) png_get_io_ptr(png); + png_closure = (struct canvas_png_write_closure_t *) png_get_io_ptr(png); status = png_closure->write_func(png_closure->closure, data, size); if (unlikely(status)) { cairo_status_t *error = (cairo_status_t *) png_get_error_ptr(png); @@ -342,7 +209,7 @@ static void stream_write_func(png_structp png, png_bytep data, png_size_t size) } static cairo_status_t canvas_write_to_png_stream(cairo_surface_t *surface, cairo_write_func_t write_func, void *closure) { - struct png_write_closure_t png_closure; + struct canvas_png_write_closure_t png_closure; if (cairo_surface_status(surface)) { return cairo_surface_status(surface); @@ -351,6 +218,6 @@ static cairo_status_t canvas_write_to_png_stream(cairo_surface_t *surface, cairo png_closure.write_func = write_func; png_closure.closure = closure; - return canvas_write_png(surface, stream_write_func, &png_closure); + return canvas_write_png(surface, canvas_stream_write_func, &png_closure); } #endif