diff --git a/src/mol-gl/compute/util.ts b/src/mol-gl/compute/util.ts index 645d4b90146570e047a2a524ef4ea6039ee85132..b641a03889cafc0e90f283c5fa8035cd97aed44c 100644 --- a/src/mol-gl/compute/util.ts +++ b/src/mol-gl/compute/util.ts @@ -6,11 +6,12 @@ import { WebGLContext } from '../../mol-gl/webgl/context'; import { Texture } from '../../mol-gl/webgl/texture'; -import { printTextureImage } from '../../mol-gl/renderable/util'; +import { PrintImageOptions, printTextureImage } from '../../mol-gl/renderable/util'; import { defaults, ValueCell } from '../../mol-util'; import { ValueSpec, AttributeSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema'; import { Vec2 } from '../../mol-math/linear-algebra'; import { GLRenderingContext } from '../../mol-gl/webgl/compat'; +import { PixelData } from '../../mol-util/image'; export const QuadPositions = new Float32Array([ 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle @@ -41,7 +42,7 @@ function getArrayForTexture(gl: GLRenderingContext, texture: Texture, size: numb throw new Error('unknown/unsupported texture type'); } -export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, height?: number) { +export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, height?: number): PixelData { const { gl, resources } = ctx; width = defaults(width, texture.getWidth()); height = defaults(height, texture.getHeight()); @@ -55,6 +56,8 @@ export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, return { array, width, height }; } -export function printTexture(ctx: WebGLContext, texture: Texture, scale: number) { - printTextureImage(readTexture(ctx, texture), scale); +export function printTexture(ctx: WebGLContext, texture: Texture, options: Partial<PrintImageOptions> = {}) { + const pixelData = readTexture(ctx, texture); + PixelData.flipY(pixelData); + printTextureImage(pixelData, options); } \ No newline at end of file diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index 10354cec4d5319e53751544af3834c31277cfcac..c7317af09a52d2f3f88cd7c384aa0fcb7cb34655 100644 --- a/src/mol-gl/renderable/util.ts +++ b/src/mol-gl/renderable/util.ts @@ -39,7 +39,15 @@ export function createTextureImage<T extends Uint8Array | Float32Array>(n: numbe return { array, width, height }; } -export function printTextureImage(textureImage: TextureImage<any>, scale = 1) { +const DefaultPrintImageOptions = { + scale: 1, + pixelated: false, + id: 'molstar.debug.image' +}; +export type PrintImageOptions = typeof DefaultPrintImageOptions + +export function printTextureImage(textureImage: TextureImage<any>, options: Partial<PrintImageOptions> = {}) { + const { array, width, height } = textureImage; const itemSize = array.length / (width * height); const data = new Uint8ClampedArray(width * height * 4); @@ -54,32 +62,50 @@ export function printTextureImage(textureImage: TextureImage<any>, scale = 1) { } else { console.warn(`itemSize '${itemSize}' not supported`); } - return printImageData(new ImageData(data, width, height), scale); + return printImageData(new ImageData(data, width, height), options); } -export function printImageData(imageData: ImageData, scale = 1, pixelated = false) { - const canvas = document.createElement('canvas'); +let tmpCanvas: HTMLCanvasElement; +let tmpCanvasCtx: CanvasRenderingContext2D; +let tmpContainer: HTMLDivElement; + +export function printImageData(imageData: ImageData, options: Partial<PrintImageOptions> = {}) { + const o = { ...DefaultPrintImageOptions, ...options }; + const canvas = tmpCanvas || document.createElement('canvas'); + tmpCanvas = canvas; canvas.width = imageData.width; canvas.height = imageData.height; - const ctx = canvas.getContext('2d'); + const ctx = tmpCanvasCtx || canvas.getContext('2d'); + tmpCanvasCtx = ctx; if (!ctx) throw new Error('Could not create canvas 2d context'); ctx.putImageData(imageData, 0, 0); + + if (!tmpContainer) { + tmpContainer = document.createElement('div'); + tmpContainer.style.position = 'absolute'; + tmpContainer.style.bottom = '0px'; + tmpContainer.style.right = '0px'; + tmpContainer.style.border = 'solid orange'; + tmpContainer.style.pointerEvents = 'none'; + document.body.appendChild(tmpContainer); + } + canvas.toBlob(imgBlob => { - const objectURL = window.URL.createObjectURL(imgBlob); - const img = document.createElement('img'); + const objectURL = URL.createObjectURL(imgBlob); + const existingImg = document.getElementById(o.id) as HTMLImageElement; + const img = existingImg || document.createElement('img'); + img.id = o.id; img.src = objectURL; - img.style.width = imageData.width * scale + 'px'; - img.style.height = imageData.height * scale + 'px'; - if (pixelated) { + img.style.width = imageData.width * o.scale + 'px'; + img.style.height = imageData.height * o.scale + 'px'; + if (o.pixelated) { // not supported in Firefox and IE img.style.imageRendering = 'pixelated'; } img.style.position = 'relative'; - img.style.top = '0px'; - img.style.left = '0px'; img.style.border = 'solid grey'; img.style.pointerEvents = 'none'; - document.body.appendChild(img); + if (!existingImg) tmpContainer.appendChild(img); }, 'image/png'); } diff --git a/src/mol-util/image.ts b/src/mol-util/image.ts index f9d57f2934cf598a68d866668d99205e6f207c77..79a2c1a629c6ca6eb3a302f895c1371e0a535ad6 100644 --- a/src/mol-util/image.ts +++ b/src/mol-util/image.ts @@ -7,13 +7,13 @@ export { PixelData }; interface PixelData { - readonly array: Uint8Array + readonly array: Uint8Array | Float32Array readonly width: number readonly height: number } namespace PixelData { - export function create(array: Uint8Array, width: number, height: number): PixelData { + export function create(array: Uint8Array | Float32Array, width: number, height: number): PixelData { return { array, width, height }; } @@ -36,8 +36,9 @@ namespace PixelData { /** to undo pre-multiplied alpha */ export function divideByAlpha(pixelData: PixelData): PixelData { const { array } = pixelData; + const factor = (array instanceof Uint8Array) ? 255 : 1; for (let i = 0, il = array.length; i < il; i += 4) { - const a = array[i + 3] / 255; + const a = array[i + 3] / factor; array[i] /= a; array[i + 1] /= a; array[i + 2] /= a; diff --git a/src/tests/browser/font-atlas.ts b/src/tests/browser/font-atlas.ts index 26b847276ae37e7de65cf134233ae096466349cd..2ea65f5d8281d62f785ab4cc2f35eae4abc8a3e8 100644 --- a/src/tests/browser/font-atlas.ts +++ b/src/tests/browser/font-atlas.ts @@ -33,7 +33,7 @@ function test() { fontAtlas.get(String.fromCharCode(0x212B)); console.timeEnd('Angstrom Sign'); - printTextureImage(fontAtlas.texture, 0.5); + printTextureImage(fontAtlas.texture, { scale: 0.5 }); console.log(`${Object.keys(fontAtlas.mapped).length} chars prepared`); }