UNPKG

@luma.gl/engine

Version:

3D Engine Components for luma.gl

83 lines 3.24 kB
// luma.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors /** * Reads pixels from a Framebuffer or Texture object into an HTML Image * @todo - can we move this to @luma.gl/core? * @param source * @param options options passed to copyToDataUrl * @returns */ export function copyTextureToImage(source, options) { const dataUrl = copyTextureToDataUrl(source, options); const targetImage = options?.targetImage || new Image(); targetImage.src = dataUrl; return targetImage; } /** * Reads pixels from a Framebuffer or Texture object to a dataUrl * @todo - can we move this to @luma.gl/core? * @param source texture or framebuffer to read from * @param options */ export function copyTextureToDataUrl(source, options = {}) { const { sourceAttachment, targetMaxHeight = Number.MAX_SAFE_INTEGER } = options; let data = source.device.readPixelsToArrayWebGL(source, { sourceAttachment }); // Scale down let { width, height } = source; while (height > targetMaxHeight) { ({ data, width, height } = scalePixels({ data, width, height })); } // Flip to top down coordinate system flipRows({ data, width, height }); const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const context = canvas.getContext('2d'); if (!context) { throw new Error('Failed to create context'); } // Copy the pixels to a 2D canvas const imageData = context?.createImageData(width, height); imageData.data.set(data); context.putImageData(imageData, 0, 0); return canvas.toDataURL('image/png'); } // HELPERS /** * Flip rows (can be used on arrays returned from `Framebuffer.readPixels`) * https: *stackoverflow.com/questions/41969562/ * how-can-i-flip-the-result-of-webglrenderingcontext-readpixels * @param param0 */ export function flipRows(options) { const { data, width, height, bytesPerPixel = 4, temp } = options; const bytesPerRow = width * bytesPerPixel; // make a temp buffer to hold one row const tempBuffer = temp || new Uint8Array(bytesPerRow); for (let y = 0; y < height / 2; ++y) { const topOffset = y * bytesPerRow; const bottomOffset = (height - y - 1) * bytesPerRow; // make copy of a row on the top half tempBuffer.set(data.subarray(topOffset, topOffset + bytesPerRow)); // copy a row from the bottom half to the top data.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); // copy the copy of the top half row to the bottom half data.set(tempBuffer, bottomOffset); } } export function scalePixels(options) { const { data, width, height } = options; const newWidth = Math.round(width / 2); const newHeight = Math.round(height / 2); const newData = new Uint8Array(newWidth * newHeight * 4); for (let y = 0; y < newHeight; y++) { for (let x = 0; x < newWidth; x++) { for (let c = 0; c < 4; c++) { newData[(y * newWidth + x) * 4 + c] = data[(y * 2 * width + x * 2) * 4 + c]; } } } return { data: newData, width: newWidth, height: newHeight }; } //# sourceMappingURL=copy-texture-to-image.js.map