@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.h ADDED
@@ -0,0 +1,146 @@
1
+ // Copyright (c) 2010 LearnBoost <tj@learnboost.com>
2
+
3
+ #pragma once
4
+
5
+ #include <cairo.h>
6
+ #include "CanvasError.h"
7
+ #include <functional>
8
+ #include <napi.h>
9
+ #include <stdint.h> // node < 7 uses libstdc++ on macOS which lacks complete c++11
10
+
11
+ #ifdef HAVE_JPEG
12
+ #include <jpeglib.h>
13
+ #include <jerror.h>
14
+ #endif
15
+
16
+ #ifdef HAVE_GIF
17
+ #include <gif_lib.h>
18
+
19
+ #if GIFLIB_MAJOR > 5 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1
20
+ #define GIF_CLOSE_FILE(gif) DGifCloseFile(gif, NULL)
21
+ #else
22
+ #define GIF_CLOSE_FILE(gif) DGifCloseFile(gif)
23
+ #endif
24
+ #endif
25
+
26
+ #ifdef HAVE_RSVG
27
+ #include <librsvg/rsvg.h>
28
+ // librsvg <= 2.36.1, identified by undefined macro, needs an extra include
29
+ #ifndef LIBRSVG_CHECK_VERSION
30
+ #include <librsvg/rsvg-cairo.h>
31
+ #endif
32
+ #endif
33
+
34
+ using JPEGDecodeL = std::function<uint32_t (uint8_t* const src)>;
35
+
36
+ class Image : public Napi::ObjectWrap<Image> {
37
+ public:
38
+ char *filename;
39
+ int width, height;
40
+ int naturalWidth, naturalHeight;
41
+ Napi::Env env;
42
+ static Napi::FunctionReference constructor;
43
+ static void Initialize(Napi::Env& env, Napi::Object& target);
44
+ Image(const Napi::CallbackInfo& info);
45
+ Napi::Value GetComplete(const Napi::CallbackInfo& info);
46
+ Napi::Value GetWidth(const Napi::CallbackInfo& info);
47
+ Napi::Value GetHeight(const Napi::CallbackInfo& info);
48
+ Napi::Value GetNaturalWidth(const Napi::CallbackInfo& info);
49
+ Napi::Value GetNaturalHeight(const Napi::CallbackInfo& info);
50
+ Napi::Value GetDataMode(const Napi::CallbackInfo& info);
51
+ void SetDataMode(const Napi::CallbackInfo& info, const Napi::Value& value);
52
+ void SetWidth(const Napi::CallbackInfo& info, const Napi::Value& value);
53
+ void SetHeight(const Napi::CallbackInfo& info, const Napi::Value& value);
54
+ static Napi::Value GetSource(const Napi::CallbackInfo& info);
55
+ static void SetSource(const Napi::CallbackInfo& info);
56
+ inline uint8_t *data(){ return cairo_image_surface_get_data(_surface); }
57
+ inline int stride(){ return cairo_image_surface_get_stride(_surface); }
58
+ static int isPNG(uint8_t *data);
59
+ static int isJPEG(uint8_t *data);
60
+ static int isGIF(uint8_t *data);
61
+ static int isSVG(uint8_t *data, unsigned len);
62
+ static int isBMP(uint8_t *data, unsigned len);
63
+ static cairo_status_t readPNG(void *closure, unsigned char *data, unsigned len);
64
+ inline int isComplete(){ return COMPLETE == state; }
65
+ cairo_surface_t *surface();
66
+ cairo_status_t loadSurface();
67
+ cairo_status_t loadFromBuffer(uint8_t *buf, unsigned len);
68
+ cairo_status_t loadPNGFromBuffer(uint8_t *buf);
69
+ cairo_status_t loadPNG();
70
+ void clearData();
71
+ #ifdef HAVE_RSVG
72
+ cairo_status_t loadSVGFromBuffer(uint8_t *buf, unsigned len);
73
+ cairo_status_t loadSVG(FILE *stream);
74
+ cairo_status_t renderSVGToSurface();
75
+ #endif
76
+ #ifdef HAVE_GIF
77
+ cairo_status_t loadGIFFromBuffer(uint8_t *buf, unsigned len);
78
+ cairo_status_t loadGIF(FILE *stream);
79
+ #endif
80
+ #ifdef HAVE_JPEG
81
+ enum Orientation {
82
+ NORMAL,
83
+ MIRROR_HORIZ,
84
+ MIRROR_VERT,
85
+ ROTATE_180,
86
+ ROTATE_90_CW,
87
+ ROTATE_270_CW,
88
+ MIRROR_HORIZ_AND_ROTATE_90_CW,
89
+ MIRROR_HORIZ_AND_ROTATE_270_CW
90
+ };
91
+ cairo_status_t loadJPEGFromBuffer(uint8_t *buf, unsigned len);
92
+ cairo_status_t loadJPEG(FILE *stream);
93
+ void jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode);
94
+ cairo_status_t decodeJPEGIntoSurface(jpeg_decompress_struct *info, Orientation orientation);
95
+ cairo_status_t decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len);
96
+ cairo_status_t assignDataAsMime(uint8_t *data, int len, const char *mime_type);
97
+
98
+ class Reader {
99
+ public:
100
+ virtual bool hasBytes(unsigned n) const = 0;
101
+ virtual uint8_t getNext() = 0;
102
+ virtual void skipBytes(unsigned n) = 0;
103
+ };
104
+ Orientation getExifOrientation(Reader& jpeg);
105
+ void updateDimensionsForOrientation(Orientation orientation);
106
+ void rotatePixels(uint8_t* pixels, int width, int height, int channels, Orientation orientation);
107
+ #endif
108
+ cairo_status_t loadBMPFromBuffer(uint8_t *buf, unsigned len);
109
+ cairo_status_t loadBMP(FILE *stream);
110
+ CanvasError errorInfo;
111
+ void loaded();
112
+ cairo_status_t load();
113
+ ~Image();
114
+
115
+ enum {
116
+ DEFAULT
117
+ , LOADING
118
+ , COMPLETE
119
+ } state;
120
+
121
+ enum data_mode_t {
122
+ DATA_IMAGE = 1
123
+ , DATA_MIME = 2
124
+ } data_mode;
125
+
126
+ typedef enum {
127
+ UNKNOWN
128
+ , GIF
129
+ , JPEG
130
+ , PNG
131
+ , SVG
132
+ } type;
133
+
134
+ static type extension(const char *filename);
135
+
136
+ private:
137
+ cairo_surface_t *_surface;
138
+ uint8_t *_data = nullptr;
139
+ int _data_len;
140
+ #ifdef HAVE_RSVG
141
+ RsvgHandle *_rsvg;
142
+ bool _is_svg;
143
+ int _svg_last_width;
144
+ int _svg_last_height;
145
+ #endif
146
+ };
@@ -0,0 +1,138 @@
1
+ // Copyright (c) 2010 LearnBoost <tj@learnboost.com>
2
+
3
+ #include "ImageData.h"
4
+ #include "InstanceData.h"
5
+
6
+ /*
7
+ * Initialize ImageData.
8
+ */
9
+
10
+ void
11
+ ImageData::Initialize(Napi::Env& env, Napi::Object& exports) {
12
+ Napi::HandleScope scope(env);
13
+
14
+ InstanceData *data = env.GetInstanceData<InstanceData>();
15
+
16
+ Napi::Function ctor = DefineClass(env, "ImageData", {
17
+ InstanceAccessor<&ImageData::GetWidth>("width", napi_default_jsproperty),
18
+ InstanceAccessor<&ImageData::GetHeight>("height", napi_default_jsproperty)
19
+ });
20
+
21
+ exports.Set("ImageData", ctor);
22
+ data->ImageDataCtor = Napi::Persistent(ctor);
23
+ }
24
+
25
+ /*
26
+ * Initialize a new ImageData object.
27
+ */
28
+
29
+ ImageData::ImageData(const Napi::CallbackInfo& info) : Napi::ObjectWrap<ImageData>(info), env(info.Env()) {
30
+ Napi::TypedArray dataArray;
31
+ uint32_t width;
32
+ uint32_t height;
33
+ uint32_t length;
34
+
35
+ if (info[0].IsNumber() && info[1].IsNumber()) {
36
+ width = info[0].As<Napi::Number>().Uint32Value();
37
+ if (width == 0) {
38
+ Napi::RangeError::New(env, "The source width is zero.").ThrowAsJavaScriptException();
39
+ return;
40
+ }
41
+ height = info[1].As<Napi::Number>().Uint32Value();
42
+ if (height == 0) {
43
+ Napi::RangeError::New(env, "The source height is zero.").ThrowAsJavaScriptException();
44
+ return;
45
+ }
46
+ if ((uint64_t)width * height > INT32_MAX / 4) {
47
+ // INT32_MAX is what Firefox limits ImageData to
48
+ std::string msg = "buffer exceeds " + std::to_string(INT32_MAX) + " bytes";
49
+ Napi::Error::New(env, msg).ThrowAsJavaScriptException();
50
+ return;
51
+ }
52
+ length = width * height * 4; // ImageData(w, h) constructor assumes 4 BPP; documented.
53
+
54
+ dataArray = Napi::Uint8Array::New(env, length, napi_uint8_clamped_array);
55
+ } else if (
56
+ info[0].IsTypedArray() &&
57
+ info[0].As<Napi::TypedArray>().TypedArrayType() == napi_uint8_clamped_array &&
58
+ info[1].IsNumber()
59
+ ) {
60
+ dataArray = info[0].As<Napi::Uint8Array>();
61
+
62
+ length = dataArray.ElementLength();
63
+ if (length == 0) {
64
+ Napi::RangeError::New(env, "The input data has a zero byte length.").ThrowAsJavaScriptException();
65
+ return;
66
+ }
67
+
68
+ // Don't assert that the ImageData length is a multiple of four because some
69
+ // data formats are not 4 BPP.
70
+
71
+ width = info[1].As<Napi::Number>().Uint32Value();
72
+ if (width == 0) {
73
+ Napi::RangeError::New(env, "The source width is zero.").ThrowAsJavaScriptException();
74
+ return;
75
+ }
76
+
77
+ // Don't assert that the byte length is a multiple of 4 * width, ditto.
78
+
79
+ if (info[2].IsNumber()) { // Explicit height given
80
+ height = info[2].As<Napi::Number>().Uint32Value();
81
+ } else { // Calculate height assuming 4 BPP
82
+ int size = length / 4;
83
+ height = size / width;
84
+ }
85
+ } else if (
86
+ info[0].IsTypedArray() &&
87
+ info[0].As<Napi::TypedArray>().TypedArrayType() == napi_uint16_array &&
88
+ info[1].IsNumber()
89
+ ) { // Intended for RGB16_565 format
90
+ dataArray = info[0].As<Napi::TypedArray>();
91
+
92
+ length = dataArray.ElementLength();
93
+ if (length == 0) {
94
+ Napi::RangeError::New(env, "The input data has a zero byte length.").ThrowAsJavaScriptException();
95
+ return;
96
+ }
97
+
98
+ width = info[1].As<Napi::Number>().Uint32Value();
99
+ if (width == 0) {
100
+ Napi::RangeError::New(env, "The source width is zero.").ThrowAsJavaScriptException();
101
+ return;
102
+ }
103
+
104
+ if (info[2].IsNumber()) { // Explicit height given
105
+ height = info[2].As<Napi::Number>().Uint32Value();
106
+ } else { // Calculate height assuming 2 BPP
107
+ int size = length / 2;
108
+ height = size / width;
109
+ }
110
+ } else {
111
+ Napi::TypeError::New(env, "Expected (Uint8ClampedArray, width[, height]), (Uint16Array, width[, height]) or (width, height)").ThrowAsJavaScriptException();
112
+ return;
113
+ }
114
+
115
+ _width = width;
116
+ _height = height;
117
+ _data = dataArray.As<Napi::Uint8Array>().Data();
118
+
119
+ info.This().As<Napi::Object>().Set("data", dataArray);
120
+ }
121
+
122
+ /*
123
+ * Get width.
124
+ */
125
+
126
+ Napi::Value
127
+ ImageData::GetWidth(const Napi::CallbackInfo& info) {
128
+ return Napi::Number::New(env, width());
129
+ }
130
+
131
+ /*
132
+ * Get height.
133
+ */
134
+
135
+ Napi::Value
136
+ ImageData::GetHeight(const Napi::CallbackInfo& info) {
137
+ return Napi::Number::New(env, height());
138
+ }
@@ -0,0 +1,26 @@
1
+ // Copyright (c) 2010 LearnBoost <tj@learnboost.com>
2
+
3
+ #pragma once
4
+
5
+ #include <napi.h>
6
+ #include <stdint.h> // node < 7 uses libstdc++ on macOS which lacks complete c++11
7
+
8
+ class ImageData : public Napi::ObjectWrap<ImageData> {
9
+ public:
10
+ static void Initialize(Napi::Env& env, Napi::Object& exports);
11
+ ImageData(const Napi::CallbackInfo& info);
12
+ Napi::Value GetWidth(const Napi::CallbackInfo& info);
13
+ Napi::Value GetHeight(const Napi::CallbackInfo& info);
14
+
15
+ inline uint32_t width() { return _width; }
16
+ inline uint32_t height() { return _height; }
17
+ inline uint8_t *data() { return _data; }
18
+
19
+ Napi::Env env;
20
+
21
+ private:
22
+ uint32_t _width;
23
+ uint32_t _height;
24
+ uint8_t *_data;
25
+
26
+ };
@@ -0,0 +1,12 @@
1
+ #include <napi.h>
2
+
3
+ struct InstanceData {
4
+ Napi::FunctionReference CanvasCtor;
5
+ Napi::FunctionReference CanvasGradientCtor;
6
+ Napi::FunctionReference DOMMatrixCtor;
7
+ Napi::FunctionReference ImageCtor;
8
+ Napi::FunctionReference parseFont;
9
+ Napi::FunctionReference Context2dCtor;
10
+ Napi::FunctionReference ImageDataCtor;
11
+ Napi::FunctionReference CanvasPatternCtor;
12
+ };
@@ -0,0 +1,157 @@
1
+ #pragma once
2
+
3
+ #include "closure.h"
4
+ #include <jpeglib.h>
5
+ #include <jerror.h>
6
+
7
+ /*
8
+ * Expanded data destination object for closure output,
9
+ * inspired by IJG's jdatadst.c
10
+ */
11
+
12
+ struct closure_destination_mgr {
13
+ jpeg_destination_mgr pub;
14
+ JpegClosure* closure;
15
+ JOCTET *buffer;
16
+ int bufsize;
17
+ };
18
+
19
+ void
20
+ init_closure_destination(j_compress_ptr cinfo){
21
+ // we really don't have to do anything here
22
+ }
23
+
24
+ boolean
25
+ empty_closure_output_buffer(j_compress_ptr cinfo){
26
+ closure_destination_mgr *dest = (closure_destination_mgr *) cinfo->dest;
27
+ Napi::Env env = dest->closure->canvas->Env();
28
+ Napi::HandleScope scope(env);
29
+ Napi::AsyncContext async(env, "canvas:empty_closure_output_buffer");
30
+
31
+ Napi::Object buf = Napi::Buffer<char>::New(env, (char *)dest->buffer, dest->bufsize);
32
+
33
+ // emit "data"
34
+ dest->closure->cb.MakeCallback(env.Global(), {env.Null(), buf}, async);
35
+
36
+ dest->buffer = (JOCTET *)malloc(dest->bufsize);
37
+ cinfo->dest->next_output_byte = dest->buffer;
38
+ cinfo->dest->free_in_buffer = dest->bufsize;
39
+ return true;
40
+ }
41
+
42
+ void
43
+ term_closure_destination(j_compress_ptr cinfo){
44
+ closure_destination_mgr *dest = (closure_destination_mgr *) cinfo->dest;
45
+ Napi::Env env = dest->closure->canvas->Env();
46
+ Napi::HandleScope scope(env);
47
+ Napi::AsyncContext async(env, "canvas:term_closure_destination");
48
+
49
+ /* emit remaining data */
50
+ Napi::Object buf = Napi::Buffer<char>::New(env, (char *)dest->buffer, dest->bufsize - dest->pub.free_in_buffer);
51
+
52
+ dest->closure->cb.MakeCallback(env.Global(), {env.Null(), buf}, async);
53
+
54
+ // emit "end"
55
+ dest->closure->cb.MakeCallback(env.Global(), {env.Null(), env.Null()}, async);
56
+ }
57
+
58
+ void
59
+ jpeg_closure_dest(j_compress_ptr cinfo, JpegClosure* closure, int bufsize){
60
+ closure_destination_mgr * dest;
61
+
62
+ /* The destination object is made permanent so that multiple JPEG images
63
+ * can be written to the same buffer without re-executing jpeg_mem_dest.
64
+ */
65
+ if (cinfo->dest == NULL) { /* first time for this JPEG object? */
66
+ cinfo->dest = (struct jpeg_destination_mgr *)
67
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
68
+ sizeof(closure_destination_mgr));
69
+ }
70
+
71
+ dest = (closure_destination_mgr *) cinfo->dest;
72
+
73
+ cinfo->dest->init_destination = &init_closure_destination;
74
+ cinfo->dest->empty_output_buffer = &empty_closure_output_buffer;
75
+ cinfo->dest->term_destination = &term_closure_destination;
76
+
77
+ dest->closure = closure;
78
+ dest->bufsize = bufsize;
79
+ dest->buffer = (JOCTET *)malloc(bufsize);
80
+
81
+ cinfo->dest->next_output_byte = dest->buffer;
82
+ cinfo->dest->free_in_buffer = dest->bufsize;
83
+ }
84
+
85
+ void encode_jpeg(jpeg_compress_struct cinfo, cairo_surface_t *surface, int quality, bool progressive, int chromaHSampFactor, int chromaVSampFactor) {
86
+ int w = cairo_image_surface_get_width(surface);
87
+ int h = cairo_image_surface_get_height(surface);
88
+
89
+ cinfo.in_color_space = JCS_RGB;
90
+ cinfo.input_components = 3;
91
+ cinfo.image_width = w;
92
+ cinfo.image_height = h;
93
+ jpeg_set_defaults(&cinfo);
94
+ if (progressive)
95
+ jpeg_simple_progression(&cinfo);
96
+ jpeg_set_quality(&cinfo, quality, (quality < 25) ? 0 : 1);
97
+ cinfo.comp_info[0].h_samp_factor = chromaHSampFactor;
98
+ cinfo.comp_info[0].v_samp_factor = chromaVSampFactor;
99
+
100
+ JSAMPROW slr;
101
+ jpeg_start_compress(&cinfo, TRUE);
102
+ unsigned char *dst;
103
+ unsigned int *src = (unsigned int *)cairo_image_surface_get_data(surface);
104
+ int sl = 0;
105
+ dst = (unsigned char *)malloc(w * 3);
106
+ while (sl < h) {
107
+ unsigned char *dp = dst;
108
+ int x = 0;
109
+ while (x < w) {
110
+ dp[0] = (*src >> 16) & 255;
111
+ dp[1] = (*src >> 8) & 255;
112
+ dp[2] = *src & 255;
113
+ src++;
114
+ dp += 3;
115
+ x++;
116
+ }
117
+ slr = dst;
118
+ jpeg_write_scanlines(&cinfo, &slr, 1);
119
+ sl++;
120
+ }
121
+ free(dst);
122
+ jpeg_finish_compress(&cinfo);
123
+ jpeg_destroy_compress(&cinfo);
124
+ }
125
+
126
+ void
127
+ write_to_jpeg_stream(cairo_surface_t *surface, int bufsize, JpegClosure* closure) {
128
+ jpeg_compress_struct cinfo;
129
+ jpeg_error_mgr jerr;
130
+ cinfo.err = jpeg_std_error(&jerr);
131
+ jpeg_create_compress(&cinfo);
132
+ jpeg_closure_dest(&cinfo, closure, bufsize);
133
+ encode_jpeg(
134
+ cinfo,
135
+ surface,
136
+ closure->quality,
137
+ closure->progressive,
138
+ closure->chromaSubsampling,
139
+ closure->chromaSubsampling);
140
+ }
141
+
142
+ void
143
+ write_to_jpeg_buffer(cairo_surface_t* surface, JpegClosure* closure) {
144
+ jpeg_compress_struct cinfo;
145
+ jpeg_error_mgr jerr;
146
+ cinfo.err = jpeg_std_error(&jerr);
147
+ jpeg_create_compress(&cinfo);
148
+ cinfo.client_data = closure;
149
+ cinfo.dest = closure->jpeg_dest_mgr;
150
+ encode_jpeg(
151
+ cinfo,
152
+ surface,
153
+ closure->quality,
154
+ closure->progressive,
155
+ closure->chromaSubsampling,
156
+ closure->chromaSubsampling);
157
+ }