Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Applying an SVG filter to the canvas #157

Open
velara3 opened this issue Jan 25, 2024 · 8 comments
Open

Applying an SVG filter to the canvas #157

velara3 opened this issue Jan 25, 2024 · 8 comments

Comments

@velara3
Copy link

velara3 commented Jan 25, 2024

The documentation says, "Skia canvas supports the full set of CSS filter image processing operators"

In the examples I've seen applying a filter is as simple as setting the filter property:

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

ctx.filter = "blur(4px)";

The CSS filter documentation mentions the URL() filter:

A CSS url(). Takes an IRI pointing to an SVG filter element, which may be embedded in an external XML file.

How would I do the same in Skia Canvas?

Could it be embedded? Will it load an external file?

Embedded:

canvas.filter = url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3C/svg%3E");

External:

canvas.filter = url("filter.svg#hueRotate");

// filter.svg: 
<?xml version="1.0" standalone="no"?>
<svg width="1" height="1" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <filter id="hueRotate">
      <feColorMatrix type="hueRotate" values="270"/>
    </filter>
  </defs>
</svg>

I realize now I can probably test this. However, I didn't find any examples in the documentation.

@mpaperno
Copy link
Contributor

There is currently no support for any SVG features at all, nor is there anything to handle url() types. The filters currently parsed can be found in the code here, for what it's worth... which I think is all of them except url().

Skia itself must support it, but I have no idea about skia-safe (the intermediate library layer used by skia-canvas), or what would be involved in implementing it. Just being able to load SVGs natively would be a great start.

@velara3
Copy link
Author

velara3 commented Jan 27, 2024

It looks like Skia itself has SVG support. I couldn't find a library with the term skia safe but I see the import statements in the code and a few links to Rust Skia.

I don't know that I know how to implement it either. It would be great to have.

Basically, I'm trying to apply filters to canvas elements server side and the filters Skia Canvas support are the CSS filters (except URL()) but they are somewhat primitive if IIUC.

The URL() CSS filter would allow SVG filters which seem to offer much wider range.

Although, if it was possible, having a way to apply multiple filters in code might solve the issue. Something like:

var filters = [];
var dropShadow = new DropShadow(3,3,1, rgba(0,0,0,.5));
filters.push(dropShadow);
var blur = new Blur(3,6);
filters.push(blur);
var displacementMap = new DisplacementMap();
filters.push(displacementMap);
canvas.filters = filters;

@mpaperno
Copy link
Contributor

Yea, sorry, "Rust Skia" is what I meant (it's called "skia safe" in the Rust package manager for some reason). And from what I understand it support most, if not all, Skia features. But skia-canvas only implements a subset. There's PR #155 which would probably be a good place to start on updating.

Perhaps I misunderstand what you mean, but multiple filters can be added now like in the standard, by separating the filter functions with spaces in the ctx.filter = call, eg. ctx.filter = "contrast(1.4) sepia(1) drop-shadow(-9px 9px 3px #e81)".

Cheers,
-Max

@velara3
Copy link
Author

velara3 commented Jan 28, 2024

I'm aware of adding multiple filters inline. What I meant by adding multiple filters manually was to do it longhand in JavaScript code rather than by string values. So instead of:

ctx.filter = "contrast(1.4) sepia(1) drop-shadow(-9px 9px 3px #e81)";

something like this:

var filters = [];
var aFoundationalFilter = new FoundationalFilter();
aFoundationalFilter.matrix = new Matrix(0,0,1,0,0,1);
aFoundationalFilter.offsetX = 10;
aFoundationalFilter.offsetY = 10;
filters.push(aFoundationalFilter);
var compositeFilter = new CompositeFilter();
compositeFilter.valueA = 20;
compositeFilter.valueB = 20;
filters.push(compositeFilter);
ctx.applyFilters(filters);

I'd rather create the filter objects and set values on them if that makes sense. Some of my past experience in image data filters make sense and some areas I've still got more to learn.

@mpaperno
Copy link
Contributor

mpaperno commented Jan 28, 2024

Well there's no "filter object type" in standard JS that I know of, and the context filter property only takes a string, so.... to be more imperative about adding filters like you suggest, you'd need to add those features somehow. Even if there already was a applyFilters() convenience method, currently it would still just be parsing filter strings, presumably... so I'm not sure what the benefit would be.

You could have your filter objects output a string as a result, collect them into an array (eg. filters.push(compositeFilter.toString())), then simply ctx.filter = filters.join(' ').

That's actually almost exactly what I've done in some code where the app's users can add arbitrary canvas filters as image layers.

@mpaperno
Copy link
Contributor

Totally OT now, but if there was a built-in way to track added filters so that they can later be reset automatically, w/out having to specify the filter again with "no op" values (eg. blur(0)), that would be pretty slick. For example when one wants to apply filter(s) to a particular drawing layer/path within a larger composition. Can be added on top as a JS layer of course, but having that in the binary core would likely be a lot more efficient.

@velara3
Copy link
Author

velara3 commented Feb 19, 2024

Indeed. One would hope that there would be class references for all the HTML and SVG markup elements that exist. So that they could be applied and modified like you describe. I have to pass on this library for now but will check back periodically for this support. For now, I believe my only option is to try client side.

@velara3
Copy link
Author

velara3 commented Feb 20, 2024

If there's anything I can do to help push this along (besides writing the code - I'm unable to contribute at this moment) let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants