From 6dea923406c052d5529a7a911d108a3f1a85bf42 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 14 Oct 2024 18:35:55 -0500 Subject: [PATCH] feat: [#150] Support SVG images closes: #150 --- sandbox/src/game.ts | 38 ++++++++++++++++++++++++++++++ src/engine/Graphics/ImageSource.ts | 31 +++++++++++++----------- src/engine/Graphics/Sprite.ts | 5 ++-- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/sandbox/src/game.ts b/sandbox/src/game.ts index 22e6e8600..a25b0d000 100644 --- a/sandbox/src/game.ts +++ b/sandbox/src/game.ts @@ -152,12 +152,50 @@ cards2.draw(game.graphicsContext, 0, 0); jump.volume = 0.3; +var svg = (tags: TemplateStringsArray) => tags[0]; + +var svgImage = ex.ImageSource.fromSvgString(svg` + + + +`); + +var svgActor = new ex.Actor({ + name: 'svg', + pos: ex.vec(200, 200) +}); +svgActor.graphics.add( + svgImage.toSprite({ + destSize: { + width: 100, + height: 100 + }, + sourceView: { + x: 400, + y: 0, + width: 400, + height: 400 + } + }) +); +game.add(svgActor); + var boot = new ex.Loader(); // var boot = new ex.Loader({ // fullscreenAfterLoad: true, // fullscreenContainer: document.getElementById('container') // }); // boot.suppressPlayButton = true; +boot.addResource(svgImage); boot.addResource(heartImageSource); boot.addResource(heartTex); boot.addResource(imageRun); diff --git a/src/engine/Graphics/ImageSource.ts b/src/engine/Graphics/ImageSource.ts index 54a8b6996..09a1375b0 100644 --- a/src/engine/Graphics/ImageSource.ts +++ b/src/engine/Graphics/ImageSource.ts @@ -1,11 +1,12 @@ import { Resource } from '../Resources/Resource'; -import { Sprite } from './Sprite'; +import { Sprite, SpriteOptions } from './Sprite'; import { Loadable } from '../Interfaces/Index'; import { Logger } from '../Util/Log'; import { ImageFiltering } from './Filtering'; import { Future } from '../Util/Future'; import { TextureLoader } from '../Graphics/Context/texture-loader'; import { ImageWrapping } from './Wrapping'; +import { GraphicOptions } from './Graphic'; export interface ImageSourceOptions { filtering?: ImageFiltering; @@ -75,19 +76,19 @@ export class ImageSource implements Loadable { /** * The path to the image, can also be a data url like 'data:image/' - * @param path {string} Path to the image resource relative from the HTML document hosting the game, or absolute + * @param pathOrBase64 {string} Path to the image resource relative from the HTML document hosting the game, or absolute * @param options */ - constructor(path: string, options?: ImageSourceOptions); + constructor(pathOrBase64: string, options?: ImageSourceOptions); /** * The path to the image, can also be a data url like 'data:image/' - * @param path {string} Path to the image resource relative from the HTML document hosting the game, or absolute + * @param pathOrBase64 {string} Path to the image resource relative from the HTML document hosting the game, or absolute * @param bustCache {boolean} Should excalibur add a cache busting querystring? * @param filtering {ImageFiltering} Optionally override the image filtering set by {@apilink EngineOptions.antialiasing} */ - constructor(path: string, bustCache: boolean, filtering?: ImageFiltering); - constructor(path: string, bustCacheOrOptions: boolean | ImageSourceOptions | undefined, filtering?: ImageFiltering) { - this.path = path; + constructor(pathOrBase64: string, bustCache: boolean, filtering?: ImageFiltering); + constructor(pathOrBase64: string, bustCacheOrOptions: boolean | ImageSourceOptions | undefined, filtering?: ImageFiltering) { + this.path = pathOrBase64; let bustCache: boolean | undefined = false; let wrapping: ImageWrapConfiguration | ImageWrapping | undefined; if (typeof bustCacheOrOptions === 'boolean') { @@ -95,7 +96,7 @@ export class ImageSource implements Loadable { } else { ({ filtering, wrapping, bustCache } = { ...bustCacheOrOptions }); } - this._resource = new Resource(path, 'blob', bustCache); + this._resource = new Resource(pathOrBase64, 'blob', bustCache); this.filtering = filtering ?? this.filtering; if (typeof wrapping === 'string') { this.wrapping = { @@ -105,9 +106,9 @@ export class ImageSource implements Loadable { } else { this.wrapping = wrapping ?? this.wrapping; } - if (path.endsWith('.gif')) { + if (pathOrBase64.endsWith('.gif')) { this._logger.warn( - `Use the ex.Gif type to load gifs, you may have mixed results with ${path} in ex.ImageSource. Fully supported: svg, jpg, bmp, and png` + `Use the ex.Gif type to load gifs, you may have mixed results with ${pathOrBase64} in ex.ImageSource. Fully supported: svg, jpg, bmp, and png` ); } } @@ -153,8 +154,10 @@ export class ImageSource implements Loadable { return imageSource; } - static fromSvgElement(image: SVGElement, options?: ImageSourceOptions) { - // TODO implement + static fromSvgString(svgSource: string, options?: ImageSourceOptions) { + const blob = new Blob([svgSource], { type: 'image/svg+xml' }); + const url = URL.createObjectURL(blob); + return new ImageSource(url, options); } /** @@ -222,8 +225,8 @@ export class ImageSource implements Loadable { /** * Build a sprite from this ImageSource */ - public toSprite(): Sprite { - return Sprite.from(this); + public toSprite(options?: Omit): Sprite { + return Sprite.from(this, options); } /** diff --git a/src/engine/Graphics/Sprite.ts b/src/engine/Graphics/Sprite.ts index 7a0c512c7..6d70dab7a 100644 --- a/src/engine/Graphics/Sprite.ts +++ b/src/engine/Graphics/Sprite.ts @@ -28,9 +28,10 @@ export class Sprite extends Graphic { public destSize: DestinationSize; private _dirty = true; - public static from(image: ImageSource): Sprite { + public static from(image: ImageSource, options?: Omit): Sprite { return new Sprite({ - image: image + image, + ...options }); }