@shqld/canvas 2.11.2-rc.1

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