/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _lib_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var _lib_index_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_lib_index_js__WEBPACK_IMPORTED_MODULE_0__); // user canvas var c = document.getElementById('c') var ctx = c.getContext('2d') // gif patch canvas var tempCanvas = document.createElement('canvas') var tempCtx = tempCanvas.getContext('2d') // full gif canvas var gifCanvas = document.createElement('canvas') var gifCtx = gifCanvas.getContext('2d') var url = document.getElementById('url') // default gif url.value = '/demo/horses.gif' document.getElementById('loadGIF').onclick = loadGIF document.getElementById('playpause').onclick = playpause document.getElementById('edgedetect').onchange = () => { bEdgeDetect = !bEdgeDetect } document.getElementById('grayscale').onchange = () => { bGrayscale = !bGrayscale } document.getElementById('invert').onchange = () => { bInvert = !bInvert } document.getElementById('pixels').onchange = e => { pixelPercent = e.target.value } // load the default gif loadGIF() var gif // load a gif with the current input url value function loadGIF() { var oReq = new XMLHttpRequest() oReq.open('GET', url.value, true) oReq.responseType = 'arraybuffer' oReq.onload = function(oEvent) { var arrayBuffer = oReq.response // Note: not oReq.responseText if (arrayBuffer) { gif = Object(_lib_index_js__WEBPACK_IMPORTED_MODULE_0__["parseGIF"])(arrayBuffer) var frames = Object(_lib_index_js__WEBPACK_IMPORTED_MODULE_0__["decompressFrames"])(gif, true) // render the gif renderGIF(frames) } } oReq.send(null) } var playing = false var bEdgeDetect = false var bInvert = false var bGrayscale = false var pixelPercent = 100 var loadedFrames var frameIndex function playpause() { playing = !playing if (playing) { renderFrame() } } function renderGIF(frames) { loadedFrames = frames frameIndex = 0 c.width = frames[0].dims.width c.height = frames[0].dims.height gifCanvas.width = c.width gifCanvas.height = c.height if (!playing) { playpause() } } var frameImageData function drawPatch(frame) { var dims = frame.dims if ( !frameImageData || dims.width != frameImageData.width || dims.height != frameImageData.height ) { tempCanvas.width = dims.width tempCanvas.height = dims.height frameImageData = tempCtx.createImageData(dims.width, dims.height) } // set the patch data as an override frameImageData.data.set(frame.patch) // draw the patch back over the canvas tempCtx.putImageData(frameImageData, 0, 0) gifCtx.drawImage(tempCanvas, dims.left, dims.top) } var edge = function(data, output) { var odata = output.data var width = gif.lsd.width var height = gif.lsd.height var conv = [-1, -1, -1, -1, 8, -1, -1, -1, -1] var halfside = Math.floor(3 / 2) for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var r = 0, g = 0, b = 0 for (var cy = 0; cy < 3; cy++) { for (var cx = 0; cx < 3; cx++) { var scy = y - halfside + cy var scx = x - halfside + cx if (scy >= 0 && scy < height && scx >= 0 && scx < width) { var src = (scy * width + scx) * 4 var f = cy * 3 + cx r += data[src] * conv[f] g += data[src + 1] * conv[f] b += data[src + 2] * conv[f] } } } var i = (y * width + x) * 4 odata[i] = r odata[i + 1] = g odata[i + 2] = b odata[i + 3] = 255 } } return output } var invert = function(data) { for (var i = 0; i < data.length; i += 4) { data[i] = 255 - data[i] // red data[i + 1] = 255 - data[i + 1] // green data[i + 2] = 255 - data[i + 2] // blue data[i + 3] = 255 } } var grayscale = function(data) { for (var i = 0; i < data.length; i += 4) { var avg = (data[i] + data[i + 1] + data[i + 2]) / 3 data[i] = avg // red data[i + 1] = avg // green data[i + 2] = avg // blue data[i + 3] = 255 } } function manipulate() { var imageData = gifCtx.getImageData(0, 0, gifCanvas.width, gifCanvas.height) var other = gifCtx.createImageData(gifCanvas.width, gifCanvas.height) if (bEdgeDetect) { imageData = edge(imageData.data, other) } if (bInvert) { invert(imageData.data) } if (bGrayscale) { grayscale(imageData.data) } // do pixelation var pixelsX = 5 + Math.floor((pixelPercent / 100) * (c.width - 5)) var pixelsY = (pixelsX * c.height) / c.width if (pixelPercent != 100) { ctx.mozImageSmoothingEnabled = false ctx.webkitImageSmoothingEnabled = false ctx.imageSmoothingEnabled = false } ctx.putImageData(imageData, 0, 0) ctx.drawImage(c, 0, 0, c.width, c.height, 0, 0, pixelsX, pixelsY) ctx.drawImage(c, 0, 0, pixelsX, pixelsY, 0, 0, c.width, c.height) } function renderFrame() { // get the frame var frame = loadedFrames[frameIndex] var start = new Date().getTime() if (frame.disposalType === 2) { gifCtx.clearRect(0, 0, c.width, c.height) } // draw the patch drawPatch(frame) // perform manipulation manipulate() // update the frame index frameIndex++ if (frameIndex >= loadedFrames.length) { frameIndex = 0 } var end = new Date().getTime() var diff = end - start if (playing) { // delay the next gif frame setTimeout(function() { requestAnimationFrame(renderFrame) //renderFrame(); }, Math.max(0, Math.floor(frame.delay - diff))) } } /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decompressFrames = exports.decompressFrame = exports.parseGIF = void 0; var _gif = _interopRequireDefault(__webpack_require__(2)); var _jsBinarySchemaParser = __webpack_require__(3); var _uint = __webpack_require__(4); var _deinterlace = __webpack_require__(5); var _lzw = __webpack_require__(6); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } var parseGIF = function parseGIF(arrayBuffer) { var byteData = new Uint8Array(arrayBuffer); return (0, _jsBinarySchemaParser.parse)((0, _uint.buildStream)(byteData), _gif["default"]); }; exports.parseGIF = parseGIF; var generatePatch = function generatePatch(image) { var totalPixels = image.pixels.length; var patchData = new Uint8ClampedArray(totalPixels * 4); for (var i = 0; i < totalPixels; i++) { var pos = i * 4; var colorIndex = image.pixels[i]; var color = image.colorTable[colorIndex] || [0, 0, 0]; patchData[pos] = color[0]; patchData[pos + 1] = color[1]; patchData[pos + 2] = color[2]; patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0; } return patchData; }; var decompressFrame = function decompressFrame(frame, gct, buildImagePatch) { if (!frame.image) { console.warn('gif frame does not have associated image.'); return; } var image = frame.image; // get the number of pixels var totalPixels = image.descriptor.width * image.descriptor.height; // do lzw decompression var pixels = (0, _lzw.lzw)(image.data.minCodeSize, image.data.blocks, totalPixels); // deal with interlacing if necessary if (image.descriptor.lct.interlaced) { pixels = (0, _deinterlace.deinterlace)(pixels, image.descriptor.width); } var resultImage = { pixels: pixels, dims: { top: frame.image.descriptor.top, left: frame.image.descriptor.left, width: frame.image.descriptor.width, height: frame.image.descriptor.height } }; // color table if (image.descriptor.lct && image.descriptor.lct.exists) { resultImage.colorTable = image.lct; } else { resultImage.colorTable = gct; } // add per frame relevant gce information if (frame.gce) { resultImage.delay = (frame.gce.delay || 10) * 10; // convert to ms resultImage.disposalType = frame.gce.extras.disposal; // transparency if (frame.gce.extras.transparentColorGiven) { resultImage.transparentIndex = frame.gce.transparentColorIndex; } } // create canvas usable imagedata if desired if (buildImagePatch) { resultImage.patch = generatePatch(resultImage); } return resultImage; }; exports.decompressFrame = decompressFrame; var decompressFrames = function decompressFrames(parsedGif, buildImagePatches) { return parsedGif.frames.filter(function (f) { return f.image; }).map(function (f) { return decompressFrame(f, parsedGif.gct, buildImagePatches); }); }; exports.decompressFrames = decompressFrames; /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _ = __webpack_require__(3); var _uint = __webpack_require__(4); // a set of 0x00 terminated subblocks var subBlocksSchema = { blocks: function blocks(stream) { var terminator = 0x00; var chunks = []; var streamSize = stream.data.length; var total = 0; for (var size = (0, _uint.readByte)()(stream); size !== terminator; size = (0, _uint.readByte)()(stream)) { // catch corrupted files with no terminator if (stream.pos + size >= streamSize) { var availableSize = streamSize - stream.pos; chunks.push((0, _uint.readBytes)(availableSize)(stream)); total += availableSize; break; } chunks.push((0, _uint.readBytes)(size)(stream)); total += size; } var result = new Uint8Array(total); var offset = 0; for (var i = 0; i < chunks.length; i++) { result.set(chunks[i], offset); offset += chunks[i].length; } return result; } }; // global control extension var gceSchema = (0, _.conditional)({ gce: [{ codes: (0, _uint.readBytes)(2) }, { byteSize: (0, _uint.readByte)() }, { extras: (0, _uint.readBits)({ future: { index: 0, length: 3 }, disposal: { index: 3, length: 3 }, userInput: { index: 6 }, transparentColorGiven: { index: 7 } }) }, { delay: (0, _uint.readUnsigned)(true) }, { transparentColorIndex: (0, _uint.readByte)() }, { terminator: (0, _uint.readByte)() }] }, function (stream) { var codes = (0, _uint.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0xf9; }); // image pipeline block var imageSchema = (0, _.conditional)({ image: [{ code: (0, _uint.readByte)() }, { descriptor: [{ left: (0, _uint.readUnsigned)(true) }, { top: (0, _uint.readUnsigned)(true) }, { width: (0, _uint.readUnsigned)(true) }, { height: (0, _uint.readUnsigned)(true) }, { lct: (0, _uint.readBits)({ exists: { index: 0 }, interlaced: { index: 1 }, sort: { index: 2 }, future: { index: 3, length: 2 }, size: { index: 5, length: 3 } }) }] }, (0, _.conditional)({ lct: (0, _uint.readArray)(3, function (stream, result, parent) { return Math.pow(2, parent.descriptor.lct.size + 1); }) }, function (stream, result, parent) { return parent.descriptor.lct.exists; }), { data: [{ minCodeSize: (0, _uint.readByte)() }, subBlocksSchema] }] }, function (stream) { return (0, _uint.peekByte)()(stream) === 0x2c; }); // plain text block var textSchema = (0, _.conditional)({ text: [{ codes: (0, _uint.readBytes)(2) }, { blockSize: (0, _uint.readByte)() }, { preData: function preData(stream, result, parent) { return (0, _uint.readBytes)(parent.text.blockSize)(stream); } }, subBlocksSchema] }, function (stream) { var codes = (0, _uint.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0x01; }); // application block var applicationSchema = (0, _.conditional)({ application: [{ codes: (0, _uint.readBytes)(2) }, { blockSize: (0, _uint.readByte)() }, { id: function id(stream, result, parent) { return (0, _uint.readString)(parent.blockSize)(stream); } }, subBlocksSchema] }, function (stream) { var codes = (0, _uint.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0xff; }); // comment block var commentSchema = (0, _.conditional)({ comment: [{ codes: (0, _uint.readBytes)(2) }, subBlocksSchema] }, function (stream) { var codes = (0, _uint.peekBytes)(2)(stream); return codes[0] === 0x21 && codes[1] === 0xfe; }); var schema = [{ header: [{ signature: (0, _uint.readString)(3) }, { version: (0, _uint.readString)(3) }] }, { lsd: [{ width: (0, _uint.readUnsigned)(true) }, { height: (0, _uint.readUnsigned)(true) }, { gct: (0, _uint.readBits)({ exists: { index: 0 }, resolution: { index: 1, length: 3 }, sort: { index: 4 }, size: { index: 5, length: 3 } }) }, { backgroundColorIndex: (0, _uint.readByte)() }, { pixelAspectRatio: (0, _uint.readByte)() }] }, (0, _.conditional)({ gct: (0, _uint.readArray)(3, function (stream, result) { return Math.pow(2, result.lsd.gct.size + 1); }) }, function (stream, result) { return result.lsd.gct.exists; }), // content frames { frames: (0, _.loop)([gceSchema, applicationSchema, commentSchema, imageSchema, textSchema], function (stream) { var nextCode = (0, _uint.peekByte)()(stream); // rather than check for a terminator, we should check for the existence // of an ext or image block to avoid infinite loops //var terminator = 0x3B; //return nextCode !== terminator; return nextCode === 0x21 || nextCode === 0x2c; }) }]; var _default = schema; exports["default"] = _default; /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.loop = exports.conditional = exports.parse = void 0; var parse = function parse(stream, schema) { var result = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var parent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : result; if (Array.isArray(schema)) { schema.forEach(function (partSchema) { return parse(stream, partSchema, result, parent); }); } else if (typeof schema === 'function') { schema(stream, result, parent, parse); } else { var key = Object.keys(schema)[0]; if (Array.isArray(schema[key])) { parent[key] = {}; parse(stream, schema[key], result, parent[key]); } else { parent[key] = schema[key](stream, result, parent, parse); } } return result; }; exports.parse = parse; var conditional = function conditional(schema, conditionFunc) { return function (stream, result, parent, parse) { if (conditionFunc(stream, result, parent)) { parse(stream, schema, result, parent); } }; }; exports.conditional = conditional; var loop = function loop(schema, continueFunc) { return function (stream, result, parent, parse) { var arr = []; while (continueFunc(stream, result, parent)) { var newParent = {}; parse(stream, schema, result, newParent); arr.push(newParent); } return arr; }; }; exports.loop = loop; /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readBits = exports.readArray = exports.readUnsigned = exports.readString = exports.peekBytes = exports.readBytes = exports.peekByte = exports.readByte = exports.buildStream = void 0; // Default stream and parsers for Uint8TypedArray data type var buildStream = function buildStream(uint8Data) { return { data: uint8Data, pos: 0 }; }; exports.buildStream = buildStream; var readByte = function readByte() { return function (stream) { return stream.data[stream.pos++]; }; }; exports.readByte = readByte; var peekByte = function peekByte() { var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; return function (stream) { return stream.data[stream.pos + offset]; }; }; exports.peekByte = peekByte; var readBytes = function readBytes(length) { return function (stream) { return stream.data.subarray(stream.pos, stream.pos += length); }; }; exports.readBytes = readBytes; var peekBytes = function peekBytes(length) { return function (stream) { return stream.data.subarray(stream.pos, stream.pos + length); }; }; exports.peekBytes = peekBytes; var readString = function readString(length) { return function (stream) { return Array.from(readBytes(length)(stream)).map(function (value) { return String.fromCharCode(value); }).join(''); }; }; exports.readString = readString; var readUnsigned = function readUnsigned(littleEndian) { return function (stream) { var bytes = readBytes(2)(stream); return littleEndian ? (bytes[1] << 8) + bytes[0] : (bytes[0] << 8) + bytes[1]; }; }; exports.readUnsigned = readUnsigned; var readArray = function readArray(byteSize, totalOrFunc) { return function (stream, result, parent) { var total = typeof totalOrFunc === 'function' ? totalOrFunc(stream, result, parent) : totalOrFunc; var parser = readBytes(byteSize); var arr = new Array(total); for (var i = 0; i < total; i++) { arr[i] = parser(stream); } return arr; }; }; exports.readArray = readArray; var subBitsTotal = function subBitsTotal(bits, startIndex, length) { var result = 0; for (var i = 0; i < length; i++) { result += bits[startIndex + i] && Math.pow(2, length - i - 1); } return result; }; var readBits = function readBits(schema) { return function (stream) { var _byte = readByte()(stream); // convert the byte to bit array var bits = new Array(8); for (var i = 0; i < 8; i++) { bits[7 - i] = !!(_byte & 1 << i); } // convert the bit array to values based on the schema return Object.keys(schema).reduce(function (res, key) { var def = schema[key]; if (def.length) { res[key] = subBitsTotal(bits, def.index, def.length); } else { res[key] = bits[def.index]; } return res; }, {}); }; }; exports.readBits = readBits; /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.deinterlace = void 0; /** * Deinterlace function from https://github.com/shachaf/jsgif */ var deinterlace = function deinterlace(pixels, width) { var newPixels = new Array(pixels.length); var rows = pixels.length / width; var cpRow = function cpRow(toRow, fromRow) { var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width); newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels)); }; // See appendix E. var offsets = [0, 4, 2, 1]; var steps = [8, 8, 4, 2]; var fromRow = 0; for (var pass = 0; pass < 4; pass++) { for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) { cpRow(toRow, fromRow); fromRow++; } } return newPixels; }; exports.deinterlace = deinterlace; /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.lzw = void 0; /** * javascript port of java LZW decompression * Original java author url: https://gist.github.com/devunwired/4479231 */ var lzw = function lzw(minCodeSize, data, pixelCount) { var MAX_STACK_SIZE = 4096; var nullCode = -1; var npix = pixelCount; var available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, i, datum, data_size, first, top, bi, pi; var dstPixels = new Array(pixelCount); var prefix = new Array(MAX_STACK_SIZE); var suffix = new Array(MAX_STACK_SIZE); var pixelStack = new Array(MAX_STACK_SIZE + 1); // Initialize GIF data stream decoder. data_size = minCodeSize; clear = 1 << data_size; end_of_information = clear + 1; available = clear + 2; old_code = nullCode; code_size = data_size + 1; code_mask = (1 << code_size) - 1; for (code = 0; code < clear; code++) { prefix[code] = 0; suffix[code] = code; } // Decode GIF pixel stream. var datum, bits, count, first, top, pi, bi; datum = bits = count = first = top = pi = bi = 0; for (i = 0; i < npix;) { if (top === 0) { if (bits < code_size) { // get the next byte datum += data[bi] << bits; bits += 8; bi++; continue; } // Get the next code. code = datum & code_mask; datum >>= code_size; bits -= code_size; // Interpret the code if (code > available || code == end_of_information) { break; } if (code == clear) { // Reset decoder. code_size = data_size + 1; code_mask = (1 << code_size) - 1; available = clear + 2; old_code = nullCode; continue; } if (old_code == nullCode) { pixelStack[top++] = suffix[code]; old_code = code; first = code; continue; } in_code = code; if (code == available) { pixelStack[top++] = first; code = old_code; } while (code > clear) { pixelStack[top++] = suffix[code]; code = prefix[code]; } first = suffix[code] & 0xff; pixelStack[top++] = first; // add a new string to the table, but only if space is available // if not, just continue with current table until a clear code is found // (deferred clear code implementation as per GIF spec) if (available < MAX_STACK_SIZE) { prefix[available] = old_code; suffix[available] = first; available++; if ((available & code_mask) === 0 && available < MAX_STACK_SIZE) { code_size++; code_mask += available; } } old_code = in_code; } // Pop a pixel off the pixel stack. top--; dstPixels[pi++] = pixelStack[top]; i++; } for (i = pi; i < npix; i++) { dstPixels[i] = 0; // clear missing pixels } return dstPixels; }; exports.lzw = lzw; /***/ }) /******/ ]);