From 346f3a9817b1e0812565396b9811a1ce5adc97b8 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 13 Dec 2013 15:28:31 -0500 Subject: reorganize --- gif.js | 1690 ---------------------------------------------------------------- 1 file changed, 1690 deletions(-) delete mode 100644 gif.js (limited to 'gif.js') diff --git a/gif.js b/gif.js deleted file mode 100644 index a2c22fa..0000000 --- a/gif.js +++ /dev/null @@ -1,1690 +0,0 @@ -// gif.js by @timb :) -;(function(e,t,n,r){function i(r){if(!n[r]){if(!t[r]){if(e)return e(r);throw new Error("Cannot find module '"+r+"'")}var s=n[r]={exports:{}};t[r][0](function(e){var n=t[r][1][e];return i(n?n:e)},s,s.exports)}return n[r].exports}for(var s=0;s 1) { // on("msg", f) - var msgMap = {}; - msgMap[arguments[0]] = arguments[1]; - var opts = arguments[2] || {}; - } else { // on({"msg": f, ...}) - var msgMap = arguments[0]; - var opts = arguments[1] || {}; - } - - for (var string in msgMap){ - - var msgs = string.split(" "); - - var fs = msgMap[string]; - if (!Array.isArray(fs)) fs = [fs]; - - for(var i=0, f; f=fs[i]; i++){ - if (!f.uid) f.uid = Uid(); - } - - for(var i=0, msg; msg=msgs[i]; i++){ - - var listeners = (msg.indexOf("*") === -1) ? - chan.listeners : - chan.globListeners; - - // todo: this probably wastes a lot of memory? - // make a copy of the listener, add to it, and replace the listener - // why not just push directly? - // send might be iterating over it... and that will fuck up the iteration - listeners[msg] = (msg in listeners) ? - listeners[msg].concat(fs) : - fs.concat(); - } - } - - return chan; - -}; - - - -/* -off() -off("a:b:c") -off(f) -off("a:b:c", f) -off("a:b:c d:e:f") -off([f, f2]) -off({"a": f, "b": f2}) -*/ -Tube.proto.off = function(){ var chan = this; - - var listeners, i, msgs, msg; - - // off() : delete all listeners. but replace, instead of delete - if (arguments.length === 0) { - chan.listeners = {}; - chan.globListeners = {}; - return chan; - } - - // off("a:b:c d:e:f") - // remove all matching listeners - if (arguments.length === 1 && typeof arguments[0] === "string"){ - // question... will this fuck up send if we delete in the middle of it dispatching? - msgs = arguments[0].split(" "); - - for (i=0; msg=msgs[i]; i++){ - delete chan.listeners[msg]; - delete chan.globListeners[msg]; - } - return chan; - } - - // off(f) or off([f, f2]) - // remove all matching functions - if (typeof arguments[0] === "function" || Array.isArray(arguments[0])) { - var fs = (typeof arguments[0] === "function") ? - [arguments[0]] : - arguments[0]; - // TODO - return chan; - } - - // off("a:b:c", f) or off({"a": f, "b": f2}) - if (arguments.length > 1) { // off("msg", f) - var msgMap = {}; - msgMap[arguments[0]] = arguments[1]; - } else { // off({"msg": f, ...}) - var msgMap = arguments[0]; - } - - for (var string in msgMap){ - msgs = string.split(" "); - - var fs = msgMap[string]; - if (typeof fs === "function") fs = [fs]; - - for(var i=0; msg=msgs[i]; i++){ - - if (msg in chan.listeners) - listeners = chan.listeners; - else if (msg in chan.globListeners) - listeners = chan.globListeners; - else - continue; - - // gotta do this carefully in case we are still iterating through the listener in send - // build a new array and assign it to the property, instead of mutating it. - - // console.log(" length of listeners[" + msg + "]: " + listeners[msg].length) - // console.log(listeners[msg].join(",")); - // console.log(fs.join(",")); - - listeners[msg] = listeners[msg].filter( - function(f){ return fs.indexOf(f) === -1 } - ); - // console.log(" length of listeners[" + msg + "]: " + listeners[msg].length) - - } - } - - return chan; - -}; - - -/* - -c = Tube() -c.on("foo", fn) -c("foo", "bar", []) - -will call fn("bar", [], "foo") - -*/ -Tube.proto.send = function(msgString /*, data... */){ - - // todo: don't do this? - if (!Tube.total[msgString]) Tube.total[msgString] = 0 - Tube.total[msgString]+=1; - - var listener, - listeners = this.listeners, - globListeners = this.globListeners, - //args = Array.prototype.splice.call(arguments, 1), - msgs = tokenize(msgString), - msg, f; - - if (arguments.length) { - var args = Array.prototype.splice.call(arguments, 1); - args.push(msgString); - - } else { - var args = []; - } - - for (var m=0; msg=msgs[m]; m++){ - - var fsToRun = []; - var uidKeyFnValue = {}; - var uidKeyMsgStringValue = {}; - - // note this will die on errors - // todo: implement http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/ - // exact matches - if (listener = listeners[msg]) { - for (var i=0; f=listener[i]; i++){ - // fsToRun.push([f, msg]); - uidKeyFnValue[f.uid] = f; - uidKeyMsgStringValue[f.uid] = msg; - } - } - - // glob matches - var msgSplit = msg.split(":"); - - for (var pattern in globListeners){ - - if (pattern !== "*") { // * always matches - var patternSplit = globcache[pattern] || (globcache[pattern] = pattern.split(":")); - if (!globber(patternSplit, msgSplit)) continue; - } - - listener = globListeners[pattern]; - - for (var i=0; f=listener[i]; i++){ - //f.apply(window, args); // hm possibly pass the actual message to the func - // fsToRun.push([f, msg]); - uidKeyFnValue[f.uid] = f; - uidKeyMsgStringValue[f.uid] = msg; - } - } - - var fns = []; - for (var f in uidKeyFnValue) fns.push(uidKeyFnValue[f]); - - for (var i=0, f; f=fns[i]; i++) - f.apply(f, args); - - } - - return this; - -}; - -module.exports = Tube; -},{"./object/setproto":7,"./string/tokenize":8,"./string/globber":9,"./uid":10,"./nexttick":11}],7:[function(require,module,exports){// TODO: replace all uses of -// setproto(foo, proto) -// with -// foo.__proto__ = proto -// when IE is no longer shit - -var setproto = function(obj, proto){ - if (obj.__proto__) - obj.__proto__ = proto; - else - for (var key in proto) - obj[key] = proto[key]; -}; -module.exports = setproto; -},{}],8:[function(require,module,exports){// trimmed string into array of strings -var tokenize = function(str, splitOn){ - return str - .trim() - .split(splitOn || tokenize.default); -}; -tokenize.default = /\s+/g; - -module.exports = tokenize; -},{}],9:[function(require,module,exports){// can use * to match 0 or more : separated msgs -// globber("*".split(":"), "a:b:c".split(":")) => true -// globber("*:c".split(":"), "a:b:c".split(":")) => true -// globber("a:*".split(":"), "a:b:c".split(":")) => true -// globber("a:*:c".split(":"), "a:b:c".split(":")) => true - -// based on codegolf.stackexchange.com/questions/467/implement-glob-matcher -var globber = function(patterns, strings) { - // console.log("globber called with: " + patterns.join(":"), strings.join(":")) - var first = patterns[0], - rest = patterns.slice(1), - len = strings.length, - matchFound; - - if(first === '*') { - for(var i = 0; i <= len; ++i) { - // console.log("* " + i + " trying " + rest.join(":") + " with " + strings.slice(i).join(":")) - if(globber(rest, strings.slice(i))) return true; - } - return false; - } else { - matchFound = (first === strings[0]); - // console.log ("literal matching " + first + " " + strings[0] + " " + !!matched) - } - - return matchFound && ((!rest.length && !len) || globber(rest, strings.slice(1))); -}; -module.exports = globber; -},{}],10:[function(require,module,exports){var Uid = function(){ - return (Uid.counter++ + ""); -} - -Uid.counter = 1; - -module.exports = Uid; -},{}],11:[function(require,module,exports){// based on https://github.com/timoxley/next-tick/blob/master/index.js - -// postMessage behaves badly on IE8 -if (window.ActiveXObject || !window.postMessage) { - - var nextTick = function(fn) { - setTimeout(fn, 0); - } - -} else { - - // based on setZeroTimeout by David Baron - // - http://dbaron.org/log/20100309-faster-timeouts - var timeouts = [] - , name = 'next-tick-zero-timeout' - - window.addEventListener('message', function(e){ - if (e.source == window && e.data == name) { - if (e.stopPropagation) e.stopPropagation(); - if (timeouts.length) timeouts.shift()(); - } - }, true); - - var nextTick = function(fn){ - timeouts.push(fn); - window.postMessage(name, '*'); - } - -} - -module.exports = nextTick; -},{}],4:[function(require,module,exports){var Benchmark = require('./benchmark'); -var setproto = require('./object/setproto'); -var extend = require('./object/extend'); -var Tube = require('./tube'); - -// usage: Buffer(url || File || ArrayBuffer) -var BufferLoader = function(src, opts){ - - var loader = Tube(); - setproto(loader, BufferLoader.proto); - - if (opts && opts.benchmark) loader.benchmark = opts.benchmark; - - //buf.remember("load error") - // loader.on("load", function(abuf){ setupBuffer(loader, abuf) }); // <-- hm, needs ref to buf - - loader.src = src; - - return loader; -} - -BufferLoader.proto = {}; -extend(BufferLoader.proto, Tube.proto); - -BufferLoader.proto.load = function(){ var loader = this; - var src = loader.src; - if (typeof src === "string") loader.loadFromUrl(src) - else if (src instanceof File) loader.loadFromFile(src); - else if (src instanceof ArrayBuffer) loader("load", src); -} - -BufferLoader.proto.loadFromFile = function(file){ var loader = this; - - var r = new FileReader(); - - r.addEventListener('load', function(e){ - if (loader.benchmark) loader.benchmark.stop("fetch-from-disk"); - loader('load', r.result, e); - // console.log(r.file.name + " 100%: " + r.result.byteLength + " bytes") - }); - - r.addEventListener('error', function(e){ - // console.log(arguments); - loader('error', e, r) - }); - - r.addEventListener('progress', function(e){ - //console.log(r.file.name + " " + (e.loaded / e.total * 100) + "%: " + e.loaded + " bytes") - loader('progress', e) - }); - - if (loader.benchmark) loader.benchmark.start("fetch-from-disk"); - r.readAsArrayBuffer(file); - -}; - - -BufferLoader.proto.loadFromUrl = function(url){ var loader=this; - - var xhr = new XMLHttpRequest(); - xhr.open("GET", url); - xhr.responseType = "arraybuffer"; - - xhr.addEventListener('load', function(e){ - if (loader.benchmark) loader.benchmark.stop("fetch-from-network"); - // console.log(r.file.name + " 100%: " + r.result.byteLength + " bytes") - loader('load', xhr.response, e); - }); - - xhr.addEventListener('error', function(e){ - // console.log(arguments); - loader('error', e, xhr); - }); - - xhr.addEventListener('progress', function(e){ - //console.log(r.file.name + " " + (e.loaded / e.total * 100) + "%: " + e.loaded + " bytes") - loader('progress', e) - }); - - if (loader.benchmark) loader.benchmark.start("fetch-from-network"); - // xhr.open("GET", url); - - xhr.send(); - -}; - -module.exports = BufferLoader; -},{"./benchmark":2,"./object/setproto":7,"./object/extend":12,"./tube":3}],12:[function(require,module,exports){module.exports = function(destination, source) { - for (var property in source) - destination[property] = source[property]; - return destination; -}; -},{}],5:[function(require,module,exports){(function(){var blockSigs = require('./spec').blockSigs; -var extSigs = require('./spec').extSigs; -var palette = require('./palette'); -var makeCurrentFrame = require('./animate').makeCurrentFrame; - -var BinarySpecReader = require('../../binaryspec'); -var spec = require('./spec').spec; -var specReader = BinarySpecReader(spec); - -var decode = function(gif, opts){ - decodeNextPart(gif); -}; - - -// this gets called when more binary data is ready to process -// it gets set as BufferedReader's onload function -/* -var moreDataInBufferEvent = function(gif){ - if (gif.waitingForFileBuffer) decodeNextPart(gif); -}; -*/ - -/* -loads the next part of the gif, eventually calling the gif's onload function when every part has been loaded - -this function gets called when a previous part has finished loading, -and when there's more data in the buffered reader - -if the next part has a fixed size according to spec, we know whether we have to wait on the buffer to fill or not -if the next part has a variable size, call the howToDecode function which will try to load it -*/ -var decodeNextPart = function(gif, nextPart){ - //gif.waitingForFileBuffer = false; - - nextPart = nextPart || "header"; - var buf = gif.buf; - - while (nextPart !== "done" && nextPart !== "error"){ - - if (nextPart in howToDecode && typeof howToDecode[nextPart] === "function") { // dont know size - nextPart = howToDecode[nextPart](gif); - } else { // we know exact size of next part - - var partSize = specReader.parts[nextPart].byteSize; - - if (buf.abuf.byteLength < partSize + buf.cursor){ - gif.waitingForFileBuffer = true; - return; - } else { - var fields = specReader.decodeBinaryFieldsToJSON(nextPart, buf.cursor, buf); - buf.cursor += specReader.parts[nextPart].byteSize; - nextPart = handleLoadedPart[nextPart](gif, fields); - } - } - // todo: maybe do this when and return unknown from dataBlock read - // if (nextPart === "unknown") nextPart = "dataBlock" - - } - - if (nextPart === "done"){ - // create palette from all frames' palettes - - if (gif.benchmark) gif.benchmark.start("palette"); - gif.paletteTotal = palette.create(gif); - if (gif.benchmark) gif.benchmark.stop("palette"); - - // todo: move elsewhere - makeCurrentFrame.bind(gif)(); -// GIF.decode.doneDecodingCleanup(gif); - //gif.tube("progress", "done"); - gif.decoded = true; - gif.tube("decoded"); - return; - } - -}; - - -var howToDecode = { - "globalPalette": function(gif){ var buf = gif.buf; - var paletteSize = gif.paletteSize * 3; // r,g,b bytes - - if (buf.abuf.byteLength < buf.cursor + paletteSize) { - gif.waitingForFileBuffer = true; - return; - } - - //gif.palette = GIF.palette.binary2rgba(gif.reader.buffer.slice(gif.cursor, gif.cursor + paletteSize)); - gif.palette = palette.binary2rgba(new Uint8Array(buf.abuf, buf.cursor, paletteSize)); - buf.cursor += paletteSize; - - return "dataBlock"; - }, - "localPalette": function(gif){ var buf = gif.buf; - var paletteSize = gif.frames[gif.frames.length - 1].paletteSize * 3; // r,g,b bytes - - if (buf.abuf.byteLength < buf.cursor + paletteSize) { - gif.waitingForFileBuffer = true; - return; - } - - gif.frames[gif.frames.length - 1].palette = palette.binary2rgba(new Uint8Array(buf.abuf, buf.cursor, paletteSize)); - buf.cursor += paletteSize; - return "imageData"; - }, - "dataBlock": function(gif){ var buf = gif.buf; - - if (buf.abuf.byteLength < buf.cursor + 1) { - gif.waitingForFileBuffer = true; - return; - } - - var blockType = "unknown", - extType = "unknown", - nextPart; - - var blockSig = buf.u8[buf.cursor]; - - if (blockSig in blockSigs) { - blockType = blockSigs[blockSig]; - } else { - //console.log("unknown data block type ("+ Number(blockSig).toString(16) +")!"); - nextPart = "done"; - } - - if (blockType === "extension"){ // we need to determine what kind of extension the block is - // need next two bytes to find out what kind of extension the data block is - if (buf.abuf.byteLength < buf.cursor + 2) { - gif.waitingForFileBuffer = true; - return; - console.log("ran out of buffer") - } - var extSig = buf.u8[buf.cursor+1]; - if (extSig in extSigs) { - extType = extSigs[extSig]; - nextPart = extType; - } else { - //console.log("unknown extension type ("+ Number(extSig).toString(16) +")") - nextPart = "done"; - } - } else { - if (blockType === "unknown") { blockType = "dataBlock"; buf.cursor += 1 } - nextPart = blockType; - } - - return nextPart; - - }, - "trailer": function(gif){ - return "done"; - } -}; - -var handleLoadedPart = { - "header": function(gif, fields){ - - if (fields.signature != "GIF"){ - gif.tube("error", "file doesn't seem to be a gif (file signature: '"+fields.signature+"')"); - return "error"; - } - - // gif.version = fields.version - - return "screenDesc"; - }, - "screenDesc": function(gif, fields){ - for (var field in fields) gif[field] = fields[field]; - // todo: make nicer - // this should be a u3 not this bit[3] bit-shifted horseshit - gif.paletteSize = (gif.paletteSize[0] << 2) + (gif.paletteSize[1] << 1) + (gif.paletteSize[2]); - gif.paletteSize = Math.pow(2, gif.paletteSize + 1) - - if (gif.paletteExists) return "globalPalette"; - else return "dataBlock"; - }, - "imageDesc": function(gif, fields){ - // make a blank image frame if none exists or the last frame already has image data - // we don't know if a blank frame has already been created because a graphic control block might - // have come before this block and made one - if (!gif.frames.length || ("w" in gif.frames[gif.frames.length - 1])) - gif.frames.push({}) - - var frame = gif.frames[gif.frames.length - 1] - - for (var field in fields) frame[field] = fields[field]; - frame.paletteSize = (frame.paletteSize[0] << 2) + (frame.paletteSize[1] << 1) + (frame.paletteSize[2]); - frame.paletteSize = Math.pow(2, frame.paletteSize + 1) - - if (frame.paletteExists) return "localPalette"; - else return "imageData"; - }, - // if sublocks was able to read: - // increase cursor from subblocks - // increase cursor from fields - // else - // set waiting - // subtract 2 from cursor (for the 2 that was read to get the data block) - // subtract part.bytesize from cursor (not needed in this case?) - "applicationExtension": function(gif, fields){ - var blockinfo = readSubBlocks(gif); - if (blockinfo === false) { - gif.waitingForFileBuffer = true - gif.buf.cursor -= specReader.parts.applicationExtension.byteSize - return; - } else { - var extension = {"data": blockinfo}; - for(var field in fields) - extension[field] = fields[field] - gif.extensions.push(extension) - return "dataBlock"; - } - }, - "comment": function(gif, fields){ - var blockinfo = readSubBlocks(gif); - if (blockinfo === false) { - gif.waitingForFileBuffer = true; - gif.buf.cursor -= specReader.parts.comment.byteSize; - return; - } else { - var extension = {"comment": blockinfo}; - for(var field in fields) - extension[field] = fields[field]; - gif.extensions.push(extension); - return "dataBlock"; - } - }, - "plainText": function(gif, fields){ - var blockinfo = readSubBlocks(gif); - if (blockinfo === false) { - gif.waitingForFileBuffer = true; - gif.buf.cursor -= specReader.parts.plainText.byteSize; - return; - } else { - var extension = {"plainText": blockinfo}; - for(var field in fields) - extension[field] = fields[field]; - gif.extensions.push(extension); - return "dataBlock"; - } - }, - "graphicControl": function(gif, fields){ - var dm = (fields.disposalMethod[0] << 2) + (fields.disposalMethod[1] << 1) + (fields.disposalMethod[2]); - gif.frames.push({"delay": fields.delay, - "transparentIndex": fields.transparentColor ? fields.transparentIndex : -1, - "disposalMethod": dm}) - return "dataBlock"; - }, - "imageData": function(gif, fields){ - var blockinfo = readSubBlocks(gif); - if (blockinfo === false) { - gif.waitingForFileBuffer = true - gif.buf.cursor -= specReader.parts.imageData.byteSize - console.log("fucked") - return; - } else { - -// TODO: ENABLE THIS! -// gif.tube("progress", "found " + gif.frames.length + " frames"); - - var frame = gif.frames[gif.frames.length - 1]; - //var palette = ("palette" in frame) ? frame.palette : gif.palette // local palette otherwise global palette - frame.lzwCodeSize = fields.lzwCodeSize; - frame.blockinfo = blockinfo; -// var transparentIndex = ("transparentIndex" in frame) ? frame.transparentIndex : -1 - return "dataBlock"; - } - - } -}; - -// read subblocks out of a gif's buffer... -// reads block sizes and returns an object with a start cursor and an array of block ends -// returns false if there's not enough data -var readSubBlocks = function(gif){ - - if (gif.benchmark) gif.benchmark.start("read-subblocks"); - - var blockEnds = []; - - var buf = gif.buf, - u8 = buf.u8, - byteLength = u8.byteLength; - - // gif.cursor is for whole file... pos is a cursor for just this blob - var startBlockCursor = buf.cursor; - var pos = buf.cursor; - startBlockCursor += 1; - var byteSize = 0; - var outOfData = false - - // only actually advance cursor if we can read in all the sub blocks from the buffer - var cursorTemp = 0; - - while(!outOfData){ - // get block size - if (byteLength < pos + 1) { outOfData = true; break;} - byteSize = u8[pos]; - pos += 1; - - // a sub block with size 0 indicates end of sub blocks - if (byteSize === 0) { cursorTemp += 1; break;} - - // read block - if (byteLength < pos + byteSize) { outOfData = true; break;} - blockEnds.push(pos + byteSize); - - pos += byteSize - cursorTemp += byteSize + 1 - // gif.subBlocksRead += 1 - } - - // TODO? CLEAN UP! - if (outOfData) { - // gif.bufferMisses += 1 - // gif.benchmark.wasted += (Date.now() - start) / 1000 - console.log("out of data") - return false - - } else { // end of sub blocks happened - buf.cursor += cursorTemp - //if ("onprogress" in gif) gif.onprogress(gif, Math.floor(gif.cursor / gif.file.size * 100)) -// gif.benchmark.subblocks += (Date.now() - start) / 1000; - if (gif.benchmark) gif.benchmark.stop("read-subblocks"); - return {start: startBlockCursor, blockEnds: blockEnds}; - - } -}; - -module.exports = decode; -})() -},{"./spec":13,"./palette":14,"./animate":15,"../../binaryspec":16}],13:[function(require,module,exports){var blockSigs = { - 0x21: "extension", - 0x2c: "imageDesc", - 0x3b: "trailer" -}; - -exports.blockSigs = blockSigs; - -var extSigs = { - 0xf9: "graphicControl", - 0xfe: "comment", - 0x01: "plainText", - 0xff: "applicationExtension" -}; - -exports.extSigs = extSigs; - -/* -GIF.getGeneralBlockType = function(sig){ - if (sig == 0x3b) - return "trailer" - else if (sig < 0x7F) - return "graphic rendering block" - else if (sig < 0xF9) - return "control block" - else return "special purpose block" -} - -*/ - -var spec = { - "header": [ - "str[3] signature", - "str[3] version" - ], - "screenDesc": [ - "u16 w", - "u16 h", - "bit paletteExists", - "bit[3] resolution ignore", - "bit sortFlag ignore", - "bit[3] paletteSize", - "u8 bgColorIndex", - "u8 aspectRatio ignore" - ], - "imageDesc": [ - "u8 sig ignore", - "u16 x", - "u16 y", - "u16 w", - "u16 h", - "bit paletteExists", - "bit interlaced", - "bit sortFlag", - "bit[2] reserved ignore", - "bit[3] paletteSize" - ], - "applicationExtension": [ - "u8 sig ignore", - "u8 extSig ignore", - "u8 blockSize ignore", - "str[8] identifier", - "str[3] authCode ignore" - ], - "graphicControl": [ - "u8 sig ignore", - "u8 extSig ignore", - "u8 blockSize ignore", - "bit[3] reserved ignore", - "bit[3] disposalMethod", - "bit userInput ignore", - "bit transparentColor", - "u16 delay", - "u8 transparentIndex", - "u8 blockTerminator ignore" - ], - "comment": [ - "u8 sig ignore", - "u8 extSig ignore" - ], - "plainText": [ - "u8 sig ignore", - "u8 extSig ignore", - "u8 blockSize", - "u16 textGridLeft", - "u16 textGridTop", - "u16 textGridWidth", - "u16 textGridHeight", - "u8 charCellWidth", - "u8 charCellHeight", - "u8 fgColorIndex", - "u8 bgColorIndex" - ], - "imageData": [ - "u8 lzwCodeSize" - ] -}; - -exports.spec = spec; -},{}],14:[function(require,module,exports){(function(){var rgba2css = require('../../color/rgba2css'); -var create2d = require('../../create/2d'); -var createImageData = require('../../create/imagedata'); - -var palette = {}; - -// flat typed array of r g b a values from binary GIF palette -palette.binary2rgba = function(abuf /*, transparentIndex */){ - var table = new Uint8Array(abuf.byteLength/3*4); - var counter = 0 - for(var i = 0, length = abuf.byteLength/3*4; i 0) ? frame.delay * 10 : defaultDelay; - totalDelay += delay; - delays.push(totalDelay); - } - - this.currentFrame = makeCurrentFrameFunction(delays); -}; - -var makeCurrentFrameFunction = function(delays){ - var totalTime = delays[delays.length - 1]; - return function(timestamp){ - var r = (timestamp || Date.now()) % totalTime; - for(var i=0; i 1); - // char(2) is 16 bits. uint(16) is 16 bits - var bitSize = bitSizes[fieldType] * fieldLength; - - parsedSpec.fields.push({"name": instruction[1], - "type": fieldType, - "ignore": ignore, - "bitSize": bitSize, - "isArray": isArray}); - size += bitSize; - } - - parsedSpec.bitSize = size; - parsedSpec.byteSize = size / 8; - - return parsedSpec; -}; - -// decodes a chunk according to data types in gif.spec.js -// todo: rewrite the binary decoding stuff to not be so shit -BinarySpecReader.proto.decodeBinaryFieldsToJSON = function(partName, cursor, buf){ var reader = this; - - var part = reader.parts[partName]; - - var fields = {}, numFields = part.fields.length, bitPos = 0; - - for(var i = 0; i < numFields; i++){ - - var field = part.fields[i]; - if (!field.ignore) { - var bitOffset = bitPos % 8; - var decodeByteStart = Math.floor((bitPos - bitOffset) / 8); - var decodeByteEnd = decodeByteStart + Math.ceil(field.bitSize / 8); - - switch(field.type){ - case "u8": - fields[field.name] = buf.u8[cursor + decodeByteStart]; break; - case "i8": - fields[field.name] = buf.dv.getInt8(cursor + decodeByteStart); break; - case "u16": - fields[field.name] = buf.dv.getUint16(cursor + decodeByteStart, true); break; - case "i16": - fields[field.name] = buf.dv.getInt16(cursor + decodeByteStart, true); break; - case "u32": - fields[field.name] = buf.dv.getUint32(cursor + decodeByteStart, true); break; - case "i32": - fields[field.name] = buf.dv.getInt32(cursor + decodeByteStart, true); break; - case "str": - fields[field.name] = abuf2str(buf.abuf, cursor + decodeByteStart, field.bitSize >> 3); break; - case "bit": - if (!field.isArray) { - fields[field.name] = new BitView(buf.abuf, cursor + decodeByteStart).getBit(bitOffset); - } else { - var bv = new BitView(buf.abuf, cursor + decodeByteStart); - var bits = []; - for (var bb=bitOffset; bb> 3]; - var off = idx & 0x7; - return (v >> (7-off)) & 1; -}; - -module.exports = BitView; -},{}],6:[function(require,module,exports){var create2d = require('../../create/2d'); -var createImageData = require('../../create/imagedata'); -//import queueOrNextTick from 'lib/nexttick4'; -var nextTick = require('../../nexttick'); - -var lzwImageData = require('./decode-lzw'); - -/* - this has two rendering types... canvas and webgl - both build gif image frames from binary data that was decoded when the gif loaded - - canvas method will build full-frame canvas objects and place them into each frame of the gif. - eg, render.canvas(gif) will create gif.frames[0].ctx and so on - - webgl method builds imagedata textures and tries to pack multiple frames efficiently into channels if it can. - - in gifs, each frame stored might just be a rectangle that changed from the previous frame. - these are referred to as "raw frames" in this code -*/ - -var render = function(gif, config){ - - config = config || {}; - - var bench = gif.benchmark || false; - - var frameNum = config.frameNum || 0; - - if (frameNum === 0){ // preallocate all frames - for (var i=0; i= gif.frames.length) { // done making frames - //gif.tube("progress", "done") - gif.rendered = true; - gif.tube("rendered"); - return - } - - var frame = gif.frames[frameNum]; - var pixeldata = gif.buf.pixeldata; - - // gif.percentLoaded = frameNum / gif.frames.length - //gif.tube("decompressing frame " + (frameNum+1)) - - - - // lzw - if (bench) bench.start("decompress-lzw"); - // var pixeldata = new Uint8Array(frame.w * frame.h); - //pixeldata.area = frame.w * frame.h; - lzwImageData(frame.blockinfo, gif.buf.u8, frame.lzwCodeSize, frame.w, frame.h, pixeldata); - if (bench) bench.stop("decompress-lzw"); - - // deinterlace - if (frame.interlaced) { - if (bench) bench.start("deinterlace"); - pixeldata = deinterlacePixels(pixeldata, frame.w, frame.h) - if (bench) bench.stop("deinterlace"); - } - - // canvas-ize - if (bench) bench.start("pixeldata-to-canvas"); - makeFullFrame(pixeldata, gif, frameNum); - if (bench) bench.stop("pixeldata-to-canvas"); - - // todo: queue this better - var func = render.bind(undefined, gif, {"frameNum": frameNum+1}); - // queueOrNextTick(func); - nextTick(func); - // setZeroTimeout(func); - //setTimeout(func, 1) // otherwise progress won't show in chrome - -}; - -var makeFullFrame = function(pixeldata, gif, frameNum){ - - var frame = gif.frames[frameNum], - ctx = frame.ctx; - - if (frameNum === 0){ // don't need previous frame info to do disposal if it's the first frame - ctx.putImageData(pixelDataToImageData(pixeldata, gif, frame), frame.x, frame.y, - 0,0,frame.w,frame.h); - return; - } - - var prevFrameNum = frameNum-1, - prevFrame = gif.frames[prevFrameNum], - prevCanvas = prevFrame.ctx.canvas, - rawCtx; - - // disposal method is 0 (unspecified) or 1 (do not dispose) - // do nothing, paste new frame image over old one - if (prevFrame.disposalMethod === 0 || prevFrame.disposalMethod === 1){ - rawCtx = makeRawFrameAsContext(gif, frameNum, pixeldata); - ctx.drawImage(prevCanvas, 0, 0) - ctx.drawImage(rawCtx.canvas, 0,0,frame.w,frame.h, frame.x,frame.y,frame.w,frame.h) - } - - // disposal method is 2 (restore to background color) - // but everyone just restores to transparency - // see notes on http://www.imagemagick.org/Usage/anim_basics/#background - if (prevFrame.disposalMethod === 2){ - // fast path... whole frame cleared - if (prevFrame.x === 0 && prevFrame.y === 0 && prevFrame.w === gif.w && prevFrame.h === gif.h) { - // var rawContext = makeRawFrameAsContext(gif, frameNum) - // frame.context.drawImage(rawContext.canvas, frame.x, frame.y); - ctx.putImageData(makeRawFrameAsImageData(gif, frameNum, pixeldata), frame.x, frame.y, - 0,0,frame.w,frame.h); - } else { // draw the edges of the previous frame and then draw the current frame overtop - /* - .__________. - |__________| - | | | | - | | | | - |_|______|_| - |__________| - */ - //top - if (prevFrame.y > 0) - ctx.drawImage(prevCanvas, 0,0, gif.w,prevFrame.y, - 0,0, gif.w,prevFrame.y); - //left - if (prevFrame.x > 0) - ctx.drawImage(prevCanvas, 0,prevFrame.y, prevFrame.x,prevFrame.h, - 0,prevFrame.y, prevFrame.x,prevFrame.h); - // right - if (prevFrame.x+prevFrame.w < gif.w) - ctx.drawImage(prevCanvas, prevFrame.x+prevFrame.w, prevFrame.y, (gif.w-prevFrame.x-prevFrame.w),prevFrame.h, - prevFrame.x+prevFrame.w, prevFrame.y, (gif.w-prevFrame.x-prevFrame.w),prevFrame.h); - // bottom - if (prevFrame.y+prevFrame.h < gif.h) - ctx.drawImage(prevCanvas, 0,prevFrame.y+prevFrame.h, gif.w,(gif.h-prevFrame.y-prevFrame.h), - 0,prevFrame.y+prevFrame.h, gif.w,(gif.h-prevFrame.y-prevFrame.h)) - - rawCtx = makeRawFrameAsContext(gif, frameNum, pixeldata); - ctx.drawImage(rawCtx.canvas, 0,0,frame.w,frame.h, frame.x,frame.y,frame.w,frame.h); - - } - } - - // disposal method is 3 (restore to previous) - if (prevFrame.disposalMethod === 3){ - // look for last previous frame that doesn't have "previous" disposal method - while(prevFrameNum > 0 && gif.frames[prevFrameNum].disposalMethod === 3) prevFrameNum -= 1; - prevFrame = gif.frames[prevFrameNum] - // console.log(prevFrameNum) - if (prevFrame.disposalMethod != 3) ctx.drawImage(prevFrame.ctx.canvas, 0, 0) - rawCtx = makeRawFrameAsContext(gif, frameNum, pixeldata); - ctx.drawImage(rawCtx.canvas, 0,0,frame.w,frame.h, frame.x, frame.y,frame.w,frame.h) - } - -} - -var makeRawFrameAsContext = function(gif, frameNum, pixeldata){ - // cache a context to reuse - if (makeRawFrameAsContext.ctx && - makeRawFrameAsContext.ctx.canvas.width === gif.w && - makeRawFrameAsContext.ctx.canvas.height === gif.h) { - var ctx = makeRawFrameAsContext.ctx; - } else { - var ctx = makeRawFrameAsContext.ctx = create2d(gif.w, gif.h); - } - - var frame = gif.frames[frameNum]; - pixeldata = pixeldata || frame.pixelData; - var palette = ("palette" in frame) ? frame.palette : gif.palette; - var transparentIndex = ("transparentIndex" in frame) ? frame.transparentIndex : -1; - - if (transparentIndex > -1) palette[(transparentIndex*4)+3] = 0; - - var rawImageData = pixelData2imageData(gif, palette, pixeldata, frame.w, frame.h, transparentIndex); - - ctx.putImageData(rawImageData, 0, 0, 0,0,frame.w,frame.h) - - return ctx - - // return imageData2contextDirty(rawImageData, 0,0,frame.w,frame.h); -}; - -var makeRawFrameAsImageData = function(gif, frameNum, pixeldata){ - var frame = gif.frames[frameNum]; - pixeldata = pixeldata || frame.pixelData; - var palette = ("palette" in frame) ? frame.palette : gif.palette; - var transparentIndex = ("transparentIndex" in frame) ? frame.transparentIndex : -1; - - if (transparentIndex > -1) palette[(transparentIndex*4)+3] = 0; - - return pixelData2imageData(gif, palette, pixeldata, frame.w, frame.h, transparentIndex) -}; - -var pixelDataToImageData = function(pixeldata, gif, frame){ - // var frame = gif.frames[frameNum]; - var palette = ("palette" in frame) ? frame.palette : gif.palette; - var transparentIndex = ("transparentIndex" in frame) ? frame.transparentIndex : -1; - - if (transparentIndex > -1) palette[(transparentIndex*4)+3] = 0; - - return pixelData2imageData(gif, palette, pixeldata, frame.w, frame.h, transparentIndex) -}; - - -var imageData2context = function(imageData){ - var ctx = create2d(imageData.width, imageData.height) - ctx.putImageData(imageData, 0, 0) - return ctx -}; -var imageData2contextDirty = function(imageData, dx,dy,dw,dh){ - var ctx = create2d(imageData.width, imageData.height) - ctx.putImageData(imageData, 0, 0, dx,dy,dw,dh) - return ctx -}; - -var pixelData2imageData = function(gif, palette, pixeldata, w, h, transparentIndex){ - if (pixelData2imageData.imagedata && - pixelData2imageData.imagedata.width === gif.w && - pixelData2imageData.imagedata.height === gif.h) { - var imagedata = pixelData2imageData.imagedata; - } else { - var imagedata = pixelData2imageData.imagedata = createImageData(gif.w, gif.h); - } - - var data = imagedata.data - // var i = pixeldata.length; - var i = 0; - - for (var y=0; y>= code_size; - bits -= code_size; - - if (code > available) { - console.log(":("); - // console.log(num2bin(code)); - // break; - } // if we get here something bad happened ;( - if (code === end_of_information) { console.log("fuck"); break}; - if (code === clear) { - 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; - // ^^ timb: not needed? - // Add a new string to the string table, - if (available >= MaxStack) { /*console.log("maxstack!");*/ /*break;*/ } - pixelStack[top++] = first; - prefix[available] = old_code; - suffix[available] = first; - available++; - /* why does == have higher precedence than & ? - i just looked it up and i think it is because javascript - has basically the same precedence as C and - "once upon a time, C didn't have the logical operators && and ||, - and the bitwise operators & and | did double duty." - */ - if ((available & code_mask) === 0 && available < MaxStack){ - code_size++; - code_mask += available; - } - old_code = in_code; - } - - top--; - if (paletteRemap) - pixels[pi++] = paletteRemap[pixelStack[top]]; - else - pixels[pi++] = pixelStack[top]; - i++; - } - - // not needed: typed arrays init'd to 0 already - // for (i = pi, len=pixels.length; i < len; i++) - // pixels[i] = 0; // clear missing pixels - - - return pixels -}; - -var MaxStack = 4096; -// lzwImageData.prefix = new Uint16Array(MaxStack*2) -// lzwImageData.suffix = new Uint8Array(MaxStack) - -module.exports = lzwImageData; -},{}]},{},[1]); -- cgit v1.2.3-70-g09d2