Shopify / react-native-skia

High-performance React Native Graphics using Skia
https://shopify.github.io/react-native-skia
MIT License
6.64k stars 419 forks source link

How to handle SVG images on web? #2487

Open greenGizmo opened 2 weeks ago

greenGizmo commented 2 weeks ago

Description

Since ImageSVG is not supported on web and it is also not indented to be supported, what is the recommended way to handle SVG images on web with skia?

wcandillon commented 2 weeks ago

That's a great question.

We might be able to polyfill the behavior here. In the meantime, you can simply use Skia.Image.MakeImageFromNativeBuffer(YourSVGDOMElement); and this should work nicely.

On Mon, Jun 17, 2024 at 12:41 PM greenGizmo @.***> wrote:

Description

Since ImageSVG is not supported on web and it is also not indented to be supported, what is the recommended way to handle SVG images on web with skia?

— Reply to this email directly, view it on GitHub or unsubscribe. You are receiving this email because you are subscribed to this thread.

Triage notifications on the go with GitHub Mobile for iOS or Android.

greenGizmo commented 2 weeks ago

Thanks for the response. I will try this 😃.

green-osman commented 1 week ago

Could you please provide an example ? How can we access an svg file as a DOMELEMENT with "Skia.Image.MakeImageFromNativeBuffer(YourSVGDOMElement)"

the following was causing the error "invalid Native Buffer":

image
wcandillon commented 1 week ago

you need to load it as an invisible (display: none) svg in your dom tree

On Tue 18 Jun 2024 at 09:33, Osman Öztas @.***> wrote:

Could you please provide an example ? How can we access an svg file as a DOMELEMENT with "Skia.Image.MakeImageFromNativeBuffer(YourSVGDOMElement)"

the following was causing the error "invalid Native Buffer": image.png (view on web) https://github.com/Shopify/react-native-skia/assets/142877500/bce6a1d6-4e77-4cba-9f28-381984c9e292

— Reply to this email directly, view it on GitHub https://github.com/Shopify/react-native-skia/issues/2487#issuecomment-2175399181 or unsubscribe https://github.com/notifications/unsubscribe-auth/AACKXVU5A3TVKYOHWDBZFK3ZH7PFFBFKMF2HI4TJMJ2XIZLTSSBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDUOJ2WLJDOMFWWLLTXMF2GG2C7MFRXI2LWNF2HTAVFOZQWY5LFUVUXG43VMWSG4YLNMWVXI2DSMVQWIX3UPFYGLAVFOZQWY5LFVIZTKMRZGIYDEMBWGWSG4YLNMWUWQYLTL5WGCYTFNSWHG5LCNJSWG5C7OR4XAZNMJFZXG5LFINXW23LFNZ2KM5DPOBUWG44TQKSHI6LQMWVHEZLQN5ZWS5DPOJ42K5TBNR2WLKJUGI2TQNBVGMZDLAVEOR4XAZNFNFZXG5LFUV3GC3DVMWVDEMZVG4YDCMJRG43IFJDUPFYGLJLMMFRGK3FFOZQWY5LFVIZTKMRZGIYDEMBWGWTXI4TJM5TWK4VGMNZGKYLUMU . You are receiving this email because you commented on the thread.

Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub .

greenGizmo commented 1 week ago

I tried this, but it doesn't work. Also I get this Error: "Uncaught ReferenceError: VideoFrame is not defined"

  const parser = new DOMParser();
  const svg = parser.parseFromString(
    '<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /></svg>',
    'image/svg+xml',
  );
  svg.documentElement.setAttribute('id', 'test');
  svg.documentElement.setAttribute('style', '{display:none;}');
  document.body.appendChild(svg.documentElement);
  const el = document.getElementById('test');
  const skiaSVG = Skia.Image.MakeImageFromNativeBuffer(el);
wcandillon commented 1 week ago

it might be because VideoFrame is likely chrome only, thank you for catching this. in the meantime this should work on chrome right?

On Tue 18 Jun 2024 at 10:53, greenGizmo @.***> wrote:

I tried this, but it doesn't work. Also I get this Error: "Uncaught ReferenceError: VideoFrame is not defined"

const parser = new DOMParser(); const svg = parser.parseFromString( '', 'image/svg+xml', ); svg.documentElement.setAttribute('id', 'test'); svg.documentElement.setAttribute('style', '{display:none;}'); document.body.appendChild(svg.documentElement); const el = document.getElementById('test'); const skiaSVG = Skia.Image.MakeImageFromNativeBuffer(el);

— Reply to this email directly, view it on GitHub https://github.com/Shopify/react-native-skia/issues/2487#issuecomment-2175562086 or unsubscribe https://github.com/notifications/unsubscribe-auth/AACKXVRZNUGOOZTZO3V4M3DZH7YPJBFKMF2HI4TJMJ2XIZLTSSBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDUOJ2WLJDOMFWWLLTXMF2GG2C7MFRXI2LWNF2HTAVFOZQWY5LFUVUXG43VMWSG4YLNMWVXI2DSMVQWIX3UPFYGLAVFOZQWY5LFVIZTKMRZGIYDEMBWGWSG4YLNMWUWQYLTL5WGCYTFNSWHG5LCNJSWG5C7OR4XAZNMJFZXG5LFINXW23LFNZ2KM5DPOBUWG44TQKSHI6LQMWVHEZLQN5ZWS5DPOJ42K5TBNR2WLKJUGI2TQNBVGMZDLAVEOR4XAZNFNFZXG5LFUV3GC3DVMWVDEMZVG4YDCMJRG43IFJDUPFYGLJLMMFRGK3FFOZQWY5LFVIZTKMRZGIYDEMBWGWTXI4TJM5TWK4VGMNZGKYLUMU . You are receiving this email because you commented on the thread.

Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub .

greenGizmo commented 1 week ago

You are right, the VideoFrame Error does not appear in Chrome (I was using Firefox before). But now a new error appears: Error: Invalid NativeBuffer. So I guess my example is wrong?

wcandillon commented 1 week ago

I'm trying it right now, will reply soon :)

On Tue, Jun 18, 2024 at 11:12 AM greenGizmo @.***> wrote:

You are right, the VideoFrame Error does not appear in Chrome (I was using Firefox before). But now a new error appears: Error: Invalid NativeBuffer. So I guess my example is wrong?

— Reply to this email directly, view it on GitHub or unsubscribe. You are receiving this email because you commented on the thread.

Triage notifications on the go with GitHub Mobile for iOS or Android.

wcandillon commented 1 week ago

I manager to get it work and will ship it to RN Skia but right now I don't have access to do so. Here's how you can implement this:

  1. configure webpack so that require("./file.svg"); returns a url or the text content of the svg, not a react element.
  2. load the svg an html image: MakeFromString(str: string): SkSVG | null { const parser = new DOMParser(); const svgDoc = parser.parseFromString(str, "image/svg+xml"); const svgElement = svgDoc.documentElement;

const attrWidth = svgElement.getAttribute("width"); const attrHeight = svgElement.getAttribute("height"); let width = attrWidth ? parseFloat(attrWidth) : null; let height = attrHeight ? parseFloat(attrHeight) : null;

const svgDataUrl = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(str); // Create a new HTMLImageElement const img = new Image(); img.src = svgDataUrl;

// Optionally set styles or attributes on the image img.style.display = "none"; img.alt = "SVG Image"; if (!width || !height) { const viewBox = svgElement.getAttribute("viewBox"); if (viewBox) { const viewBoxValues = viewBox.split(" "); if (viewBoxValues.length === 4) { width = width || parseFloat(viewBoxValues[2]); height = height || parseFloat(viewBoxValues[3]); } } } if (width && height) { img.width = width; img.height = height; }

img.onerror = (e) => { console.error("SVG failed to load", e); }; document.body.appendChild(img); return new JsiSkSVG(this.CanvasKit, img); }

  1. draw the svg: drawSvg(svg: SkSVG, _width?: number, _height?: number) { const image = this.CanvasKit.MakeImageFromCanvasImageSource( (svg as JsiSkSVG).ref ); this.ref.drawImage(image, 0, 0); }

On Tue, Jun 18, 2024 at 11:50 AM William Candillon @.***> wrote:

I'm trying it right now, will reply soon :)

On Tue, Jun 18, 2024 at 11:12 AM greenGizmo @.***> wrote:

You are right, the VideoFrame Error does not appear in Chrome (I was using Firefox before). But now a new error appears: Error: Invalid NativeBuffer. So I guess my example is wrong?

— Reply to this email directly, view it on GitHub or unsubscribe. You are receiving this email because you commented on the thread.

Triage notifications on the go with GitHub Mobile for iOS or Android.

wcandillon commented 1 week ago

this is the proper diff/patch you can even use directly if you want: https://github.com/wcandillon/react-native-skia/pull/4

On Tue, Jun 18, 2024 at 1:30 PM William Candillon @.***> wrote:

I manager to get it work and will ship it to RN Skia but right now I don't have access to do so. Here's how you can implement this:

  1. configure webpack so that require("./file.svg"); returns a url or the text content of the svg, not a react element.
  2. load the svg an html image: MakeFromString(str: string): SkSVG | null { const parser = new DOMParser(); const svgDoc = parser.parseFromString(str, "image/svg+xml"); const svgElement = svgDoc.documentElement;

const attrWidth = svgElement.getAttribute("width"); const attrHeight = svgElement.getAttribute("height"); let width = attrWidth ? parseFloat(attrWidth) : null; let height = attrHeight ? parseFloat(attrHeight) : null;

const svgDataUrl = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(str); // Create a new HTMLImageElement const img = new Image(); img.src = svgDataUrl;

// Optionally set styles or attributes on the image img.style.display = "none"; img.alt = "SVG Image"; if (!width || !height) { const viewBox = svgElement.getAttribute("viewBox"); if (viewBox) { const viewBoxValues = viewBox.split(" "); if (viewBoxValues.length === 4) { width = width || parseFloat(viewBoxValues[2]); height = height || parseFloat(viewBoxValues[3]); } } } if (width && height) { img.width = width; img.height = height; }

img.onerror = (e) => { console.error("SVG failed to load", e); }; document.body.appendChild(img); return new JsiSkSVG(this.CanvasKit, img); }

  1. draw the svg: drawSvg(svg: SkSVG, _width?: number, _height?: number) { const image = this.CanvasKit.MakeImageFromCanvasImageSource( (svg as JsiSkSVG).ref ); this.ref.drawImage(image, 0, 0); }

On Tue, Jun 18, 2024 at 11:50 AM William Candillon @.***> wrote:

I'm trying it right now, will reply soon :)

On Tue, Jun 18, 2024 at 11:12 AM greenGizmo @.***> wrote:

You are right, the VideoFrame Error does not appear in Chrome (I was using Firefox before). But now a new error appears: Error: Invalid NativeBuffer. So I guess my example is wrong?

— Reply to this email directly, view it on GitHub or unsubscribe. You are receiving this email because you commented on the thread.

Triage notifications on the go with GitHub Mobile for iOS or Android.

greenGizmo commented 1 week ago

Many thanks for your support. We will try this out! And since your solution does not use MakeImageFromNativeBuffer this should also work in other browsers than Chrome, right?

wcandillon commented 1 week ago

correct but I also fixed the issue that made MakeImageFromNativeBuffer not work on old firefox versions

On Tue, Jun 18, 2024 at 2:20 PM greenGizmo @.***> wrote:

Many thanks for your support. We will try this out! And since your solution does not use MakeImageFromNativeBuffer this should also work in other browsers than Chrome, right?

— Reply to this email directly, view it on GitHub or unsubscribe. You are receiving this email because you commented on the thread.

Triage notifications on the go with GitHub Mobile for iOS or Android.