skia-canvas 0.9.24 → 0.9.28

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 (55) hide show
  1. package/CHANGELOG.md +195 -0
  2. package/README.md +188 -84
  3. package/lib/index.d.ts +231 -0
  4. package/lib/index.js +72 -34
  5. package/package.json +14 -9
  6. package/Makefile +0 -60
  7. package/src/canvas.rs +0 -417
  8. package/src/context/api.rs +0 -1056
  9. package/src/context/mod.rs +0 -814
  10. package/src/gradient.rs +0 -174
  11. package/src/image.rs +0 -99
  12. package/src/lib.rs +0 -222
  13. package/src/path.rs +0 -520
  14. package/src/pattern.rs +0 -130
  15. package/src/texture.rs +0 -124
  16. package/src/typography.rs +0 -624
  17. package/src/utils.rs +0 -701
  18. package/test/assets/AmstelvarAlpha-VF.ttf +0 -0
  19. package/test/assets/blend-bg.png +0 -0
  20. package/test/assets/blend-fg.png +0 -0
  21. package/test/assets/checkers.png +0 -0
  22. package/test/assets/globe.jpg +0 -0
  23. package/test/assets/grayscale.jpg +0 -0
  24. package/test/assets/halved-1.jpeg +0 -0
  25. package/test/assets/halved-2.jpeg +0 -0
  26. package/test/assets/image/format.bmp +0 -0
  27. package/test/assets/image/format.gif +0 -0
  28. package/test/assets/image/format.ico +0 -0
  29. package/test/assets/image/format.jpg +0 -0
  30. package/test/assets/image/format.pdf +5 -1322
  31. package/test/assets/image/format.png +0 -0
  32. package/test/assets/path/effect-interpolate@2x.png +0 -0
  33. package/test/assets/path/effect-jitter@2x.png +0 -0
  34. package/test/assets/path/effect-points@2x.png +0 -0
  35. package/test/assets/path/effect-round@2x.png +0 -0
  36. package/test/assets/path/effect-simplify@2x.png +0 -0
  37. package/test/assets/path/effect-trim@2x.png +0 -0
  38. package/test/assets/path/effect-unwind@2x.png +0 -0
  39. package/test/assets/path/lineDashMarker@2x.png +0 -0
  40. package/test/assets/path/operation-none.svg +0 -6
  41. package/test/assets/path/operations@2x.png +0 -0
  42. package/test/assets/path/outlineText@2x.png +0 -0
  43. package/test/assets/pentagon-cmyk.jpg +0 -0
  44. package/test/assets/pentagon-grayscale.jpg +0 -0
  45. package/test/assets/pentagon.png +0 -0
  46. package/test/assets/quadrants.png +0 -0
  47. package/test/assets/star.png +0 -0
  48. package/test/assets/state.png +0 -0
  49. package/test/canvas.test.js +0 -490
  50. package/test/context2d.test.js +0 -738
  51. package/test/media.test.js +0 -204
  52. package/test/path2d.test.js +0 -614
  53. package/test/visual/index.html +0 -193
  54. package/test/visual/index.js +0 -68
  55. package/test/visual/tests.js +0 -2669
package/CHANGELOG.md ADDED
@@ -0,0 +1,195 @@
1
+ # Changelog
2
+
3
+ <!-- ## 🥚 ⟩ [Unreleased] -->
4
+
5
+ ## 📦 ⟩ [v0.9.28] ⟩ Jan 12, 2022
6
+
7
+ ### New Features
8
+ - Added TypeScript definitions for extensions to the DOM spec (contributed by [@cprecioso](https://github.com/cprecioso))
9
+ - Added 3D-perspective transformations via the new [createProjection()](https://github.com/samizdatco/skia-canvas#createprojectionquad-basis) context method
10
+ - Colors can now use the [hwb()](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hwb()) model
11
+
12
+ ### Breaking Changes
13
+ - The **Canvas** [`.async`](https://github.com/samizdatco/skia-canvas#async) property has been **deprecated** and will be removed in a future release.
14
+ - The `saveAs`, `toBuffer`, and `toDataURL` methods will now be async-only (likewise the [shorthand properties](https://github.com/samizdatco/skia-canvas#pdf-svg-jpg-and-png)).
15
+ - Use their synchronous counterparts (`saveAsSync`, `toBufferSync`, and `toDataURLSync`) if you want to block execution while exporting images.
16
+ - The [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData/ImageData) constructor now orders its arguments properly: the optional buffer/array argument now comes first
17
+
18
+ ### Bugfixes
19
+ - Fixed a stack overflow that was occurring when images became too deeply nested for the default deallocator to handle (primarily due to many thousands of image exports from the same canvas)
20
+ - The `source-in`, `source-out`, `destination-atop`, and `copy` composite operations now work correctly for paths rather than rendering shapes without color (contributed by [@meihuanyu](https://github.com/meihuanyu))
21
+ - Shape primitives now behave consistently with browsers when being added to a non-empty path:
22
+ - `rect()` now issues an initial `moveTo` rather than extending the path, then leaves the ‘current’ point in its upper left corner
23
+ - `ellipse()` extends the current path rather than implicitly closing it (contributed by [@meihuanyu](https://github.com/meihuanyu))
24
+ - `arc()` also extends the current path rather than closing it
25
+
26
+ ### Misc. Improvements
27
+ - Upgraded Skia to milestone 96
28
+ - Added workflow for creating docker build environments
29
+
30
+
31
+ ## 📦 ⟩ [v0.9.27] ⟩ Oct 23, 2021
32
+
33
+ ### New Features
34
+ - Added pre-compiled binaries for Alpine Linux using the [musl](https://musl.libc.org) C library
35
+
36
+
37
+ ## 📦 ⟩ [v0.9.26] ⟩ Oct 18, 2021
38
+
39
+ ### New Features
40
+ - Added pre-compiled binaries for 32-bit and 64-bit ARM on Linux (a.k.a. Raspberry Pi)
41
+
42
+ ### Bugfixes
43
+ - Windows text rendering has been restored after failing due to changes involving the `icudtl.dat` file
44
+ - `FontLibrary.use` now reports an error if the specified font file doesn't exist
45
+ - Fixed a crash that could result from calling `measureText` with various unicode escapes
46
+
47
+ ### Misc. Improvements
48
+ - Upgraded Skia to milestone 94
49
+ - Now embedding a more recent version of the FreeType library on Linux with support for more font formats
50
+
51
+
52
+ ## 📦 ⟩ [v0.9.25] ⟩ Aug 22, 2021
53
+
54
+ ### Bugfixes
55
+ - Improved image scaling when a larger image is being shrunk down to a smaller size via [`drawImage()`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)
56
+ - modified [`imageSmoothingQuality`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality) settings to provide a more meaningful range across `low`, `medium`, and `high`
57
+ - [`measureText()`](https://github.com/samizdatco/skia-canvas#measuretextstr-width) now returns correct metrics regardless of current `textAlign` setting
58
+ - Rolled back `icudtl.dat` changes on Windows (which suppressed the misleading warning message but required running as Administrator)
59
+
60
+ ### Misc. Improvements
61
+ - Now using [Neon](https://github.com/neon-bindings/neon) v0.9 (with enhanced async event scheduling)
62
+
63
+
64
+ ## 📦 ⟩ [v0.9.24] ⟩ Aug 18, 2021
65
+
66
+ ### New Features
67
+ - **Path2D** objects now have a read/write [`d`](https://github.com/samizdatco/skia-canvas/#d) property with an [SVG representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#path_commands) of the path’s contours and an [`unwind()`](https://github.com/samizdatco/skia-canvas/#unwind) method for converting from even-odd to non-zero winding rules
68
+ - The [`createTexture()`](https://github.com/samizdatco/skia-canvas#createtexturespacing-path-line-color-angle-offset0) context method returns **CanvasTexture** objects which can be assigned to `fillStyle` or `strokeStyle`
69
+ - Textures draw either a parallel-lines pattern or one derived from the provided **Path2D** object and positioning parameters
70
+ - The marker used when `setLineDash` is active can now be customized by assigning a **Path2D** to the context’s [`lineDashMarker`](https://github.com/samizdatco/skia-canvas#linedashmarker) property (default dashing can be restored by assigning `null`)
71
+ - The marker’s orientation & shape relative to the path being stroked can be controlled by the [`lineDashFit`](https://github.com/samizdatco/skia-canvas#linedashfit) property which defaults to `"turn"` but can be set to `"move"` (which preserves orientation) or `"follow"` (which distorts the marker’s shape to match the contour)
72
+
73
+ ### Bugfixes
74
+
75
+ - Removed use of the `??` operator which is unavailable prior to Node 14
76
+ - Prevented a spurious warning on windows incorrectly claiming that the `icudtl.dat` file could not be found
77
+
78
+ ### Misc. Improvements
79
+
80
+ - The **Path2D** [`simplify()`](https://github.com/samizdatco/skia-canvas/#simplifyrulenonzero) method now takes an optional fill-rule argument
81
+ - Added support for versions of macOS starting with 10.13 (High Sierra)
82
+
83
+
84
+ ## 📦 ⟩ [v0.9.23] ⟩ Jul 12, 2021
85
+
86
+ ### New Features
87
+
88
+ - [Conic béziers][conic_bezier] can now be drawn to the context or a Path2D with the [`conicCurveTo()`][conic_curveto] method
89
+ - Text can be converted to a Path2D using the context’s new [`outlineText()`][outline_text] method
90
+ - Path2D objects can now report back on their internal geometry with:
91
+ - the [`edges`][edges] property which contains an array of line-drawing commands describing the path’s individual contours
92
+ - the [`contains()`][contains] method which tests whether a given point is on/within the path
93
+ - the [`points()`][points] method which returns an array of `[x, y]` pairs at the requested spacing along the curve’s periphery
94
+ - A modified copy of a source Path2D can now be created using:
95
+ - [`offset()`][offset] or [`transform()`][transform] to shift position or apply a DOMMatrix respectively
96
+ - [`jitter()`][jitter] to break the path into smaller sections and apply random noise to the segments’ positions
97
+ - [`round()`][round] to round off every sharp corner in a path to a particular radius
98
+ - [`trim()`][trim] to select a percentage-based subsection of the path
99
+ - Two similar paths can be ‘tweened’ into a proportional combination of their coordinates using the [`interpolate()`][interpolate] method
100
+
101
+ ### Bugfixes
102
+
103
+ - Passing a Path2D argument to the `fill()` or `stroke()` method no longer disturbs the context’s ‘current’ path (if one has been created using `beginPath()`)
104
+ - The `filter` property will now accept percentage values greater than 999%
105
+
106
+ ### Misc. Improvements
107
+
108
+ - The `newPage()` and `saveAs()` methods now work in the browser, including the ability to save image sequences to a zip archive. The browser’s canvas is still doing all the drawing however, so file export formats will be limited to PNG and JPEG and none of the other Skia-specific extensions will be available.
109
+ - The file-export methods now accept a [`matte`][matte] value in their options object which can be used to set the background color for any portions of the canvas that were left semi-transparent
110
+ - Canvas dimensions are no longer rounded-off to integer values (at least until a bitmap needs to be generated for export)
111
+ - Linux builds will now run on some older systems going back to glibc 2.24
112
+
113
+ [conic_bezier]: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers#the-conic-bézier-curve
114
+ [conic_curveto]: https://github.com/samizdatco/skia-canvas#coniccurvetocpx-cpy-x-y-weight
115
+ [outline_text]: https://github.com/samizdatco/skia-canvas#outlinetextstr
116
+ [matte]: https://github.com/samizdatco/skia-canvas#matte
117
+
118
+ [edges]: https://github.com/samizdatco/skia-canvas#edges
119
+ [contains]: https://github.com/samizdatco/skia-canvas#containsx-y
120
+ [points]: https://github.com/samizdatco/skia-canvas#pointsstep1
121
+ [offset]: https://github.com/samizdatco/skia-canvas#offsetdx-dy
122
+ [transform]: https://github.com/samizdatco/skia-canvas#transformmatrix-or-transforma-b-c-d-e-f
123
+
124
+ [interpolate]: https://github.com/samizdatco/skia-canvas#interpolateotherpath-weight
125
+ [jitter]: https://github.com/samizdatco/skia-canvas#jittersegmentlength-amount-seed0
126
+ [round]: https://github.com/samizdatco/skia-canvas#roundradius
127
+ [simplify]: https://github.com/samizdatco/skia-canvas#simplify
128
+ [trim]: https://github.com/samizdatco/skia-canvas#trimstart-end-inverted
129
+
130
+
131
+ ## 📦 ⟩ [v0.9.22] ⟩ Jun 09, 2021
132
+
133
+ ### New Features
134
+
135
+ - Rasterization and file i/o are now handled asynchronously in a background thread. See the discussion of Canvas’s new [`async`](https://github.com/samizdatco/skia-canvas#async) property for details.
136
+ - Output files can now be generated at pixel-ratios > 1 for High-DPI screens. `SaveAs` and the other canvas output functions all accept an optional [`density`](https://github.com/samizdatco/skia-canvas#density) argument which is an integer ≥1 and will upscale the image accordingly. The density can also be passed using the `filename` argument by ending the name with an ‘@’ suffix like `some-image@2x.png`.
137
+ - SVG exports can optionally convert text to paths by setting the [`outline`](https://github.com/samizdatco/skia-canvas#outline) argument to `true`.
138
+
139
+ ### Breaking Changes
140
+
141
+ - The canvas functions dealing with rasterization (`toBuffer`, `toDataURL`, `png`, `jpg`, `pdf`, and `svg`) and file i/o (`saveAs`) are now asynchronous and return `Promise` objects. The old, synchronous behavior is still available on a canvas-by-canvas basis by setting its `async` property to `false`.
142
+ - The optional `quality` argument accepted by the output methods is now a float in the range 0–1 rather than an integer from 0–100. This is consistent with the [encoderOptions](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL) arg in the spec. Quality now defaults to 0.92 (again, as per the spec) rather than lossless.
143
+
144
+ ### Bugfixes
145
+
146
+ - `measureText` was reporting zero when asked to measure a string that was entirely made of whitespace. This is still the case for ‘blank‘ lines when `textWrap` is set to `true` but in the default, single-line mode the metrics will now report the width of the whitespace.
147
+ - Changed the way text rendering was staged so that SVG exports didn’t *entirely omit(!)* text from their output. As a result, `Context2D`s now use an external `Typesetter` struct to manage layout and rendering.
148
+
149
+
150
+ ## 📦 ⟩ [v0.9.21] ⟩ May 22, 2021
151
+
152
+ ### New Features
153
+ - Now runs on Windows and Apple Silicon Macs.
154
+ - Precompiled binaries support Node 10, 12, 14+.
155
+ - Image objects can be initialized from PNG, JPEG, GIF, BMP, or ICO data.
156
+ - Path2D objects can now be combined using [boolean operators](https://github.com/samizdatco/skia-canvas/#complement-difference-intersect-union-and-xor) and can measure their own [bounding boxes](https://github.com/samizdatco/skia-canvas/#bounds).
157
+ - Context objects now support [`createConicGradient()`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createConicGradient).
158
+ - Image objects now return a promise from their [`decode()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode) method allowing for async loading without the [`loadImage`](https://github.com/samizdatco/skia-canvas/#loadimage) helper.
159
+
160
+ ### Bugfixes
161
+ - Calling `drawImage` with a `Canvas` object as the argument now uses a Skia `Pict` rather than a `Drawable` as the interchange format, meaning it can actually respect the canvas's current `globalAlpha` and `globalCompositeOperation` state (fixed #6).
162
+ - Improved some spurious error messages when trying to generate a graphics file from a canvas whose width and/or height was set to zero (fixed #5).
163
+ - `CanvasPattern`s now respect the `imageSmoothingEnabled` setting
164
+ - The `counterclockwise` arg to `ellipse` and `arc` is now correctly treated as optional.
165
+
166
+ ### Misc. Improvements
167
+ - Made the `console.log` representations of the canvas-related objects friendlier.
168
+ - Added new test suites for `Path2D`, `Image`, and `Canvas`’s format support.
169
+ - Created [workflows](https://github.com/samizdatco/skia-canvas/tree/master/.github/workflows) to automate precompiled binary builds, testing, and npm package updating.
170
+
171
+
172
+ ## 📦 ⟩ [v0.9.20] ⟩ Mar 27, 2021
173
+
174
+ ### Bugfixes
175
+ - The `loadImage` helper can now handle `Buffer` arguments
176
+
177
+ ### Misc. Improvements
178
+ - Improved documentation of compilation steps and use of line height with `ctx.font`
179
+
180
+
181
+ ## 📦 ⟩ [v0.9.19] ⟩ Aug 30, 2020
182
+
183
+ **Initial public release** 🎉
184
+
185
+ [unreleased]: https://github.com/samizdatco/skia-canvas/compare/v0.9.28...HEAD
186
+ [v0.9.28]: https://github.com/samizdatco/skia-canvas/compare/v0.9.27...v0.9.28
187
+ [v0.9.27]: https://github.com/samizdatco/skia-canvas/compare/v0.9.26...v0.9.27
188
+ [v0.9.26]: https://github.com/samizdatco/skia-canvas/compare/v0.9.25...v0.9.26
189
+ [v0.9.25]: https://github.com/samizdatco/skia-canvas/compare/v0.9.24...v0.9.25
190
+ [v0.9.24]: https://github.com/samizdatco/skia-canvas/compare/v0.9.23...v0.9.24
191
+ [v0.9.23]: https://github.com/samizdatco/skia-canvas/compare/v0.9.22...v0.9.23
192
+ [v0.9.22]: https://github.com/samizdatco/skia-canvas/compare/v0.9.21...v0.9.22
193
+ [v0.9.21]: https://github.com/samizdatco/skia-canvas/compare/v0.9.20...v0.9.21
194
+ [v0.9.20]: https://github.com/samizdatco/skia-canvas/compare/v0.9.19...v0.9.20
195
+ [v0.9.19]: https://github.com/samizdatco/skia-canvas/compare/v0.9.15...v0.9.19
package/README.md CHANGED
@@ -9,9 +9,10 @@ In particular, Skia Canvas:
9
9
  - is fast and compact since all the heavy lifting is done by native code written in Rust and C++
10
10
  - can generate output in both raster (JPEG & PNG) and vector (PDF & SVG) image formats
11
11
  - can save images to [files][saveAs], return them as [Buffers][toBuffer], or encode [dataURL][toDataURL_ext] strings
12
- - uses native threads and [EventQueues](https://docs.rs/neon/0.8.3-napi/neon/event/struct.EventQueue.html) for asynchronous rendering and file I/O
12
+ - uses native threads and [channels](https://docs.rs/neon/0.9.0/neon/event/struct.Channel.html) for asynchronous rendering and file I/O
13
13
  - can create [multiple ‘pages’][newPage] on a given canvas and then [output][saveAs] them as a single, multi-page PDF or an image-sequence saved to multiple files
14
14
  - can [simplify][p2d_simplify], [blunt][p2d_round], [combine][bool-ops], [excerpt][p2d_trim], and [atomize][p2d_points] bézier paths using [efficient](https://www.youtube.com/watch?v=OmfliNQsk88) boolean operations or point-by-point [interpolation][p2d_interpolate]
15
+ - can apply [3D perspective][createProjection()] transformations in addition to [scaling][scale()], [rotation][rotate()], and [translation][translate()]
15
16
  - can fill shapes with vector-based [Textures][createTexture()] in addition to bitmap-based [Patterns][createPattern()] and supports line-drawing with custom [markers][lineDashMarker]
16
17
  - fully supports the [CSS filter effects][filter] image processing operators
17
18
  - offers rich typographic control including:
@@ -24,45 +25,6 @@ In particular, Skia Canvas:
24
25
  - use of non-system fonts [loaded](#usefamilyname-fontpaths) from local files
25
26
 
26
27
 
27
- ### Basic Usage
28
- ```js
29
- const {Canvas, loadImage} = require('skia-canvas'),
30
- rand = n => Math.floor(n * Math.random());
31
-
32
- let canvas = new Canvas(600, 600),
33
- ctx = canvas.getContext("2d"),
34
- {width, height} = canvas;
35
-
36
- // draw a sea of blurred dots filling the canvas
37
- ctx.filter = 'blur(12px) hue-rotate(20deg)'
38
- for (let i=0; i<800; i++){
39
- ctx.fillStyle = `hsl(${rand(40)}deg, 80%, 50%)`
40
- ctx.beginPath()
41
- ctx.arc(rand(width), rand(height), rand(20)+5, 0, 2*Math.PI)
42
- ctx.fill()
43
- }
44
-
45
- // mask all of the dots that don't overlap with the text
46
- ctx.filter = 'none'
47
- ctx.globalCompositeOperation = 'destination-in'
48
- ctx.font='italic 480px Times, DejaVu Serif'
49
- ctx.textAlign = 'center'
50
- ctx.textBaseline = 'top'
51
- ctx.fillText('¶', width/2, 0)
52
-
53
- // draw a background behind the clipped text
54
- ctx.globalCompositeOperation = 'destination-over'
55
- ctx.fillStyle = '#182927'
56
- ctx.fillRect(0,0, width,height)
57
-
58
- // save the graphic...
59
- canvas.saveAs("pilcrow.png")
60
- // ...or use a shorthand for canvas.toBuffer("png")
61
- fs.writeFileSync("pilcrow.png", canvas.png)
62
- // ...or embed it in a string
63
- console.log(`<img src="${canvas.toDataURL("png")}">`)
64
- ```
65
-
66
28
  ## Installation
67
29
 
68
30
  If you’re running on a supported platform, installation should be as simple as:
@@ -72,13 +34,6 @@ $ npm install skia-canvas
72
34
 
73
35
  This will download a pre-compiled library from the project’s most recent [release](https://github.com/samizdatco/skia-canvas/releases).
74
36
 
75
- ### Dependencies
76
-
77
- Nearly everything you need is statically linked into the library.
78
-
79
- A notable exception is the [Fontconfig](https://www.freedesktop.org/wiki/Software/fontconfig/) library (and its associated [FreeType](https://www.freetype.org) renderer) which must be installed separately if you’re running on Linux.
80
-
81
-
82
37
  ### Platform Support
83
38
 
84
39
  The underlying Rust library uses [N-API](https://nodejs.org/api/n-api.html) v6 which allows it to run on Node.js versions:
@@ -88,13 +43,18 @@ The underlying Rust library uses [N-API](https://nodejs.org/api/n-api.html) v6 w
88
43
 
89
44
  Pre-compiled binaries are available for:
90
45
 
91
- - Linux (x86)
92
- - macOS (x86 & Apple silicon)
93
- - Windows (x86)
46
+ - Linux (x64, arm64, & armhf)
47
+ - macOS (x64 & Apple silicon)
48
+ - Windows (x64)
49
+
50
+ Nearly everything you need is statically linked into the library. A notable exception is the [Fontconfig](https://www.freedesktop.org/wiki/Software/fontconfig/) library which must be installed separately if you’re running on Linux.
51
+
94
52
 
95
53
  ### Running in Docker
96
54
 
97
- The library is compatible with Linux systems using glibc 2.24 or later. Currently the `rust-skia` library [will not compile](https://github.com/rust-skia/rust-skia/issues/356) against the [musl](https://musl.libc.org) library used by Alpine Linux—though this may change in the future. For now, if you are setting up a [Dockerfile](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/) that uses [`node`](https://hub.docker.com/_/node) as its basis, you’ll want to set your `FROM` image to one of the (Debian-derived) defaults like `node:16`, `node:14`, `node:12`, `node:buster`, `node:stretch`, or simply:
55
+ The library is compatible with Linux systems using [glibc](https://www.gnu.org/software/libc/) 2.24 or later as well as Alpine Linux (x64) and the [musl](https://musl.libc.org) C library it favors. In both cases, Fontconfig must be installed on the system for `skia-canvas` to operate correctly.
56
+
57
+ If you are setting up a [Dockerfile](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/) that uses [`node`](https://hub.docker.com/_/node) as its basis, the simplest approach is to set your `FROM` image to one of the (Debian-derived) defaults like `node:16`, `node:14`, `node:12`, `node:buster`, `node:stretch`, or simply:
98
58
  ```dockerfile
99
59
  FROM node
100
60
  ```
@@ -103,7 +63,14 @@ You can also use the ‘slim’ image if you manually install fontconfig:
103
63
 
104
64
  ```dockerfile
105
65
  FROM node:slim
106
- RUN apt-get update && apt-get install -y -q --no-install-recommends libfontconfig1
66
+ RUN apt-get update && apt-get install -y -q --no-install-recommends libfontconfig1
67
+ ```
68
+
69
+ If you wish to use Alpine as the underlying distribution, you can start with something along the lines of:
70
+
71
+ ```dockerfile
72
+ FROM node:alpine
73
+ RUN apk update && apk add fontconfig
107
74
  ```
108
75
 
109
76
  ### Compiling from Source
@@ -115,10 +82,57 @@ Start by installing:
115
82
  1. The [Rust compiler](https://www.rust-lang.org/tools/install) and cargo package manager using [`rustup`](https://rust-lang.github.io/rustup/)
116
83
  2. A C compiler toolchain like LLVM/Clang or MSVC
117
84
  3. Python 2.7 (used by Skia's [build process](https://skia.org/docs/user/build/))
118
- 4. On Linux: Fontconfig, OpenSSL, X11, and Mesa
85
+ 4. On Linux: Fontconfig and OpenSSL
119
86
 
120
87
  [Detailed instructions](https://github.com/rust-skia/rust-skia#building) for setting up these dependencies on different operating systems can be found in the ‘Building’ section of the Rust Skia documentation. Once all the necessary compilers and libraries are present, running `npm run build` will give you a usable library (after a fairly lengthy compilation process).
121
88
 
89
+ ## Example Usage
90
+ ```js
91
+ const {Canvas, loadImage} = require('skia-canvas'),
92
+ rand = n => Math.floor(n * Math.random()),
93
+ fs = require('fs')
94
+
95
+ let canvas = new Canvas(600, 600),
96
+ ctx = canvas.getContext("2d"),
97
+ {width, height} = canvas;
98
+
99
+ // draw a sea of blurred dots filling the canvas
100
+ ctx.filter = 'blur(12px) hue-rotate(20deg)'
101
+ for (let i=0; i<800; i++){
102
+ ctx.fillStyle = `hsl(${rand(40)}deg, 80%, 50%)`
103
+ ctx.beginPath()
104
+ ctx.arc(rand(width), rand(height), rand(20)+5, 0, 2*Math.PI)
105
+ ctx.fill()
106
+ }
107
+
108
+ // mask all of the dots that don't overlap with the text
109
+ ctx.filter = 'none'
110
+ ctx.globalCompositeOperation = 'destination-in'
111
+ ctx.font='italic 480px Times, DejaVu Serif'
112
+ ctx.textAlign = 'center'
113
+ ctx.textBaseline = 'top'
114
+ ctx.fillText('¶', width/2, 0)
115
+
116
+ // draw a background behind the clipped text
117
+ ctx.globalCompositeOperation = 'destination-over'
118
+ ctx.fillStyle = '#182927'
119
+ ctx.fillRect(0,0, width,height)
120
+
121
+ // render to files using a background thread
122
+ async function render(){
123
+ // save the graphic...
124
+ await canvas.saveAs("pilcrow.png")
125
+ // ...or use a shorthand for canvas.toBuffer("png")
126
+ let pngData = await canvas.png
127
+ // ...or embed it in a string
128
+ console.log(`<img src="${await canvas.toDataURL("png")}">`)
129
+ }
130
+ render()
131
+
132
+ // ...or save the file synchronously from the main thread
133
+ canvas.saveAsSync("pilcrow.png")
134
+ ```
135
+
122
136
 
123
137
 
124
138
  # API Documentation
@@ -149,22 +163,22 @@ The Canvas object is a stand-in for the HTML `<canvas>` element. It defines imag
149
163
 
150
164
  | Image Dimensions | Rendering Contexts | Output |
151
165
  | -- | -- | -- |
152
- | [**width**][canvas_width] | [**pages**][canvas_pages] ⚡ | [**async**][canvas_async] ⚡ |
166
+ | [**width**][canvas_width] | [**pages**][canvas_pages] ⚡ | ~~[**async**][canvas_async]~~ ⚡ |
153
167
  | [**height**][canvas_height] | [getContext()][getContext] | [**pdf**, **png**, **svg**, **jpg**][shorthands] ⚡ |
154
- | | [newPage()][newPage] ⚡ | [saveAs()][saveAs] ⚡ |
155
- | | | [toBuffer()][toBuffer] ⚡ |
156
- | | | [toDataURL()][toDataURL_mdn] [⚡][toDataURL_ext] |
168
+ | | [newPage()][newPage] ⚡ | [saveAs()][saveAs] / [saveAsSync()][saveAs] ⚡ |
169
+ | | | [toBuffer()][toBuffer] / [toBufferSync()][toBuffer] ⚡ |
170
+ | | | [toDataURL()][toDataURL_ext] / [toDataURLSync()][toDataURL_ext] ⚡ |
157
171
 
158
172
  [canvas_width]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/width
159
173
  [canvas_height]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/height
160
174
  [canvas_async]: #async
161
175
  [canvas_pages]: #pages
162
176
  [getContext]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
163
- [saveAs]: #saveasfilename-page-format-density1-quality092-outlinefalse
164
- [toBuffer]: #tobufferformat-page-density-quality-outline
177
+ [saveAs]: #saveasfilename-page-format-matte-density1-quality092-outlinefalse
178
+ [toBuffer]: #tobufferformat-page-matte-density-quality-outline
165
179
  [newPage]: #newpagewidth-height
166
180
  [toDataURL_mdn]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
167
- [toDataURL_ext]: #todataurlformat-page-density-quality-outline
181
+ [toDataURL_ext]: #todataurlformat-page-matte-density-quality-outline
168
182
  [shorthands]: #pdf-svg-jpg-and-png
169
183
 
170
184
  #### Creating new `Canvas` objects
@@ -176,21 +190,24 @@ let defaultCanvas = new Canvas() // without arguments, defaults to 300 × 150 px
176
190
  let squareCanvas = new Canvas(512, 512) // creates a 512 px square
177
191
  ```
178
192
 
179
- ##### PROPERTIES
180
-
181
- #### `.async`
193
+ #### Saving graphics to files, buffers, and strings
182
194
 
183
- When the canvas renders images and writes them to disk, it does so in a background thread so as not to block execution within your script. As a result you’ll generally want to deal with the canvas from within an `async` function and be sure to use the `await` keyword when accessing any of its output methods or shorthand properties:
195
+ When the canvas renders images and writes them to disk, it does so in a background thread so as not to block execution within your script. As a result you’ll generally want to deal with the canvas from within an `async` function and be sure to use the `await` keyword when accessing any of its output methods or shorthand properties (all of which return Promises):
184
196
  - [`saveAs()`][saveAs]
185
197
  - [`toBuffer()`][toBuffer]
186
198
  - [`toDataURL()`][toDataURL_ext]
187
199
  - [`.pdf`, `.svg`, `.jpg`, and `.png`][shorthands]
188
200
 
189
- In cases where this is not the desired behavior, you can switch these methods into a synchronous mode for a particular canvas by setting its `async` property to `false`. For instance, both of the example functions below will generate PNG & PDF from the canvas, though the first will be more efficient (particularly for parallel contexts like request-handlers in an HTTP server or batch exports):
190
- ```js
191
201
 
202
+ In cases where this is not the desired behavior, you can use the synchronous equivalents for the primary export functions. They accept identical arguments to their async versions but block execution and return their values synchronously rather than wrapped in Promises. Also note that the [shorthand properties][shorthands] do not have synchronous versions:
203
+ - [`saveAsSync()`][saveAs]
204
+ - [`toBufferSync()`][toBuffer]
205
+ - [`toDataURLSync()`][toDataURL_ext]
206
+
207
+ For instance, both of the example functions below will generate PNG & PDF from the canvas, though the first will be more efficient (particularly for parallel contexts like request-handlers in an HTTP server or batch exports):
208
+
209
+ ```js
192
210
  let canvas = new Canvas()
193
- console.log(canvas.async) // -> true by default
194
211
 
195
212
  async function normal(){
196
213
  let pngURL = await canvas.toDataURL("png")
@@ -198,14 +215,16 @@ async function normal(){
198
215
  }
199
216
 
200
217
  function synchronous(){
201
- canvas.async = false // switch into synchronous mode
202
- let pngURL = canvas.toDataURL("png")
203
- let pdfBuffer = canvas.pdf
218
+ let pngURL = canvas.toDataURLSync("png")
219
+ let pdfBuffer = canvas.toBufferSync("pdf")
204
220
  }
205
221
  ```
206
222
 
223
+ ##### PROPERTIES
207
224
 
225
+ #### ~~`.async`~~
208
226
 
227
+ **The async property has been deprecated** and will be removed in a future release. Use the [`saveAsSync()`][saveAs], [`toBufferSync()`][toBuffer], and [`toDataURLSync()`][toDataURL_ext] methods if the default, asynchronous versions aren't to your liking.
209
228
 
210
229
  #### `.pages`
211
230
 
@@ -234,6 +253,10 @@ An integer can optionally be placed between the braces to indicate the number of
234
253
  ##### page
235
254
  The optional `page` argument accepts an integer that allows for the individual selection of pages in a multi-page canvas. Note that page indexing starts with page 1 **not** 0. The page value can also be negative, counting from the end of the canvas’s `.pages` array. For instance, `.saveAs("currentPage.png", {page:-1})` is equivalent to omitting `page` since they both yield the canvas’s most recently added page.
236
255
 
256
+ ##### format
257
+
258
+ The image format to generate, specified either as a mime-type string or file extension. The `format` argument will take precedence over the type specified through the `filename` argument’s extension, but is primarily useful when generating a file whose name cannot end with an extension for other reasons.
259
+
237
260
  ##### matte
238
261
  The optional `matte` argument accepts a color-string specifying the background that should be drawn *behind* the canvas in the exported image. Any transparent portions of the image will be filled with the matte color.
239
262
 
@@ -251,11 +274,11 @@ The `quality` option is a number between 0 and 1.0 that controls the level of JP
251
274
  ##### outline
252
275
  When generating SVG output containing text, you have two options for how to handle the fonts that were used. By default, SVG files will contain `<text>` elements that refer to the fonts by name in the embedded stylesheet. This requires that viewers of the SVG have the same fonts available on their system (or accessible as webfonts). Setting the optional `outline` argument to `true` will trace all the letterforms and ‘burn’ them into the file as bézier paths. This will result in a much larger file (and one in which the original text strings will be unrecoverable), but it will be viewable regardless of the specifics of the system it’s displayed on.
253
276
 
254
- #### `toBuffer(format, {page, density, quality, outline})`
277
+ #### `toBuffer(format, {page, matte, density, quality, outline})`
255
278
 
256
279
  Node [`Buffer`][Buffer] objects containing various image formats can be created by passing either a format string like `"svg"` or a mime-type like `"image/svg+xml"`. An ‘@’ suffix can be added to the format string to specify a pixel-density (for instance, `"jpg@2x"`). The optional arguments behave the same as in the `saveAs` method.
257
280
 
258
- #### `toDataURL(format, {page, density, quality, outline})`
281
+ #### `toDataURL(format, {page, matte, density, quality, outline})`
259
282
 
260
283
  This method accepts the same arguments and behaves similarly to `.toBuffer`. However instead of returning a Buffer, it returns a string of the form `"data:<mime-type>;base64,<image-data>"` which can be used as a `src` attribute in `<img>` tags, embedded into CSS, etc.
261
284
 
@@ -265,17 +288,32 @@ This method accepts the same arguments and behaves similarly to `.toBuffer`. How
265
288
  Most of your interaction with the canvas will actually be directed toward its ‘rendering context’, a supporting object you can acquire by calling the canvas’s [getContext()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext) and [newPage()][newPage] methods.
266
289
 
267
290
 
268
- | Canvas State | Drawing | Pattern & Color | Line Style | Transform |
269
- |------------------------------------------|----------------------------------------------|---------------------------------------------------|-----------------------------------------|------------------------------------------|
270
- | [**canvas**][canvas_attr] ⧸[⚡](#canvas) | [clearRect()][clearRect()] | [**fillStyle**][fillStyle] | [**lineCap**][lineCap] | [**currentTransform**][currentTransform] |
271
- | [beginPath()][beginPath()] | [fillRect()][fillRect()] | [**strokeStyle**][strokeStyle] | [**lineDashFit** ⚡][lineDashFit] | [getTransform()][getTransform()] |
272
- | [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [setTransform()][setTransform()] |
273
- | [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [resetTransform()][resetTransform()] |
274
- | [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [transform()][transform()] |
275
- | [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [translate()][translate()] |
276
- | [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [rotate()][rotate()] |
277
- | | | | [getLineDash()][getLineDash()] | [scale()][scale()] |
278
- | | | | [setLineDash()][setLineDash()] | |
291
+ | Canvas State | Drawing | Pattern & Color | Line Style | Transform |
292
+ |------------------------------------------|----------------------------------------------|---------------------------------------------------|-----------------------------------------|---------------------------------------------|
293
+ | [**canvas**][canvas_attr] ⧸[⚡](#canvas) | [clearRect()][clearRect()] | [**fillStyle**][fillStyle] | [**lineCap**][lineCap] | [**currentTransform**][currentTransform] |
294
+ | [beginPath()][beginPath()] | [fillRect()][fillRect()] | [**strokeStyle**][strokeStyle] | [**lineDashFit** ⚡][lineDashFit] | [createProjection() ⚡][createProjection()] |
295
+ | [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [getTransform()][getTransform()] |
296
+ | [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [setTransform()][setTransform()] |
297
+ | [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [resetTransform()][resetTransform()] |
298
+ | [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [transform()][transform()] |
299
+ | [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [translate()][translate()] |
300
+ | | | | [getLineDash()][getLineDash()] | [rotate()][rotate()] |
301
+ | | | | [setLineDash()][setLineDash()] | [scale()][scale()] |
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
279
317
 
280
318
 
281
319
  | Bezier Paths | Typography | Images | Compositing Effects |
@@ -375,11 +413,76 @@ The `lineDashFit` attribute can be set to `"move"`, `"turn"`, or `"follow"` and
375
413
 
376
414
  Adds a line segment connecting the current point to (*x, y*) but curving toward the control point (*cpx, cpy*) along the way. The `weight` argument controls how close the curve will come to the control point. If the weight is `0`, the result will be a straight line from the current point to (*x, y*). With a weight of `1.0`, the function is equivalent to calling `quadraticCurveTo()`. Weights greater than `1.0` will pull the line segment ever closer to the control point.
377
415
 
416
+ #### `createProjection(quad, [basis])`
417
+
418
+ This method returns a [DOMMatrix][DOMMatrix] object which can be used to simulate perspective effects or other distortions in which the four corners of the canvas are mapped to an arbitrary quadrilateral (four sided polygon). The matrix must be passed to the context's [setTransform][setTransform()] method for it take effect.
419
+
420
+ ##### `quad`
421
+
422
+ The `quad` argument defines the **target** of the transformation. It specifies four points that establish where the four corners of the source coordinate space will be positioned within the viewport. If these points form a polygon other than a rectangle, lines drawn along the x & y axes of the source space will no longer be perpendicular—trapezoids allow for ‘vanishing point’ effects and parallelograms create ‘skew’.
423
+
424
+ The geometry of the quadrilateral should be described as an Array of either 8 or 4 numbers specifying an arbitrary polygon or rectangle respectively:
425
+
426
+ ```js
427
+ [x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
428
+ [left, top, right, bottom] // four edges of a rectangle
429
+
430
+ // internal arrays for grouping are also allowed
431
+ [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
432
+ ```
433
+
434
+ ##### `basis`
435
+
436
+ The optional `basis` argument defines the **source** quadrilateral whose corners will be mapped to the positions defined by `quad`. If no `basis` is specified, the canvas's bounding box will be used (i.e., the rectangle from ⟨`0`, `0`⟩ to ⟨`canvas.width`, `canvas.height`⟩). Note that drawing commands that go outside of the `basis` region may well be visible—it only establishes the geometry of the projection, not the [clipping][clip()] path.
437
+
438
+ The `basis` polygon can be described using 2, 4, or 8 numbers, using the canvas dimensions to fill in the unspecified coordinates:
439
+ ```js
440
+ [width, height] // rectangle from ⟨0, 0⟩ to ⟨width, height⟩
441
+ [left, top, right, bottom] // four edges of a rectangle
442
+ [x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
443
+ ```
444
+
445
+ ----
446
+
447
+ The projection matrix will apply to all types of drawing: shapes, images, and text. This example transforms a white box and red `"@"` character into a trapezoid bounded by the vertical midline of the canvas and its left and right edges. Since no `basis` argument is provided, it will default to using the current canvas bounds as the rectangle to be mapped onto that trapezoid.
448
+
449
+ ```js
450
+ let canvas = new Canvas(512, 512),
451
+ ctx = canvas.getContext("2d"),
452
+ {width:w, height:h} = canvas;
453
+ ctx.font = '900 480px Times'
454
+ ctx.textAlign = 'center'
455
+ ctx.fillStyle = '#aaa'
456
+ ctx.fillRect(0, 0, w, h)
457
+
458
+ let quad = [
459
+ w*.33, h/2, // upper left
460
+ w*.66, h/2, // upper right
461
+ w, h*.9, // bottom right
462
+ 0, h*.9, // bottom left
463
+ ]
464
+
465
+ let matrix = ctx.createProjection(quad) // use default basis
466
+ ctx.setTransform(matrix)
467
+
468
+ ctx.fillStyle = 'white'
469
+ ctx.fillRect(10, 10, w-20, h-20)
470
+
471
+ ctx.fillStyle = '#900'
472
+ ctx.fillText("@", w/2, h-40)
473
+
474
+ ```
475
+
476
+ The results below show the image generated when the `createProjection()` call is omitted entirely, called (as above) with just a `quad` argument, or called with two different values for the optional `basis` argument:
477
+
478
+ ![Paths and text with a perspective transform](/test/assets/path/projection@2x.png)
479
+
480
+
378
481
  #### `createTexture(spacing, {path, line, color, angle, offset=0})`
379
482
 
380
483
  The `createTexture()` method returns a `CanvasTexture` object that can be assigned to the context’s `strokeStyle` or `fillStyle` property. Similar to a `CanvasPattern`, a `CanvasTexture` defines a repeating pattern that will be drawn instead of a flat color, but textures define their content using *vectors* rather than bitmaps.
381
484
 
382
- Textures can be based on a user-provided Path2D object or will draw a stripe pattern of parallel lines if a path isn’t provided.
485
+ Textures can be based on a user-provided Path2D object or will draw a stripe pattern of parallel lines if a path isn’t provided.
383
486
 
384
487
  ##### `spacing`
385
488
 
@@ -816,6 +919,7 @@ Many thanks to the [`node-canvas`](https://github.com/Automattic/node-canvas) de
816
919
  [conicCurveTo]: #coniccurvetocpx-cpy-x-y-weight
817
920
  [outlineText()]: #outlinetextstr
818
921
  [createTexture()]: #createtexturespacing-path-line-color-angle-offset0
922
+ [createProjection()]: #createprojectionquad-basis
819
923
  [lineDashMarker]: #linedashmarker
820
924
  [lineDashFit]: #linedashfit
821
925