Skip to content

Commit

Permalink
Support JPEG textures in the JS API.
Browse files Browse the repository at this point in the history
This restores the canvas-based decoding method that was removed in #214,
but only for JPEG images. We continue to decode PNG images in C because
canvas-decoding mucks with alpha and prevents us from reading 16-bit
PNG.
  • Loading branch information
prideout committed Dec 7, 2018
1 parent 71e9c13 commit f4f6fb7
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 17 deletions.
13 changes: 12 additions & 1 deletion web/filament-js/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Filament.loadClassExtensions = function() {

/// createTextureFromPng ::method:: Creates a 2D [Texture] from the raw contents of a PNG file.
/// buffer ::argument:: asset string, or Uint8Array, or [Buffer] with PNG file contents
/// options ::argument:: JavaScript object with optional `rgbm`, `noalpha`, and `nomips` keys.
/// options ::argument:: object with optional `srgb`, `rgbm`, `noalpha`, and `nomips` keys.
/// ::retval:: [Texture]
Filament.Engine.prototype.createTextureFromPng = function(buffer, options) {
buffer = getBufferDescriptor(buffer);
Expand All @@ -106,6 +106,17 @@ Filament.loadClassExtensions = function() {
return result;
};

/// createTextureFromJpeg ::method:: Creates a 2D [Texture] from a JPEG image.
/// image ::argument:: asset string or DOM Image that has already been loaded
/// options ::argument:: JavaScript object with optional `srgb` and `nomips` keys.
/// ::retval:: [Texture]
Filament.Engine.prototype.createTextureFromJpeg = function(image, options) {
if ('string' == typeof image || image instanceof String) {
image = Filament.assets[image];
}
return Filament._createTextureFromJpeg(image, this, options);
};

/// loadFilamesh ::method:: Consumes the contents of a filamesh file and creates a renderable.
/// buffer ::argument:: asset string, or Uint8Array, or [Buffer] with filamesh contents
/// definstance ::argument:: Optional default [MaterialInstance]
Expand Down
1 change: 1 addition & 0 deletions web/filament-js/filament.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export class Engine {
public createScene(): Scene;
public createSkyFromKtx(url: string): Skybox;
public createSwapChain(): SwapChain;
public createTextureFromJpeg(url: string): Texture;
public createTextureFromPng(url: string): Texture;
public createView(): View;
public destroySkybox(skybox: Skybox): void;
Expand Down
38 changes: 38 additions & 0 deletions web/filament-js/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,44 @@ Filament._createTextureFromPng = function(pngdata, engine, options) {
return tex;
};

Filament._createTextureFromJpeg = function(image, engine, options) {

options = options || {};
const srgb = !!options['srgb'];
const nomips = !!options['nomips'];

var context2d = document.createElement('canvas').getContext('2d');
context2d.canvas.width = image.width;
context2d.canvas.height = image.height;
context2d.width = image.width;
context2d.height = image.height;
context2d.globalCompositeOperation = 'copy';
context2d.drawImage(image, 0, 0);

var imgdata = context2d.getImageData(0, 0, image.width, image.height).data.buffer;
var decodedjpeg = new Uint8Array(imgdata);

const TF = Filament.Texture$InternalFormat;
const texformat = srgb ? TF.SRGB8_A8 : TF.RGBA8;
const pbformat = Filament.PixelDataFormat.RGBA;
const pbtype = Filament.PixelDataType.UBYTE;

const tex = Filament.Texture.Builder()
.width(image.width)
.height(image.height)
.levels(nomips ? 1 : 0xff)
.sampler(Filament.Texture$Sampler.SAMPLER_2D)
.format(texformat)
.build(engine);

const pixelbuffer = Filament.PixelBuffer(decodedjpeg, pbformat, pbtype);
tex.setImage(engine, 0, pixelbuffer);
if (!nomips) {
tex.generateMipmaps(engine);
}
return tex;
};

/// getSupportedFormats ::function:: Queries WebGL to check which compressed formats are supported.
/// ::retval:: object with boolean values and the following keys: s3tc, astc, etc
Filament.getSupportedFormats = function() {
Expand Down
44 changes: 28 additions & 16 deletions web/filament-js/wasmloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Filament.remainingInitializationTasks = 1;
/// be ready at initialization time.
///
/// When the callback is called, each downloaded asset is available in the `Filament.assets` global
/// object, which contains a mapping from URL's to Uint8Array objects.
/// object, which contains a mapping from URL's to Uint8Array objects and DOM images.
///
/// assets ::argument:: Array of strings containing URL's of required assets.
/// onready ::argument:: callback that gets invoked after all assets have been downloaded and the \
Expand All @@ -58,18 +58,10 @@ Filament.init = function(assets, onready) {
}

// Issue a fetch for each asset. After the last asset is downloaded, trigger the callback.
assets.forEach(function(name) {
fetch(name).then(function(response) {
if (!response.ok) {
throw new Error(name);
}
return response.arrayBuffer();
}).then(function(arrayBuffer) {
Filament.assets[name] = new Uint8Array(arrayBuffer);
if (--Filament.remainingInitializationTasks === 0) {
Filament.onReady();
}
});
Filament.fetch(assets, null, function(name) {
if (--Filament.remainingInitializationTasks == 0 && Filament.onReady) {
Filament.onReady();
}
});
};

Expand All @@ -85,18 +77,38 @@ Filament.postRun = function() {

/// fetch ::function:: Downloads assets and invokes a callback when done.
/// assets ::argument:: Array of strings containing URL's of required assets.
/// onready ::argument:: callback that gets invoked after all assets have been downloaded.
Filament.fetch = function(assets, onDone) {
/// onDone ::argument:: callback that gets invoked after all assets have been downloaded.
/// onFetch ::argument:: optional callback that's invoked after each asset is downloaded.
Filament.fetch = function(assets, onDone, onFetched) {
var remainingAssets = assets.length;
assets.forEach(function(name) {
const lower = name.toLowerCase();
if (lower.endsWith('.jpeg') || lower.endsWith('.jpg')) {
var img = new Image();
img.src = name;
img.decoding = 'async';
img.onload = function() {
Filament.assets[name] = img;
if (onFetched) {
onFetched(name);
}
if (--remainingAssets === 0 && onDone) {
onDone();
}
};
return;
}
fetch(name).then(function(response) {
if (!response.ok) {
throw new Error(name);
}
return response.arrayBuffer();
}).then(function(arrayBuffer) {
Filament.assets[name] = new Uint8Array(arrayBuffer);
if (--remainingAssets === 0) {
if (onFetched) {
onFetched(name);
}
if (--remainingAssets === 0 && onDone) {
onDone();
}
});
Expand Down

0 comments on commit f4f6fb7

Please sign in to comment.