@shqld/canvas 3.2.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 (51) hide show
  1. package/Readme.md +654 -0
  2. package/binding.gyp +229 -0
  3. package/browser.js +31 -0
  4. package/index.d.ts +507 -0
  5. package/index.js +94 -0
  6. package/lib/DOMMatrix.js +678 -0
  7. package/lib/bindings.js +113 -0
  8. package/lib/canvas.js +113 -0
  9. package/lib/context2d.js +11 -0
  10. package/lib/image.js +97 -0
  11. package/lib/jpegstream.js +41 -0
  12. package/lib/pattern.js +15 -0
  13. package/lib/pdfstream.js +35 -0
  14. package/lib/pngstream.js +42 -0
  15. package/package.json +77 -0
  16. package/scripts/install.js +19 -0
  17. package/src/Backends.h +9 -0
  18. package/src/Canvas.cc +1026 -0
  19. package/src/Canvas.h +128 -0
  20. package/src/CanvasError.h +37 -0
  21. package/src/CanvasGradient.cc +113 -0
  22. package/src/CanvasGradient.h +20 -0
  23. package/src/CanvasPattern.cc +129 -0
  24. package/src/CanvasPattern.h +33 -0
  25. package/src/CanvasRenderingContext2d.cc +3527 -0
  26. package/src/CanvasRenderingContext2d.h +238 -0
  27. package/src/CharData.h +233 -0
  28. package/src/FontParser.cc +605 -0
  29. package/src/FontParser.h +115 -0
  30. package/src/Image.cc +1719 -0
  31. package/src/Image.h +146 -0
  32. package/src/ImageData.cc +138 -0
  33. package/src/ImageData.h +26 -0
  34. package/src/InstanceData.h +12 -0
  35. package/src/JPEGStream.h +157 -0
  36. package/src/PNG.h +292 -0
  37. package/src/Point.h +11 -0
  38. package/src/Util.h +9 -0
  39. package/src/bmp/BMPParser.cc +459 -0
  40. package/src/bmp/BMPParser.h +60 -0
  41. package/src/bmp/LICENSE.md +24 -0
  42. package/src/closure.cc +52 -0
  43. package/src/closure.h +98 -0
  44. package/src/color.cc +796 -0
  45. package/src/color.h +30 -0
  46. package/src/dll_visibility.h +20 -0
  47. package/src/init.cc +114 -0
  48. package/src/register_font.cc +352 -0
  49. package/src/register_font.h +7 -0
  50. package/util/has_lib.js +119 -0
  51. package/util/win_jpeg_lookup.js +21 -0
package/src/Image.cc ADDED
@@ -0,0 +1,1719 @@
1
+ // Copyright (c) 2010 LearnBoost <tj@learnboost.com>
2
+
3
+ #include "Image.h"
4
+ #include "InstanceData.h"
5
+
6
+ #include "bmp/BMPParser.h"
7
+ #include "Canvas.h"
8
+ #include <cerrno>
9
+ #include <cstdlib>
10
+ #include <cstring>
11
+ #include <node_buffer.h>
12
+ #include <sys/stat.h>
13
+
14
+ /* Cairo limit:
15
+ * https://lists.cairographics.org/archives/cairo/2010-December/021422.html
16
+ */
17
+ static constexpr int canvas_max_side = (1 << 15) - 1;
18
+
19
+ #ifdef HAVE_GIF
20
+ typedef struct {
21
+ uint8_t *buf;
22
+ unsigned len;
23
+ unsigned pos;
24
+ } gif_data_t;
25
+ #endif
26
+
27
+ #ifdef HAVE_JPEG
28
+ #include <csetjmp>
29
+
30
+ struct canvas_jpeg_error_mgr: jpeg_error_mgr {
31
+ Image* image;
32
+ jmp_buf setjmp_buffer;
33
+ };
34
+ #endif
35
+
36
+ /*
37
+ * Read closure used by loadFromBuffer.
38
+ */
39
+
40
+ typedef struct {
41
+ Napi::Env env;
42
+ unsigned len;
43
+ uint8_t *buf;
44
+ } read_closure_t;
45
+
46
+ /*
47
+ * Initialize Image.
48
+ */
49
+
50
+ void
51
+ Image::Initialize(Napi::Env& env, Napi::Object& exports) {
52
+ InstanceData *data = env.GetInstanceData<InstanceData>();
53
+ Napi::HandleScope scope(env);
54
+
55
+ Napi::Function ctor = DefineClass(env, "Image", {
56
+ InstanceAccessor<&Image::GetComplete>("complete", napi_default_jsproperty),
57
+ InstanceAccessor<&Image::GetWidth, &Image::SetWidth>("width", napi_default_jsproperty),
58
+ InstanceAccessor<&Image::GetHeight, &Image::SetHeight>("height", napi_default_jsproperty),
59
+ InstanceAccessor<&Image::GetNaturalWidth>("naturalWidth", napi_default_jsproperty),
60
+ InstanceAccessor<&Image::GetNaturalHeight>("naturalHeight", napi_default_jsproperty),
61
+ InstanceAccessor<&Image::GetDataMode, &Image::SetDataMode>("dataMode", napi_default_jsproperty),
62
+ StaticValue("MODE_IMAGE", Napi::Number::New(env, DATA_IMAGE), napi_default_jsproperty),
63
+ StaticValue("MODE_MIME", Napi::Number::New(env, DATA_MIME), napi_default_jsproperty)
64
+ });
65
+
66
+ // Used internally in lib/image.js
67
+ exports.Set("GetSource", Napi::Function::New(env, &GetSource));
68
+ exports.Set("SetSource", Napi::Function::New(env, &SetSource));
69
+
70
+ data->ImageCtor = Napi::Persistent(ctor);
71
+ exports.Set("Image", ctor);
72
+ }
73
+
74
+ /*
75
+ * Initialize a new Image.
76
+ */
77
+
78
+ Image::Image(const Napi::CallbackInfo& info) : ObjectWrap<Image>(info), env(info.Env()) {
79
+ data_mode = DATA_IMAGE;
80
+ info.This().ToObject().Unwrap().Set("onload", env.Null());
81
+ info.This().ToObject().Unwrap().Set("onerror", env.Null());
82
+ filename = NULL;
83
+ _data = nullptr;
84
+ _data_len = 0;
85
+ _surface = NULL;
86
+ width = height = 0;
87
+ naturalWidth = naturalHeight = 0;
88
+ state = DEFAULT;
89
+ #ifdef HAVE_RSVG
90
+ _rsvg = NULL;
91
+ _is_svg = false;
92
+ _svg_last_width = _svg_last_height = 0;
93
+ #endif
94
+ }
95
+
96
+ /*
97
+ * Get complete boolean.
98
+ */
99
+
100
+ Napi::Value
101
+ Image::GetComplete(const Napi::CallbackInfo& info) {
102
+ return Napi::Boolean::New(env, true);
103
+ }
104
+
105
+ /*
106
+ * Get dataMode.
107
+ */
108
+
109
+ Napi::Value
110
+ Image::GetDataMode(const Napi::CallbackInfo& info) {
111
+ return Napi::Number::New(env, data_mode);
112
+ }
113
+
114
+ /*
115
+ * Set dataMode.
116
+ */
117
+
118
+ void
119
+ Image::SetDataMode(const Napi::CallbackInfo& info, const Napi::Value& value) {
120
+ if (value.IsNumber()) {
121
+ int mode = value.As<Napi::Number>().Uint32Value();
122
+ data_mode = (data_mode_t) mode;
123
+ }
124
+ }
125
+
126
+ /*
127
+ * Get natural width
128
+ */
129
+
130
+ Napi::Value
131
+ Image::GetNaturalWidth(const Napi::CallbackInfo& info) {
132
+ return Napi::Number::New(env, naturalWidth);
133
+ }
134
+
135
+ /*
136
+ * Get width.
137
+ */
138
+
139
+ Napi::Value
140
+ Image::GetWidth(const Napi::CallbackInfo& info) {
141
+ return Napi::Number::New(env, width);
142
+ }
143
+
144
+ /*
145
+ * Set width.
146
+ */
147
+
148
+ void
149
+ Image::SetWidth(const Napi::CallbackInfo& info, const Napi::Value& value) {
150
+ if (value.IsNumber()) {
151
+ width = value.As<Napi::Number>().Uint32Value();
152
+ }
153
+ }
154
+
155
+ /*
156
+ * Get natural height
157
+ */
158
+
159
+ Napi::Value
160
+ Image::GetNaturalHeight(const Napi::CallbackInfo& info) {
161
+ return Napi::Number::New(env, naturalHeight);
162
+ }
163
+
164
+ /*
165
+ * Get height.
166
+ */
167
+
168
+ Napi::Value
169
+ Image::GetHeight(const Napi::CallbackInfo& info) {
170
+ return Napi::Number::New(env, height);
171
+ }
172
+ /*
173
+ * Set height.
174
+ */
175
+
176
+ void
177
+ Image::SetHeight(const Napi::CallbackInfo& info, const Napi::Value& value) {
178
+ if (value.IsNumber()) {
179
+ height = value.As<Napi::Number>().Uint32Value();
180
+ }
181
+ }
182
+
183
+ /*
184
+ * Get src path.
185
+ */
186
+
187
+ Napi::Value
188
+ Image::GetSource(const Napi::CallbackInfo& info){
189
+ Napi::Env env = info.Env();
190
+ Image *img = Image::Unwrap(info.This().As<Napi::Object>());
191
+ return Napi::String::New(env, img->filename ? img->filename : "");
192
+ }
193
+
194
+ /*
195
+ * Clean up assets and variables.
196
+ */
197
+
198
+ void
199
+ Image::clearData() {
200
+ if (_surface) {
201
+ cairo_surface_destroy(_surface);
202
+ Napi::MemoryManagement::AdjustExternalMemory(env, -_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
+
229
+ void
230
+ Image::SetSource(const Napi::CallbackInfo& info){
231
+ Napi::Env env = info.Env();
232
+ Napi::Object This = info.This().As<Napi::Object>();
233
+ Image *img = Image::Unwrap(This);
234
+
235
+ cairo_status_t status = CAIRO_STATUS_READ_ERROR;
236
+
237
+ Napi::Value value = info[0];
238
+
239
+ img->clearData();
240
+ // Clear errno in case some unrelated previous syscall failed
241
+ errno = 0;
242
+
243
+ // url string
244
+ if (value.IsString()) {
245
+ std::string src = value.As<Napi::String>().Utf8Value();
246
+ if (img->filename) free(img->filename);
247
+ img->filename = strdup(src.c_str());
248
+ status = img->load();
249
+ // Buffer
250
+ } else if (value.IsBuffer()) {
251
+ uint8_t *buf = value.As<Napi::Buffer<uint8_t>>().Data();
252
+ unsigned len = value.As<Napi::Buffer<uint8_t>>().Length();
253
+ status = img->loadFromBuffer(buf, len);
254
+ }
255
+
256
+ if (status) {
257
+ Napi::Value onerrorFn;
258
+ if (This.Get("onerror").UnwrapTo(&onerrorFn) && onerrorFn.IsFunction()) {
259
+ Napi::Error arg;
260
+ if (img->errorInfo.empty()) {
261
+ arg = Napi::Error::New(env, Napi::String::New(env, cairo_status_to_string(status)));
262
+ } else {
263
+ arg = img->errorInfo.toError(env);
264
+ }
265
+ onerrorFn.As<Napi::Function>().Call({ arg.Value() });
266
+ }
267
+ } else {
268
+ img->loaded();
269
+ Napi::Value onloadFn;
270
+ if (This.Get("onload").UnwrapTo(&onloadFn) && onloadFn.IsFunction()) {
271
+ onloadFn.As<Napi::Function>().Call({});
272
+ }
273
+ }
274
+ }
275
+
276
+ /*
277
+ * Load image data from `buf` by sniffing
278
+ * the bytes to determine format.
279
+ */
280
+
281
+ cairo_status_t
282
+ Image::loadFromBuffer(uint8_t *buf, unsigned len) {
283
+ if (len == 0) return CAIRO_STATUS_READ_ERROR;
284
+
285
+ uint8_t data[4] = {0};
286
+ memcpy(data, buf, (len < 4 ? len : 4) * sizeof(uint8_t));
287
+
288
+ if (isPNG(data)) return loadPNGFromBuffer(buf);
289
+
290
+ if (isGIF(data)) {
291
+ #ifdef HAVE_GIF
292
+ return loadGIFFromBuffer(buf, len);
293
+ #else
294
+ this->errorInfo.set("node-canvas was built without GIF support");
295
+ return CAIRO_STATUS_READ_ERROR;
296
+ #endif
297
+ }
298
+
299
+ if (isJPEG(data)) {
300
+ #ifdef HAVE_JPEG
301
+ if (DATA_IMAGE == data_mode) return loadJPEGFromBuffer(buf, len);
302
+ if (DATA_MIME == data_mode) return decodeJPEGBufferIntoMimeSurface(buf, len);
303
+ if ((DATA_IMAGE | DATA_MIME) == data_mode) {
304
+ cairo_status_t status;
305
+ status = loadJPEGFromBuffer(buf, len);
306
+ if (status) return status;
307
+ return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
308
+ }
309
+ #else // HAVE_JPEG
310
+ this->errorInfo.set("node-canvas was built without JPEG support");
311
+ return CAIRO_STATUS_READ_ERROR;
312
+ #endif
313
+ }
314
+
315
+ // confirm svg using first 1000 chars
316
+ // if a very long comment precedes the root <svg> tag, isSVG returns false
317
+ unsigned head_len = (len < 1000 ? len : 1000);
318
+ if (isSVG(buf, head_len)) {
319
+ #ifdef HAVE_RSVG
320
+ return loadSVGFromBuffer(buf, len);
321
+ #else
322
+ this->errorInfo.set("node-canvas was built without SVG support");
323
+ return CAIRO_STATUS_READ_ERROR;
324
+ #endif
325
+ }
326
+
327
+ if (isBMP(buf, len))
328
+ return loadBMPFromBuffer(buf, len);
329
+
330
+ this->errorInfo.set("Unsupported image type");
331
+ return CAIRO_STATUS_READ_ERROR;
332
+ }
333
+
334
+ /*
335
+ * Load PNG data from `buf`.
336
+ */
337
+
338
+ cairo_status_t
339
+ Image::loadPNGFromBuffer(uint8_t *buf) {
340
+ read_closure_t closure{ env, 0, buf };
341
+ _surface = cairo_image_surface_create_from_png_stream(readPNG, &closure);
342
+ cairo_status_t status = cairo_surface_status(_surface);
343
+ if (status) return status;
344
+ return CAIRO_STATUS_SUCCESS;
345
+ }
346
+
347
+ /*
348
+ * Read PNG data.
349
+ */
350
+
351
+ cairo_status_t
352
+ Image::readPNG(void *c, uint8_t *data, unsigned int len) {
353
+ read_closure_t *closure = (read_closure_t *) c;
354
+ memcpy(data, closure->buf + closure->len, len);
355
+ closure->len += len;
356
+ return CAIRO_STATUS_SUCCESS;
357
+ }
358
+
359
+ /*
360
+ * Destroy image and associated surface.
361
+ */
362
+
363
+ Image::~Image() {
364
+ clearData();
365
+ }
366
+
367
+ /*
368
+ * Initiate image loading.
369
+ */
370
+
371
+ cairo_status_t
372
+ Image::load() {
373
+ if (LOADING != state) {
374
+ state = LOADING;
375
+ return loadSurface();
376
+ }
377
+ return CAIRO_STATUS_READ_ERROR;
378
+ }
379
+
380
+ /*
381
+ * Set state, assign dimensions.
382
+ */
383
+
384
+ void
385
+ Image::loaded() {
386
+ Napi::HandleScope scope(env);
387
+ state = COMPLETE;
388
+
389
+ width = naturalWidth = cairo_image_surface_get_width(_surface);
390
+ height = naturalHeight = cairo_image_surface_get_height(_surface);
391
+ _data_len = naturalHeight * cairo_image_surface_get_stride(_surface);
392
+ Napi::MemoryManagement::AdjustExternalMemory(env, _data_len);
393
+ }
394
+
395
+ /*
396
+ * Returns this image's surface.
397
+ */
398
+ cairo_surface_t *Image::surface() {
399
+ #ifdef HAVE_RSVG
400
+ if (_is_svg && (_svg_last_width != width || _svg_last_height != height)) {
401
+ if (_surface != NULL) {
402
+ cairo_surface_destroy(_surface);
403
+ _surface = NULL;
404
+ }
405
+
406
+ cairo_status_t status = renderSVGToSurface();
407
+ if (status != CAIRO_STATUS_SUCCESS) {
408
+ g_object_unref(_rsvg);
409
+ Napi::Error::New(env, cairo_status_to_string(status)).ThrowAsJavaScriptException();
410
+
411
+ return NULL;
412
+ }
413
+ }
414
+ #endif
415
+ return _surface;
416
+ }
417
+
418
+ /*
419
+ * Load cairo surface from the image src.
420
+ *
421
+ * TODO: support more formats
422
+ * TODO: use node IO or at least thread pool
423
+ */
424
+
425
+ cairo_status_t
426
+ Image::loadSurface() {
427
+ FILE *stream = fopen(filename, "rb");
428
+ if (!stream) {
429
+ this->errorInfo.set(NULL, "fopen", errno, filename);
430
+ return CAIRO_STATUS_READ_ERROR;
431
+ }
432
+ uint8_t buf[5];
433
+ if (1 != fread(&buf, 5, 1, stream)) {
434
+ fclose(stream);
435
+ return CAIRO_STATUS_READ_ERROR;
436
+ }
437
+ rewind(stream);
438
+
439
+ // png
440
+ if (isPNG(buf)) {
441
+ fclose(stream);
442
+ return loadPNG();
443
+ }
444
+
445
+
446
+ if (isGIF(buf)) {
447
+ #ifdef HAVE_GIF
448
+ return loadGIF(stream);
449
+ #else
450
+ this->errorInfo.set("node-canvas was built without GIF support");
451
+ return CAIRO_STATUS_READ_ERROR;
452
+ #endif
453
+ }
454
+
455
+ if (isJPEG(buf)) {
456
+ #ifdef HAVE_JPEG
457
+ return loadJPEG(stream);
458
+ #else
459
+ this->errorInfo.set("node-canvas was built without JPEG support");
460
+ return CAIRO_STATUS_READ_ERROR;
461
+ #endif
462
+ }
463
+
464
+ // confirm svg using first 1000 chars
465
+ // if a very long comment precedes the root <svg> tag, isSVG returns false
466
+ uint8_t head[1000] = {0};
467
+ fseek(stream, 0 , SEEK_END);
468
+ long len = ftell(stream);
469
+ unsigned head_len = (len < 1000 ? len : 1000);
470
+ unsigned head_size = head_len * sizeof(uint8_t);
471
+ rewind(stream);
472
+ if (head_size != fread(&head, 1, head_size, stream)) {
473
+ fclose(stream);
474
+ return CAIRO_STATUS_READ_ERROR;
475
+ }
476
+ rewind(stream);
477
+ if (isSVG(head, head_len)) {
478
+ #ifdef HAVE_RSVG
479
+ return loadSVG(stream);
480
+ #else
481
+ this->errorInfo.set("node-canvas was built without SVG support");
482
+ return CAIRO_STATUS_READ_ERROR;
483
+ #endif
484
+ }
485
+
486
+ if (isBMP(buf, 2))
487
+ return loadBMP(stream);
488
+
489
+ fclose(stream);
490
+
491
+ this->errorInfo.set("Unsupported image type");
492
+ return CAIRO_STATUS_READ_ERROR;
493
+ }
494
+
495
+ /*
496
+ * Load PNG.
497
+ */
498
+
499
+ cairo_status_t
500
+ Image::loadPNG() {
501
+ _surface = cairo_image_surface_create_from_png(filename);
502
+ return cairo_surface_status(_surface);
503
+ }
504
+
505
+ // GIF support
506
+
507
+ #ifdef HAVE_GIF
508
+
509
+ /*
510
+ * Return the alpha color for `gif` at `frame`, or -1.
511
+ */
512
+
513
+ int
514
+ get_gif_transparent_color(GifFileType *gif, int frame) {
515
+ ExtensionBlock *ext = gif->SavedImages[frame].ExtensionBlocks;
516
+ int len = gif->SavedImages[frame].ExtensionBlockCount;
517
+ for (int x = 0; x < len; ++x, ++ext) {
518
+ if ((ext->Function == GRAPHICS_EXT_FUNC_CODE) && (ext->Bytes[0] & 1)) {
519
+ return ext->Bytes[3] == 0 ? 0 : (uint8_t) ext->Bytes[3];
520
+ }
521
+ }
522
+ return -1;
523
+ }
524
+
525
+ /*
526
+ * Memory GIF reader callback.
527
+ */
528
+
529
+ int
530
+ read_gif_from_memory(GifFileType *gif, GifByteType *buf, int len) {
531
+ gif_data_t *data = (gif_data_t *) gif->UserData;
532
+ if ((data->pos + len) > data->len) len = data->len - data->pos;
533
+ memcpy(buf, data->pos + data->buf, len);
534
+ data->pos += len;
535
+ return len;
536
+ }
537
+
538
+ /*
539
+ * Load GIF.
540
+ */
541
+
542
+ cairo_status_t
543
+ Image::loadGIF(FILE *stream) {
544
+ struct stat s;
545
+ int fd = fileno(stream);
546
+
547
+ // stat
548
+ if (fstat(fd, &s) < 0) {
549
+ fclose(stream);
550
+ return CAIRO_STATUS_READ_ERROR;
551
+ }
552
+
553
+ uint8_t *buf = (uint8_t *) malloc(s.st_size);
554
+
555
+ if (!buf) {
556
+ fclose(stream);
557
+ this->errorInfo.set(NULL, "malloc", errno);
558
+ return CAIRO_STATUS_NO_MEMORY;
559
+ }
560
+
561
+ size_t read = fread(buf, s.st_size, 1, stream);
562
+ fclose(stream);
563
+
564
+ cairo_status_t result = CAIRO_STATUS_READ_ERROR;
565
+ if (1 == read) result = loadGIFFromBuffer(buf, s.st_size);
566
+ free(buf);
567
+
568
+ return result;
569
+ }
570
+
571
+ /*
572
+ * Load give from `buf` and the given `len`.
573
+ */
574
+
575
+ cairo_status_t
576
+ Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) {
577
+ int i = 0;
578
+ GifFileType* gif;
579
+
580
+ gif_data_t gifd = { buf, len, 0 };
581
+
582
+ #if GIFLIB_MAJOR >= 5
583
+ int errorcode;
584
+ if ((gif = DGifOpen((void*) &gifd, read_gif_from_memory, &errorcode)) == NULL)
585
+ return CAIRO_STATUS_READ_ERROR;
586
+ #else
587
+ if ((gif = DGifOpen((void*) &gifd, read_gif_from_memory)) == NULL)
588
+ return CAIRO_STATUS_READ_ERROR;
589
+ #endif
590
+
591
+ if (GIF_OK != DGifSlurp(gif)) {
592
+ GIF_CLOSE_FILE(gif);
593
+ return CAIRO_STATUS_READ_ERROR;
594
+ }
595
+
596
+ if (gif->SWidth > canvas_max_side || gif->SHeight > canvas_max_side) {
597
+ GIF_CLOSE_FILE(gif);
598
+ return CAIRO_STATUS_INVALID_SIZE;
599
+ }
600
+
601
+ width = naturalWidth = gif->SWidth;
602
+ height = naturalHeight = gif->SHeight;
603
+
604
+ uint8_t *data = new uint8_t[naturalWidth * naturalHeight * 4];
605
+ if (!data) {
606
+ GIF_CLOSE_FILE(gif);
607
+ this->errorInfo.set(NULL, "malloc", errno);
608
+ return CAIRO_STATUS_NO_MEMORY;
609
+ }
610
+
611
+ GifImageDesc *img = &gif->SavedImages[i].ImageDesc;
612
+
613
+ // local colormap takes precedence over global
614
+ ColorMapObject *colormap = img->ColorMap
615
+ ? img->ColorMap
616
+ : gif->SColorMap;
617
+
618
+ if (colormap == nullptr) {
619
+ GIF_CLOSE_FILE(gif);
620
+ return CAIRO_STATUS_READ_ERROR;
621
+ }
622
+
623
+ int bgColor = 0;
624
+ int alphaColor = get_gif_transparent_color(gif, i);
625
+ if (gif->SColorMap) bgColor = (uint8_t) gif->SBackGroundColor;
626
+ else if(alphaColor >= 0) bgColor = alphaColor;
627
+
628
+ uint8_t *src_data = (uint8_t*) gif->SavedImages[i].RasterBits;
629
+ uint32_t *dst_data = (uint32_t*) data;
630
+
631
+ if (!gif->Image.Interlace) {
632
+ if (naturalWidth == img->Width && naturalHeight == img->Height) {
633
+ for (int y = 0; y < naturalHeight; ++y) {
634
+ for (int x = 0; x < naturalWidth; ++x) {
635
+ *dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
636
+ | colormap->Colors[*src_data].Red << 16
637
+ | colormap->Colors[*src_data].Green << 8
638
+ | colormap->Colors[*src_data].Blue;
639
+
640
+ dst_data++;
641
+ src_data++;
642
+ }
643
+ }
644
+ } else {
645
+ // Image does not take up whole "screen" so we need to fill-in the background
646
+ int bottom = img->Top + img->Height;
647
+ int right = img->Left + img->Width;
648
+
649
+ uint32_t bgPixel =
650
+ ((bgColor == alphaColor) ? 0 : 255) << 24
651
+ | colormap->Colors[bgColor].Red << 16
652
+ | colormap->Colors[bgColor].Green << 8
653
+ | colormap->Colors[bgColor].Blue;
654
+
655
+ for (int y = 0; y < naturalHeight; ++y) {
656
+ for (int x = 0; x < naturalWidth; ++x) {
657
+ if (y < img->Top || y >= bottom || x < img->Left || x >= right) {
658
+ *dst_data = bgPixel;
659
+ dst_data++;
660
+ } else {
661
+ *dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
662
+ | colormap->Colors[*src_data].Red << 16
663
+ | colormap->Colors[*src_data].Green << 8
664
+ | colormap->Colors[*src_data].Blue;
665
+ dst_data++;
666
+ src_data++;
667
+ }
668
+ }
669
+ }
670
+ }
671
+ } else {
672
+ // Image is interlaced so that it streams nice over 14.4k and 28.8k modems :)
673
+ // We first load in 1/8 of the image, followed by another 1/8, followed by
674
+ // 1/4 and finally the remaining 1/2.
675
+ int ioffs[] = { 0, 4, 2, 1 };
676
+ int ijumps[] = { 8, 8, 4, 2 };
677
+
678
+ uint8_t *src_ptr = src_data;
679
+ uint32_t *dst_ptr;
680
+
681
+ for(int z = 0; z < 4; z++) {
682
+ for(int y = ioffs[z]; y < naturalHeight; y += ijumps[z]) {
683
+ dst_ptr = dst_data + naturalWidth * y;
684
+ for(int x = 0; x < naturalWidth; ++x) {
685
+ *dst_ptr = ((*src_ptr == alphaColor) ? 0 : 255) << 24
686
+ | (colormap->Colors[*src_ptr].Red) << 16
687
+ | (colormap->Colors[*src_ptr].Green) << 8
688
+ | (colormap->Colors[*src_ptr].Blue);
689
+
690
+ dst_ptr++;
691
+ src_ptr++;
692
+ }
693
+ }
694
+ }
695
+ }
696
+
697
+ GIF_CLOSE_FILE(gif);
698
+
699
+ // New image surface
700
+ _surface = cairo_image_surface_create_for_data(
701
+ data
702
+ , CAIRO_FORMAT_ARGB32
703
+ , naturalWidth
704
+ , naturalHeight
705
+ , cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, naturalWidth));
706
+
707
+ cairo_status_t status = cairo_surface_status(_surface);
708
+
709
+ if (status) {
710
+ delete[] data;
711
+ return status;
712
+ }
713
+
714
+ _data = data;
715
+
716
+ return CAIRO_STATUS_SUCCESS;
717
+ }
718
+ #endif /* HAVE_GIF */
719
+
720
+ // JPEG support
721
+
722
+ #ifdef HAVE_JPEG
723
+
724
+ // libjpeg 6.2 does not have jpeg_mem_src; define it ourselves here unless
725
+ // libjpeg 8 is installed.
726
+ #if JPEG_LIB_VERSION < 80 && !defined(MEM_SRCDST_SUPPORTED)
727
+
728
+ /* Read JPEG image from a memory segment */
729
+ static void
730
+ init_source(j_decompress_ptr cinfo) {}
731
+
732
+ static boolean
733
+ fill_input_buffer(j_decompress_ptr cinfo) {
734
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
735
+ return TRUE;
736
+ }
737
+ static void
738
+ skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
739
+ struct jpeg_source_mgr* src = (struct jpeg_source_mgr*) cinfo->src;
740
+ if (num_bytes > 0) {
741
+ src->next_input_byte += (size_t) num_bytes;
742
+ src->bytes_in_buffer -= (size_t) num_bytes;
743
+ }
744
+ }
745
+
746
+ static void term_source (j_decompress_ptr cinfo) {}
747
+ static void jpeg_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes) {
748
+ struct jpeg_source_mgr* src;
749
+
750
+ if (cinfo->src == NULL) {
751
+ cinfo->src = (struct jpeg_source_mgr *)
752
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
753
+ sizeof(struct jpeg_source_mgr));
754
+ }
755
+
756
+ src = (struct jpeg_source_mgr*) cinfo->src;
757
+ src->init_source = init_source;
758
+ src->fill_input_buffer = fill_input_buffer;
759
+ src->skip_input_data = skip_input_data;
760
+ src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
761
+ src->term_source = term_source;
762
+ src->bytes_in_buffer = nbytes;
763
+ src->next_input_byte = (JOCTET*)buffer;
764
+ }
765
+
766
+ #endif
767
+
768
+ class BufferReader : public Image::Reader {
769
+ public:
770
+ BufferReader(uint8_t* buf, unsigned len) : _buf(buf), _len(len), _idx(0) {}
771
+
772
+ bool hasBytes(unsigned n) const override { return (_idx + n - 1 < _len); }
773
+
774
+ uint8_t getNext() override {
775
+ return _buf[_idx++];
776
+ }
777
+
778
+ void skipBytes(unsigned n) override { _idx += n; }
779
+
780
+ private:
781
+ uint8_t* _buf; // we do not own this
782
+ unsigned _len;
783
+ unsigned _idx;
784
+ };
785
+
786
+ class StreamReader : public Image::Reader {
787
+ public:
788
+ StreamReader(FILE *stream) : _stream(stream), _len(0), _idx(0) {
789
+ fseek(_stream, 0, SEEK_END);
790
+ _len = ftell(_stream);
791
+ fseek(_stream, 0, SEEK_SET);
792
+ }
793
+
794
+ bool hasBytes(unsigned n) const override { return (_idx + n - 1 < _len); }
795
+
796
+ uint8_t getNext() override {
797
+ ++_idx;
798
+ return getc(_stream);
799
+ }
800
+
801
+ void skipBytes(unsigned n) override {
802
+ _idx += n;
803
+ fseek(_stream, _idx, SEEK_SET);
804
+ }
805
+
806
+ private:
807
+ FILE* _stream;
808
+ unsigned _len;
809
+ unsigned _idx;
810
+ };
811
+
812
+ void Image::jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode) {
813
+ int stride = naturalWidth * 4;
814
+ for (int y = 0; y < naturalHeight; ++y) {
815
+ jpeg_read_scanlines(args, &src, 1);
816
+ uint32_t *row = (uint32_t*)(data + stride * y);
817
+ for (int x = 0; x < naturalWidth; ++x) {
818
+ int bx = args->output_components * x;
819
+ row[x] = decode(src + bx);
820
+ }
821
+ }
822
+ }
823
+
824
+ /*
825
+ * Takes an initialised jpeg_decompress_struct and decodes the
826
+ * data into _surface.
827
+ */
828
+
829
+ cairo_status_t
830
+ Image::decodeJPEGIntoSurface(jpeg_decompress_struct *args, Orientation orientation) {
831
+ const int channels = 4;
832
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
833
+
834
+ uint8_t *data = new uint8_t[naturalWidth * naturalHeight * channels];
835
+ if (!data) {
836
+ jpeg_abort_decompress(args);
837
+ jpeg_destroy_decompress(args);
838
+ this->errorInfo.set(NULL, "malloc", errno);
839
+ return CAIRO_STATUS_NO_MEMORY;
840
+ }
841
+
842
+ uint8_t *src = new uint8_t[naturalWidth * args->output_components];
843
+ if (!src) {
844
+ free(data);
845
+ jpeg_abort_decompress(args);
846
+ jpeg_destroy_decompress(args);
847
+ this->errorInfo.set(NULL, "malloc", errno);
848
+ return CAIRO_STATUS_NO_MEMORY;
849
+ }
850
+
851
+ // These are the three main cases to handle. libjpeg converts YCCK to CMYK
852
+ // and YCbCr to RGB by default.
853
+ switch (args->out_color_space) {
854
+ case JCS_CMYK:
855
+ jpegToARGB(args, data, src, [](uint8_t const* src) {
856
+ uint16_t k = static_cast<uint16_t>(src[3]);
857
+ uint8_t r = k * src[0] / 255;
858
+ uint8_t g = k * src[1] / 255;
859
+ uint8_t b = k * src[2] / 255;
860
+ return 255 << 24 | r << 16 | g << 8 | b;
861
+ });
862
+ break;
863
+ case JCS_RGB:
864
+ jpegToARGB(args, data, src, [](uint8_t const* src) {
865
+ uint8_t r = src[0], g = src[1], b = src[2];
866
+ return 255 << 24 | r << 16 | g << 8 | b;
867
+ });
868
+ break;
869
+ case JCS_GRAYSCALE:
870
+ jpegToARGB(args, data, src, [](uint8_t const* src) {
871
+ uint8_t v = src[0];
872
+ return 255 << 24 | v << 16 | v << 8 | v;
873
+ });
874
+ break;
875
+ default:
876
+ this->errorInfo.set("Unsupported JPEG encoding");
877
+ status = CAIRO_STATUS_READ_ERROR;
878
+ break;
879
+ }
880
+
881
+ updateDimensionsForOrientation(orientation);
882
+
883
+ if (!status) {
884
+ _surface = cairo_image_surface_create_for_data(
885
+ data
886
+ , CAIRO_FORMAT_ARGB32
887
+ , naturalWidth
888
+ , naturalHeight
889
+ , cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, naturalWidth));
890
+ }
891
+
892
+ jpeg_finish_decompress(args);
893
+ jpeg_destroy_decompress(args);
894
+ status = cairo_surface_status(_surface);
895
+
896
+ rotatePixels(data, naturalWidth, naturalHeight, channels, orientation);
897
+
898
+ delete[] src;
899
+
900
+ if (status) {
901
+ delete[] data;
902
+ return status;
903
+ }
904
+
905
+ _data = data;
906
+
907
+ return CAIRO_STATUS_SUCCESS;
908
+ }
909
+
910
+ /*
911
+ * Callback to recover from jpeg errors
912
+ */
913
+
914
+ static void canvas_jpeg_error_exit(j_common_ptr cinfo) {
915
+ canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);
916
+ cjerr->output_message(cinfo);
917
+ // Return control to the setjmp point
918
+ longjmp(cjerr->setjmp_buffer, 1);
919
+ }
920
+
921
+ // Capture libjpeg errors instead of writing stdout
922
+ static void canvas_jpeg_output_message(j_common_ptr cinfo) {
923
+ canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);
924
+ char buff[JMSG_LENGTH_MAX];
925
+ cjerr->format_message(cinfo, buff);
926
+ // (Only the last message will be returned to JS land.)
927
+ cjerr->image->errorInfo.set(buff);
928
+ }
929
+
930
+ /*
931
+ * Takes a jpeg data buffer and assigns it as mime data to a
932
+ * dummy surface
933
+ */
934
+
935
+ cairo_status_t
936
+ Image::decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len) {
937
+ // TODO: remove this duplicate logic
938
+ // JPEG setup
939
+ struct jpeg_decompress_struct args;
940
+ struct canvas_jpeg_error_mgr err;
941
+
942
+ err.image = this;
943
+ args.err = jpeg_std_error(&err);
944
+ args.err->error_exit = canvas_jpeg_error_exit;
945
+ args.err->output_message = canvas_jpeg_output_message;
946
+
947
+ // Establish the setjmp return context for canvas_jpeg_error_exit to use
948
+ if (setjmp(err.setjmp_buffer)) {
949
+ // If we get here, the JPEG code has signaled an error.
950
+ // We need to clean up the JPEG object, close the input file, and return.
951
+ jpeg_destroy_decompress(&args);
952
+ return CAIRO_STATUS_READ_ERROR;
953
+ }
954
+
955
+ jpeg_create_decompress(&args);
956
+
957
+ jpeg_mem_src(&args, buf, len);
958
+
959
+ jpeg_read_header(&args, 1);
960
+ jpeg_start_decompress(&args);
961
+ width = naturalWidth = args.output_width;
962
+ height = naturalHeight = args.output_height;
963
+
964
+ // Data alloc
965
+ // 8 pixels per byte using Alpha Channel format to reduce memory requirement.
966
+ int buf_size = naturalHeight * cairo_format_stride_for_width(CAIRO_FORMAT_A1, naturalWidth);
967
+ uint8_t *data = new uint8_t[buf_size];
968
+ if (!data) {
969
+ this->errorInfo.set(NULL, "malloc", errno);
970
+ return CAIRO_STATUS_NO_MEMORY;
971
+ }
972
+
973
+ BufferReader reader(buf, len);
974
+ Orientation orientation = getExifOrientation(reader);
975
+ updateDimensionsForOrientation(orientation);
976
+
977
+ // New image surface
978
+ _surface = cairo_image_surface_create_for_data(
979
+ data
980
+ , CAIRO_FORMAT_A1
981
+ , naturalWidth
982
+ , naturalHeight
983
+ , cairo_format_stride_for_width(CAIRO_FORMAT_A1, naturalWidth));
984
+
985
+ // Cleanup
986
+ jpeg_abort_decompress(&args);
987
+ jpeg_destroy_decompress(&args);
988
+ cairo_status_t status = cairo_surface_status(_surface);
989
+
990
+ if (status) {
991
+ delete[] data;
992
+ return status;
993
+ }
994
+
995
+ rotatePixels(data, naturalWidth, naturalHeight, 1, orientation);
996
+
997
+ _data = data;
998
+
999
+ return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
1000
+ }
1001
+
1002
+ /*
1003
+ * Helper function for disposing of a mime data closure.
1004
+ */
1005
+
1006
+ void
1007
+ clearMimeData(void *closure) {
1008
+ Napi::MemoryManagement::AdjustExternalMemory(
1009
+ static_cast<read_closure_t *>(closure)->env,
1010
+ -static_cast<int>((static_cast<read_closure_t *>(closure)->len)));
1011
+ free(static_cast<read_closure_t *>(closure)->buf);
1012
+ free(closure);
1013
+ }
1014
+
1015
+ /*
1016
+ * Assign a given buffer as mime data against the surface.
1017
+ * The provided buffer will be copied, and the copy will
1018
+ * be automatically freed when the surface is destroyed.
1019
+ */
1020
+
1021
+ cairo_status_t
1022
+ Image::assignDataAsMime(uint8_t *data, int len, const char *mime_type) {
1023
+ uint8_t *mime_data = (uint8_t *) malloc(len);
1024
+ if (!mime_data) {
1025
+ this->errorInfo.set(NULL, "malloc", errno);
1026
+ return CAIRO_STATUS_NO_MEMORY;
1027
+ }
1028
+
1029
+ read_closure_t *mime_closure = (read_closure_t *) malloc(sizeof(read_closure_t));
1030
+ if (!mime_closure) {
1031
+ free(mime_data);
1032
+ this->errorInfo.set(NULL, "malloc", errno);
1033
+ return CAIRO_STATUS_NO_MEMORY;
1034
+ }
1035
+
1036
+ memcpy(mime_data, data, len);
1037
+
1038
+ mime_closure->env = env;
1039
+ mime_closure->buf = mime_data;
1040
+ mime_closure->len = len;
1041
+
1042
+ Napi::MemoryManagement::AdjustExternalMemory(env, len);
1043
+
1044
+ return cairo_surface_set_mime_data(_surface
1045
+ , mime_type
1046
+ , mime_data
1047
+ , len
1048
+ , clearMimeData
1049
+ , mime_closure);
1050
+ }
1051
+
1052
+ /*
1053
+ * Load jpeg from buffer.
1054
+ */
1055
+
1056
+ cairo_status_t
1057
+ Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) {
1058
+ BufferReader reader(buf, len);
1059
+ Orientation orientation = getExifOrientation(reader);
1060
+
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, orientation);
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
+ Orientation orientation = NORMAL;
1105
+ {
1106
+ StreamReader reader(stream);
1107
+ orientation = getExifOrientation(reader);
1108
+ rewind(stream);
1109
+ }
1110
+
1111
+ // JPEG setup
1112
+ struct jpeg_decompress_struct args;
1113
+ struct canvas_jpeg_error_mgr err;
1114
+
1115
+ err.image = this;
1116
+ args.err = jpeg_std_error(&err);
1117
+ args.err->error_exit = canvas_jpeg_error_exit;
1118
+ args.err->output_message = canvas_jpeg_output_message;
1119
+
1120
+ // Establish the setjmp return context for canvas_jpeg_error_exit to use
1121
+ if (setjmp(err.setjmp_buffer)) {
1122
+ // If we get here, the JPEG code has signaled an error.
1123
+ // We need to clean up the JPEG object, close the input file, and return.
1124
+ jpeg_destroy_decompress(&args);
1125
+ return CAIRO_STATUS_READ_ERROR;
1126
+ }
1127
+
1128
+ jpeg_create_decompress(&args);
1129
+
1130
+ jpeg_stdio_src(&args, stream);
1131
+
1132
+ jpeg_read_header(&args, 1);
1133
+ jpeg_start_decompress(&args);
1134
+
1135
+ if (args.output_width > canvas_max_side || args.output_height > canvas_max_side) {
1136
+ jpeg_destroy_decompress(&args);
1137
+ return CAIRO_STATUS_INVALID_SIZE;
1138
+ }
1139
+
1140
+ width = naturalWidth = args.output_width;
1141
+ height = naturalHeight = args.output_height;
1142
+
1143
+ status = decodeJPEGIntoSurface(&args, orientation);
1144
+ fclose(stream);
1145
+ } else { // We'll need the actual source jpeg data, so read fully.
1146
+ uint8_t *buf;
1147
+ unsigned len;
1148
+
1149
+ fseek(stream, 0, SEEK_END);
1150
+ len = ftell(stream);
1151
+ fseek(stream, 0, SEEK_SET);
1152
+
1153
+ buf = (uint8_t *) malloc(len);
1154
+ if (!buf) {
1155
+ this->errorInfo.set(NULL, "malloc", errno);
1156
+ return CAIRO_STATUS_NO_MEMORY;
1157
+ }
1158
+
1159
+ if (fread(buf, len, 1, stream) != 1) {
1160
+ status = CAIRO_STATUS_READ_ERROR;
1161
+ } else if ((DATA_IMAGE | DATA_MIME) == data_mode) {
1162
+ status = loadJPEGFromBuffer(buf, len);
1163
+ if (!status) status = assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
1164
+ } else if (DATA_MIME == data_mode) {
1165
+ status = decodeJPEGBufferIntoMimeSurface(buf, len);
1166
+ }
1167
+ #if defined(_MSC_VER)
1168
+ else if (DATA_IMAGE == data_mode) {
1169
+ status = loadJPEGFromBuffer(buf, len);
1170
+ }
1171
+ #endif
1172
+ else {
1173
+ status = CAIRO_STATUS_READ_ERROR;
1174
+ }
1175
+
1176
+ fclose(stream);
1177
+ free(buf);
1178
+ }
1179
+
1180
+ return status;
1181
+ }
1182
+
1183
+ /*
1184
+ * Returns the Exif orientation if one exists, otherwise returns NORMAL
1185
+ */
1186
+
1187
+ Image::Orientation
1188
+ Image::getExifOrientation(Reader& jpeg) {
1189
+ static const char kJpegStartOfImage = (char)0xd8;
1190
+ static const char kJpegStartOfFrameBaseline = (char)0xc0;
1191
+ static const char kJpegStartOfFrameProgressive = (char)0xc2;
1192
+ static const char kJpegHuffmanTable = (char)0xc4;
1193
+ static const char kJpegQuantizationTable = (char)0xdb;
1194
+ static const char kJpegRestartInterval = (char)0xdd;
1195
+ static const char kJpegComment = (char)0xfe;
1196
+ static const char kJpegStartOfScan = (char)0xda;
1197
+ static const char kJpegApp0 = (char)0xe0;
1198
+ static const char kJpegApp1 = (char)0xe1;
1199
+
1200
+ // Find the Exif tag (if it exists)
1201
+ int exif_len = 0;
1202
+ bool done = false;
1203
+ while (!done && jpeg.hasBytes(1)) {
1204
+ while (jpeg.hasBytes(1) && jpeg.getNext() != 0xff) {
1205
+ // noop
1206
+ }
1207
+ if (jpeg.hasBytes(1)) {
1208
+ char tag = jpeg.getNext();
1209
+ switch (tag) {
1210
+ case kJpegStartOfImage:
1211
+ break; // beginning of file, no extra bytes
1212
+ case kJpegRestartInterval:
1213
+ jpeg.skipBytes(4);
1214
+ break;
1215
+ case kJpegStartOfFrameBaseline:
1216
+ case kJpegStartOfFrameProgressive:
1217
+ case kJpegHuffmanTable:
1218
+ case kJpegQuantizationTable:
1219
+ case kJpegComment:
1220
+ case kJpegApp0:
1221
+ case kJpegApp1: {
1222
+ if (jpeg.hasBytes(2)) {
1223
+ uint16_t tag_len = 0;
1224
+ tag_len |= jpeg.getNext() << 8;
1225
+ tag_len |= jpeg.getNext();
1226
+ // The tag length includes the two bytes for the length
1227
+ uint16_t tag_content_len = std::max(0, tag_len - 2);
1228
+ if (tag != kJpegApp1 || !jpeg.hasBytes(tag_content_len)) {
1229
+ jpeg.skipBytes(tag_content_len); // skip JPEG tags we ignore.
1230
+ } else if (!jpeg.hasBytes(6)) {
1231
+ jpeg.skipBytes(tag_content_len); // too short to have "Exif\0\0"
1232
+ } else {
1233
+ if (jpeg.getNext() == 'E' && jpeg.getNext() == 'x' &&
1234
+ jpeg.getNext() == 'i' && jpeg.getNext() == 'f' &&
1235
+ jpeg.getNext() == '\0' && jpeg.getNext() == '\0') {
1236
+ exif_len = tag_content_len - 6;
1237
+ done = true;
1238
+ } else {
1239
+ jpeg.skipBytes(tag_content_len); // too short to have "Exif\0\0"
1240
+ }
1241
+ }
1242
+ } else {
1243
+ done = true; // shouldn't happen: corrupt file or we have a bug
1244
+ }
1245
+ break;
1246
+ }
1247
+ case kJpegStartOfScan:
1248
+ default:
1249
+ done = true; // got to the image, apparently no exif tags here
1250
+ break;
1251
+ }
1252
+ }
1253
+ }
1254
+
1255
+ // Parse exif if it exists. If it does, we have already checked that jpeglen
1256
+ // is longer than exifStart + exifLen, so we can safely index the data
1257
+ if (exif_len > 0) {
1258
+ // The first two bytes of TIFF header are "II" if little-endian ("Intel")
1259
+ // and "MM" if big-endian ("Motorola")
1260
+ const bool isLE = (jpeg.getNext() == 'I');
1261
+ jpeg.skipBytes(3); // +1 for the other I/M, +2 for 0x002a
1262
+
1263
+ auto readUint16Little = [](Reader &jpeg) -> uint32_t {
1264
+ uint16_t val = uint16_t(jpeg.getNext());
1265
+ val |= uint16_t(jpeg.getNext()) << 8;
1266
+ return val;
1267
+ };
1268
+ auto readUint32Little = [](Reader &jpeg) -> uint32_t {
1269
+ uint32_t val = uint32_t(jpeg.getNext());
1270
+ val |= uint32_t(jpeg.getNext()) << 8;
1271
+ val |= uint32_t(jpeg.getNext()) << 16;
1272
+ val |= uint32_t(jpeg.getNext()) << 24;
1273
+ return val;
1274
+ };
1275
+ auto readUint16Big = [](Reader &jpeg) -> uint32_t {
1276
+ uint16_t val = uint16_t(jpeg.getNext()) << 8;
1277
+ val |= uint16_t(jpeg.getNext());
1278
+ return val;
1279
+ };
1280
+ auto readUint32Big = [](Reader &jpeg) -> uint32_t {
1281
+ uint32_t val = uint32_t(jpeg.getNext()) << 24;
1282
+ val |= uint32_t(jpeg.getNext()) << 16;
1283
+ val |= uint32_t(jpeg.getNext()) << 8;
1284
+ val |= uint32_t(jpeg.getNext());
1285
+ return val;
1286
+ };
1287
+ // The first two bytes of TIFF header are "II" if little-endian ("Intel")
1288
+ // and "MM" if big-endian ("Motorola")
1289
+ auto readUint32 = [readUint32Little, readUint32Big, isLE](Reader &jpeg) -> uint32_t {
1290
+ return isLE ? readUint32Little(jpeg) : readUint32Big(jpeg);
1291
+ };
1292
+ auto readUint16 = [readUint16Little, readUint16Big, isLE](Reader &jpeg) -> uint32_t {
1293
+ return isLE ? readUint16Little(jpeg) : readUint16Big(jpeg);
1294
+ };
1295
+ // offset to the IFD0 (offset from beginning of TIFF header, II/MM,
1296
+ // which is 8 bytes before where we are after reading the uint32)
1297
+ jpeg.skipBytes(readUint32(jpeg) - 8);
1298
+
1299
+ // Read the IFD0 ("Image File Directory 0")
1300
+ // | NN | n entries in directory (2 bytes)
1301
+ // | TT | tt | nnnn | vvvv | entry: tag (2b), data type (2b),
1302
+ // n components (4b), value/offset (4b)
1303
+ if (jpeg.hasBytes(2)) {
1304
+ uint16_t nEntries = readUint16(jpeg);
1305
+ for (uint16_t i = 0; i < nEntries && jpeg.hasBytes(2); ++i) {
1306
+ uint16_t tag = readUint16(jpeg);
1307
+ // The entry is 12 bytes. We already read the 2 bytes for the tag.
1308
+ jpeg.skipBytes(6); // skip 2 for the data type, skip 4 n components.
1309
+ if (tag == 0x112) {
1310
+ switch (readUint16(jpeg)) { // orientation tag is always one uint16
1311
+ case 1: return NORMAL;
1312
+ case 2: return MIRROR_HORIZ;
1313
+ case 3: return ROTATE_180;
1314
+ case 4: return MIRROR_VERT;
1315
+ case 5: return MIRROR_HORIZ_AND_ROTATE_270_CW;
1316
+ case 6: return ROTATE_90_CW;
1317
+ case 7: return MIRROR_HORIZ_AND_ROTATE_90_CW;
1318
+ case 8: return ROTATE_270_CW;
1319
+ default: return NORMAL;
1320
+ }
1321
+ } else {
1322
+ jpeg.skipBytes(4); // skip the four bytes for the value
1323
+ }
1324
+ }
1325
+ }
1326
+ }
1327
+
1328
+ return NORMAL;
1329
+ }
1330
+
1331
+ /*
1332
+ * Updates the dimensions of the bitmap according to the orientation
1333
+ */
1334
+
1335
+ void Image::updateDimensionsForOrientation(Orientation orientation) {
1336
+ switch (orientation) {
1337
+ case ROTATE_90_CW:
1338
+ case ROTATE_270_CW:
1339
+ case MIRROR_HORIZ_AND_ROTATE_90_CW:
1340
+ case MIRROR_HORIZ_AND_ROTATE_270_CW: {
1341
+ int tmp = naturalWidth;
1342
+ naturalWidth = naturalHeight;
1343
+ naturalHeight = tmp;
1344
+ tmp = width;
1345
+ width = height;
1346
+ height = tmp;
1347
+ break;
1348
+ }
1349
+ case NORMAL:
1350
+ case MIRROR_HORIZ:
1351
+ case MIRROR_VERT:
1352
+ case ROTATE_180:
1353
+ default: {
1354
+ break;
1355
+ }
1356
+ }
1357
+ }
1358
+
1359
+ /*
1360
+ * Rotates the pixels to the correct orientation.
1361
+ */
1362
+
1363
+ void
1364
+ Image::rotatePixels(uint8_t* pixels, int width, int height, int channels,
1365
+ Orientation orientation) {
1366
+ auto swapPixel = [channels](uint8_t* pixels, int src_idx, int dst_idx) {
1367
+ uint8_t tmp;
1368
+ for (int i = 0; i < channels; ++i) {
1369
+ tmp = pixels[src_idx + i];
1370
+ pixels[src_idx + i] = pixels[dst_idx + i];
1371
+ pixels[dst_idx + i] = tmp;
1372
+ }
1373
+ };
1374
+
1375
+ auto mirrorHoriz = [swapPixel](uint8_t* pixels, int width, int height, int channels) {
1376
+ int midX = width / 2; // ok to truncate if odd, since we don't swap a center pixel
1377
+ for (int y = 0; y < height; ++y) {
1378
+ for (int x = 0; x < midX; ++x) {
1379
+ int orig_idx = (y * width + x) * channels;
1380
+ int new_idx = (y * width + width - 1 - x) * channels;
1381
+ swapPixel(pixels, orig_idx, new_idx);
1382
+ }
1383
+ }
1384
+ };
1385
+
1386
+ auto mirrorVert = [swapPixel](uint8_t* pixels, int width, int height, int channels) {
1387
+ int midY = height / 2; // ok to truncate if odd, since we don't swap a center pixel
1388
+ for (int y = 0; y < midY; ++y) {
1389
+ for (int x = 0; x < width; ++x) {
1390
+ int orig_idx = (y * width + x) * channels;
1391
+ int new_idx = ((height - y - 1) * width + x) * channels;
1392
+ swapPixel(pixels, orig_idx, new_idx);
1393
+ }
1394
+ }
1395
+ };
1396
+
1397
+ auto rotate90 = [](uint8_t* pixels, int width, int height, int channels) {
1398
+ const int n_bytes = width * height * channels;
1399
+ uint8_t *unrotated = new uint8_t[n_bytes];
1400
+ if (!unrotated) {
1401
+ return;
1402
+ }
1403
+ std::memcpy(unrotated, pixels, n_bytes);
1404
+ for (int y = 0; y < height; ++y) {
1405
+ for (int x = 0; x < width ; ++x) {
1406
+ int orig_idx = (y * width + x) * channels;
1407
+ int new_idx = (x * height + height - 1 - y) * channels;
1408
+ std::memcpy(pixels + new_idx, unrotated + orig_idx, channels);
1409
+ }
1410
+ }
1411
+ };
1412
+
1413
+ auto rotate270 = [](uint8_t* pixels, int width, int height, int channels) {
1414
+ const int n_bytes = width * height * channels;
1415
+ uint8_t *unrotated = new uint8_t[n_bytes];
1416
+ if (!unrotated) {
1417
+ return;
1418
+ }
1419
+ std::memcpy(unrotated, pixels, n_bytes);
1420
+ for (int y = 0; y < height; ++y) {
1421
+ for (int x = 0; x < width ; ++x) {
1422
+ int orig_idx = (y * width + x) * channels;
1423
+ int new_idx = ((width - 1 - x) * height + y) * channels;
1424
+ std::memcpy(pixels + new_idx, unrotated + orig_idx, channels);
1425
+ }
1426
+ }
1427
+ };
1428
+
1429
+ switch (orientation) {
1430
+ case MIRROR_HORIZ:
1431
+ mirrorHoriz(pixels, width, height, channels);
1432
+ break;
1433
+ case MIRROR_VERT:
1434
+ mirrorVert(pixels, width, height, channels);
1435
+ break;
1436
+ case ROTATE_180:
1437
+ mirrorHoriz(pixels, width, height, channels);
1438
+ mirrorVert(pixels, width, height, channels);
1439
+ break;
1440
+ case ROTATE_90_CW:
1441
+ rotate90(pixels, height, width, channels); // swap w/h because we need orig w/h
1442
+ break;
1443
+ case ROTATE_270_CW:
1444
+ rotate270(pixels, height, width, channels); // swap w/h because we need orig w/h
1445
+ break;
1446
+ case MIRROR_HORIZ_AND_ROTATE_90_CW:
1447
+ mirrorHoriz(pixels, height, width, channels); // swap w/h because we need orig w/h
1448
+ rotate90(pixels, height, width, channels);
1449
+ break;
1450
+ case MIRROR_HORIZ_AND_ROTATE_270_CW:
1451
+ mirrorHoriz(pixels, height, width, channels); // swap w/h because we need orig w/h
1452
+ rotate270(pixels, height, width, channels);
1453
+ break;
1454
+ case NORMAL:
1455
+ default:
1456
+ break;
1457
+ }
1458
+ }
1459
+
1460
+ #endif /* HAVE_JPEG */
1461
+
1462
+ #ifdef HAVE_RSVG
1463
+
1464
+ /*
1465
+ * Load SVG from buffer
1466
+ */
1467
+
1468
+ cairo_status_t
1469
+ Image::loadSVGFromBuffer(uint8_t *buf, unsigned len) {
1470
+ _is_svg = true;
1471
+
1472
+ if (NULL == (_rsvg = rsvg_handle_new_from_data(buf, len, nullptr))) {
1473
+ return CAIRO_STATUS_READ_ERROR;
1474
+ }
1475
+
1476
+ double d_width;
1477
+ double d_height;
1478
+
1479
+ rsvg_handle_get_intrinsic_size_in_pixels(_rsvg, &d_width, &d_height);
1480
+
1481
+ width = naturalWidth = d_width;
1482
+ height = naturalHeight = d_height;
1483
+
1484
+ if (width <= 0 || height <= 0) {
1485
+ this->errorInfo.set("Width and height must be set on the svg element");
1486
+ return CAIRO_STATUS_READ_ERROR;
1487
+ }
1488
+
1489
+ return renderSVGToSurface();
1490
+ }
1491
+
1492
+ /*
1493
+ * Renders the Rsvg handle to this image's surface
1494
+ */
1495
+ cairo_status_t
1496
+ Image::renderSVGToSurface() {
1497
+ cairo_status_t status;
1498
+
1499
+ _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
1500
+
1501
+ status = cairo_surface_status(_surface);
1502
+ if (status != CAIRO_STATUS_SUCCESS) {
1503
+ g_object_unref(_rsvg);
1504
+ return status;
1505
+ }
1506
+
1507
+ cairo_t *cr = cairo_create(_surface);
1508
+ status = cairo_status(cr);
1509
+ if (status != CAIRO_STATUS_SUCCESS) {
1510
+ g_object_unref(_rsvg);
1511
+ return status;
1512
+ }
1513
+
1514
+ RsvgRectangle viewport = {
1515
+ 0, // x
1516
+ 0, // y
1517
+ static_cast<double>(width),
1518
+ static_cast<double>(height)
1519
+ };
1520
+ gboolean render_ok = rsvg_handle_render_document(_rsvg, cr, &viewport, nullptr);
1521
+ if (!render_ok) {
1522
+ g_object_unref(_rsvg);
1523
+ cairo_destroy(cr);
1524
+ return CAIRO_STATUS_READ_ERROR; // or WRITE?
1525
+ }
1526
+
1527
+ cairo_destroy(cr);
1528
+
1529
+ _svg_last_width = width;
1530
+ _svg_last_height = height;
1531
+
1532
+ return status;
1533
+ }
1534
+
1535
+ /*
1536
+ * Load SVG
1537
+ */
1538
+
1539
+ cairo_status_t
1540
+ Image::loadSVG(FILE *stream) {
1541
+ _is_svg = true;
1542
+
1543
+ struct stat s;
1544
+ int fd = fileno(stream);
1545
+
1546
+ // stat
1547
+ if (fstat(fd, &s) < 0) {
1548
+ fclose(stream);
1549
+ return CAIRO_STATUS_READ_ERROR;
1550
+ }
1551
+
1552
+ uint8_t *buf = (uint8_t *) malloc(s.st_size);
1553
+
1554
+ if (!buf) {
1555
+ fclose(stream);
1556
+ return CAIRO_STATUS_NO_MEMORY;
1557
+ }
1558
+
1559
+ size_t read = fread(buf, s.st_size, 1, stream);
1560
+ fclose(stream);
1561
+
1562
+ cairo_status_t result = CAIRO_STATUS_READ_ERROR;
1563
+ if (1 == read) result = loadSVGFromBuffer(buf, s.st_size);
1564
+ free(buf);
1565
+
1566
+ return result;
1567
+ }
1568
+
1569
+ #endif /* HAVE_RSVG */
1570
+
1571
+ /*
1572
+ * Load BMP from buffer.
1573
+ */
1574
+
1575
+ cairo_status_t Image::loadBMPFromBuffer(uint8_t *buf, unsigned len){
1576
+ BMPParser::Parser parser;
1577
+
1578
+ // Reversed ARGB32 with pre-multiplied alpha
1579
+ uint8_t pixFmt[5] = {2, 1, 0, 3, 1};
1580
+ parser.parse(buf, len, pixFmt);
1581
+
1582
+ if (parser.getStatus() != BMPParser::Status::OK) {
1583
+ errorInfo.reset();
1584
+ errorInfo.message = parser.getErrMsg();
1585
+ return CAIRO_STATUS_READ_ERROR;
1586
+ }
1587
+
1588
+ width = naturalWidth = parser.getWidth();
1589
+ height = naturalHeight = parser.getHeight();
1590
+ uint8_t *data = parser.getImgd();
1591
+
1592
+ _surface = cairo_image_surface_create_for_data(
1593
+ data,
1594
+ CAIRO_FORMAT_ARGB32,
1595
+ width,
1596
+ height,
1597
+ cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width)
1598
+ );
1599
+
1600
+ // No need to delete the data
1601
+ cairo_status_t status = cairo_surface_status(_surface);
1602
+ if (status) return status;
1603
+
1604
+ _data = data;
1605
+ parser.clearImgd();
1606
+
1607
+ return CAIRO_STATUS_SUCCESS;
1608
+ }
1609
+
1610
+ /*
1611
+ * Load BMP.
1612
+ */
1613
+
1614
+ cairo_status_t Image::loadBMP(FILE *stream){
1615
+ struct stat s;
1616
+ int fd = fileno(stream);
1617
+
1618
+ // Stat
1619
+ if (fstat(fd, &s) < 0) {
1620
+ fclose(stream);
1621
+ return CAIRO_STATUS_READ_ERROR;
1622
+ }
1623
+
1624
+ uint8_t *buf = new uint8_t[s.st_size];
1625
+
1626
+ if (!buf) {
1627
+ fclose(stream);
1628
+ errorInfo.set(NULL, "malloc", errno);
1629
+ return CAIRO_STATUS_NO_MEMORY;
1630
+ }
1631
+
1632
+ size_t read = fread(buf, s.st_size, 1, stream);
1633
+ fclose(stream);
1634
+
1635
+ cairo_status_t result = CAIRO_STATUS_READ_ERROR;
1636
+ if (read == 1) result = loadBMPFromBuffer(buf, s.st_size);
1637
+ delete[] buf;
1638
+
1639
+ return result;
1640
+ }
1641
+
1642
+ /*
1643
+ * Return UNKNOWN, SVG, GIF, JPEG, or PNG based on the filename.
1644
+ */
1645
+
1646
+ Image::type
1647
+ Image::extension(const char *filename) {
1648
+ size_t len = strlen(filename);
1649
+ filename += len;
1650
+ if (len >= 5 && 0 == strcmp(".jpeg", filename - 5)) return Image::JPEG;
1651
+ if (len >= 4 && 0 == strcmp(".gif", filename - 4)) return Image::GIF;
1652
+ if (len >= 4 && 0 == strcmp(".jpg", filename - 4)) return Image::JPEG;
1653
+ if (len >= 4 && 0 == strcmp(".png", filename - 4)) return Image::PNG;
1654
+ if (len >= 4 && 0 == strcmp(".svg", filename - 4)) return Image::SVG;
1655
+ return Image::UNKNOWN;
1656
+ }
1657
+
1658
+ /*
1659
+ * Sniff bytes 0..1 for JPEG's magic number ff d8.
1660
+ */
1661
+
1662
+ int
1663
+ Image::isJPEG(uint8_t *data) {
1664
+ return 0xff == data[0] && 0xd8 == data[1];
1665
+ }
1666
+
1667
+ /*
1668
+ * Sniff bytes 0..2 for "GIF".
1669
+ */
1670
+
1671
+ int
1672
+ Image::isGIF(uint8_t *data) {
1673
+ return 'G' == data[0] && 'I' == data[1] && 'F' == data[2];
1674
+ }
1675
+
1676
+ /*
1677
+ * Sniff bytes 1..3 for "PNG".
1678
+ */
1679
+
1680
+ int
1681
+ Image::isPNG(uint8_t *data) {
1682
+ return 'P' == data[1] && 'N' == data[2] && 'G' == data[3];
1683
+ }
1684
+
1685
+ /*
1686
+ * Skip "<?" and "<!" tags to test if root tag starts "<svg"
1687
+ */
1688
+ int
1689
+ Image::isSVG(uint8_t *data, unsigned len) {
1690
+ for (unsigned i = 3; i < len; i++) {
1691
+ if ('<' == data[i-3]) {
1692
+ switch (data[i-2]) {
1693
+ case '?':
1694
+ case '!':
1695
+ break;
1696
+ case 's':
1697
+ return ('v' == data[i-1] && 'g' == data[i]);
1698
+ default:
1699
+ return false;
1700
+ }
1701
+ }
1702
+ }
1703
+ return false;
1704
+ }
1705
+
1706
+ /*
1707
+ * Check for valid BMP signatures
1708
+ */
1709
+
1710
+ int Image::isBMP(uint8_t *data, unsigned len) {
1711
+ if(len < 2) return false;
1712
+ std::string sig = std::string(1, (char)data[0]) + (char)data[1];
1713
+ return sig == "BM" ||
1714
+ sig == "BA" ||
1715
+ sig == "CI" ||
1716
+ sig == "CP" ||
1717
+ sig == "IC" ||
1718
+ sig == "PT";
1719
+ }