diff --git a/src/Canvas.cc b/src/Canvas.cc index 27d2c2c..bd20663 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -45,6 +45,15 @@ Canvas::Initialize(Handle target) { proto->SetAccessor(NanSymbol("type"), GetType); proto->SetAccessor(NanSymbol("width"), GetWidth, SetWidth); proto->SetAccessor(NanSymbol("height"), GetHeight, SetHeight); + + proto->Set("PNG_NO_FILTERS", Uint32::New(PNG_NO_FILTERS)); + proto->Set("PNG_FILTER_NONE", Uint32::New(PNG_FILTER_NONE)); + proto->Set("PNG_FILTER_SUB", Uint32::New(PNG_FILTER_SUB)); + proto->Set("PNG_FILTER_UP", Uint32::New(PNG_FILTER_UP)); + proto->Set("PNG_FILTER_AVG", Uint32::New(PNG_FILTER_AVG)); + proto->Set("PNG_FILTER_PAETH", Uint32::New(PNG_FILTER_PAETH)); + proto->Set("PNG_ALL_FILTERS", Uint32::New(PNG_ALL_FILTERS)); + target->Set(NanSymbol("Canvas"), ctor->GetFunction()); } @@ -225,6 +234,7 @@ NAN_METHOD(Canvas::ToBuffer) { NanScope(); cairo_status_t status; uint32_t compression_level = 6; + uint32_t filter = PNG_ALL_FILTERS; Canvas *canvas = ObjectWrap::Unwrap(args.This()); // TODO: async / move this out @@ -236,38 +246,48 @@ NAN_METHOD(Canvas::ToBuffer) { NanReturnValue(buf); } - 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; + if (args.Length() > 1 && !(args[1]->StrictEquals(Undefined()) && args[2]->StrictEquals(Undefined()))) { + if (!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 { - compression_level = tmp; + 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 { + 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."); } - } else { - return NanThrowTypeError("Compression level must be a number."); - } + } + + if (!args[2]->StrictEquals(Undefined())) { + if (args[2]->IsUint32()) { + filter = args[1]->Uint32Value(); + } else { + return NanThrowTypeError("Invalid filter value."); + } + } } // Async if (args[0]->IsFunction()) { closure_t *closure = (closure_t *) malloc(sizeof(closure_t)); - status = closure_init(closure, canvas, compression_level); + status = closure_init(closure, canvas, compression_level, filter); // ensure closure is ok if (status) { @@ -293,7 +313,7 @@ NAN_METHOD(Canvas::ToBuffer) { // Sync } else { closure_t closure; - status = closure_init(&closure, canvas, compression_level); + status = closure_init(&closure, canvas, compression_level, filter); // ensure closure is ok if (status) { @@ -342,36 +362,47 @@ streamPNG(void *c, const uint8_t *data, unsigned len) { NAN_METHOD(Canvas::StreamPNGSync) { NanScope(); uint32_t compression_level = 6; + uint32_t filter = PNG_ALL_FILTERS; // 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; + if (args.Length() > 1 && !(args[1]->StrictEquals(Undefined()) && args[2]->StrictEquals(Undefined()))) { + if (!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 { - compression_level = tmp; + 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 { + 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."); } - } else { - return NanThrowTypeError("Compression level must be a number."); - } + } + + if (!args[2]->StrictEquals(Undefined())) { + if (args[2]->IsUint32()) { + filter = args[1]->Uint32Value(); + } else { + return NanThrowTypeError("Invalid filter value."); + } + } } @@ -379,6 +410,7 @@ NAN_METHOD(Canvas::StreamPNGSync) { closure_t closure; closure.fn = Handle::Cast(args[0]); closure.compression_level = compression_level; + closure.filter = filter; TryCatch try_catch; @@ -443,7 +475,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, 0); + cairo_status_t status = closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS); 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 c3c2aa4..81ad160 100644 --- a/src/PNG.h +++ b/src/PNG.h @@ -129,6 +129,7 @@ static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr writ 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); + png_set_filter(png, 0, ((closure_t *) ((canvas_png_write_closure_t *) closure)->closure)->filter); switch (cairo_image_surface_get_format(surface)) { case CAIRO_FORMAT_ARGB32: diff --git a/src/closure.h b/src/closure.h index 9590425..1b3147b 100644 --- a/src/closure.h +++ b/src/closure.h @@ -30,7 +30,8 @@ typedef struct { uint8_t *data; Canvas *canvas; cairo_status_t status; - unsigned int compression_level; + uint32_t compression_level; + uint32_t filter; } closure_t; /* @@ -38,12 +39,13 @@ typedef struct { */ cairo_status_t -closure_init(closure_t *closure, Canvas *canvas, unsigned int compression_level) { +closure_init(closure_t *closure, Canvas *canvas, unsigned int compression_level, unsigned int filter) { 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; + closure->filter = filter; return CAIRO_STATUS_SUCCESS; }