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

Add support for data url loading #506

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions packages/loader-base/src/SpineLoaderAbstract.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { ISkeletonData, ISkeletonParser, TextureAtlas } from '@pixi-spine/base';
import { AssetExtension, checkExtension, LoadAsset, Loader, LoaderParserPriority } from '@pixi/assets';
import { AssetExtension, checkDataUrl, checkExtension, LoadAsset, Loader, LoaderParserPriority } from '@pixi/assets';
import { BaseTexture, extensions, ExtensionType, settings, Texture, utils } from '@pixi/core';
import { makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject } from './atlasLoader';

type SPINEJSON = any;
type SPINEBINARY = ArrayBuffer;

const validJSONExtension = '.json';

const validJSONMIME = 'application/json';

const validAtlasMIMEs = ['application/octet-stream', 'text/plain'];

const validImageMIMEs = ['image/jpeg', 'image/png'];

function isJson(resource: unknown): resource is SPINEJSON {
return resource.hasOwnProperty('bones');
}
Expand Down Expand Up @@ -55,7 +63,7 @@ export abstract class SpineLoaderAbstract<SKD extends ISkeletonData> {

// #region Parsing spine data
testParse(asset: unknown, options: LoadAsset): Promise<boolean> {
const isJsonSpineModel = checkExtension(options.src, '.json') && isJson(asset);
const isJsonSpineModel = checkDataUrl(options.src, validJSONMIME) || (checkExtension(options.src, validJSONExtension) && isJson(asset));
const isBinarySpineModel = checkExtension(options.src, '.skel') && isBuffer(asset);

// From 6.x loader. If the atlas is strictly false we bail
Expand All @@ -73,7 +81,7 @@ export abstract class SpineLoaderAbstract<SKD extends ISkeletonData> {
basePath += '/';
}

const isJsonSpineModel = checkExtension(loadAsset.src, '.json') && isJson(asset);
const isJsonSpineModel = checkDataUrl(loadAsset.src, validJSONMIME) || (checkExtension(loadAsset.src, validJSONExtension) && isJson(asset));
// const isBinarySpineModel = fileExt === 'slel' && isBuffer(asset);

let parser: ISkeletonParser = null;
Expand Down Expand Up @@ -101,21 +109,33 @@ export abstract class SpineLoaderAbstract<SKD extends ISkeletonData> {
}

// if for some odd reason, you dumped the text information of the atlas into the metadata...
const textAtlas = metadata.atlasRawData;
let textAtlas = metadata.atlasRawData;

// Maybe you passed a data URL to instead of a path
const isSpineAtlasFileURL = checkDataUrl(metadata.spineAtlasFile, validAtlasMIMEs);

// If it's an URL then decode it and assign it to textAtlas
if (isSpineAtlasFileURL) {
textAtlas = atob(metadata.spineAtlasFile.split(',')[1]);
}
if (textAtlas) {
let auxResolve = null;
let auxReject = null;
const atlasPromise = new Promise<TextureAtlas>((resolve, reject) => {
auxResolve = resolve;
auxReject = reject;
});
const atlas = new TextureAtlas(textAtlas, makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject(loader, basePath, metadata.imageMetadata), (newAtlas) => {
if (!newAtlas) {
auxReject('Something went terribly wrong loading a spine .atlas file\nMost likely your texture failed to load.');
const imageURL = typeof metadata.image === 'string' && checkDataUrl(metadata.image, validImageMIMEs) ? metadata.image : null;
const atlas = new TextureAtlas(
textAtlas,
makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject(loader, basePath, metadata.imageMetadata, imageURL),
(newAtlas) => {
if (!newAtlas) {
auxReject('Something went terribly wrong loading a spine .atlas file\nMost likely your texture failed to load.');
}
auxResolve(atlas);
}
auxResolve(atlas);
});
);
const textureAtlas = await atlasPromise;

return spineAdapter.parseData(parser, textureAtlas, dataToParse);
Expand Down Expand Up @@ -168,7 +188,7 @@ export interface ISpineMetadata {
spineAtlas?: Partial<TextureAtlas>;
// If you are going to download an .atlas file, you can specify an alias here for cache/future lookup
spineAtlasAlias?: string[];
// If you want to use a custom .atlas file, you can specify the path here. **It must be a .atlas file or you need your own parser!**
// If you want to use a custom .atlas file or data URL, you can specify the path here. **It must be a .atlas file or you need your own parser!**
spineAtlasFile?: string;
// If for some reason, you have the raw text content of an .atlas file, and want to use it dump it here
atlasRawData?: string;
Expand All @@ -178,6 +198,6 @@ export interface ISpineMetadata {
imageMetadata?: any;
// If you already have atlas pages loaded as pixi textures and want to use that to create the atlas, you can pass them here
images?: Record<string, Texture | BaseTexture>;
// If your spine only uses one atlas page and you have it as a pixi texture, you can pass it here
image?: Texture | BaseTexture;
// If your spine only uses one atlas page and you have it as a pixi texture or data URL, you can pass it here
image?: Texture | BaseTexture | string;
}
4 changes: 2 additions & 2 deletions packages/loader-base/src/atlasLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineMet
* Ugly function to promisify the spine texture atlas loader function.
* @public
*/
export const makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject = (loader: Loader, atlasBasePath: string, imageMetadata: any) => {
export const makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject = (loader: Loader, atlasBasePath: string, imageMetadata: any, imageURL?:string) => {
return async (pageName: string, textureLoadedCallback: (tex: BaseTexture) => any): Promise<void> => {
// const url = utils.path.join(...atlasBasePath.split(utils.path.sep), pageName); // Broken in upstream

const url = utils.path.normalize([...atlasBasePath.split(utils.path.sep), pageName].join(utils.path.sep));

const texture = await loader.load<Texture>({ src: url, data: imageMetadata });
const texture = await loader.load<Texture>(imageURL? imageURL:{ src: url, data: imageMetadata });

textureLoadedCallback(texture.baseTexture);
};
Expand Down