UNPKG

34.5 kBtext/x-cView Raw
1// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
2
3#include "Image.h"
4
5#include "bmp/BMPParser.h"
6#include "Canvas.h"
7#include <cerrno>
8#include <cstdlib>
9#include <cstring>
10#include <node_buffer.h>
11#include "Util.h"
12
13/* Cairo limit:
14 * https://lists.cairographics.org/archives/cairo/2010-December/021422.html
15 */
16static constexpr int canvas_max_side = (1 << 15) - 1;
17
18#ifdef HAVE_GIF
19typedef struct {
20 uint8_t *buf;
21 unsigned len;
22 unsigned pos;
23} gif_data_t;
24#endif
25
26#ifdef HAVE_JPEG
27#include <csetjmp>
28
29struct canvas_jpeg_error_mgr: jpeg_error_mgr {
30 Image* image;
31 jmp_buf setjmp_buffer;
32};
33#endif
34
35/*
36 * Read closure used by loadFromBuffer.
37 */
38
39typedef struct {
40 unsigned len;
41 uint8_t *buf;
42} read_closure_t;
43
44using namespace v8;
45
46Nan::Persistent<FunctionTemplate> Image::constructor;
47
48/*
49 * Initialize Image.
50 */
51
52void
53Image::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
54 Nan::HandleScope scope;
55
56 Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(Image::New);
57 constructor.Reset(ctor);
58 ctor->InstanceTemplate()->SetInternalFieldCount(1);
59 ctor->SetClassName(Nan::New("Image").ToLocalChecked());
60
61 // Prototype
62 Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
63 SetProtoAccessor(proto, Nan::New("complete").ToLocalChecked(), GetComplete, NULL, ctor);
64 SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth, ctor);
65 SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight, ctor);
66 SetProtoAccessor(proto, Nan::New("naturalWidth").ToLocalChecked(), GetNaturalWidth, NULL, ctor);
67 SetProtoAccessor(proto, Nan::New("naturalHeight").ToLocalChecked(), GetNaturalHeight, NULL, ctor);
68 SetProtoAccessor(proto, Nan::New("dataMode").ToLocalChecked(), GetDataMode, SetDataMode, ctor);
69
70 ctor->Set(Nan::New("MODE_IMAGE").ToLocalChecked(), Nan::New<Number>(DATA_IMAGE));
71 ctor->Set(Nan::New("MODE_MIME").ToLocalChecked(), Nan::New<Number>(DATA_MIME));
72
73 Local<Context> ctx = Nan::GetCurrentContext();
74 Nan::Set(target, Nan::New("Image").ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked());
75
76 // Used internally in lib/image.js
77 NAN_EXPORT(target, GetSource);
78 NAN_EXPORT(target, SetSource);
79}
80
81/*
82 * Initialize a new Image.
83 */
84
85NAN_METHOD(Image::New) {
86 if (!info.IsConstructCall()) {
87 return Nan::ThrowTypeError("Class constructors cannot be invoked without 'new'");
88 }
89
90 Image *img = new Image;
91 img->data_mode = DATA_IMAGE;
92 img->Wrap(info.This());
93 Nan::Set(info.This(), Nan::New("onload").ToLocalChecked(), Nan::Null()).Check();
94 Nan::Set(info.This(), Nan::New("onerror").ToLocalChecked(), Nan::Null()).Check();
95 info.GetReturnValue().Set(info.This());
96}
97
98/*
99 * Get complete boolean.
100 */
101
102NAN_GETTER(Image::GetComplete) {
103 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
104 info.GetReturnValue().Set(Nan::New<Boolean>(Image::COMPLETE == img->state));
105}
106
107/*
108 * Get dataMode.
109 */
110
111NAN_GETTER(Image::GetDataMode) {
112 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
113 info.GetReturnValue().Set(Nan::New<Number>(img->data_mode));
114}
115
116/*
117 * Set dataMode.
118 */
119
120NAN_SETTER(Image::SetDataMode) {
121 if (value->IsNumber()) {
122 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
123 int mode = Nan::To<uint32_t>(value).FromMaybe(0);
124 img->data_mode = (data_mode_t) mode;
125 }
126}
127
128/*
129 * Get natural width
130 */
131
132NAN_GETTER(Image::GetNaturalWidth) {
133 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
134 info.GetReturnValue().Set(Nan::New<Number>(img->naturalWidth));
135}
136
137/*
138 * Get width.
139 */
140
141NAN_GETTER(Image::GetWidth) {
142 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
143 info.GetReturnValue().Set(Nan::New<Number>(img->width));
144}
145
146/*
147 * Set width.
148 */
149
150NAN_SETTER(Image::SetWidth) {
151 if (value->IsNumber()) {
152 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
153 img->width = Nan::To<uint32_t>(value).FromMaybe(0);
154 }
155}
156
157/*
158 * Get natural height
159 */
160
161NAN_GETTER(Image::GetNaturalHeight) {
162 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
163 info.GetReturnValue().Set(Nan::New<Number>(img->naturalHeight));
164}
165
166/*
167 * Get height.
168 */
169
170NAN_GETTER(Image::GetHeight) {
171 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
172 info.GetReturnValue().Set(Nan::New<Number>(img->height));
173}
174/*
175 * Set height.
176 */
177
178NAN_SETTER(Image::SetHeight) {
179 if (value->IsNumber()) {
180 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
181 img->height = Nan::To<uint32_t>(value).FromMaybe(0);
182 }
183}
184
185/*
186 * Get src path.
187 */
188
189NAN_METHOD(Image::GetSource){
190 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
191 info.GetReturnValue().Set(Nan::New<String>(img->filename ? img->filename : "").ToLocalChecked());
192}
193
194/*
195 * Clean up assets and variables.
196 */
197
198void
199Image::clearData() {
200 if (_surface) {
201 cairo_surface_destroy(_surface);
202 Nan::AdjustExternalMemory(-_data_len);
203 _data_len = 0;
204 _surface = NULL;
205 }
206
207 delete[] _data;
208 _data = nullptr;
209
210 free(filename);
211 filename = NULL;
212
213#ifdef HAVE_RSVG
214 if (_rsvg != NULL) {
215 g_object_unref(_rsvg);
216 _rsvg = NULL;
217 }
218#endif
219
220 width = height = 0;
221 naturalWidth = naturalHeight = 0;
222 state = DEFAULT;
223}
224
225/*
226 * Set src path.
227 */
228
229NAN_METHOD(Image::SetSource){
230 Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
231 cairo_status_t status = CAIRO_STATUS_READ_ERROR;
232
233 Local<Value> value = info[0];
234
235 img->clearData();
236 // Clear errno in case some unrelated previous syscall failed
237 errno = 0;
238
239 // url string
240 if (value->IsString()) {
241 Nan::Utf8String src(value);
242 if (img->filename) free(img->filename);
243 img->filename = strdup(*src);
244 status = img->load();
245 // Buffer
246 } else if (node::Buffer::HasInstance(value)) {
247 uint8_t *buf = (uint8_t *) node::Buffer::Data(Nan::To<Object>(value).ToLocalChecked());
248 unsigned len = node::Buffer::Length(Nan::To<Object>(value).ToLocalChecked());
249 status = img->loadFromBuffer(buf, len);
250 }
251
252 if (status) {
253 Local<Value> onerrorFn = Nan::Get(info.This(), Nan::New("onerror").ToLocalChecked()).ToLocalChecked();
254 if (onerrorFn->IsFunction()) {
255 Local<Value> argv[1];
256 CanvasError errorInfo = img->errorInfo;
257 if (errorInfo.cerrno) {
258 argv[0] = Nan::ErrnoException(errorInfo.cerrno, errorInfo.syscall.c_str(), errorInfo.message.c_str(), errorInfo.path.c_str());
259 } else if (!errorInfo.message.empty()) {
260 argv[0] = Nan::Error(Nan::New(errorInfo.message).ToLocalChecked());
261 } else {
262 argv[0] = Nan::Error(Nan::New(cairo_status_to_string(status)).ToLocalChecked());
263 }
264 Local<Context> ctx = Nan::GetCurrentContext();
265 Nan::Call(onerrorFn.As<Function>(), ctx->Global(), 1, argv).ToLocalChecked();
266 }
267 } else {
268 img->loaded();
269 Local<Value> onloadFn = Nan::Get(info.This(), Nan::New("onload").ToLocalChecked()).ToLocalChecked();
270 if (onloadFn->IsFunction()) {
271 Local<Context> ctx = Nan::GetCurrentContext();
272 Nan::Call(onloadFn.As<Function>(), ctx->Global(), 0, NULL).ToLocalChecked();
273 }
274 }
275}
276
277/*
278 * Load image data from `buf` by sniffing
279 * the bytes to determine format.
280 */
281
282cairo_status_t
283Image::loadFromBuffer(uint8_t *buf, unsigned len) {
284 uint8_t data[4] = {0};
285 memcpy(data, buf, (len < 4 ? len : 4) * sizeof(uint8_t));
286
287 if (isPNG(data)) return loadPNGFromBuffer(buf);
288
289 if (isGIF(data)) {
290#ifdef HAVE_GIF
291 return loadGIFFromBuffer(buf, len);
292#else
293 this->errorInfo.set("node-canvas was built without GIF support");
294 return CAIRO_STATUS_READ_ERROR;
295#endif
296 }
297
298 if (isJPEG(data)) {
299#ifdef HAVE_JPEG
300 if (DATA_IMAGE == data_mode) return loadJPEGFromBuffer(buf, len);
301 if (DATA_MIME == data_mode) return decodeJPEGBufferIntoMimeSurface(buf, len);
302 if ((DATA_IMAGE | DATA_MIME) == data_mode) {
303 cairo_status_t status;
304 status = loadJPEGFromBuffer(buf, len);
305 if (status) return status;
306 return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
307 }
308#else // HAVE_JPEG
309 this->errorInfo.set("node-canvas was built without JPEG support");
310 return CAIRO_STATUS_READ_ERROR;
311#endif
312 }
313
314 // confirm svg using first 1000 chars
315 // if a very long comment precedes the root <svg> tag, isSVG returns false
316 unsigned head_len = (len < 1000 ? len : 1000);
317 if (isSVG(buf, head_len)) {
318#ifdef HAVE_RSVG
319 return loadSVGFromBuffer(buf, len);
320#else
321 this->errorInfo.set("node-canvas was built without SVG support");
322 return CAIRO_STATUS_READ_ERROR;
323#endif
324 }
325
326 if (isBMP(buf, len))
327 return loadBMPFromBuffer(buf, len);
328
329 this->errorInfo.set("Unsupported image type");
330 return CAIRO_STATUS_READ_ERROR;
331}
332
333/*
334 * Load PNG data from `buf`.
335 */
336
337cairo_status_t
338Image::loadPNGFromBuffer(uint8_t *buf) {
339 read_closure_t closure;
340 closure.len = 0;
341 closure.buf = buf;
342 _surface = cairo_image_surface_create_from_png_stream(readPNG, &closure);
343 cairo_status_t status = cairo_surface_status(_surface);
344 if (status) return status;
345 return CAIRO_STATUS_SUCCESS;
346}
347
348/*
349 * Read PNG data.
350 */
351
352cairo_status_t
353Image::readPNG(void *c, uint8_t *data, unsigned int len) {
354 read_closure_t *closure = (read_closure_t *) c;
355 memcpy(data, closure->buf + closure->len, len);
356 closure->len += len;
357 return CAIRO_STATUS_SUCCESS;
358}
359
360/*
361 * Initialize a new Image.
362 */
363
364Image::Image() {
365 filename = NULL;
366 _data = nullptr;
367 _data_len = 0;
368 _surface = NULL;
369 width = height = 0;
370 naturalWidth = naturalHeight = 0;
371 state = DEFAULT;
372#ifdef HAVE_RSVG
373 _rsvg = NULL;
374 _is_svg = false;
375 _svg_last_width = _svg_last_height = 0;
376#endif
377}
378
379/*
380 * Destroy image and associated surface.
381 */
382
383Image::~Image() {
384 clearData();
385}
386
387/*
388 * Initiate image loading.
389 */
390
391cairo_status_t
392Image::load() {
393 if (LOADING != state) {
394 state = LOADING;
395 return loadSurface();
396 }
397 return CAIRO_STATUS_READ_ERROR;
398}
399
400/*
401 * Set state, assign dimensions.
402 */
403
404void
405Image::loaded() {
406 Nan::HandleScope scope;
407 state = COMPLETE;
408
409 width = naturalWidth = cairo_image_surface_get_width(_surface);
410 height = naturalHeight = cairo_image_surface_get_height(_surface);
411 _data_len = naturalHeight * cairo_image_surface_get_stride(_surface);
412 Nan::AdjustExternalMemory(_data_len);
413}
414
415/*
416 * Returns this image's surface.
417 */
418cairo_surface_t *Image::surface() {
419#ifdef HAVE_RSVG
420 if (_is_svg && (_svg_last_width != width || _svg_last_height != height)) {
421 if (_surface != NULL) {
422 cairo_surface_destroy(_surface);
423 _surface = NULL;
424 }
425
426 cairo_status_t status = renderSVGToSurface();
427 if (status != CAIRO_STATUS_SUCCESS) {
428 g_object_unref(_rsvg);
429 Nan::ThrowError(Canvas::Error(status));
430 return NULL;
431 }
432 }
433#endif
434 return _surface;
435}
436
437/*
438 * Load cairo surface from the image src.
439 *
440 * TODO: support more formats
441 * TODO: use node IO or at least thread pool
442 */
443
444cairo_status_t
445Image::loadSurface() {
446 FILE *stream = fopen(filename, "rb");
447 if (!stream) {
448 this->errorInfo.set(NULL, "fopen", errno, filename);
449 return CAIRO_STATUS_READ_ERROR;
450 }
451 uint8_t buf[5];
452 if (1 != fread(&buf, 5, 1, stream)) {
453 fclose(stream);
454 return CAIRO_STATUS_READ_ERROR;
455 }
456 rewind(stream);
457
458 // png
459 if (isPNG(buf)) {
460 fclose(stream);
461 return loadPNG();
462 }
463
464
465 if (isGIF(buf)) {
466#ifdef HAVE_GIF
467 return loadGIF(stream);
468#else
469 this->errorInfo.set("node-canvas was built without GIF support");
470 return CAIRO_STATUS_READ_ERROR;
471#endif
472 }
473
474 if (isJPEG(buf)) {
475#ifdef HAVE_JPEG
476 return loadJPEG(stream);
477#else
478 this->errorInfo.set("node-canvas was built without JPEG support");
479 return CAIRO_STATUS_READ_ERROR;
480#endif
481 }
482
483 // confirm svg using first 1000 chars
484 // if a very long comment precedes the root <svg> tag, isSVG returns false
485 uint8_t head[1000] = {0};
486 fseek(stream, 0 , SEEK_END);
487 long len = ftell(stream);
488 unsigned head_len = (len < 1000 ? len : 1000);
489 unsigned head_size = head_len * sizeof(uint8_t);
490 rewind(stream);
491 if (head_size != fread(&head, 1, head_size, stream)) {
492 fclose(stream);
493 return CAIRO_STATUS_READ_ERROR;
494 }
495 rewind(stream);
496 if (isSVG(head, head_len)) {
497#ifdef HAVE_RSVG
498 return loadSVG(stream);
499#else
500 this->errorInfo.set("node-canvas was built without SVG support");
501 return CAIRO_STATUS_READ_ERROR;
502#endif
503 }
504
505 if (isBMP(buf, 2))
506 return loadBMP(stream);
507
508 fclose(stream);
509
510 this->errorInfo.set("Unsupported image type");
511 return CAIRO_STATUS_READ_ERROR;
512}
513
514/*
515 * Load PNG.
516 */
517
518cairo_status_t
519Image::loadPNG() {
520 _surface = cairo_image_surface_create_from_png(filename);
521 return cairo_surface_status(_surface);
522}
523
524// GIF support
525
526#ifdef HAVE_GIF
527
528/*
529 * Return the alpha color for `gif` at `frame`, or -1.
530 */
531
532int
533get_gif_transparent_color(GifFileType *gif, int frame) {
534 ExtensionBlock *ext = gif->SavedImages[frame].ExtensionBlocks;
535 int len = gif->SavedImages[frame].ExtensionBlockCount;
536 for (int x = 0; x < len; ++x, ++ext) {
537 if ((ext->Function == GRAPHICS_EXT_FUNC_CODE) && (ext->Bytes[0] & 1)) {
538 return ext->Bytes[3] == 0 ? 0 : (uint8_t) ext->Bytes[3];
539 }
540 }
541 return -1;
542}
543
544/*
545 * Memory GIF reader callback.
546 */
547
548int
549read_gif_from_memory(GifFileType *gif, GifByteType *buf, int len) {
550 gif_data_t *data = (gif_data_t *) gif->UserData;
551 if ((data->pos + len) > data->len) len = data->len - data->pos;
552 memcpy(buf, data->pos + data->buf, len);
553 data->pos += len;
554 return len;
555}
556
557/*
558 * Load GIF.
559 */
560
561cairo_status_t
562Image::loadGIF(FILE *stream) {
563 struct stat s;
564 int fd = fileno(stream);
565
566 // stat
567 if (fstat(fd, &s) < 0) {
568 fclose(stream);
569 return CAIRO_STATUS_READ_ERROR;
570 }
571
572 uint8_t *buf = (uint8_t *) malloc(s.st_size);
573
574 if (!buf) {
575 fclose(stream);
576 this->errorInfo.set(NULL, "malloc", errno);
577 return CAIRO_STATUS_NO_MEMORY;
578 }
579
580 size_t read = fread(buf, s.st_size, 1, stream);
581 fclose(stream);
582
583 cairo_status_t result = CAIRO_STATUS_READ_ERROR;
584 if (1 == read) result = loadGIFFromBuffer(buf, s.st_size);
585 free(buf);
586
587 return result;
588}
589
590/*
591 * Load give from `buf` and the given `len`.
592 */
593
594cairo_status_t
595Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) {
596 int i = 0;
597 GifFileType* gif;
598
599 gif_data_t gifd = { buf, len, 0 };
600
601#if GIFLIB_MAJOR >= 5
602 int errorcode;
603 if ((gif = DGifOpen((void*) &gifd, read_gif_from_memory, &errorcode)) == NULL)
604 return CAIRO_STATUS_READ_ERROR;
605#else
606 if ((gif = DGifOpen((void*) &gifd, read_gif_from_memory)) == NULL)
607 return CAIRO_STATUS_READ_ERROR;
608#endif
609
610 if (GIF_OK != DGifSlurp(gif)) {
611 GIF_CLOSE_FILE(gif);
612 return CAIRO_STATUS_READ_ERROR;
613 }
614
615 if (gif->SWidth > canvas_max_side || gif->SHeight > canvas_max_side) {
616 GIF_CLOSE_FILE(gif);
617 return CAIRO_STATUS_INVALID_SIZE;
618 }
619
620 width = naturalWidth = gif->SWidth;
621 height = naturalHeight = gif->SHeight;
622
623 uint8_t *data = new uint8_t[naturalWidth * naturalHeight * 4];
624 if (!data) {
625 GIF_CLOSE_FILE(gif);
626 this->errorInfo.set(NULL, "malloc", errno);
627 return CAIRO_STATUS_NO_MEMORY;
628 }
629
630 GifImageDesc *img = &gif->SavedImages[i].ImageDesc;
631
632 // local colormap takes precedence over global
633 ColorMapObject *colormap = img->ColorMap
634 ? img->ColorMap
635 : gif->SColorMap;
636
637 if (colormap == nullptr) {
638 GIF_CLOSE_FILE(gif);
639 return CAIRO_STATUS_READ_ERROR;
640 }
641
642 int bgColor = 0;
643 int alphaColor = get_gif_transparent_color(gif, i);
644 if (gif->SColorMap) bgColor = (uint8_t) gif->SBackGroundColor;
645 else if(alphaColor >= 0) bgColor = alphaColor;
646
647 uint8_t *src_data = (uint8_t*) gif->SavedImages[i].RasterBits;
648 uint32_t *dst_data = (uint32_t*) data;
649
650 if (!gif->Image.Interlace) {
651 if (naturalWidth == img->Width && naturalHeight == img->Height) {
652 for (int y = 0; y < naturalHeight; ++y) {
653 for (int x = 0; x < naturalWidth; ++x) {
654 *dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
655 | colormap->Colors[*src_data].Red << 16
656 | colormap->Colors[*src_data].Green << 8
657 | colormap->Colors[*src_data].Blue;
658
659 dst_data++;
660 src_data++;
661 }
662 }
663 } else {
664 // Image does not take up whole "screen" so we need to fill-in the background
665 int bottom = img->Top + img->Height;
666 int right = img->Left + img->Width;
667
668 uint32_t bgPixel =
669 ((bgColor == alphaColor) ? 0 : 255) << 24
670 | colormap->Colors[bgColor].Red << 16
671 | colormap->Colors[bgColor].Green << 8
672 | colormap->Colors[bgColor].Blue;
673
674 for (int y = 0; y < naturalHeight; ++y) {
675 for (int x = 0; x < naturalWidth; ++x) {
676 if (y < img->Top || y >= bottom || x < img->Left || x >= right) {
677 *dst_data = bgPixel;
678 dst_data++;
679 } else {
680 *dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
681 | colormap->Colors[*src_data].Red << 16
682 | colormap->Colors[*src_data].Green << 8
683 | colormap->Colors[*src_data].Blue;
684 dst_data++;
685 src_data++;
686 }
687 }
688 }
689 }
690 } else {
691 // Image is interlaced so that it streams nice over 14.4k and 28.8k modems :)
692 // We first load in 1/8 of the image, followed by another 1/8, followed by
693 // 1/4 and finally the remaining 1/2.
694 int ioffs[] = { 0, 4, 2, 1 };
695 int ijumps[] = { 8, 8, 4, 2 };
696
697 uint8_t *src_ptr = src_data;
698 uint32_t *dst_ptr;
699
700 for(int z = 0; z < 4; z++) {
701 for(int y = ioffs[z]; y < naturalHeight; y += ijumps[z]) {
702 dst_ptr = dst_data + naturalWidth * y;
703 for(int x = 0; x < naturalWidth; ++x) {
704 *dst_ptr = ((*src_ptr == alphaColor) ? 0 : 255) << 24
705 | (colormap->Colors[*src_ptr].Red) << 16
706 | (colormap->Colors[*src_ptr].Green) << 8
707 | (colormap->Colors[*src_ptr].Blue);
708
709 dst_ptr++;
710 src_ptr++;
711 }
712 }
713 }
714 }
715
716 GIF_CLOSE_FILE(gif);
717
718 // New image surface
719 _surface = cairo_image_surface_create_for_data(
720 data
721 , CAIRO_FORMAT_ARGB32
722 , naturalWidth
723 , naturalHeight
724 , cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, naturalWidth));
725
726 cairo_status_t status = cairo_surface_status(_surface);
727
728 if (status) {
729 delete[] data;
730 return status;
731 }
732
733 _data = data;
734
735 return CAIRO_STATUS_SUCCESS;
736}
737#endif /* HAVE_GIF */
738
739// JPEG support
740
741#ifdef HAVE_JPEG
742
743// libjpeg 6.2 does not have jpeg_mem_src; define it ourselves here unless
744// libjpeg 8 is installed.
745#if JPEG_LIB_VERSION < 80 && !defined(MEM_SRCDST_SUPPORTED)
746
747/* Read JPEG image from a memory segment */
748static void
749init_source(j_decompress_ptr cinfo) {}
750
751static boolean
752fill_input_buffer(j_decompress_ptr cinfo) {
753 ERREXIT(cinfo, JERR_INPUT_EMPTY);
754 return TRUE;
755}
756static void
757skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
758 struct jpeg_source_mgr* src = (struct jpeg_source_mgr*) cinfo->src;
759 if (num_bytes > 0) {
760 src->next_input_byte += (size_t) num_bytes;
761 src->bytes_in_buffer -= (size_t) num_bytes;
762 }
763}
764
765static void term_source (j_decompress_ptr cinfo) {}
766static void jpeg_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes) {
767 struct jpeg_source_mgr* src;
768
769 if (cinfo->src == NULL) {
770 cinfo->src = (struct jpeg_source_mgr *)
771 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
772 sizeof(struct jpeg_source_mgr));
773 }
774
775 src = (struct jpeg_source_mgr*) cinfo->src;
776 src->init_source = init_source;
777 src->fill_input_buffer = fill_input_buffer;
778 src->skip_input_data = skip_input_data;
779 src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
780 src->term_source = term_source;
781 src->bytes_in_buffer = nbytes;
782 src->next_input_byte = (JOCTET*)buffer;
783}
784
785#endif
786
787void Image::jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode) {
788 int stride = naturalWidth * 4;
789 for (int y = 0; y < naturalHeight; ++y) {
790 jpeg_read_scanlines(args, &src, 1);
791 uint32_t *row = (uint32_t*)(data + stride * y);
792 for (int x = 0; x < naturalWidth; ++x) {
793 int bx = args->output_components * x;
794 row[x] = decode(src + bx);
795 }
796 }
797}
798
799/*
800 * Takes an initialised jpeg_decompress_struct and decodes the
801 * data into _surface.
802 */
803
804cairo_status_t
805Image::decodeJPEGIntoSurface(jpeg_decompress_struct *args) {
806 cairo_status_t status = CAIRO_STATUS_SUCCESS;
807
808 uint8_t *data = new uint8_t[naturalWidth * naturalHeight * 4];
809 if (!data) {
810 jpeg_abort_decompress(args);
811 jpeg_destroy_decompress(args);
812 this->errorInfo.set(NULL, "malloc", errno);
813 return CAIRO_STATUS_NO_MEMORY;
814 }
815
816 uint8_t *src = new uint8_t[naturalWidth * args->output_components];
817 if (!src) {
818 free(data);
819 jpeg_abort_decompress(args);
820 jpeg_destroy_decompress(args);
821 this->errorInfo.set(NULL, "malloc", errno);
822 return CAIRO_STATUS_NO_MEMORY;
823 }
824
825 // These are the three main cases to handle. libjpeg converts YCCK to CMYK
826 // and YCbCr to RGB by default.
827 switch (args->out_color_space) {
828 case JCS_CMYK:
829 jpegToARGB(args, data, src, [](uint8_t const* src) {
830 uint16_t k = static_cast<uint16_t>(src[3]);
831 uint8_t r = k * src[0] / 255;
832 uint8_t g = k * src[1] / 255;
833 uint8_t b = k * src[2] / 255;
834 return 255 << 24 | r << 16 | g << 8 | b;
835 });
836 break;
837 case JCS_RGB:
838 jpegToARGB(args, data, src, [](uint8_t const* src) {
839 uint8_t r = src[0], g = src[1], b = src[2];
840 return 255 << 24 | r << 16 | g << 8 | b;
841 });
842 break;
843 case JCS_GRAYSCALE:
844 jpegToARGB(args, data, src, [](uint8_t const* src) {
845 uint8_t v = src[0];
846 return 255 << 24 | v << 16 | v << 8 | v;
847 });
848 break;
849 default:
850 this->errorInfo.set("Unsupported JPEG encoding");
851 status = CAIRO_STATUS_READ_ERROR;
852 break;
853 }
854
855 if (!status) {
856 _surface = cairo_image_surface_create_for_data(
857 data
858 , CAIRO_FORMAT_ARGB32
859 , naturalWidth
860 , naturalHeight
861 , cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, naturalWidth));
862 }
863
864 jpeg_finish_decompress(args);
865 jpeg_destroy_decompress(args);
866 status = cairo_surface_status(_surface);
867
868 delete[] src;
869
870 if (status) {
871 delete[] data;
872 return status;
873 }
874
875 _data = data;
876
877 return CAIRO_STATUS_SUCCESS;
878}
879
880/*
881 * Callback to recover from jpeg errors
882 */
883
884static void canvas_jpeg_error_exit(j_common_ptr cinfo) {
885 canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);
886 cjerr->output_message(cinfo);
887 // Return control to the setjmp point
888 longjmp(cjerr->setjmp_buffer, 1);
889}
890
891// Capture libjpeg errors instead of writing stdout
892static void canvas_jpeg_output_message(j_common_ptr cinfo) {
893 canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);
894 char buff[JMSG_LENGTH_MAX];
895 cjerr->format_message(cinfo, buff);
896 // (Only the last message will be returned to JS land.)
897 cjerr->image->errorInfo.set(buff);
898}
899
900/*
901 * Takes a jpeg data buffer and assigns it as mime data to a
902 * dummy surface
903 */
904
905cairo_status_t
906Image::decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len) {
907 // TODO: remove this duplicate logic
908 // JPEG setup
909 struct jpeg_decompress_struct args;
910 struct canvas_jpeg_error_mgr err;
911
912 err.image = this;
913 args.err = jpeg_std_error(&err);
914 args.err->error_exit = canvas_jpeg_error_exit;
915 args.err->output_message = canvas_jpeg_output_message;
916
917 // Establish the setjmp return context for canvas_jpeg_error_exit to use
918 if (setjmp(err.setjmp_buffer)) {
919 // If we get here, the JPEG code has signaled an error.
920 // We need to clean up the JPEG object, close the input file, and return.
921 jpeg_destroy_decompress(&args);
922 return CAIRO_STATUS_READ_ERROR;
923 }
924
925 jpeg_create_decompress(&args);
926
927 jpeg_mem_src(&args, buf, len);
928
929 jpeg_read_header(&args, 1);
930 jpeg_start_decompress(&args);
931 width = naturalWidth = args.output_width;
932 height = naturalHeight = args.output_height;
933
934 // Data alloc
935 // 8 pixels per byte using Alpha Channel format to reduce memory requirement.
936 int buf_size = naturalHeight * cairo_format_stride_for_width(CAIRO_FORMAT_A1, naturalWidth);
937 uint8_t *data = new uint8_t[buf_size];
938 if (!data) {
939 this->errorInfo.set(NULL, "malloc", errno);
940 return CAIRO_STATUS_NO_MEMORY;
941 }
942
943 // New image surface
944 _surface = cairo_image_surface_create_for_data(
945 data
946 , CAIRO_FORMAT_A1
947 , naturalWidth
948 , naturalHeight
949 , cairo_format_stride_for_width(CAIRO_FORMAT_A1, naturalWidth));
950
951 // Cleanup
952 jpeg_abort_decompress(&args);
953 jpeg_destroy_decompress(&args);
954 cairo_status_t status = cairo_surface_status(_surface);
955
956 if (status) {
957 delete[] data;
958 return status;
959 }
960
961 _data = data;
962
963 return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
964}
965
966/*
967 * Helper function for disposing of a mime data closure.
968 */
969
970void
971clearMimeData(void *closure) {
972 Nan::AdjustExternalMemory(
973 -static_cast<int>((static_cast<read_closure_t *>(closure)->len)));
974 free(static_cast<read_closure_t *>(closure)->buf);
975 free(closure);
976}
977
978/*
979 * Assign a given buffer as mime data against the surface.
980 * The provided buffer will be copied, and the copy will
981 * be automatically freed when the surface is destroyed.
982 */
983
984cairo_status_t
985Image::assignDataAsMime(uint8_t *data, int len, const char *mime_type) {
986 uint8_t *mime_data = (uint8_t *) malloc(len);
987 if (!mime_data) {
988 this->errorInfo.set(NULL, "malloc", errno);
989 return CAIRO_STATUS_NO_MEMORY;
990 }
991
992 read_closure_t *mime_closure = (read_closure_t *) malloc(sizeof(read_closure_t));
993 if (!mime_closure) {
994 free(mime_data);
995 this->errorInfo.set(NULL, "malloc", errno);
996 return CAIRO_STATUS_NO_MEMORY;
997 }
998
999 memcpy(mime_data, data, len);
1000
1001 mime_closure->buf = mime_data;
1002 mime_closure->len = len;
1003
1004 Nan::AdjustExternalMemory(len);
1005
1006 return cairo_surface_set_mime_data(_surface
1007 , mime_type
1008 , mime_data
1009 , len
1010 , clearMimeData
1011 , mime_closure);
1012}
1013
1014/*
1015 * Load jpeg from buffer.
1016 */
1017
1018cairo_status_t
1019Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) {
1020 // TODO: remove this duplicate logic
1021 // JPEG setup
1022 struct jpeg_decompress_struct args;
1023 struct canvas_jpeg_error_mgr err;
1024
1025 err.image = this;
1026 args.err = jpeg_std_error(&err);
1027 args.err->error_exit = canvas_jpeg_error_exit;
1028 args.err->output_message = canvas_jpeg_output_message;
1029
1030 // Establish the setjmp return context for canvas_jpeg_error_exit to use
1031 if (setjmp(err.setjmp_buffer)) {
1032 // If we get here, the JPEG code has signaled an error.
1033 // We need to clean up the JPEG object, close the input file, and return.
1034 jpeg_destroy_decompress(&args);
1035 return CAIRO_STATUS_READ_ERROR;
1036 }
1037
1038 jpeg_create_decompress(&args);
1039
1040 jpeg_mem_src(&args, buf, len);
1041
1042 jpeg_read_header(&args, 1);
1043 jpeg_start_decompress(&args);
1044 width = naturalWidth = args.output_width;
1045 height = naturalHeight = args.output_height;
1046
1047 return decodeJPEGIntoSurface(&args);
1048}
1049
1050/*
1051 * Load JPEG, convert RGB to ARGB.
1052 */
1053
1054cairo_status_t
1055Image::loadJPEG(FILE *stream) {
1056 cairo_status_t status;
1057
1058#if defined(_MSC_VER)
1059 if (false) { // Force using loadJPEGFromBuffer
1060#else
1061 if (data_mode == DATA_IMAGE) { // Can lazily read in the JPEG.
1062#endif
1063 // JPEG setup
1064 struct jpeg_decompress_struct args;
1065 struct canvas_jpeg_error_mgr err;
1066
1067 err.image = this;
1068 args.err = jpeg_std_error(&err);
1069 args.err->error_exit = canvas_jpeg_error_exit;
1070 args.err->output_message = canvas_jpeg_output_message;
1071
1072 // Establish the setjmp return context for canvas_jpeg_error_exit to use
1073 if (setjmp(err.setjmp_buffer)) {
1074 // If we get here, the JPEG code has signaled an error.
1075 // We need to clean up the JPEG object, close the input file, and return.
1076 jpeg_destroy_decompress(&args);
1077 return CAIRO_STATUS_READ_ERROR;
1078 }
1079
1080 jpeg_create_decompress(&args);
1081
1082 jpeg_stdio_src(&args, stream);
1083
1084 jpeg_read_header(&args, 1);
1085 jpeg_start_decompress(&args);
1086
1087 if (args.output_width > canvas_max_side || args.output_height > canvas_max_side) {
1088 jpeg_destroy_decompress(&args);
1089 return CAIRO_STATUS_INVALID_SIZE;
1090 }
1091
1092 width = naturalWidth = args.output_width;
1093 height = naturalHeight = args.output_height;
1094
1095 status = decodeJPEGIntoSurface(&args);
1096 fclose(stream);
1097 } else { // We'll need the actual source jpeg data, so read fully.
1098 uint8_t *buf;
1099 unsigned len;
1100
1101 fseek(stream, 0, SEEK_END);
1102 len = ftell(stream);
1103 fseek(stream, 0, SEEK_SET);
1104
1105 buf = (uint8_t *) malloc(len);
1106 if (!buf) {
1107 this->errorInfo.set(NULL, "malloc", errno);
1108 return CAIRO_STATUS_NO_MEMORY;
1109 }
1110
1111 if (fread(buf, len, 1, stream) != 1) {
1112 status = CAIRO_STATUS_READ_ERROR;
1113 } else if ((DATA_IMAGE | DATA_MIME) == data_mode) {
1114 status = loadJPEGFromBuffer(buf, len);
1115 if (!status) status = assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
1116 } else if (DATA_MIME == data_mode) {
1117 status = decodeJPEGBufferIntoMimeSurface(buf, len);
1118 }
1119#if defined(_MSC_VER)
1120 else if (DATA_IMAGE == data_mode) {
1121 status = loadJPEGFromBuffer(buf, len);
1122 }
1123#endif
1124 else {
1125 status = CAIRO_STATUS_READ_ERROR;
1126 }
1127
1128 fclose(stream);
1129 free(buf);
1130 }
1131
1132 return status;
1133}
1134
1135#endif /* HAVE_JPEG */
1136
1137#ifdef HAVE_RSVG
1138
1139/*
1140 * Load SVG from buffer
1141 */
1142
1143cairo_status_t
1144Image::loadSVGFromBuffer(uint8_t *buf, unsigned len) {
1145 _is_svg = true;
1146
1147 cairo_status_t status;
1148 GError *gerr = NULL;
1149
1150 if (NULL == (_rsvg = rsvg_handle_new_from_data(buf, len, &gerr))) {
1151 return CAIRO_STATUS_READ_ERROR;
1152 }
1153
1154 RsvgDimensionData *dims = new RsvgDimensionData();
1155 rsvg_handle_get_dimensions(_rsvg, dims);
1156
1157 width = naturalWidth = dims->width;
1158 height = naturalHeight = dims->height;
1159
1160 status = renderSVGToSurface();
1161 if (status != CAIRO_STATUS_SUCCESS) {
1162 g_object_unref(_rsvg);
1163 return status;
1164 }
1165
1166 return CAIRO_STATUS_SUCCESS;
1167}
1168
1169/*
1170 * Renders the Rsvg handle to this image's surface
1171 */
1172cairo_status_t
1173Image::renderSVGToSurface() {
1174 cairo_status_t status;
1175
1176 _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
1177
1178 status = cairo_surface_status(_surface);
1179 if (status != CAIRO_STATUS_SUCCESS) {
1180 g_object_unref(_rsvg);
1181 return status;
1182 }
1183
1184 cairo_t *cr = cairo_create(_surface);
1185 cairo_scale(cr,
1186 (double)width / (double)naturalWidth,
1187 (double)height / (double)naturalHeight);
1188 status = cairo_status(cr);
1189 if (status != CAIRO_STATUS_SUCCESS) {
1190 g_object_unref(_rsvg);
1191 return status;
1192 }
1193
1194 gboolean render_ok = rsvg_handle_render_cairo(_rsvg, cr);
1195 if (!render_ok) {
1196 g_object_unref(_rsvg);
1197 cairo_destroy(cr);
1198 return CAIRO_STATUS_READ_ERROR; // or WRITE?
1199 }
1200
1201 cairo_destroy(cr);
1202
1203 _svg_last_width = width;
1204 _svg_last_height = height;
1205
1206 return status;
1207}
1208
1209/*
1210 * Load SVG
1211 */
1212
1213cairo_status_t
1214Image::loadSVG(FILE *stream) {
1215 _is_svg = true;
1216
1217 struct stat s;
1218 int fd = fileno(stream);
1219
1220 // stat
1221 if (fstat(fd, &s) < 0) {
1222 fclose(stream);
1223 return CAIRO_STATUS_READ_ERROR;
1224 }
1225
1226 uint8_t *buf = (uint8_t *) malloc(s.st_size);
1227
1228 if (!buf) {
1229 fclose(stream);
1230 return CAIRO_STATUS_NO_MEMORY;
1231 }
1232
1233 size_t read = fread(buf, s.st_size, 1, stream);
1234 fclose(stream);
1235
1236 cairo_status_t result = CAIRO_STATUS_READ_ERROR;
1237 if (1 == read) result = loadSVGFromBuffer(buf, s.st_size);
1238 free(buf);
1239
1240 return result;
1241}
1242
1243#endif /* HAVE_RSVG */
1244
1245/*
1246 * Load BMP from buffer.
1247 */
1248
1249cairo_status_t Image::loadBMPFromBuffer(uint8_t *buf, unsigned len){
1250 BMPParser::Parser parser;
1251
1252 // Reversed ARGB32 with pre-multiplied alpha
1253 uint8_t pixFmt[5] = {2, 1, 0, 3, 1};
1254 parser.parse(buf, len, pixFmt);
1255
1256 if (parser.getStatus() != BMPParser::Status::OK) {
1257 errorInfo.reset();
1258 errorInfo.message = parser.getErrMsg();
1259 return CAIRO_STATUS_READ_ERROR;
1260 }
1261
1262 width = naturalWidth = parser.getWidth();
1263 height = naturalHeight = parser.getHeight();
1264 uint8_t *data = parser.getImgd();
1265
1266 _surface = cairo_image_surface_create_for_data(
1267 data,
1268 CAIRO_FORMAT_ARGB32,
1269 width,
1270 height,
1271 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width)
1272 );
1273
1274 // No need to delete the data
1275 cairo_status_t status = cairo_surface_status(_surface);
1276 if (status) return status;
1277
1278 _data = data;
1279 parser.clearImgd();
1280
1281 return CAIRO_STATUS_SUCCESS;
1282}
1283
1284/*
1285 * Load BMP.
1286 */
1287
1288cairo_status_t Image::loadBMP(FILE *stream){
1289 struct stat s;
1290 int fd = fileno(stream);
1291
1292 // Stat
1293 if (fstat(fd, &s) < 0) {
1294 fclose(stream);
1295 return CAIRO_STATUS_READ_ERROR;
1296 }
1297
1298 uint8_t *buf = new uint8_t[s.st_size];
1299
1300 if (!buf) {
1301 fclose(stream);
1302 errorInfo.set(NULL, "malloc", errno);
1303 return CAIRO_STATUS_NO_MEMORY;
1304 }
1305
1306 size_t read = fread(buf, s.st_size, 1, stream);
1307 fclose(stream);
1308
1309 cairo_status_t result = CAIRO_STATUS_READ_ERROR;
1310 if (read == 1) result = loadBMPFromBuffer(buf, s.st_size);
1311 delete[] buf;
1312
1313 return result;
1314}
1315
1316/*
1317 * Return UNKNOWN, SVG, GIF, JPEG, or PNG based on the filename.
1318 */
1319
1320Image::type
1321Image::extension(const char *filename) {
1322 size_t len = strlen(filename);
1323 filename += len;
1324 if (len >= 5 && 0 == strcmp(".jpeg", filename - 5)) return Image::JPEG;
1325 if (len >= 4 && 0 == strcmp(".gif", filename - 4)) return Image::GIF;
1326 if (len >= 4 && 0 == strcmp(".jpg", filename - 4)) return Image::JPEG;
1327 if (len >= 4 && 0 == strcmp(".png", filename - 4)) return Image::PNG;
1328 if (len >= 4 && 0 == strcmp(".svg", filename - 4)) return Image::SVG;
1329 return Image::UNKNOWN;
1330}
1331
1332/*
1333 * Sniff bytes 0..1 for JPEG's magic number ff d8.
1334 */
1335
1336int
1337Image::isJPEG(uint8_t *data) {
1338 return 0xff == data[0] && 0xd8 == data[1];
1339}
1340
1341/*
1342 * Sniff bytes 0..2 for "GIF".
1343 */
1344
1345int
1346Image::isGIF(uint8_t *data) {
1347 return 'G' == data[0] && 'I' == data[1] && 'F' == data[2];
1348}
1349
1350/*
1351 * Sniff bytes 1..3 for "PNG".
1352 */
1353
1354int
1355Image::isPNG(uint8_t *data) {
1356 return 'P' == data[1] && 'N' == data[2] && 'G' == data[3];
1357}
1358
1359/*
1360 * Skip "<?" and "<!" tags to test if root tag starts "<svg"
1361 */
1362int
1363Image::isSVG(uint8_t *data, unsigned len) {
1364 for (unsigned i = 3; i < len; i++) {
1365 if ('<' == data[i-3]) {
1366 switch (data[i-2]) {
1367 case '?':
1368 case '!':
1369 break;
1370 case 's':
1371 return ('v' == data[i-1] && 'g' == data[i]);
1372 default:
1373 return false;
1374 }
1375 }
1376 }
1377 return false;
1378}
1379
1380/*
1381 * Check for valid BMP signatures
1382 */
1383
1384int Image::isBMP(uint8_t *data, unsigned len) {
1385 if(len < 2) return false;
1386 std::string sig = std::string(1, (char)data[0]) + (char)data[1];
1387 return sig == "BM" ||
1388 sig == "BA" ||
1389 sig == "CI" ||
1390 sig == "CP" ||
1391 sig == "IC" ||
1392 sig == "PT";
1393}