| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
- module.exports = {
- vertex: "precision lowp float;\n\nattribute vec2 aPosition;\nattribute vec2 aLumaPosition;\nattribute vec2 aChromaPosition;\nvarying vec2 vLumaPosition;\nvarying vec2 vChromaPosition;\nvoid main() {\n gl_Position = vec4(aPosition, 0, 1);\n vLumaPosition = aLumaPosition;\n vChromaPosition = aChromaPosition;\n}\n",
- fragment: "// inspired by https://github.com/mbebenita/Broadway/blob/master/Player/canvas.js\n\nprecision lowp float;\n\nuniform sampler2D uTextureY;\nuniform sampler2D uTextureCb;\nuniform sampler2D uTextureCr;\nvarying vec2 vLumaPosition;\nvarying vec2 vChromaPosition;\nvoid main() {\n // Y, Cb, and Cr planes are uploaded as LUMINANCE textures.\n float fY = texture2D(uTextureY, vLumaPosition).x;\n float fCb = texture2D(uTextureCb, vChromaPosition).x;\n float fCr = texture2D(uTextureCr, vChromaPosition).x;\n\n // Premultipy the Y...\n float fYmul = fY * 1.1643828125;\n\n // And convert that to RGB!\n gl_FragColor = vec4(\n fYmul + 1.59602734375 * fCr - 0.87078515625,\n fYmul - 0.39176171875 * fCb - 0.81296875 * fCr + 0.52959375,\n fYmul + 2.017234375 * fCb - 1.081390625,\n 1\n );\n}\n",
- vertexStripe: "precision lowp float;\n\nattribute vec2 aPosition;\nattribute vec2 aTexturePosition;\nvarying vec2 vTexturePosition;\n\nvoid main() {\n gl_Position = vec4(aPosition, 0, 1);\n vTexturePosition = aTexturePosition;\n}\n",
- fragmentStripe: "// extra 'stripe' texture fiddling to work around IE 11's poor performance on gl.LUMINANCE and gl.ALPHA textures\n\nprecision lowp float;\n\nuniform sampler2D uStripe;\nuniform sampler2D uTexture;\nvarying vec2 vTexturePosition;\nvoid main() {\n // Y, Cb, and Cr planes are mapped into a pseudo-RGBA texture\n // so we can upload them without expanding the bytes on IE 11\n // which doesn't allow LUMINANCE or ALPHA textures\n // The stripe textures mark which channel to keep for each pixel.\n // Each texture extraction will contain the relevant value in one\n // channel only.\n\n float fLuminance = dot(\n texture2D(uStripe, vTexturePosition),\n texture2D(uTexture, vTexturePosition)\n );\n\n gl_FragColor = vec4(fLuminance, fLuminance, fLuminance, 1);\n}\n"
- };
- },{}],2:[function(require,module,exports){
- window.YUVBuffer = require('yuv-buffer')
- window.YUVCanvas = require('./../src/yuv-canvas.js')
- },{"./../src/yuv-canvas.js":9,"yuv-buffer":3}],3:[function(require,module,exports){
- /*
- Copyright (c) 2014-2016 Brion Vibber <brion@pobox.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- MPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- ONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /**
- * Represents metadata about a YUV frame format.
- * @typedef {Object} YUVFormat
- * @property {number} width - width of encoded frame in luma pixels
- * @property {number} height - height of encoded frame in luma pixels
- * @property {number} chromaWidth - width of encoded frame in chroma pixels
- * @property {number} chromaHeight - height of encoded frame in chroma pixels
- * @property {number} cropLeft - upper-left X coordinate of visible crop region, in luma pixels
- * @property {number} cropTop - upper-left Y coordinate of visible crop region, in luma pixels
- * @property {number} cropWidth - width of visible crop region, in luma pixels
- * @property {number} cropHeight - height of visible crop region, in luma pixels
- * @property {number} displayWidth - final display width of visible region, in luma pixels
- * @property {number} displayHeight - final display height of visible region, in luma pixels
- */
- /**
- * Represents underlying image data for a single luma or chroma plane.
- * Cannot be interpreted without the format data from a frame buffer.
- * @typedef {Object} YUVPlane
- * @property {Uint8Array} bytes - typed array containing image data bytes
- * @property {number} stride - byte distance between rows in data
- */
- /**
- * Represents a YUV image frame buffer, with enough format information
- * to interpret the data usefully. Buffer objects use generic objects
- * under the hood and can be transferred between worker threads using
- * the structured clone algorithm.
- *
- * @typedef {Object} YUVFrame
- * @property {YUVFormat} format
- * @property {YUVPlane} y
- * @property {YUVPlane} u
- * @property {YUVPlane} v
- */
- /**
- * Holder namespace for utility functions and constants related to
- * YUV frame and plane buffers.
- *
- * @namespace
- */
- var YUVBuffer = {
- /**
- * Validate a plane dimension
- * @param {number} dim - vertical or horizontal dimension
- * @throws exception on zero, negative, or non-integer value
- */
- validateDimension: function(dim) {
- if (dim <= 0 || dim !== (dim | 0)) {
- throw 'YUV plane dimensions must be a positive integer';
- }
- },
- /**
- * Validate a plane offset
- * @param {number} dim - vertical or horizontal dimension
- * @throws exception on negative or non-integer value
- */
- validateOffset: function(dim) {
- if (dim < 0 || dim !== (dim | 0)) {
- throw 'YUV plane offsets must be a non-negative integer';
- }
- },
- /**
- * Validate and fill out a YUVFormat object structure.
- *
- * At least width and height fields are required; other fields will be
- * derived if left missing or empty:
- * - chromaWidth and chromaHeight will be copied from width and height as for a 4:4:4 layout
- * - cropLeft and cropTop will be 0
- * - cropWidth and cropHeight will be set to whatever of the frame is visible after cropTop and cropLeft are applied
- * - displayWidth and displayHeight will be set to cropWidth and cropHeight.
- *
- * @param {YUVFormat} fields - input fields, must include width and height.
- * @returns {YUVFormat} - validated structure, with all derivable fields filled out.
- * @throws exception on invalid fields or missing width/height
- */
- format: function(fields) {
- var width = fields.width,
- height = fields.height,
- chromaWidth = fields.chromaWidth || width,
- chromaHeight = fields.chromaHeight || height,
- cropLeft = fields.cropLeft || 0,
- cropTop = fields.cropTop || 0,
- cropWidth = fields.cropWidth || width - cropLeft,
- cropHeight = fields.cropHeight || height - cropTop,
- displayWidth = fields.displayWidth || cropWidth,
- displayHeight = fields.displayHeight || cropHeight;
- this.validateDimension(width);
- this.validateDimension(height);
- this.validateDimension(chromaWidth);
- this.validateDimension(chromaHeight);
- this.validateOffset(cropLeft);
- this.validateOffset(cropTop);
- this.validateDimension(cropWidth);
- this.validateDimension(cropHeight);
- this.validateDimension(displayWidth);
- this.validateDimension(displayHeight);
- return {
- width: width,
- height: height,
- chromaWidth: chromaWidth,
- chromaHeight: chromaHeight,
- cropLeft: cropLeft,
- cropTop: cropTop,
- cropWidth: cropWidth,
- cropHeight: cropHeight,
- displayWidth: displayWidth,
- displayHeight: displayHeight
- };
- },
- /**
- * Allocate a new YUVPlane object of the given size.
- * @param {number} stride - byte distance between rows
- * @param {number} rows - number of rows to allocate
- * @returns {YUVPlane} - freshly allocated planar buffer
- */
- allocPlane: function(stride, rows) {
- YUVBuffer.validateDimension(stride);
- YUVBuffer.validateDimension(rows);
- return {
- bytes: new Uint8Array(stride * rows),
- stride: stride
- }
- },
- /**
- * Pick a suitable stride for a custom-allocated thingy
- * @param {number} width - width in bytes
- * @returns {number} - new width in bytes at least as large
- * @throws exception on invalid input width
- */
- suitableStride: function(width) {
- YUVBuffer.validateDimension(width);
- var alignment = 4,
- remainder = width % alignment;
- if (remainder == 0) {
- return width;
- } else {
- return width + (alignment - remainder);
- }
- },
- /**
- * Allocate or extract a YUVPlane object from given dimensions/source.
- * @param {number} width - width in pixels
- * @param {number} height - height in pixels
- * @param {Uint8Array} source - input byte array; optional (will create empty buffer if missing)
- * @param {number} stride - row length in bytes; optional (will create a default if missing)
- * @param {number} offset - offset into source array to extract; optional (will start at 0 if missing)
- * @returns {YUVPlane} - freshly allocated planar buffer
- */
- allocPlane: function(width, height, source, stride, offset) {
- var size, bytes;
- this.validateDimension(width);
- this.validateDimension(height);
- offset = offset || 0;
- stride = stride || this.suitableStride(width);
- this.validateDimension(stride);
- if (stride < width) {
- throw "Invalid input stride for YUV plane; must be larger than width";
- }
- size = stride * height;
- if (source) {
- if (source.length - offset < size) {
- throw "Invalid input buffer for YUV plane; must be large enough for stride times height";
- }
- bytes = source.slice(offset, offset + size);
- } else {
- bytes = new Uint8Array(size);
- stride = stride || this.suitableStride(width);
- }
- return {
- bytes: bytes,
- stride: stride
- };
- },
- /**
- * Allocate a new YUVPlane object big enough for a luma plane in the given format
- * @param {YUVFormat} format - target frame format
- * @param {Uint8Array} source - input byte array; optional (will create empty buffer if missing)
- * @param {number} stride - row length in bytes; optional (will create a default if missing)
- * @param {number} offset - offset into source array to extract; optional (will start at 0 if missing)
- * @returns {YUVPlane} - freshly allocated planar buffer
- */
- lumaPlane: function(format, source, stride, offset) {
- return this.allocPlane(format.width, format.height, source, stride, offset);
- },
- /**
- * Allocate a new YUVPlane object big enough for a chroma plane in the given format,
- * optionally copying data from an existing buffer.
- *
- * @param {YUVFormat} format - target frame format
- * @param {Uint8Array} source - input byte array; optional (will create empty buffer if missing)
- * @param {number} stride - row length in bytes; optional (will create a default if missing)
- * @param {number} offset - offset into source array to extract; optional (will start at 0 if missing)
- * @returns {YUVPlane} - freshly allocated planar buffer
- */
- chromaPlane: function(format, source, stride, offset) {
- return this.allocPlane(format.chromaWidth, format.chromaHeight, source, stride, offset);
- },
- /**
- * Allocate a new YUVFrame object big enough for the given format
- * @param {YUVFormat} format - target frame format
- * @param {YUVPlane} y - optional Y plane; if missing, fresh one will be allocated
- * @param {YUVPlane} u - optional U plane; if missing, fresh one will be allocated
- * @param {YUVPlane} v - optional V plane; if missing, fresh one will be allocated
- * @returns {YUVFrame} - freshly allocated frame buffer
- */
- frame: function(format, y, u, v) {
- y = y || this.lumaPlane(format);
- u = u || this.chromaPlane(format);
- v = v || this.chromaPlane(format);
- return {
- format: format,
- y: y,
- u: u,
- v: v
- }
- },
- /**
- * Duplicate a plane using new buffer memory.
- * @param {YUVPlane} plane - input plane to copy
- * @returns {YUVPlane} - freshly allocated and filled planar buffer
- */
- copyPlane: function(plane) {
- return {
- bytes: plane.bytes.slice(),
- stride: plane.stride
- };
- },
- /**
- * Duplicate a frame using new buffer memory.
- * @param {YUVFrame} frame - input frame to copyFrame
- * @returns {YUVFrame} - freshly allocated and filled frame buffer
- */
- copyFrame: function(frame) {
- return {
- format: frame.format,
- y: this.copyPlane(frame.y),
- u: this.copyPlane(frame.u),
- v: this.copyPlane(frame.v)
- }
- },
- /**
- * List the backing buffers for the frame's planes for transfer between
- * threads via Worker.postMessage.
- * @param {YUVFrame} frame - input frame
- * @returns {Array} - list of transferable objects
- */
- transferables: function(frame) {
- return [frame.y.bytes.buffer, frame.u.bytes.buffer, frame.v.bytes.buffer];
- }
- };
- module.exports = YUVBuffer;
- },{}],4:[function(require,module,exports){
- (function() {
- "use strict";
- /**
- * Create a YUVCanvas and attach it to an HTML5 canvas element.
- *
- * This will take over the drawing context of the canvas and may turn
- * it into a WebGL 3d canvas if possible. Do not attempt to use the
- * drawing context directly after this.
- *
- * @param {HTMLCanvasElement} canvas - HTML canvas element to attach to
- * @param {YUVCanvasOptions} options - map of options
- * @throws exception if WebGL requested but unavailable
- * @constructor
- * @abstract
- */
- function FrameSink(canvas, options) {
- throw new Error('abstract');
- }
- /**
- * Draw a single YUV frame on the underlying canvas, converting to RGB.
- * If necessary the canvas will be resized to the optimal pixel size
- * for the given buffer's format.
- *
- * @param {YUVBuffer} buffer - the YUV buffer to draw
- * @see {@link https://www.npmjs.com/package/yuv-buffer|yuv-buffer} for format
- */
- FrameSink.prototype.drawFrame = function(buffer) {
- throw new Error('abstract');
- };
- /**
- * Clear the canvas using appropriate underlying 2d or 3d context.
- */
- FrameSink.prototype.clear = function() {
- throw new Error('abstract');
- };
- module.exports = FrameSink;
- })();
- },{}],5:[function(require,module,exports){
- /*
- Copyright (c) 2014-2016 Brion Vibber <brion@pobox.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- MPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- (function() {
- "use strict";
- var FrameSink = require('./FrameSink.js'),
- YCbCr = require('./YCbCr.js');
- /**
- * @param {HTMLCanvasElement} canvas - HTML canvas eledment to attach to
- * @constructor
- */
- function SoftwareFrameSink(canvas) {
- var self = this,
- ctx = canvas.getContext('2d'),
- imageData = null,
- resampleCanvas = null,
- resampleContext = null;
- function initImageData(width, height) {
- imageData = ctx.createImageData(width, height);
- // Prefill the alpha to opaque
- var data = imageData.data,
- pixelCount = width * height * 4;
- for (var i = 0; i < pixelCount; i += 4) {
- data[i + 3] = 255;
- }
- }
- function initResampleCanvas(cropWidth, cropHeight) {
- resampleCanvas = document.createElement('canvas');
- resampleCanvas.width = cropWidth;
- resampleCanvas.height = cropHeight;
- resampleContext = resampleCanvas.getContext('2d');
- }
- /**
- * Actually draw a frame into the canvas.
- * @param {YUVFrame} buffer - YUV frame buffer object to draw
- */
- self.drawFrame = function drawFrame(buffer) {
- var format = buffer.format;
- if (canvas.width !== format.displayWidth || canvas.height !== format.displayHeight) {
- // Keep the canvas at the right size...
- canvas.width = format.displayWidth;
- canvas.height = format.displayHeight;
- }
- if (imageData === null ||
- imageData.width != format.width ||
- imageData.height != format.height) {
- initImageData(format.width, format.height);
- }
- // YUV -> RGB over the entire encoded frame
- YCbCr.convertYCbCr(buffer, imageData.data);
- var resample = (format.cropWidth != format.displayWidth || format.cropHeight != format.displayHeight);
- var drawContext;
- if (resample) {
- // hack for non-square aspect-ratio
- // putImageData doesn't resample, so we have to draw in two steps.
- if (!resampleCanvas) {
- initResampleCanvas(format.cropWidth, format.cropHeight);
- }
- drawContext = resampleContext;
- } else {
- drawContext = ctx;
- }
- // Draw cropped frame to either the final or temporary canvas
- drawContext.putImageData(imageData,
- -format.cropLeft, -format.cropTop, // must offset the offset
- format.cropLeft, format.cropTop,
- format.cropWidth, format.cropHeight);
- if (resample) {
- ctx.drawImage(resampleCanvas, 0, 0, format.displayWidth, format.displayHeight);
- }
- };
- self.clear = function() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- };
- return self;
- }
- SoftwareFrameSink.prototype = Object.create(FrameSink.prototype);
- module.exports = SoftwareFrameSink;
- })();
- },{"./FrameSink.js":4,"./YCbCr.js":7}],6:[function(require,module,exports){
- /*
- Copyright (c) 2014-2016 Brion Vibber <brion@pobox.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- MPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- (function() {
- "use strict";
- var FrameSink = require('./FrameSink.js'),
- shaders = require('../build/shaders.js');
- /**
- * Warning: canvas must not have been used for 2d drawing prior!
- *
- * @param {HTMLCanvasElement} canvas - HTML canvas element to attach to
- * @constructor
- */
- function WebGLFrameSink(canvas) {
- var self = this,
- gl = WebGLFrameSink.contextForCanvas(canvas),
- debug = false; // swap this to enable more error checks, which can slow down rendering
- if (gl === null) {
- throw new Error('WebGL unavailable');
- }
- // GL!
- function checkError() {
- if (debug) {
- err = gl.getError();
- if (err !== 0) {
- throw new Error("GL error " + err);
- }
- }
- }
- function compileShader(type, source) {
- var shader = gl.createShader(type);
- gl.shaderSource(shader, source);
- gl.compileShader(shader);
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
- var err = gl.getShaderInfoLog(shader);
- gl.deleteShader(shader);
- throw new Error('GL shader compilation for ' + type + ' failed: ' + err);
- }
- return shader;
- }
- var program,
- unpackProgram,
- err;
- // In the world of GL there are no rectangles.
- // There are only triangles.
- // THERE IS NO SPOON.
- var rectangle = new Float32Array([
- // First triangle (top left, clockwise)
- -1.0, -1.0,
- +1.0, -1.0,
- -1.0, +1.0,
- // Second triangle (bottom right, clockwise)
- -1.0, +1.0,
- +1.0, -1.0,
- +1.0, +1.0
- ]);
- var textures = {};
- var framebuffers = {};
- var stripes = {};
- var buf, positionLocation, unpackPositionLocation;
- var unpackTexturePositionBuffer, unpackTexturePositionLocation;
- var stripeLocation, unpackTextureLocation;
- var lumaPositionBuffer, lumaPositionLocation;
- var chromaPositionBuffer, chromaPositionLocation;
- function createOrReuseTexture(name) {
- if (!textures[name]) {
- textures[name] = gl.createTexture();
- }
- return textures[name];
- }
- function uploadTexture(name, width, height, data) {
- var texture = createOrReuseTexture(name);
- gl.activeTexture(gl.TEXTURE0);
- if (WebGLFrameSink.stripe) {
- var uploadTemp = !textures[name + '_temp'];
- var tempTexture = createOrReuseTexture(name + '_temp');
- gl.bindTexture(gl.TEXTURE_2D, tempTexture);
- if (uploadTemp) {
- // new texture
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.texImage2D(
- gl.TEXTURE_2D,
- 0, // mip level
- gl.RGBA, // internal format
- width / 4,
- height,
- 0, // border
- gl.RGBA, // format
- gl.UNSIGNED_BYTE, // type
- data // data!
- );
- } else {
- // update texture
- gl.texSubImage2D(
- gl.TEXTURE_2D,
- 0, // mip level
- 0, // x offset
- 0, // y offset
- width / 4,
- height,
- gl.RGBA, // format
- gl.UNSIGNED_BYTE, // type
- data // data!
- );
- }
- var stripeTexture = textures[name + '_stripe'];
- var uploadStripe = !stripeTexture;
- if (uploadStripe) {
- stripeTexture = createOrReuseTexture(name + '_stripe');
- }
- gl.bindTexture(gl.TEXTURE_2D, stripeTexture);
- if (uploadStripe) {
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.texImage2D(
- gl.TEXTURE_2D,
- 0, // mip level
- gl.RGBA, // internal format
- width,
- 1,
- 0, // border
- gl.RGBA, // format
- gl.UNSIGNED_BYTE, //type
- buildStripe(width, 1) // data!
- );
- }
- } else {
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texImage2D(
- gl.TEXTURE_2D,
- 0, // mip level
- gl.LUMINANCE, // internal format
- width,
- height,
- 0, // border
- gl.LUMINANCE, // format
- gl.UNSIGNED_BYTE, //type
- data // data!
- );
- }
- }
- function unpackTexture(name, width, height) {
- var texture = textures[name];
- // Upload to a temporary RGBA texture, then unpack it.
- // This is faster than CPU-side swizzling in ANGLE on Windows.
- gl.useProgram(unpackProgram);
- var fb = framebuffers[name];
- if (!fb) {
- // Create a framebuffer and an empty target size
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texImage2D(
- gl.TEXTURE_2D,
- 0, // mip level
- gl.RGBA, // internal format
- width,
- height,
- 0, // border
- gl.RGBA, // format
- gl.UNSIGNED_BYTE, //type
- null // data!
- );
- fb = framebuffers[name] = gl.createFramebuffer();
- }
- gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
- var tempTexture = textures[name + '_temp'];
- gl.activeTexture(gl.TEXTURE1);
- gl.bindTexture(gl.TEXTURE_2D, tempTexture);
- gl.uniform1i(unpackTextureLocation, 1);
- var stripeTexture = textures[name + '_stripe'];
- gl.activeTexture(gl.TEXTURE2);
- gl.bindTexture(gl.TEXTURE_2D, stripeTexture);
- gl.uniform1i(stripeLocation, 2);
- // Rectangle geometry
- gl.bindBuffer(gl.ARRAY_BUFFER, buf);
- gl.enableVertexAttribArray(positionLocation);
- gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
- // Set up the texture geometry...
- gl.bindBuffer(gl.ARRAY_BUFFER, unpackTexturePositionBuffer);
- gl.enableVertexAttribArray(unpackTexturePositionLocation);
- gl.vertexAttribPointer(unpackTexturePositionLocation, 2, gl.FLOAT, false, 0, 0);
- // Draw into the target texture...
- gl.viewport(0, 0, width, height);
- gl.drawArrays(gl.TRIANGLES, 0, rectangle.length / 2);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- }
- function attachTexture(name, register, index) {
- gl.activeTexture(register);
- gl.bindTexture(gl.TEXTURE_2D, textures[name]);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.uniform1i(gl.getUniformLocation(program, name), index);
- }
- function buildStripe(width) {
- if (stripes[width]) {
- return stripes[width];
- }
- var len = width,
- out = new Uint32Array(len);
- for (var i = 0; i < len; i += 4) {
- out[i ] = 0x000000ff;
- out[i + 1] = 0x0000ff00;
- out[i + 2] = 0x00ff0000;
- out[i + 3] = 0xff000000;
- }
- return stripes[width] = new Uint8Array(out.buffer);
- }
- function initProgram(vertexShaderSource, fragmentShaderSource) {
- var vertexShader = compileShader(gl.VERTEX_SHADER, vertexShaderSource);
- var fragmentShader = compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
- var program = gl.createProgram();
- gl.attachShader(program, vertexShader);
- gl.attachShader(program, fragmentShader);
- gl.linkProgram(program);
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
- var err = gl.getProgramInfoLog(program);
- gl.deleteProgram(program);
- throw new Error('GL program linking failed: ' + err);
- }
- return program;
- }
- function init() {
- if (WebGLFrameSink.stripe) {
- unpackProgram = initProgram(shaders.vertexStripe, shaders.fragmentStripe);
- unpackPositionLocation = gl.getAttribLocation(unpackProgram, 'aPosition');
- unpackTexturePositionBuffer = gl.createBuffer();
- var textureRectangle = new Float32Array([
- 0, 0,
- 1, 0,
- 0, 1,
- 0, 1,
- 1, 0,
- 1, 1
- ]);
- gl.bindBuffer(gl.ARRAY_BUFFER, unpackTexturePositionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, textureRectangle, gl.STATIC_DRAW);
- unpackTexturePositionLocation = gl.getAttribLocation(unpackProgram, 'aTexturePosition');
- stripeLocation = gl.getUniformLocation(unpackProgram, 'uStripe');
- unpackTextureLocation = gl.getUniformLocation(unpackProgram, 'uTexture');
- }
- program = initProgram(shaders.vertex, shaders.fragment);
- buf = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, buf);
- gl.bufferData(gl.ARRAY_BUFFER, rectangle, gl.STATIC_DRAW);
- positionLocation = gl.getAttribLocation(program, 'aPosition');
- lumaPositionBuffer = gl.createBuffer();
- lumaPositionLocation = gl.getAttribLocation(program, 'aLumaPosition');
- chromaPositionBuffer = gl.createBuffer();
- chromaPositionLocation = gl.getAttribLocation(program, 'aChromaPosition');
- }
- /**
- * Actually draw a frame.
- * @param {YUVFrame} buffer - YUV frame buffer object
- */
- self.drawFrame = function(buffer) {
- var format = buffer.format;
- var formatUpdate = (!program || canvas.width !== format.displayWidth || canvas.height !== format.displayHeight);
- if (formatUpdate) {
- // Keep the canvas at the right size...
- canvas.width = format.displayWidth;
- canvas.height = format.displayHeight;
- self.clear();
- }
- if (!program) {
- init();
- }
- if (formatUpdate) {
- var setupTexturePosition = function(buffer, location, texWidth) {
- // Warning: assumes that the stride for Cb and Cr is the same size in output pixels
- var textureX0 = format.cropLeft / texWidth;
- var textureX1 = (format.cropLeft + format.cropWidth) / texWidth;
- var textureY0 = (format.cropTop + format.cropHeight) / format.height;
- var textureY1 = format.cropTop / format.height;
- var textureRectangle = new Float32Array([
- textureX0, textureY0,
- textureX1, textureY0,
- textureX0, textureY1,
- textureX0, textureY1,
- textureX1, textureY0,
- textureX1, textureY1
- ]);
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
- gl.bufferData(gl.ARRAY_BUFFER, textureRectangle, gl.STATIC_DRAW);
- };
- setupTexturePosition(
- lumaPositionBuffer,
- lumaPositionLocation,
- buffer.y.stride);
- setupTexturePosition(
- chromaPositionBuffer,
- chromaPositionLocation,
- buffer.u.stride * format.width / format.chromaWidth);
- }
- // Create or update the textures...
- uploadTexture('uTextureY', buffer.y.stride, format.height, buffer.y.bytes);
- uploadTexture('uTextureCb', buffer.u.stride, format.chromaHeight, buffer.u.bytes);
- uploadTexture('uTextureCr', buffer.v.stride, format.chromaHeight, buffer.v.bytes);
- if (WebGLFrameSink.stripe) {
- // Unpack the textures after upload to avoid blocking on GPU
- unpackTexture('uTextureY', buffer.y.stride, format.height);
- unpackTexture('uTextureCb', buffer.u.stride, format.chromaHeight);
- unpackTexture('uTextureCr', buffer.v.stride, format.chromaHeight);
- }
- // Set up the rectangle and draw it
- gl.useProgram(program);
- gl.viewport(0, 0, canvas.width, canvas.height);
- attachTexture('uTextureY', gl.TEXTURE0, 0);
- attachTexture('uTextureCb', gl.TEXTURE1, 1);
- attachTexture('uTextureCr', gl.TEXTURE2, 2);
- // Set up geometry
- gl.bindBuffer(gl.ARRAY_BUFFER, buf);
- gl.enableVertexAttribArray(positionLocation);
- gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
- // Set up the texture geometry...
- gl.bindBuffer(gl.ARRAY_BUFFER, lumaPositionBuffer);
- gl.enableVertexAttribArray(lumaPositionLocation);
- gl.vertexAttribPointer(lumaPositionLocation, 2, gl.FLOAT, false, 0, 0);
- gl.bindBuffer(gl.ARRAY_BUFFER, chromaPositionBuffer);
- gl.enableVertexAttribArray(chromaPositionLocation);
- gl.vertexAttribPointer(chromaPositionLocation, 2, gl.FLOAT, false, 0, 0);
- // Aaaaand draw stuff.
- gl.drawArrays(gl.TRIANGLES, 0, rectangle.length / 2);
- };
- self.clear = function() {
- gl.viewport(0, 0, canvas.width, canvas.height);
- gl.clearColor(0.0, 0.0, 0.0, 0.0);
- gl.clear(gl.COLOR_BUFFER_BIT);
- };
- self.clear();
- return self;
- }
- // For Windows; luminance and alpha textures are ssllooww to upload,
- // so we pack into RGBA and unpack in the shaders.
- //
- // This seems to affect all browsers on Windows, probably due to fun
- // mismatches between GL and D3D.
- WebGLFrameSink.stripe = (function() {
- if (navigator.userAgent.indexOf('Windows') !== -1) {
- return true;
- }
- return false;
- })();
- WebGLFrameSink.contextForCanvas = function(canvas) {
- var options = {
- // Don't trigger discrete GPU in multi-GPU systems
- preferLowPowerToHighPerformance: true,
- powerPreference: 'low-power',
- // Don't try to use software GL rendering!
- failIfMajorPerformanceCaveat: true,
- // In case we need to capture the resulting output.
- preserveDrawingBuffer: true
- };
- return canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options);
- };
- /**
- * Static function to check if WebGL will be available with appropriate features.
- *
- * @returns {boolean} - true if available
- */
- WebGLFrameSink.isAvailable = function() {
- var canvas = document.createElement('canvas'),
- gl;
- canvas.width = 1;
- canvas.height = 1;
- try {
- gl = WebGLFrameSink.contextForCanvas(canvas);
- } catch (e) {
- return false;
- }
- if (gl) {
- var register = gl.TEXTURE0,
- width = 4,
- height = 4,
- texture = gl.createTexture(),
- data = new Uint8Array(width * height),
- texWidth = WebGLFrameSink.stripe ? (width / 4) : width,
- format = WebGLFrameSink.stripe ? gl.RGBA : gl.LUMINANCE,
- filter = WebGLFrameSink.stripe ? gl.NEAREST : gl.LINEAR;
- gl.activeTexture(register);
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
- gl.texImage2D(
- gl.TEXTURE_2D,
- 0, // mip level
- format, // internal format
- texWidth,
- height,
- 0, // border
- format, // format
- gl.UNSIGNED_BYTE, //type
- data // data!
- );
- var err = gl.getError();
- if (err) {
- // Doesn't support luminance textures?
- return false;
- } else {
- return true;
- }
- } else {
- return false;
- }
- };
- WebGLFrameSink.prototype = Object.create(FrameSink.prototype);
- module.exports = WebGLFrameSink;
- })();
- },{"../build/shaders.js":1,"./FrameSink.js":4}],7:[function(require,module,exports){
- /*
- Copyright (c) 2014-2019 Brion Vibber <brion@pobox.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- MPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- (function() {
- "use strict";
- var depower = require('./depower.js');
- /**
- * Basic YCbCr->RGB conversion
- *
- * @author Brion Vibber <brion@pobox.com>
- * @copyright 2014-2019
- * @license MIT-style
- *
- * @param {YUVFrame} buffer - input frame buffer
- * @param {Uint8ClampedArray} output - array to draw RGBA into
- * Assumes that the output array already has alpha channel set to opaque.
- */
- function convertYCbCr(buffer, output) {
- var width = buffer.format.width | 0,
- height = buffer.format.height | 0,
- hdec = depower(buffer.format.width / buffer.format.chromaWidth) | 0,
- vdec = depower(buffer.format.height / buffer.format.chromaHeight) | 0,
- bytesY = buffer.y.bytes,
- bytesCb = buffer.u.bytes,
- bytesCr = buffer.v.bytes,
- strideY = buffer.y.stride | 0,
- strideCb = buffer.u.stride | 0,
- strideCr = buffer.v.stride | 0,
- outStride = width << 2,
- YPtr = 0, Y0Ptr = 0, Y1Ptr = 0,
- CbPtr = 0, CrPtr = 0,
- outPtr = 0, outPtr0 = 0, outPtr1 = 0,
- colorCb = 0, colorCr = 0,
- multY = 0, multCrR = 0, multCbCrG = 0, multCbB = 0,
- x = 0, y = 0, xdec = 0, ydec = 0;
- if (hdec == 1 && vdec == 1) {
- // Optimize for 4:2:0, which is most common
- outPtr0 = 0;
- outPtr1 = outStride;
- ydec = 0;
- for (y = 0; y < height; y += 2) {
- Y0Ptr = y * strideY | 0;
- Y1Ptr = Y0Ptr + strideY | 0;
- CbPtr = ydec * strideCb | 0;
- CrPtr = ydec * strideCr | 0;
- for (x = 0; x < width; x += 2) {
- colorCb = bytesCb[CbPtr++] | 0;
- colorCr = bytesCr[CrPtr++] | 0;
- // Quickie YUV conversion
- // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion
- // multiplied by 256 for integer-friendliness
- multCrR = (409 * colorCr | 0) - 57088 | 0;
- multCbCrG = (100 * colorCb | 0) + (208 * colorCr | 0) - 34816 | 0;
- multCbB = (516 * colorCb | 0) - 70912 | 0;
- multY = 298 * bytesY[Y0Ptr++] | 0;
- output[outPtr0 ] = (multY + multCrR) >> 8;
- output[outPtr0 + 1] = (multY - multCbCrG) >> 8;
- output[outPtr0 + 2] = (multY + multCbB) >> 8;
- outPtr0 += 4;
- multY = 298 * bytesY[Y0Ptr++] | 0;
- output[outPtr0 ] = (multY + multCrR) >> 8;
- output[outPtr0 + 1] = (multY - multCbCrG) >> 8;
- output[outPtr0 + 2] = (multY + multCbB) >> 8;
- outPtr0 += 4;
- multY = 298 * bytesY[Y1Ptr++] | 0;
- output[outPtr1 ] = (multY + multCrR) >> 8;
- output[outPtr1 + 1] = (multY - multCbCrG) >> 8;
- output[outPtr1 + 2] = (multY + multCbB) >> 8;
- outPtr1 += 4;
- multY = 298 * bytesY[Y1Ptr++] | 0;
- output[outPtr1 ] = (multY + multCrR) >> 8;
- output[outPtr1 + 1] = (multY - multCbCrG) >> 8;
- output[outPtr1 + 2] = (multY + multCbB) >> 8;
- outPtr1 += 4;
- }
- outPtr0 += outStride;
- outPtr1 += outStride;
- ydec++;
- }
- } else {
- outPtr = 0;
- for (y = 0; y < height; y++) {
- xdec = 0;
- ydec = y >> vdec;
- YPtr = y * strideY | 0;
- CbPtr = ydec * strideCb | 0;
- CrPtr = ydec * strideCr | 0;
- for (x = 0; x < width; x++) {
- xdec = x >> hdec;
- colorCb = bytesCb[CbPtr + xdec] | 0;
- colorCr = bytesCr[CrPtr + xdec] | 0;
- // Quickie YUV conversion
- // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion
- // multiplied by 256 for integer-friendliness
- multCrR = (409 * colorCr | 0) - 57088 | 0;
- multCbCrG = (100 * colorCb | 0) + (208 * colorCr | 0) - 34816 | 0;
- multCbB = (516 * colorCb | 0) - 70912 | 0;
- multY = 298 * bytesY[YPtr++] | 0;
- output[outPtr ] = (multY + multCrR) >> 8;
- output[outPtr + 1] = (multY - multCbCrG) >> 8;
- output[outPtr + 2] = (multY + multCbB) >> 8;
- outPtr += 4;
- }
- }
- }
- }
- module.exports = {
- convertYCbCr: convertYCbCr
- };
- })();
- },{"./depower.js":8}],8:[function(require,module,exports){
- /*
- Copyright (c) 2014-2016 Brion Vibber <brion@pobox.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- MPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- (function() {
- "use strict";
- /**
- * Convert a ratio into a bit-shift count; for instance a ratio of 2
- * becomes a bit-shift of 1, while a ratio of 1 is a bit-shift of 0.
- *
- * @author Brion Vibber <brion@pobox.com>
- * @copyright 2016
- * @license MIT-style
- *
- * @param {number} ratio - the integer ratio to convert.
- * @returns {number} - number of bits to shift to multiply/divide by the ratio.
- * @throws exception if given a non-power-of-two
- */
- function depower(ratio) {
- var shiftCount = 0,
- n = ratio >> 1;
- while (n != 0) {
- n = n >> 1;
- shiftCount++
- }
- if (ratio !== (1 << shiftCount)) {
- throw 'chroma plane dimensions must be power of 2 ratio to luma plane dimensions; got ' + ratio;
- }
- return shiftCount;
- }
- module.exports = depower;
- })();
- },{}],9:[function(require,module,exports){
- /*
- Copyright (c) 2014-2016 Brion Vibber <brion@pobox.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- MPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- (function() {
- "use strict";
- var FrameSink = require('./FrameSink.js'),
- SoftwareFrameSink = require('./SoftwareFrameSink.js'),
- WebGLFrameSink = require('./WebGLFrameSink.js');
- /**
- * @typedef {Object} YUVCanvasOptions
- * @property {boolean} webGL - Whether to use WebGL to draw to the canvas and accelerate color space conversion. If left out, defaults to auto-detect.
- */
- var YUVCanvas = {
- FrameSink: FrameSink,
- SoftwareFrameSink: SoftwareFrameSink,
- WebGLFrameSink: WebGLFrameSink,
- /**
- * Attach a suitable FrameSink instance to an HTML5 canvas element.
- *
- * This will take over the drawing context of the canvas and may turn
- * it into a WebGL 3d canvas if possible. Do not attempt to use the
- * drawing context directly after this.
- *
- * @param {HTMLCanvasElement} canvas - HTML canvas element to attach to
- * @param {YUVCanvasOptions} options - map of options
- * @returns {FrameSink} - instance of suitable subclass.
- */
- attach: function(canvas, options) {
- options = options || {};
- var webGL = ('webGL' in options) ? options.webGL : WebGLFrameSink.isAvailable();
- if (webGL) {
- return new WebGLFrameSink(canvas, options);
- } else {
- return new SoftwareFrameSink(canvas, options);
- }
- }
- };
- module.exports = YUVCanvas;
- })();
- },{"./FrameSink.js":4,"./SoftwareFrameSink.js":5,"./WebGLFrameSink.js":6}]},{},[2]);
|