Skip to content

Commit 373822f

Browse files
matmenclaude
andcommitted
SVG: filters
renderSVG applies filters: url(#...) and the filter functions (blur, drop-shadow, brightness, contrast, grayscale, hue-rotate, invert, opacity, saturate, sepia). New src/render/filter.ts, ported from resvg: 8 bit premultiplied pixels like resvg's (its rounding, the linearRGB tables as the rounded curves), feGaussianBlur (box blur from sigma 2, IIR below), feOffset, feFlood, feComposite (arithmetic too), feMerge, feDropShadow, feColorMatrix, feComponentTransfer, feTile, feBlend (tiny-skia's 16 modes), feMorphology (separable, monotonic queue: linear time, 80 s -> 0.1 s for resvg's huge-radius test), feConvolveMatrix. svg.ts parses filters like usvg: regions and subregions in both units, attributes through href, inputs/results, color-interpolation-filters, invalid filters hide the element (invalid convolutions are transparent); filters with feImage, feTurbulence, feDisplacementMap or lighting are left out. An element with filters renders into a layer covering the filter regions in its scale (2 canvases of margin, like resvg), rotation and skew applied when drawn (bilinear). Scales are tiny-skia's get_scale (the rows' lengths), now for pattern tiles too. Filter attributes are plain numbers (10% isn't 0.1 there). resvg's filter tests: 300/408 (was 81; blur, offset, flood, composite, merge, color matrix, component transfer, blend, morphology, convolve all pass; the rest is mostly the unsupported primitives), copied to tests/svgs/resvg (1 MB); tests/svg.js takes 21 s instead of 5 s. Patterns 32/33, markers 61/64, images 17/56 unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lfv41PqHEGbU7SLg5onpG1
1 parent c3ccdf2 commit 373822f

606 files changed

Lines changed: 5369 additions & 13 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@
2626
loaded. Elements of embedded SVGs count towards the limit of 100000 elements rendered through references. Of
2727
resvg's 56 image tests, 17 pass the test suite's threshold, 37 without the frame around them, whose antialiasing
2828
differs (tiny-skia rounds edges to quarter pixels); most of the others load files.
29+
- `Image.renderSVG()` applies filters (`filter="url(#…)"` and the filter functions `blur()`, `drop-shadow()`,
30+
`brightness()`, `contrast()`, `grayscale()`, `hue-rotate()`, `invert()`, `opacity()`, `saturate()`, `sepia()`):
31+
feGaussianBlur, feOffset, feFlood, feComposite (with `arithmetic`), feMerge, feDropShadow, feColorMatrix,
32+
feComponentTransfer, feTile, feBlend (all 16 modes), feMorphology and feConvolveMatrix, with filter regions and
33+
subregions in both units, `color-interpolation-filters` (linear RGB by default), inputs and results, attributes
34+
through `href`. Like resvg (ported from it), filters run on 8 bit premultiplied pixels in the element's scale,
35+
rotation and skew applied afterwards; invalid filters hide the element. Filters with feImage, feTurbulence,
36+
feDisplacementMap or lighting aren't applied (the element is drawn without them). 300 of resvg's 408 filter tests
37+
pass, up from 81 (most others use the primitives that aren't supported).
2938
- Optimized lossy WebP animations are smaller: like libwebp's, they leave out pixels that changed by up to a tolerance
3039
from the quality (3 per channel at quality 85, 1 at 100), and make unchanged pixels transparent only in whole 8x8
3140
blocks. Making each unchanged pixel transparent gave speckled alpha masks where most pixels change a little

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ encode as GIF, APNG or WebP.
5454

5555
- [Text rendering](https://imagescript.matmen.dev/classes/Image.html#rendertext) with TrueType and OpenType fonts,
5656
in every script, with color emoji
57-
- [SVG rendering](https://imagescript.matmen.dev/classes/Image.html#rendersvg) (no text or filters)
57+
- [SVG rendering](https://imagescript.matmen.dev/classes/Image.html#rendersvg) (no text)
5858
- [Resizing](https://imagescript.matmen.dev/classes/Image.html#resize),
5959
[cropping](https://imagescript.matmen.dev/classes/Image.html#crop),
6060
[rotating](https://imagescript.matmen.dev/classes/Image.html#rotate),

‎THIRD_PARTY_NOTICES.md‎

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ following projects, under their own licenses. Each derived file says so in its h
2525
| [Pillow](https://github.com/python-pillow/Pillow) | `src/util/resample.ts` (filter coefficients, box, bilinear and bicubic filters) | MIT-CMU |
2626
| [HarfBuzz](https://github.com/harfbuzz/harfbuzz) | `src/render/buffer.ts`, `src/render/layout.ts`, `src/render/shape.ts`, `src/render/shapers/*` (OpenType shaping: buffer, GSUB/GPOS, normalization, fallback positioning, the script shapers and syllable grammars), `scripts/unicode.js` (shaping category tables) | Old MIT |
2727
| [Universal Shaping Engine data](https://github.com/harfbuzz/harfbuzz/tree/main/src/ms-use) by Microsoft | `scripts/ms-use` (category overrides, invalid vowel sequences), the tables generated from them in `src/render/unicode-data.ts` | MIT |
28-
| [resvg](https://github.com/linebender/resvg) by the Resvg Authors | `src/render/svg.ts` (how patterns are rendered and their reference cycles broken, following resvg and usvg; the markers' placement and orientation, ported from usvg; as many cubics per arc as kurbo makes for usvg; how images are fitted, clipped and sampled), `tests/svgs/resvg` (test cases with resvg's renderings) | MIT (or Apache-2.0) |
28+
| [resvg](https://github.com/linebender/resvg) by the Resvg Authors | `src/render/filter.ts` (the filter effects: regions, inputs, color spaces, box blur (based on [fastblur](https://github.com/fschutt/fastblur)), IIR blur, offset, flood, composite, merge, drop shadow, color matrix, component transfer, tile, morphology, convolution), `src/render/svg.ts` (the filters' attributes like usvg; how patterns are rendered and their reference cycles broken, following resvg and usvg; the markers' placement and orientation, ported from usvg; as many cubics per arc as kurbo makes for usvg; how images are fitted, clipped and sampled), `tests/svgs/resvg` (test cases with resvg's renderings) | MIT (or Apache-2.0) |
29+
| [tiny-skia](https://github.com/linebender/tiny-skia) by Yevhenii Reizner (from [Skia](https://skia.org) by Google) | `src/render/filter.ts` (feBlend's blend modes) | BSD-3-Clause |
30+
| [gaussianiir](http://www.getreuer.info/home/gaussianiir) by Pascal Getreuer | `src/render/filter.ts` (the IIR blur for small deviations, through resvg) | BSD-2-Clause (Simplified BSD) |
2931
| [Unicode Character Database](https://www.unicode.org/ucd/) | `src/render/unicode-data.ts` (character properties, generated by `scripts/unicode.js`) | Unicode License v3 |
3032

3133
## fflate, Fast CRC32 in JavaScript
@@ -520,3 +522,69 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
520522
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
521523
DEALINGS IN THE SOFTWARE.
522524
```
525+
526+
## tiny-skia
527+
528+
```
529+
Copyright (c) 2011 Google Inc. All rights reserved.
530+
Copyright (c) 2020 Yevhenii Reizner All rights reserved.
531+
532+
Redistribution and use in source and binary forms, with or without
533+
modification, are permitted provided that the following conditions are
534+
met:
535+
536+
* Redistributions of source code must retain the above copyright
537+
notice, this list of conditions and the following disclaimer.
538+
539+
* Redistributions in binary form must reproduce the above copyright
540+
notice, this list of conditions and the following disclaimer in
541+
the documentation and/or other materials provided with the
542+
distribution.
543+
544+
* Neither the name of the copyright holder nor the names of its
545+
contributors may be used to endorse or promote products derived
546+
from this software without specific prior written permission.
547+
548+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
549+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
550+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
551+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
552+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
553+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
554+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
555+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
556+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
557+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
558+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
559+
```
560+
561+
## gaussianiir
562+
563+
The IIR blur follows Pascal Getreuer's gaussianiir (through resvg), under the Simplified BSD License:
564+
565+
```
566+
Copyright (c) 2009, Pascal Getreuer
567+
All rights reserved.
568+
569+
Redistribution and use in source and binary forms, with or without
570+
modification, are permitted provided that the following conditions are
571+
met:
572+
573+
* Redistributions of source code must retain the above copyright
574+
notice, this list of conditions and the following disclaimer.
575+
* Redistributions in binary form must reproduce the above copyright
576+
notice, this list of conditions and the following disclaimer in
577+
the documentation and/or other materials provided with the distribution
578+
579+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
580+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
581+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
582+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
583+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
584+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
585+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
586+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
587+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
588+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
589+
POSSIBILITY OF SUCH DAMAGE.
590+
```

‎src/image.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2260,7 +2260,8 @@ export class Image {
22602260
/**
22612261
* Creates a new image from the given SVG
22622262
* Supports shapes, paths, transforms, use/symbol, CSS styles, gradients, patterns, strokes, markers, clip paths,
2263-
* masks, opacity and images embedded as data URIs (PNG, JPEG, GIF, WebP, SVG); text and filters are not rendered
2263+
* masks, opacity, filters (most primitives and the filter functions) and images embedded as data URIs (PNG, JPEG,
2264+
* GIF, WebP, SVG); text is not rendered
22642265
* @param svg The SVG content
22652266
* @param options See {@link SVGOptions}
22662267
* @return The rendered SVG graphic

0 commit comments

Comments
 (0)