提交
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								example/js/6520.pbf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example/js/6520.pbf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								example/js/94.pbf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example/js/94.pbf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										990
									
								
								example/js/libgif.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										990
									
								
								example/js/libgif.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,990 @@ | ||||
| /* | ||||
| 	SuperGif | ||||
|  | ||||
| 	Example usage: | ||||
|  | ||||
| 		<img src="./example1_preview.gif" rel:animated_src="./example1.gif" width="360" height="360" rel:auto_play="1" /> | ||||
|  | ||||
| 		<script type="text/javascript"> | ||||
| 			$$('img').each(function (img_tag) { | ||||
| 				if (/.*\.gif/.test(img_tag.src)) { | ||||
| 					var rub = new SuperGif({ gif: img_tag } ); | ||||
| 					rub.load(); | ||||
| 				} | ||||
| 			}); | ||||
| 		</script> | ||||
|  | ||||
| 	Image tag attributes: | ||||
|  | ||||
| 		rel:animated_src -	If this url is specified, it's loaded into the player instead of src. | ||||
| 							This allows a preview frame to be shown until animated gif data is streamed into the canvas | ||||
|  | ||||
| 		rel:auto_play -		Defaults to 1 if not specified. If set to zero, a call to the play() method is needed | ||||
|  | ||||
| 	Constructor options args | ||||
|  | ||||
| 		gif 				Required. The DOM element of an img tag. | ||||
| 		loop_mode			Optional. Setting this to false will force disable looping of the gif. | ||||
| 		auto_play 			Optional. Same as the rel:auto_play attribute above, this arg overrides the img tag info. | ||||
| 		max_width			Optional. Scale images over max_width down to max_width. Helpful with mobile. | ||||
|  		on_end				Optional. Add a callback for when the gif reaches the end of a single loop (one iteration). The first argument passed will be the gif HTMLElement. | ||||
| 		loop_delay			Optional. The amount of time to pause (in ms) after each single loop (iteration). | ||||
| 		draw_while_loading	Optional. Determines whether the gif will be drawn to the canvas whilst it is loaded. | ||||
| 		show_progress_bar	Optional. Only applies when draw_while_loading is set to true. | ||||
|  | ||||
| 	Instance methods | ||||
|  | ||||
| 		// loading | ||||
| 		load( callback )		Loads the gif specified by the src or rel:animated_src sttributie of the img tag into a canvas element and then calls callback if one is passed | ||||
| 		load_url( src, callback )	Loads the gif file specified in the src argument into a canvas element and then calls callback if one is passed | ||||
|  | ||||
| 		// play controls | ||||
| 		play -				Start playing the gif | ||||
| 		pause -				Stop playing the gif | ||||
| 		move_to(i) -		Move to frame i of the gif | ||||
| 		move_relative(i) -	Move i frames ahead (or behind if i < 0) | ||||
|  | ||||
| 		// getters | ||||
| 		get_canvas			The canvas element that the gif is playing in. Handy for assigning event handlers to. | ||||
| 		get_playing			Whether or not the gif is currently playing | ||||
| 		get_loading			Whether or not the gif has finished loading/parsing | ||||
| 		get_auto_play		Whether or not the gif is set to play automatically | ||||
| 		get_length			The number of frames in the gif | ||||
| 		get_current_frame	The index of the currently displayed frame of the gif | ||||
|  | ||||
| 		For additional customization (viewport inside iframe) these params may be passed: | ||||
| 		c_w, c_h - width and height of canvas | ||||
| 		vp_t, vp_l, vp_ w, vp_h - top, left, width and height of the viewport | ||||
|  | ||||
| 		A bonus: few articles to understand what is going on | ||||
| 			http://enthusiasms.org/post/16976438906 | ||||
| 			http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp | ||||
| 			http://humpy77.deviantart.com/journal/Frame-Delay-Times-for-Animated-GIFs-214150546 | ||||
|  | ||||
| */ | ||||
| (function (root, factory) { | ||||
|     if (typeof define === 'function' && define.amd) { | ||||
|         define([], factory); | ||||
|     } else if (typeof exports === 'object') { | ||||
|         module.exports = factory(); | ||||
|     } else { | ||||
|         root.SuperGif = factory(); | ||||
|     } | ||||
| }(this, function () { | ||||
|     // Generic functions | ||||
|     var bitsToNum = function (ba) { | ||||
|         return ba.reduce(function (s, n) { | ||||
|             return s * 2 + n; | ||||
|         }, 0); | ||||
|     }; | ||||
|  | ||||
|     var byteToBitArr = function (bite) { | ||||
|         var a = []; | ||||
|         for (var i = 7; i >= 0; i--) { | ||||
|             a.push( !! (bite & (1 << i))); | ||||
|         } | ||||
|         return a; | ||||
|     }; | ||||
|  | ||||
|     // Stream | ||||
|     /** | ||||
|      * @constructor | ||||
|      */ | ||||
|     // Make compiler happy. | ||||
|     var Stream = function (data) { | ||||
|         this.data = data; | ||||
|         this.len = this.data.length; | ||||
|         this.pos = 0; | ||||
|  | ||||
|         this.readByte = function () { | ||||
|             if (this.pos >= this.data.length) { | ||||
|                 throw new Error('Attempted to read past end of stream.'); | ||||
|             } | ||||
|             if (data instanceof Uint8Array) | ||||
|                 return data[this.pos++]; | ||||
|             else | ||||
|                 return data.charCodeAt(this.pos++) & 0xFF; | ||||
|         }; | ||||
|  | ||||
|         this.readBytes = function (n) { | ||||
|             var bytes = []; | ||||
|             for (var i = 0; i < n; i++) { | ||||
|                 bytes.push(this.readByte()); | ||||
|             } | ||||
|             return bytes; | ||||
|         }; | ||||
|  | ||||
|         this.read = function (n) { | ||||
|             var s = ''; | ||||
|             for (var i = 0; i < n; i++) { | ||||
|                 s += String.fromCharCode(this.readByte()); | ||||
|             } | ||||
|             return s; | ||||
|         }; | ||||
|  | ||||
|         this.readUnsigned = function () { // Little-endian. | ||||
|             var a = this.readBytes(2); | ||||
|             return (a[1] << 8) + a[0]; | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     var lzwDecode = function (minCodeSize, data) { | ||||
|         // TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String? | ||||
|         var pos = 0; // Maybe this streaming thing should be merged with the Stream? | ||||
|         var readCode = function (size) { | ||||
|             var code = 0; | ||||
|             for (var i = 0; i < size; i++) { | ||||
|                 if (data.charCodeAt(pos >> 3) & (1 << (pos & 7))) { | ||||
|                     code |= 1 << i; | ||||
|                 } | ||||
|                 pos++; | ||||
|             } | ||||
|             return code; | ||||
|         }; | ||||
|  | ||||
|         var output = []; | ||||
|  | ||||
|         var clearCode = 1 << minCodeSize; | ||||
|         var eoiCode = clearCode + 1; | ||||
|  | ||||
|         var codeSize = minCodeSize + 1; | ||||
|  | ||||
|         var dict = []; | ||||
|  | ||||
|         var clear = function () { | ||||
|             dict = []; | ||||
|             codeSize = minCodeSize + 1; | ||||
|             for (var i = 0; i < clearCode; i++) { | ||||
|                 dict[i] = [i]; | ||||
|             } | ||||
|             dict[clearCode] = []; | ||||
|             dict[eoiCode] = null; | ||||
|  | ||||
|         }; | ||||
|  | ||||
|         var code; | ||||
|         var last; | ||||
|  | ||||
|         while (true) { | ||||
|             last = code; | ||||
|             code = readCode(codeSize); | ||||
|  | ||||
|             if (code === clearCode) { | ||||
|                 clear(); | ||||
|                 continue; | ||||
|             } | ||||
|             if (code === eoiCode) break; | ||||
|  | ||||
|             if (code < dict.length) { | ||||
|                 if (last !== clearCode) { | ||||
|                     dict.push(dict[last].concat(dict[code][0])); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 if (code !== dict.length) throw new Error('Invalid LZW code.'); | ||||
|                 dict.push(dict[last].concat(dict[last][0])); | ||||
|             } | ||||
|             output.push.apply(output, dict[code]); | ||||
|  | ||||
|             if (dict.length === (1 << codeSize) && codeSize < 12) { | ||||
|                 // If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long. | ||||
|                 codeSize++; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // I don't know if this is technically an error, but some GIFs do it. | ||||
|         //if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.'); | ||||
|         return output; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     // The actual parsing; returns an object with properties. | ||||
|     var parseGIF = function (st, handler) { | ||||
|         handler || (handler = {}); | ||||
|  | ||||
|         // LZW (GIF-specific) | ||||
|         var parseCT = function (entries) { // Each entry is 3 bytes, for RGB. | ||||
|             var ct = []; | ||||
|             for (var i = 0; i < entries; i++) { | ||||
|                 ct.push(st.readBytes(3)); | ||||
|             } | ||||
|             return ct; | ||||
|         }; | ||||
|  | ||||
|         var readSubBlocks = function () { | ||||
|             var size, data; | ||||
|             data = ''; | ||||
|             do { | ||||
|                 size = st.readByte(); | ||||
|                 data += st.read(size); | ||||
|             } while (size !== 0); | ||||
|             return data; | ||||
|         }; | ||||
|  | ||||
|         var parseHeader = function () { | ||||
|             var hdr = {}; | ||||
|             hdr.sig = st.read(3); | ||||
|             hdr.ver = st.read(3); | ||||
|             if (hdr.sig !== 'GIF') throw new Error('Not a GIF file.'); // XXX: This should probably be handled more nicely. | ||||
|             hdr.width = st.readUnsigned(); | ||||
|             hdr.height = st.readUnsigned(); | ||||
|  | ||||
|             var bits = byteToBitArr(st.readByte()); | ||||
|             hdr.gctFlag = bits.shift(); | ||||
|             hdr.colorRes = bitsToNum(bits.splice(0, 3)); | ||||
|             hdr.sorted = bits.shift(); | ||||
|             hdr.gctSize = bitsToNum(bits.splice(0, 3)); | ||||
|  | ||||
|             hdr.bgColor = st.readByte(); | ||||
|             hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64 | ||||
|             if (hdr.gctFlag) { | ||||
|                 hdr.gct = parseCT(1 << (hdr.gctSize + 1)); | ||||
|             } | ||||
|             handler.hdr && handler.hdr(hdr); | ||||
|         }; | ||||
|  | ||||
|         var parseExt = function (block) { | ||||
|             var parseGCExt = function (block) { | ||||
|                 var blockSize = st.readByte(); // Always 4 | ||||
|                 var bits = byteToBitArr(st.readByte()); | ||||
|                 block.reserved = bits.splice(0, 3); // Reserved; should be 000. | ||||
|                 block.disposalMethod = bitsToNum(bits.splice(0, 3)); | ||||
|                 block.userInput = bits.shift(); | ||||
|                 block.transparencyGiven = bits.shift(); | ||||
|  | ||||
|                 block.delayTime = st.readUnsigned(); | ||||
|  | ||||
|                 block.transparencyIndex = st.readByte(); | ||||
|  | ||||
|                 block.terminator = st.readByte(); | ||||
|  | ||||
|                 handler.gce && handler.gce(block); | ||||
|             }; | ||||
|  | ||||
|             var parseComExt = function (block) { | ||||
|                 block.comment = readSubBlocks(); | ||||
|                 handler.com && handler.com(block); | ||||
|             }; | ||||
|  | ||||
|             var parsePTExt = function (block) { | ||||
|                 // No one *ever* uses this. If you use it, deal with parsing it yourself. | ||||
|                 var blockSize = st.readByte(); // Always 12 | ||||
|                 block.ptHeader = st.readBytes(12); | ||||
|                 block.ptData = readSubBlocks(); | ||||
|                 handler.pte && handler.pte(block); | ||||
|             }; | ||||
|  | ||||
|             var parseAppExt = function (block) { | ||||
|                 var parseNetscapeExt = function (block) { | ||||
|                     var blockSize = st.readByte(); // Always 3 | ||||
|                     block.unknown = st.readByte(); // ??? Always 1? What is this? | ||||
|                     block.iterations = st.readUnsigned(); | ||||
|                     block.terminator = st.readByte(); | ||||
|                     handler.app && handler.app.NETSCAPE && handler.app.NETSCAPE(block); | ||||
|                 }; | ||||
|  | ||||
|                 var parseUnknownAppExt = function (block) { | ||||
|                     block.appData = readSubBlocks(); | ||||
|                     // FIXME: This won't work if a handler wants to match on any identifier. | ||||
|                     handler.app && handler.app[block.identifier] && handler.app[block.identifier](block); | ||||
|                 }; | ||||
|  | ||||
|                 var blockSize = st.readByte(); // Always 11 | ||||
|                 block.identifier = st.read(8); | ||||
|                 block.authCode = st.read(3); | ||||
|                 switch (block.identifier) { | ||||
|                     case 'NETSCAPE': | ||||
|                         parseNetscapeExt(block); | ||||
|                         break; | ||||
|                     default: | ||||
|                         parseUnknownAppExt(block); | ||||
|                         break; | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             var parseUnknownExt = function (block) { | ||||
|                 block.data = readSubBlocks(); | ||||
|                 handler.unknown && handler.unknown(block); | ||||
|             }; | ||||
|  | ||||
|             block.label = st.readByte(); | ||||
|             switch (block.label) { | ||||
|                 case 0xF9: | ||||
|                     block.extType = 'gce'; | ||||
|                     parseGCExt(block); | ||||
|                     break; | ||||
|                 case 0xFE: | ||||
|                     block.extType = 'com'; | ||||
|                     parseComExt(block); | ||||
|                     break; | ||||
|                 case 0x01: | ||||
|                     block.extType = 'pte'; | ||||
|                     parsePTExt(block); | ||||
|                     break; | ||||
|                 case 0xFF: | ||||
|                     block.extType = 'app'; | ||||
|                     parseAppExt(block); | ||||
|                     break; | ||||
|                 default: | ||||
|                     block.extType = 'unknown'; | ||||
|                     parseUnknownExt(block); | ||||
|                     break; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         var parseImg = function (img) { | ||||
|             var deinterlace = function (pixels, width) { | ||||
|                 // Of course this defeats the purpose of interlacing. And it's *probably* | ||||
|                 // the least efficient way it's ever been implemented. But nevertheless... | ||||
|                 var newPixels = new Array(pixels.length); | ||||
|                 var rows = pixels.length / width; | ||||
|                 var cpRow = function (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; | ||||
|             }; | ||||
|  | ||||
|             img.leftPos = st.readUnsigned(); | ||||
|             img.topPos = st.readUnsigned(); | ||||
|             img.width = st.readUnsigned(); | ||||
|             img.height = st.readUnsigned(); | ||||
|  | ||||
|             var bits = byteToBitArr(st.readByte()); | ||||
|             img.lctFlag = bits.shift(); | ||||
|             img.interlaced = bits.shift(); | ||||
|             img.sorted = bits.shift(); | ||||
|             img.reserved = bits.splice(0, 2); | ||||
|             img.lctSize = bitsToNum(bits.splice(0, 3)); | ||||
|  | ||||
|             if (img.lctFlag) { | ||||
|                 img.lct = parseCT(1 << (img.lctSize + 1)); | ||||
|             } | ||||
|  | ||||
|             img.lzwMinCodeSize = st.readByte(); | ||||
|  | ||||
|             var lzwData = readSubBlocks(); | ||||
|  | ||||
|             img.pixels = lzwDecode(img.lzwMinCodeSize, lzwData); | ||||
|  | ||||
|             if (img.interlaced) { // Move | ||||
|                 img.pixels = deinterlace(img.pixels, img.width); | ||||
|             } | ||||
|  | ||||
|             handler.img && handler.img(img); | ||||
|         }; | ||||
|  | ||||
|         var parseBlock = function () { | ||||
|             var block = {}; | ||||
|             block.sentinel = st.readByte(); | ||||
|  | ||||
|             switch (String.fromCharCode(block.sentinel)) { // For ease of matching | ||||
|                 case '!': | ||||
|                     block.type = 'ext'; | ||||
|                     parseExt(block); | ||||
|                     break; | ||||
|                 case ',': | ||||
|                     block.type = 'img'; | ||||
|                     parseImg(block); | ||||
|                     break; | ||||
|                 case ';': | ||||
|                     block.type = 'eof'; | ||||
|                     handler.eof && handler.eof(block); | ||||
|                     break; | ||||
|                 default: | ||||
|                     throw new Error('Unknown block: 0x' + block.sentinel.toString(16)); // TODO: Pad this with a 0. | ||||
|             } | ||||
|  | ||||
|             if (block.type !== 'eof') setTimeout(parseBlock, 0); | ||||
|         }; | ||||
|  | ||||
|         var parse = function () { | ||||
|             parseHeader(); | ||||
|             setTimeout(parseBlock, 0); | ||||
|         }; | ||||
|  | ||||
|         parse(); | ||||
|     }; | ||||
|  | ||||
|     var SuperGif = function ( opts ) { | ||||
|         var options = { | ||||
|             //viewport position | ||||
|             vp_l: 0, | ||||
|             vp_t: 0, | ||||
|             vp_w: null, | ||||
|             vp_h: null, | ||||
|             //canvas sizes | ||||
|             c_w: null, | ||||
|             c_h: null | ||||
|         }; | ||||
|         for (var i in opts ) { options[i] = opts[i] } | ||||
|         if (options.vp_w && options.vp_h) options.is_vp = true; | ||||
|  | ||||
|         var stream; | ||||
|         var hdr; | ||||
|  | ||||
|         var loadError = null; | ||||
|         var loading = false; | ||||
|  | ||||
|         var transparency = null; | ||||
|         var delay = null; | ||||
|         var disposalMethod = null; | ||||
|         var disposalRestoreFromIdx = null; | ||||
|         var lastDisposalMethod = null; | ||||
|         var frame = null; | ||||
|         var lastImg = null; | ||||
|  | ||||
|         var playing = true; | ||||
|         var forward = true; | ||||
|  | ||||
|         var ctx_scaled = false; | ||||
|  | ||||
|         var frames = []; | ||||
|         var frameOffsets = []; // elements have .x and .y properties | ||||
|  | ||||
|         var gif = options.gif; | ||||
|         if (typeof options.auto_play == 'undefined') | ||||
|             options.auto_play = (!gif.getAttribute('rel:auto_play') || gif.getAttribute('rel:auto_play') == '1'); | ||||
|  | ||||
|         var onEndListener = (options.hasOwnProperty('on_end') ? options.on_end : null); | ||||
|         var loopDelay = (options.hasOwnProperty('loop_delay') ? options.loop_delay : 0); | ||||
|         var overrideLoopMode = (options.hasOwnProperty('loop_mode') ? options.loop_mode : 'auto'); | ||||
|         var drawWhileLoading = (options.hasOwnProperty('draw_while_loading') ? options.draw_while_loading : true); | ||||
|         var showProgressBar = drawWhileLoading ? (options.hasOwnProperty('show_progress_bar') ? options.show_progress_bar : true) : false; | ||||
|         var progressBarHeight = (options.hasOwnProperty('progressbar_height') ? options.progressbar_height : 25); | ||||
|         var progressBarBackgroundColor = (options.hasOwnProperty('progressbar_background_color') ? options.progressbar_background_color : 'rgba(255,255,255,0.4)'); | ||||
|         var progressBarForegroundColor = (options.hasOwnProperty('progressbar_foreground_color') ? options.progressbar_foreground_color : 'rgba(255,0,22,.8)'); | ||||
|  | ||||
|         var clear = function () { | ||||
|             transparency = null; | ||||
|             delay = null; | ||||
|             lastDisposalMethod = disposalMethod; | ||||
|             disposalMethod = null; | ||||
|             frame = null; | ||||
|         }; | ||||
|  | ||||
|         // XXX: There's probably a better way to handle catching exceptions when | ||||
|         // callbacks are involved. | ||||
|         var doParse = function () { | ||||
|             try { | ||||
|                 parseGIF(stream, handler); | ||||
|             } | ||||
|             catch (err) { | ||||
|                 doLoadError('parse'); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         var doText = function (text) { | ||||
|             toolbar.innerHTML = text; // innerText? Escaping? Whatever. | ||||
|             toolbar.style.visibility = 'visible'; | ||||
|         }; | ||||
|  | ||||
|         var setSizes = function(w, h) { | ||||
|             canvas.width = w * get_canvas_scale(); | ||||
|             canvas.height = h * get_canvas_scale(); | ||||
|             toolbar.style.minWidth = ( w * get_canvas_scale() ) + 'px'; | ||||
|  | ||||
|             tmpCanvas.width = w; | ||||
|             tmpCanvas.height = h; | ||||
|             tmpCanvas.style.width = w + 'px'; | ||||
|             tmpCanvas.style.height = h + 'px'; | ||||
|             tmpCanvas.getContext('2d').setTransform(1, 0, 0, 1, 0, 0); | ||||
|         }; | ||||
|  | ||||
|         var setFrameOffset = function(frame, offset) { | ||||
|             if (!frameOffsets[frame]) { | ||||
|                 frameOffsets[frame] = offset; | ||||
|                 return; | ||||
|             } | ||||
|             if (typeof offset.x !== 'undefined') { | ||||
|                 frameOffsets[frame].x = offset.x; | ||||
|             } | ||||
|             if (typeof offset.y !== 'undefined') { | ||||
|                 frameOffsets[frame].y = offset.y; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         var doShowProgress = function (pos, length, draw) { | ||||
|             if (draw && showProgressBar) { | ||||
|                 var height = progressBarHeight; | ||||
|                 var left, mid, top, width; | ||||
|                 if (options.is_vp) { | ||||
|                     if (!ctx_scaled) { | ||||
|                         top = (options.vp_t + options.vp_h - height); | ||||
|                         height = height; | ||||
|                         left = options.vp_l; | ||||
|                         mid = left + (pos / length) * options.vp_w; | ||||
|                         width = canvas.width; | ||||
|                     } else { | ||||
|                         top = (options.vp_t + options.vp_h - height) / get_canvas_scale(); | ||||
|                         height = height / get_canvas_scale(); | ||||
|                         left = (options.vp_l / get_canvas_scale() ); | ||||
|                         mid = left + (pos / length) * (options.vp_w / get_canvas_scale()); | ||||
|                         width = canvas.width / get_canvas_scale(); | ||||
|                     } | ||||
|                     //some debugging, draw rect around viewport | ||||
|                     if (false) { | ||||
|                         if (!ctx_scaled) { | ||||
|                             var l = options.vp_l, t = options.vp_t; | ||||
|                             var w = options.vp_w, h = options.vp_h; | ||||
|                         } else { | ||||
|                             var l = options.vp_l/get_canvas_scale(), t = options.vp_t/get_canvas_scale(); | ||||
|                             var w = options.vp_w/get_canvas_scale(), h = options.vp_h/get_canvas_scale(); | ||||
|                         } | ||||
|                         ctx.rect(l,t,w,h); | ||||
|                         ctx.stroke(); | ||||
|                     } | ||||
|                 } | ||||
|                 else { | ||||
|                     top = (canvas.height - height) / (ctx_scaled ? get_canvas_scale() : 1); | ||||
|                     mid = ((pos / length) * canvas.width) / (ctx_scaled ? get_canvas_scale() : 1); | ||||
|                     width = canvas.width / (ctx_scaled ? get_canvas_scale() : 1 ); | ||||
|                     height /= ctx_scaled ? get_canvas_scale() : 1; | ||||
|                 } | ||||
|  | ||||
|                 ctx.fillStyle = progressBarBackgroundColor; | ||||
|                 ctx.fillRect(mid, top, width - mid, height); | ||||
|  | ||||
|                 ctx.fillStyle = progressBarForegroundColor; | ||||
|                 ctx.fillRect(0, top, mid, height); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         var doLoadError = function (originOfError) { | ||||
|             var drawError = function () { | ||||
|                 ctx.fillStyle = 'black'; | ||||
|                 ctx.fillRect(0, 0, options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height); | ||||
|                 ctx.strokeStyle = 'red'; | ||||
|                 ctx.lineWidth = 3; | ||||
|                 ctx.moveTo(0, 0); | ||||
|                 ctx.lineTo(options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height); | ||||
|                 ctx.moveTo(0, options.c_h ? options.c_h : hdr.height); | ||||
|                 ctx.lineTo(options.c_w ? options.c_w : hdr.width, 0); | ||||
|                 ctx.stroke(); | ||||
|             }; | ||||
|  | ||||
|             loadError = originOfError; | ||||
|             hdr = { | ||||
|                 width: gif.width, | ||||
|                 height: gif.height | ||||
|             }; // Fake header. | ||||
|             frames = []; | ||||
|             drawError(); | ||||
|         }; | ||||
|  | ||||
|         var doHdr = function (_hdr) { | ||||
|             hdr = _hdr; | ||||
|             setSizes(hdr.width, hdr.height) | ||||
|         }; | ||||
|  | ||||
|         var doGCE = function (gce) { | ||||
|             pushFrame(); | ||||
|             clear(); | ||||
|             transparency = gce.transparencyGiven ? gce.transparencyIndex : null; | ||||
|             delay = gce.delayTime; | ||||
|             disposalMethod = gce.disposalMethod; | ||||
|             // We don't have much to do with the rest of GCE. | ||||
|         }; | ||||
|  | ||||
|         var pushFrame = function () { | ||||
|             if (!frame) return; | ||||
|             frames.push({ | ||||
|                             data: frame.getImageData(0, 0, hdr.width, hdr.height), | ||||
|                             delay: delay | ||||
|                         }); | ||||
|             frameOffsets.push({ x: 0, y: 0 }); | ||||
|         }; | ||||
|  | ||||
|         var doImg = function (img) { | ||||
|             if (!frame) frame = tmpCanvas.getContext('2d'); | ||||
|  | ||||
|             var currIdx = frames.length; | ||||
|  | ||||
|             //ct = color table, gct = global color table | ||||
|             var ct = img.lctFlag ? img.lct : hdr.gct; // TODO: What if neither exists? | ||||
|  | ||||
|             /* | ||||
|             Disposal method indicates the way in which the graphic is to | ||||
|             be treated after being displayed. | ||||
|  | ||||
|             Values :    0 - No disposal specified. The decoder is | ||||
|                             not required to take any action. | ||||
|                         1 - Do not dispose. The graphic is to be left | ||||
|                             in place. | ||||
|                         2 - Restore to background color. The area used by the | ||||
|                             graphic must be restored to the background color. | ||||
|                         3 - Restore to previous. The decoder is required to | ||||
|                             restore the area overwritten by the graphic with | ||||
|                             what was there prior to rendering the graphic. | ||||
|  | ||||
|                             Importantly, "previous" means the frame state | ||||
|                             after the last disposal of method 0, 1, or 2. | ||||
|             */ | ||||
|             if (currIdx > 0) { | ||||
|                 if (lastDisposalMethod === 3) { | ||||
|                     // Restore to previous | ||||
|                     // If we disposed every frame including first frame up to this point, then we have | ||||
|                     // no composited frame to restore to. In this case, restore to background instead. | ||||
|                     if (disposalRestoreFromIdx !== null) { | ||||
|                     	frame.putImageData(frames[disposalRestoreFromIdx].data, 0, 0); | ||||
|                     } else { | ||||
|                     	frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height); | ||||
|                     } | ||||
|                 } else { | ||||
|                     disposalRestoreFromIdx = currIdx - 1; | ||||
|                 } | ||||
|  | ||||
|                 if (lastDisposalMethod === 2) { | ||||
|                     // Restore to background color | ||||
|                     // Browser implementations historically restore to transparent; we do the same. | ||||
|                     // http://www.wizards-toolkit.org/discourse-server/viewtopic.php?f=1&t=21172#p86079 | ||||
|                     frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height); | ||||
|                 } | ||||
|             } | ||||
|             // else, Undefined/Do not dispose. | ||||
|             // frame contains final pixel data from the last frame; do nothing | ||||
|  | ||||
|             //Get existing pixels for img region after applying disposal method | ||||
|             var imgData = frame.getImageData(img.leftPos, img.topPos, img.width, img.height); | ||||
|  | ||||
|             //apply color table colors | ||||
|             img.pixels.forEach(function (pixel, i) { | ||||
|                 // imgData.data === [R,G,B,A,R,G,B,A,...] | ||||
|                 if (pixel !== transparency) { | ||||
|                     imgData.data[i * 4 + 0] = ct[pixel][0]; | ||||
|                     imgData.data[i * 4 + 1] = ct[pixel][1]; | ||||
|                     imgData.data[i * 4 + 2] = ct[pixel][2]; | ||||
|                     imgData.data[i * 4 + 3] = 255; // Opaque. | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             frame.putImageData(imgData, img.leftPos, img.topPos); | ||||
|  | ||||
|             if (!ctx_scaled) { | ||||
|                 ctx.scale(get_canvas_scale(),get_canvas_scale()); | ||||
|                 ctx_scaled = true; | ||||
|             } | ||||
|  | ||||
|             // We could use the on-page canvas directly, except that we draw a progress | ||||
|             // bar for each image chunk (not just the final image). | ||||
|             if (drawWhileLoading) { | ||||
|                 ctx.drawImage(tmpCanvas, 0, 0); | ||||
|                 drawWhileLoading = options.auto_play; | ||||
|             } | ||||
|  | ||||
|             lastImg = img; | ||||
|         }; | ||||
|  | ||||
|         var player = (function () { | ||||
|             var i = -1; | ||||
|             var iterationCount = 0; | ||||
|  | ||||
|             var showingInfo = false; | ||||
|             var pinned = false; | ||||
|  | ||||
|             /** | ||||
|              * Gets the index of the frame "up next". | ||||
|              * @returns {number} | ||||
|              */ | ||||
|             var getNextFrameNo = function () { | ||||
|                 var delta = (forward ? 1 : -1); | ||||
|                 return (i + delta + frames.length) % frames.length; | ||||
|             }; | ||||
|  | ||||
|             var stepFrame = function (amount) { // XXX: Name is confusing. | ||||
|                 i = i + amount; | ||||
|  | ||||
|                 putFrame(); | ||||
|             }; | ||||
|  | ||||
|             var step = (function () { | ||||
|                 var stepping = false; | ||||
|  | ||||
|                 var completeLoop = function () { | ||||
|                     if (onEndListener !== null) | ||||
|                         onEndListener(gif); | ||||
|                     iterationCount++; | ||||
|  | ||||
|                     if (overrideLoopMode !== false || iterationCount < 0) { | ||||
|                         doStep(); | ||||
|                     } else { | ||||
|                         stepping = false; | ||||
|                         playing = false; | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 var doStep = function () { | ||||
|                     stepping = playing; | ||||
|                     if (!stepping) return; | ||||
|  | ||||
|                     stepFrame(1); | ||||
|                     var delay = frames[i].delay * 10; | ||||
|                     if (!delay) delay = 100; // FIXME: Should this even default at all? What should it be? | ||||
|  | ||||
|                     var nextFrameNo = getNextFrameNo(); | ||||
|                     if (nextFrameNo === 0) { | ||||
|                         delay += loopDelay; | ||||
|                         setTimeout(completeLoop, delay); | ||||
|                     } else { | ||||
|                         setTimeout(doStep, delay); | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 return function () { | ||||
|                     if (!stepping) setTimeout(doStep, 0); | ||||
|                 }; | ||||
|             }()); | ||||
|  | ||||
|             var putFrame = function () { | ||||
|                 var offset; | ||||
|                 i = parseInt(i, 10); | ||||
|  | ||||
|                 if (i > frames.length - 1){ | ||||
|                     i = 0; | ||||
|                 } | ||||
|  | ||||
|                 if (i < 0){ | ||||
|                     i = 0; | ||||
|                 } | ||||
|  | ||||
|                 offset = frameOffsets[i]; | ||||
|  | ||||
|                 tmpCanvas.getContext("2d").putImageData(frames[i].data, offset.x, offset.y); | ||||
|                 ctx.globalCompositeOperation = "copy"; | ||||
|                 ctx.drawImage(tmpCanvas, 0, 0); | ||||
|             }; | ||||
|  | ||||
|             var play = function () { | ||||
|                 playing = true; | ||||
|                 step(); | ||||
|             }; | ||||
|  | ||||
|             var pause = function () { | ||||
|                 playing = false; | ||||
|             }; | ||||
|  | ||||
|  | ||||
|             return { | ||||
|                 init: function () { | ||||
|                     if (loadError) return; | ||||
|  | ||||
|                     if ( ! (options.c_w && options.c_h) ) { | ||||
|                         ctx.scale(get_canvas_scale(),get_canvas_scale()); | ||||
|                     } | ||||
|  | ||||
|                     if (options.auto_play) { | ||||
|                         step(); | ||||
|                     } | ||||
|                     else { | ||||
|                         i = 0; | ||||
|                         putFrame(); | ||||
|                     } | ||||
|                 }, | ||||
|                 step: step, | ||||
|                 play: play, | ||||
|                 pause: pause, | ||||
|                 playing: playing, | ||||
|                 move_relative: stepFrame, | ||||
|                 current_frame: function() { return i; }, | ||||
|                 length: function() { return frames.length }, | ||||
|                 move_to: function ( frame_idx ) { | ||||
|                     i = frame_idx; | ||||
|                     putFrame(); | ||||
|                 } | ||||
|             } | ||||
|         }()); | ||||
|  | ||||
|         var doDecodeProgress = function (draw) { | ||||
|             doShowProgress(stream.pos, stream.data.length, draw); | ||||
|         }; | ||||
|  | ||||
|         var doNothing = function () {}; | ||||
|         /** | ||||
|          * @param{boolean=} draw Whether to draw progress bar or not; this is not idempotent because of translucency. | ||||
|          *                       Note that this means that the text will be unsynchronized with the progress bar on non-frames; | ||||
|          *                       but those are typically so small (GCE etc.) that it doesn't really matter. TODO: Do this properly. | ||||
|          */ | ||||
|         var withProgress = function (fn, draw) { | ||||
|             return function (block) { | ||||
|                 fn(block); | ||||
|                 doDecodeProgress(draw); | ||||
|             }; | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         var handler = { | ||||
|             hdr: withProgress(doHdr), | ||||
|             gce: withProgress(doGCE), | ||||
|             com: withProgress(doNothing), | ||||
|             // I guess that's all for now. | ||||
|             app: { | ||||
|                 // TODO: Is there much point in actually supporting iterations? | ||||
|                 NETSCAPE: withProgress(doNothing) | ||||
|             }, | ||||
|             img: withProgress(doImg, true), | ||||
|             eof: function (block) { | ||||
|                 //toolbar.style.display = ''; | ||||
|                 pushFrame(); | ||||
|                 doDecodeProgress(false); | ||||
|                 if ( ! (options.c_w && options.c_h) ) { | ||||
|                     canvas.width = hdr.width * get_canvas_scale(); | ||||
|                     canvas.height = hdr.height * get_canvas_scale(); | ||||
|                 } | ||||
|                 player.init(); | ||||
|                 loading = false; | ||||
|                 if (load_callback) { | ||||
|                     load_callback(gif); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         var init = function () { | ||||
|             var parent = gif.parentNode; | ||||
|  | ||||
|             var div = document.createElement('div'); | ||||
|             canvas = document.createElement('canvas'); | ||||
|             ctx = canvas.getContext('2d'); | ||||
|             toolbar = document.createElement('div'); | ||||
|  | ||||
|             tmpCanvas = document.createElement('canvas'); | ||||
|  | ||||
|             div.width = canvas.width = gif.width; | ||||
|             div.height = canvas.height = gif.height; | ||||
|             toolbar.style.minWidth = gif.width + 'px'; | ||||
|  | ||||
|             div.className = 'jsgif'; | ||||
|             toolbar.className = 'jsgif_toolbar'; | ||||
|             div.appendChild(canvas); | ||||
|             div.appendChild(toolbar); | ||||
|  | ||||
|             parent.insertBefore(div, gif); | ||||
|             parent.removeChild(gif); | ||||
|  | ||||
|             if (options.c_w && options.c_h) setSizes(options.c_w, options.c_h); | ||||
|             initialized=true; | ||||
|         }; | ||||
|  | ||||
|         var get_canvas_scale = function() { | ||||
|             var scale; | ||||
|             if (options.max_width && hdr && hdr.width > options.max_width) { | ||||
|                 scale = options.max_width / hdr.width; | ||||
|             } | ||||
|             else { | ||||
|                 scale = 1; | ||||
|             } | ||||
|             return scale; | ||||
|         } | ||||
|  | ||||
|         var canvas, ctx, toolbar, tmpCanvas; | ||||
|         var initialized = false; | ||||
|         var load_callback = false; | ||||
|  | ||||
|         var load_setup = function(callback) { | ||||
|             if (loading) return false; | ||||
|             if (callback) load_callback = callback; | ||||
|             else load_callback = false; | ||||
|  | ||||
|             loading = true; | ||||
|             frames = []; | ||||
|             clear(); | ||||
|             disposalRestoreFromIdx = null; | ||||
|             lastDisposalMethod = null; | ||||
|             frame = null; | ||||
|             lastImg = null; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             // play controls | ||||
|             play: player.play, | ||||
|             pause: player.pause, | ||||
|             move_relative: player.move_relative, | ||||
|             move_to: player.move_to, | ||||
|  | ||||
|             // getters for instance vars | ||||
|             get_playing      : function() { return playing }, | ||||
|             get_canvas       : function() { return canvas }, | ||||
|             get_canvas_scale : function() { return get_canvas_scale() }, | ||||
|             get_loading      : function() { return loading }, | ||||
|             get_auto_play    : function() { return options.auto_play }, | ||||
|             get_length       : function() { return player.length() }, | ||||
|             get_current_frame: function() { return player.current_frame() }, | ||||
|             load_url: function(src,callback){ | ||||
|                 if (!load_setup(callback)) return; | ||||
|  | ||||
|                 var h = new XMLHttpRequest(); | ||||
|                 // new browsers (XMLHttpRequest2-compliant) | ||||
|                 h.open('GET', src, true); | ||||
|  | ||||
|                 if ('overrideMimeType' in h) { | ||||
|                     h.overrideMimeType('text/plain; charset=x-user-defined'); | ||||
|                 } | ||||
|  | ||||
|                 // old browsers (XMLHttpRequest-compliant) | ||||
|                 else if ('responseType' in h) { | ||||
|                     h.responseType = 'arraybuffer'; | ||||
|                 } | ||||
|  | ||||
|                 // IE9 (Microsoft.XMLHTTP-compliant) | ||||
|                 else { | ||||
|                     h.setRequestHeader('Accept-Charset', 'x-user-defined'); | ||||
|                 } | ||||
|  | ||||
|                 h.onloadstart = function() { | ||||
|                     // Wait until connection is opened to replace the gif element with a canvas to avoid a blank img | ||||
|                     if (!initialized) init(); | ||||
|                 }; | ||||
|                 h.onload = function(e) { | ||||
|                     if (this.status != 200) { | ||||
|                         doLoadError('xhr - response'); | ||||
|                     } | ||||
|                     // emulating response field for IE9 | ||||
|                     if (!('response' in this)) { | ||||
|                         this.response = new VBArray(this.responseText).toArray().map(String.fromCharCode).join(''); | ||||
|                     } | ||||
|                     var data = this.response; | ||||
|                     if (data.toString().indexOf("ArrayBuffer") > 0) { | ||||
|                         data = new Uint8Array(data); | ||||
|                     } | ||||
|  | ||||
|                     stream = new Stream(data); | ||||
|                     setTimeout(doParse, 0); | ||||
|                 }; | ||||
|                 h.onprogress = function (e) { | ||||
|                     if (e.lengthComputable) doShowProgress(e.loaded, e.total, true); | ||||
|                 }; | ||||
|                 h.onerror = function() { doLoadError('xhr'); }; | ||||
|                 h.send(); | ||||
|             }, | ||||
|             load: function (callback) { | ||||
|                 this.load_url(gif.getAttribute('rel:animated_src') || gif.src,callback); | ||||
|             }, | ||||
|             load_raw: function(arr, callback) { | ||||
|                 if (!load_setup(callback)) return; | ||||
|                 if (!initialized) init(); | ||||
|                 stream = new Stream(arr); | ||||
|                 setTimeout(doParse, 0); | ||||
|             }, | ||||
|             set_frame_offset: setFrameOffset | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     return SuperGif; | ||||
| })); | ||||
|  | ||||
|  | ||||
							
								
								
									
										887
									
								
								example/js/pbf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										887
									
								
								example/js/pbf.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,887 @@ | ||||
| /** | ||||
|  * Minified by jsDelivr using Terser v5.19.2. | ||||
|  * Original file: /npm/pbf@4.0.1/index.js | ||||
|  * | ||||
|  * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files | ||||
|  */ | ||||
| const SHIFT_LEFT_32 = 4294967296, SHIFT_RIGHT_32 = 1 / 4294967296, TEXT_DECODER_MIN_LENGTH = 12, | ||||
|     utf8TextDecoder = "undefined" == typeof TextDecoder ? null : new TextDecoder("utf-8"), PBF_VARINT = 0, | ||||
|     PBF_FIXED64 = 1, PBF_BYTES = 2, PBF_FIXED32 = 5; | ||||
|  | ||||
| class Pbf { | ||||
|     constructor(t = new Uint8Array(16)) { | ||||
|         this.buf = ArrayBuffer.isView(t) ? t : new Uint8Array(t), this.dataView = new DataView(this.buf.buffer), this.pos = 0, this.type = 0, this.length = this.buf.length | ||||
|     } | ||||
|  | ||||
|     readFields(t, e, i = this.length) { | ||||
|         for (; this.pos < i;) { | ||||
|             const i = this.readVarint(), s = i >> 3, r = this.pos; | ||||
|             this.type = 7 & i, t(s, e, this), this.pos === r && this.skip(i) | ||||
|         } | ||||
|         return e | ||||
|     } | ||||
|  | ||||
|     readMessage(t, e) { | ||||
|         return this.readFields(t, e, this.readVarint() + this.pos) | ||||
|     } | ||||
|  | ||||
|     readFixed32() { | ||||
|         const t = this.dataView.getUint32(this.pos, !0); | ||||
|         return this.pos += 4, t | ||||
|     } | ||||
|  | ||||
|     readSFixed32() { | ||||
|         const t = this.dataView.getInt32(this.pos, !0); | ||||
|         return this.pos += 4, t | ||||
|     } | ||||
|  | ||||
|     readFixed64() { | ||||
|         const t = this.dataView.getUint32(this.pos, !0) + 4294967296 * this.dataView.getUint32(this.pos + 4, !0); | ||||
|         return this.pos += 8, t | ||||
|     } | ||||
|  | ||||
|     readSFixed64() { | ||||
|         const t = this.dataView.getUint32(this.pos, !0) + 4294967296 * this.dataView.getInt32(this.pos + 4, !0); | ||||
|         return this.pos += 8, t | ||||
|     } | ||||
|  | ||||
|     readFloat() { | ||||
|         const t = this.dataView.getFloat32(this.pos, !0); | ||||
|         return this.pos += 4, t | ||||
|     } | ||||
|  | ||||
|     readDouble() { | ||||
|         const t = this.dataView.getFloat64(this.pos, !0); | ||||
|         return this.pos += 8, t | ||||
|     } | ||||
|  | ||||
|     readVarint(t) { | ||||
|         const e = this.buf; | ||||
|         let i, s; | ||||
|         return s = e[this.pos++], i = 127 & s, s < 128 ? i : (s = e[this.pos++], i |= (127 & s) << 7, s < 128 ? i : (s = e[this.pos++], i |= (127 & s) << 14, s < 128 ? i : (s = e[this.pos++], i |= (127 & s) << 21, s < 128 ? i : (s = e[this.pos], i |= (15 & s) << 28, readVarintRemainder(i, t, this))))) | ||||
|     } | ||||
|  | ||||
|     readVarint64() { | ||||
|         return this.readVarint(!0) | ||||
|     } | ||||
|  | ||||
|     readSVarint() { | ||||
|         const t = this.readVarint(); | ||||
|         return t % 2 == 1 ? (t + 1) / -2 : t / 2 | ||||
|     } | ||||
|  | ||||
|     readBoolean() { | ||||
|         return Boolean(this.readVarint()) | ||||
|     } | ||||
|  | ||||
|     readString() { | ||||
|         const t = this.readVarint() + this.pos, e = this.pos; | ||||
|         return this.pos = t, t - e >= 12 && utf8TextDecoder ? utf8TextDecoder.decode(this.buf.subarray(e, t)) : readUtf8(this.buf, e, t) | ||||
|     } | ||||
|  | ||||
|     readBytes() { | ||||
|         const t = this.readVarint() + this.pos, e = this.buf.subarray(this.pos, t); | ||||
|         return this.pos = t, e | ||||
|     } | ||||
|  | ||||
|     readPackedVarint(t = [], e) { | ||||
|         const i = this.readPackedEnd(); | ||||
|         for (; this.pos < i;) t.push(this.readVarint(e)); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedSVarint(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readSVarint()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedBoolean(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readBoolean()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedFloat(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readFloat()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedDouble(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readDouble()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedFixed32(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readFixed32()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedSFixed32(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readSFixed32()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedFixed64(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readFixed64()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedSFixed64(t = []) { | ||||
|         const e = this.readPackedEnd(); | ||||
|         for (; this.pos < e;) t.push(this.readSFixed64()); | ||||
|         return t | ||||
|     } | ||||
|  | ||||
|     readPackedEnd() { | ||||
|         return 2 === this.type ? this.readVarint() + this.pos : this.pos + 1 | ||||
|     } | ||||
|  | ||||
|     skip(t) { | ||||
|         const e = 7 & t; | ||||
|         if (0 === e) for (; this.buf[this.pos++] > 127;) ; else if (2 === e) this.pos = this.readVarint() + this.pos; else if (5 === e) this.pos += 4; else { | ||||
|             if (1 !== e) throw new Error(`Unimplemented type: ${e}`); | ||||
|             this.pos += 8 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     writeTag(t, e) { | ||||
|         this.writeVarint(t << 3 | e) | ||||
|     } | ||||
|  | ||||
|     realloc(t) { | ||||
|         let e = this.length || 16; | ||||
|         for (; e < this.pos + t;) e *= 2; | ||||
|         if (e !== this.length) { | ||||
|             const t = new Uint8Array(e); | ||||
|             t.set(this.buf), this.buf = t, this.dataView = new DataView(t.buffer), this.length = e | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     finish() { | ||||
|         return this.length = this.pos, this.pos = 0, this.buf.subarray(0, this.length) | ||||
|     } | ||||
|  | ||||
|     writeFixed32(t) { | ||||
|         this.realloc(4), this.dataView.setInt32(this.pos, t, !0), this.pos += 4 | ||||
|     } | ||||
|  | ||||
|     writeSFixed32(t) { | ||||
|         this.realloc(4), this.dataView.setInt32(this.pos, t, !0), this.pos += 4 | ||||
|     } | ||||
|  | ||||
|     writeFixed64(t) { | ||||
|         this.realloc(8), this.dataView.setInt32(this.pos, -1 & t, !0), this.dataView.setInt32(this.pos + 4, Math.floor(t * SHIFT_RIGHT_32), !0), this.pos += 8 | ||||
|     } | ||||
|  | ||||
|     writeSFixed64(t) { | ||||
|         this.realloc(8), this.dataView.setInt32(this.pos, -1 & t, !0), this.dataView.setInt32(this.pos + 4, Math.floor(t * SHIFT_RIGHT_32), !0), this.pos += 8 | ||||
|     } | ||||
|  | ||||
|     writeVarint(t) { | ||||
|         (t = +t || 0) > 268435455 || t < 0 ? writeBigVarint(t, this) : (this.realloc(4), this.buf[this.pos++] = 127 & t | (t > 127 ? 128 : 0), t <= 127 || (this.buf[this.pos++] = 127 & (t >>>= 7) | (t > 127 ? 128 : 0), t <= 127 || (this.buf[this.pos++] = 127 & (t >>>= 7) | (t > 127 ? 128 : 0), t <= 127 || (this.buf[this.pos++] = t >>> 7 & 127)))) | ||||
|     } | ||||
|  | ||||
|     writeSVarint(t) { | ||||
|         this.writeVarint(t < 0 ? 2 * -t - 1 : 2 * t) | ||||
|     } | ||||
|  | ||||
|     writeBoolean(t) { | ||||
|         this.writeVarint(+t) | ||||
|     } | ||||
|  | ||||
|     writeString(t) { | ||||
|         t = String(t), this.realloc(4 * t.length), this.pos++; | ||||
|         const e = this.pos; | ||||
|         this.pos = writeUtf8(this.buf, t, this.pos); | ||||
|         const i = this.pos - e; | ||||
|         i >= 128 && makeRoomForExtraLength(e, i, this), this.pos = e - 1, this.writeVarint(i), this.pos += i | ||||
|     } | ||||
|  | ||||
|     writeFloat(t) { | ||||
|         this.realloc(4), this.dataView.setFloat32(this.pos, t, !0), this.pos += 4 | ||||
|     } | ||||
|  | ||||
|     writeDouble(t) { | ||||
|         this.realloc(8), this.dataView.setFloat64(this.pos, t, !0), this.pos += 8 | ||||
|     } | ||||
|  | ||||
|     writeBytes(t) { | ||||
|         const e = t.length; | ||||
|         this.writeVarint(e), this.realloc(e); | ||||
|         for (let i = 0; i < e; i++) this.buf[this.pos++] = t[i] | ||||
|     } | ||||
|  | ||||
|     writeRawMessage(t, e) { | ||||
|         this.pos++; | ||||
|         const i = this.pos; | ||||
|         t(e, this); | ||||
|         const s = this.pos - i; | ||||
|         s >= 128 && makeRoomForExtraLength(i, s, this), this.pos = i - 1, this.writeVarint(s), this.pos += s | ||||
|     } | ||||
|  | ||||
|     writeMessage(t, e, i) { | ||||
|         this.writeTag(t, 2), this.writeRawMessage(e, i) | ||||
|     } | ||||
|  | ||||
|     writePackedVarint(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedVarint, e) | ||||
|     } | ||||
|  | ||||
|     writePackedSVarint(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedSVarint, e) | ||||
|     } | ||||
|  | ||||
|     writePackedBoolean(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedBoolean, e) | ||||
|     } | ||||
|  | ||||
|     writePackedFloat(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedFloat, e) | ||||
|     } | ||||
|  | ||||
|     writePackedDouble(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedDouble, e) | ||||
|     } | ||||
|  | ||||
|     writePackedFixed32(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedFixed32, e) | ||||
|     } | ||||
|  | ||||
|     writePackedSFixed32(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedSFixed32, e) | ||||
|     } | ||||
|  | ||||
|     writePackedFixed64(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedFixed64, e) | ||||
|     } | ||||
|  | ||||
|     writePackedSFixed64(t, e) { | ||||
|         e.length && this.writeMessage(t, writePackedSFixed64, e) | ||||
|     } | ||||
|  | ||||
|     writeBytesField(t, e) { | ||||
|         this.writeTag(t, 2), this.writeBytes(e) | ||||
|     } | ||||
|  | ||||
|     writeFixed32Field(t, e) { | ||||
|         this.writeTag(t, 5), this.writeFixed32(e) | ||||
|     } | ||||
|  | ||||
|     writeSFixed32Field(t, e) { | ||||
|         this.writeTag(t, 5), this.writeSFixed32(e) | ||||
|     } | ||||
|  | ||||
|     writeFixed64Field(t, e) { | ||||
|         this.writeTag(t, 1), this.writeFixed64(e) | ||||
|     } | ||||
|  | ||||
|     writeSFixed64Field(t, e) { | ||||
|         this.writeTag(t, 1), this.writeSFixed64(e) | ||||
|     } | ||||
|  | ||||
|     writeVarintField(t, e) { | ||||
|         this.writeTag(t, 0), this.writeVarint(e) | ||||
|     } | ||||
|  | ||||
|     writeSVarintField(t, e) { | ||||
|         this.writeTag(t, 0), this.writeSVarint(e) | ||||
|     } | ||||
|  | ||||
|     writeStringField(t, e) { | ||||
|         this.writeTag(t, 2), this.writeString(e) | ||||
|     } | ||||
|  | ||||
|     writeFloatField(t, e) { | ||||
|         this.writeTag(t, 5), this.writeFloat(e) | ||||
|     } | ||||
|  | ||||
|     writeDoubleField(t, e) { | ||||
|         this.writeTag(t, 1), this.writeDouble(e) | ||||
|     } | ||||
|  | ||||
|     writeBooleanField(t, e) { | ||||
|         this.writeVarintField(t, +e) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // exports.Pbf = Pbf | ||||
|  | ||||
| function readVarintRemainder(t, e, i) { | ||||
|     const s = i.buf; | ||||
|     let r, a; | ||||
|     if (a = s[i.pos++], r = (112 & a) >> 4, a < 128) return toNum(t, r, e); | ||||
|     if (a = s[i.pos++], r |= (127 & a) << 3, a < 128) return toNum(t, r, e); | ||||
|     if (a = s[i.pos++], r |= (127 & a) << 10, a < 128) return toNum(t, r, e); | ||||
|     if (a = s[i.pos++], r |= (127 & a) << 17, a < 128) return toNum(t, r, e); | ||||
|     if (a = s[i.pos++], r |= (127 & a) << 24, a < 128) return toNum(t, r, e); | ||||
|     if (a = s[i.pos++], r |= (1 & a) << 31, a < 128) return toNum(t, r, e); | ||||
|     throw new Error("Expected varint not more than 10 bytes") | ||||
| } | ||||
|  | ||||
| function toNum(t, e, i) { | ||||
|     return i ? 4294967296 * e + (t >>> 0) : 4294967296 * (e >>> 0) + (t >>> 0) | ||||
| } | ||||
|  | ||||
| function writeBigVarint(t, e) { | ||||
|     let i, s; | ||||
|     if (t >= 0 ? (i = t % 4294967296 | 0, s = t / 4294967296 | 0) : (i = ~(-t % 4294967296), s = ~(-t / 4294967296), 4294967295 ^ i ? i = i + 1 | 0 : (i = 0, s = s + 1 | 0)), t >= 0x10000000000000000 || t < -0x10000000000000000) throw new Error("Given varint doesn't fit into 10 bytes"); | ||||
|     e.realloc(10), writeBigVarintLow(i, s, e), writeBigVarintHigh(s, e) | ||||
| } | ||||
|  | ||||
| function writeBigVarintLow(t, e, i) { | ||||
|     i.buf[i.pos++] = 127 & t | 128, t >>>= 7, i.buf[i.pos++] = 127 & t | 128, t >>>= 7, i.buf[i.pos++] = 127 & t | 128, t >>>= 7, i.buf[i.pos++] = 127 & t | 128, t >>>= 7, i.buf[i.pos] = 127 & t | ||||
| } | ||||
|  | ||||
| function writeBigVarintHigh(t, e) { | ||||
|     const i = (7 & t) << 4; | ||||
|     e.buf[e.pos++] |= i | ((t >>>= 3) ? 128 : 0), t && (e.buf[e.pos++] = 127 & t | ((t >>>= 7) ? 128 : 0), t && (e.buf[e.pos++] = 127 & t | ((t >>>= 7) ? 128 : 0), t && (e.buf[e.pos++] = 127 & t | ((t >>>= 7) ? 128 : 0), t && (e.buf[e.pos++] = 127 & t | ((t >>>= 7) ? 128 : 0), t && (e.buf[e.pos++] = 127 & t))))) | ||||
| } | ||||
|  | ||||
| function makeRoomForExtraLength(t, e, i) { | ||||
|     const s = e <= 16383 ? 1 : e <= 2097151 ? 2 : e <= 268435455 ? 3 : Math.floor(Math.log(e) / (7 * Math.LN2)); | ||||
|     i.realloc(s); | ||||
|     for (let e = i.pos - 1; e >= t; e--) i.buf[e + s] = i.buf[e] | ||||
| } | ||||
|  | ||||
| function writePackedVarint(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeVarint(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedSVarint(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeSVarint(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedFloat(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeFloat(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedDouble(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeDouble(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedBoolean(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeBoolean(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedFixed32(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeFixed32(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedSFixed32(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeSFixed32(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedFixed64(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeFixed64(t[i]) | ||||
| } | ||||
|  | ||||
| function writePackedSFixed64(t, e) { | ||||
|     for (let i = 0; i < t.length; i++) e.writeSFixed64(t[i]) | ||||
| } | ||||
|  | ||||
| function readUtf8(t, e, i) { | ||||
|     let s = "", r = e; | ||||
|     for (; r < i;) { | ||||
|         const e = t[r]; | ||||
|         let a, o, h, n = null, d = e > 239 ? 4 : e > 223 ? 3 : e > 191 ? 2 : 1; | ||||
|         if (r + d > i) break; | ||||
|         1 === d ? e < 128 && (n = e) : 2 === d ? (a = t[r + 1], 128 == (192 & a) && (n = (31 & e) << 6 | 63 & a, n <= 127 && (n = null))) : 3 === d ? (a = t[r + 1], o = t[r + 2], 128 == (192 & a) && 128 == (192 & o) && (n = (15 & e) << 12 | (63 & a) << 6 | 63 & o, (n <= 2047 || n >= 55296 && n <= 57343) && (n = null))) : 4 === d && (a = t[r + 1], o = t[r + 2], h = t[r + 3], 128 == (192 & a) && 128 == (192 & o) && 128 == (192 & h) && (n = (15 & e) << 18 | (63 & a) << 12 | (63 & o) << 6 | 63 & h, (n <= 65535 || n >= 1114112) && (n = null))), null === n ? (n = 65533, d = 1) : n > 65535 && (n -= 65536, s += String.fromCharCode(n >>> 10 & 1023 | 55296), n = 56320 | 1023 & n), s += String.fromCharCode(n), r += d | ||||
|     } | ||||
|     return s | ||||
| } | ||||
|  | ||||
| function writeUtf8(t, e, i) { | ||||
|     for (let s, r, a = 0; a < e.length; a++) { | ||||
|         if (s = e.charCodeAt(a), s > 55295 && s < 57344) { | ||||
|             if (!r) { | ||||
|                 s > 56319 || a + 1 === e.length ? (t[i++] = 239, t[i++] = 191, t[i++] = 189) : r = s; | ||||
|                 continue | ||||
|             } | ||||
|             if (s < 56320) { | ||||
|                 t[i++] = 239, t[i++] = 191, t[i++] = 189, r = s; | ||||
|                 continue | ||||
|             } | ||||
|             s = r - 55296 << 10 | s - 56320 | 65536, r = null | ||||
|         } else r && (t[i++] = 239, t[i++] = 191, t[i++] = 189, r = null); | ||||
|         s < 128 ? t[i++] = s : (s < 2048 ? t[i++] = s >> 6 | 192 : (s < 65536 ? t[i++] = s >> 12 | 224 : (t[i++] = s >> 18 | 240, t[i++] = s >> 12 & 63 | 128), t[i++] = s >> 6 & 63 | 128), t[i++] = 63 & s | 128) | ||||
|     } | ||||
|     return i | ||||
| } | ||||
|  | ||||
| function Point(x, y) { | ||||
|     this.x = x; | ||||
|     this.y = y; | ||||
| } | ||||
|  | ||||
| Point.prototype = { | ||||
|     clone: function () { | ||||
|         return new Point(this.x, this.y); | ||||
|     }, | ||||
|  | ||||
|     add: function (p) { | ||||
|         return this.clone()._add(p); | ||||
|     }, | ||||
|     sub: function (p) { | ||||
|         return this.clone()._sub(p); | ||||
|     }, | ||||
|     mult: function (k) { | ||||
|         return this.clone()._mult(k); | ||||
|     }, | ||||
|     div: function (k) { | ||||
|         return this.clone()._div(k); | ||||
|     }, | ||||
|     rotate: function (a) { | ||||
|         return this.clone()._rotate(a); | ||||
|     }, | ||||
|     matMult: function (m) { | ||||
|         return this.clone()._matMult(m); | ||||
|     }, | ||||
|     unit: function () { | ||||
|         return this.clone()._unit(); | ||||
|     }, | ||||
|     perp: function () { | ||||
|         return this.clone()._perp(); | ||||
|     }, | ||||
|     round: function () { | ||||
|         return this.clone()._round(); | ||||
|     }, | ||||
|  | ||||
|     mag: function () { | ||||
|         return Math.sqrt(this.x * this.x + this.y * this.y); | ||||
|     }, | ||||
|  | ||||
|     equals: function (p) { | ||||
|         return this.x === p.x && | ||||
|             this.y === p.y; | ||||
|     }, | ||||
|  | ||||
|     dist: function (p) { | ||||
|         return Math.sqrt(this.distSqr(p)); | ||||
|     }, | ||||
|  | ||||
|     distSqr: function (p) { | ||||
|         var dx = p.x - this.x, | ||||
|             dy = p.y - this.y; | ||||
|         return dx * dx + dy * dy; | ||||
|     }, | ||||
|  | ||||
|     angle: function () { | ||||
|         return Math.atan2(this.y, this.x); | ||||
|     }, | ||||
|  | ||||
|     angleTo: function (b) { | ||||
|         return Math.atan2(this.y - b.y, this.x - b.x); | ||||
|     }, | ||||
|  | ||||
|     angleWith: function (b) { | ||||
|         return this.angleWithSep(b.x, b.y); | ||||
|     }, | ||||
|  | ||||
|     // Find the angle of the two vectors, solving the formula for the cross product a x b = |a||b|sin(θ) for θ. | ||||
|     angleWithSep: function (x, y) { | ||||
|         return Math.atan2( | ||||
|             this.x * y - this.y * x, | ||||
|             this.x * x + this.y * y); | ||||
|     }, | ||||
|  | ||||
|     _matMult: function (m) { | ||||
|         var x = m[0] * this.x + m[1] * this.y, | ||||
|             y = m[2] * this.x + m[3] * this.y; | ||||
|         this.x = x; | ||||
|         this.y = y; | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _add: function (p) { | ||||
|         this.x += p.x; | ||||
|         this.y += p.y; | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _sub: function (p) { | ||||
|         this.x -= p.x; | ||||
|         this.y -= p.y; | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _mult: function (k) { | ||||
|         this.x *= k; | ||||
|         this.y *= k; | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _div: function (k) { | ||||
|         this.x /= k; | ||||
|         this.y /= k; | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _unit: function () { | ||||
|         this._div(this.mag()); | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _perp: function () { | ||||
|         var y = this.y; | ||||
|         this.y = this.x; | ||||
|         this.x = -y; | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _rotate: function (angle) { | ||||
|         var cos = Math.cos(angle), | ||||
|             sin = Math.sin(angle), | ||||
|             x = cos * this.x - sin * this.y, | ||||
|             y = sin * this.x + cos * this.y; | ||||
|         this.x = x; | ||||
|         this.y = y; | ||||
|         return this; | ||||
|     }, | ||||
|  | ||||
|     _round: function () { | ||||
|         this.x = Math.round(this.x); | ||||
|         this.y = Math.round(this.y); | ||||
|         return this; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| // constructs Point from an array if necessary | ||||
| Point.convert = function (a) { | ||||
|     if (a instanceof Point) { | ||||
|         return a; | ||||
|     } | ||||
|     if (Array.isArray(a)) { | ||||
|         return new Point(a[0], a[1]); | ||||
|     } | ||||
|     return a; | ||||
| }; | ||||
|  | ||||
| //# sourceMappingURL=/sm/91dc2634fd08444face2220488dd1b2296e475fe4a0f0781089e74342af5c955.map | ||||
| class TileFeature { | ||||
|     constructor(pbf, end, extent, keys, values) { | ||||
|         // Public | ||||
|         this.properties = {}; | ||||
|         this.extent = extent; | ||||
|         this.type = 0; | ||||
|  | ||||
|         // Private | ||||
|         this._pbf = pbf; | ||||
|         this._geometry = -1; | ||||
|         this._keys = keys; | ||||
|         this._values = values; | ||||
|         pbf.readFields(readFeature, this, end); | ||||
|     } | ||||
|  | ||||
|     types() { | ||||
|         return ['Unknown', 'Point', 'LineString', 'Polygon']; | ||||
|     } | ||||
|  | ||||
|     loadGeometry() { | ||||
|         var pbf = this._pbf; | ||||
|         pbf.pos = this._geometry; | ||||
|  | ||||
|         var end = pbf.readVarint() + pbf.pos, | ||||
|             cmd = 1, | ||||
|             length = 0, | ||||
|             x = 0, | ||||
|             y = 0, | ||||
|             lines = [], | ||||
|             line; | ||||
|  | ||||
|         while (pbf.pos < end) { | ||||
|             if (!length) { | ||||
|                 var cmdLen = pbf.readVarint(); | ||||
|                 cmd = cmdLen & 0x7; | ||||
|                 length = cmdLen >> 3; | ||||
|             } | ||||
|  | ||||
|             length--; | ||||
|             if (cmd === 1 || cmd === 2) { | ||||
|                 x += pbf.readSVarint(); | ||||
|                 y += pbf.readSVarint(); | ||||
|  | ||||
|                 if (cmd === 1) { // moveTo | ||||
|                     if (line) lines.push(line); | ||||
|                     line = []; | ||||
|                 } | ||||
|  | ||||
|                 line.push(new Point(x, y)); | ||||
|  | ||||
|             } else if (cmd === 7) { | ||||
|  | ||||
|                 if (line) { | ||||
|                     line.push(line[0].clone()); // closePolygon | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } else { | ||||
|                 throw new Error('unknown command ' + cmd); | ||||
|             } | ||||
|         } | ||||
|         if (line) lines.push(line); | ||||
|  | ||||
|         return lines; | ||||
|     } | ||||
|  | ||||
|     bbox() { | ||||
|         var pbf = this._pbf; | ||||
|         pbf.pos = this._geometry; | ||||
|  | ||||
|         var end = pbf.readVarint() + pbf.pos, | ||||
|             cmd = 1, | ||||
|             length = 0, | ||||
|             x = 0, | ||||
|             y = 0, | ||||
|             x1 = Infinity, | ||||
|             x2 = -Infinity, | ||||
|             y1 = Infinity, | ||||
|             y2 = -Infinity; | ||||
|  | ||||
|         while (pbf.pos < end) { | ||||
|             if (!length) { | ||||
|                 var cmdLen = pbf.readVarint(); | ||||
|                 cmd = cmdLen & 0x7; | ||||
|                 length = cmdLen >> 3; | ||||
|             } | ||||
|  | ||||
|             length--; | ||||
|  | ||||
|             if (cmd === 1 || cmd === 2) { | ||||
|                 x += pbf.readSVarint(); | ||||
|                 y += pbf.readSVarint(); | ||||
|                 if (x < x1) x1 = x; | ||||
|                 if (x > x2) x2 = x; | ||||
|                 if (y < y1) y1 = y; | ||||
|                 if (y > y2) y2 = y; | ||||
|  | ||||
|             } else if (cmd !== 7) { | ||||
|                 throw new Error('unknown command ' + cmd); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return [x1, y1, x2, y2]; | ||||
|     } | ||||
|  | ||||
|     toGeoJSON(x = 0, y = 0, z = 1) { | ||||
|         var size = this.extent * Math.pow(2, z), | ||||
|             x0 = this.extent * x, | ||||
|             y0 = this.extent * y, | ||||
|             coords = this.loadGeometry(), | ||||
|             type = this.types()[this.type], | ||||
|             i, j; | ||||
|  | ||||
|         function project(line) { | ||||
|             for (var j = 0; j < line.length; j++) { | ||||
|                 var p = line[j] | ||||
|                 let y2 = 180 - (p.y + y0) * 360 / size; | ||||
|                 line[j] = [ | ||||
|                     (p.x + x0) * 360 / size - 180, | ||||
|                     360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90 | ||||
|                 ]; | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         switch (this.type) { | ||||
|             case 1: | ||||
|                 var points = []; | ||||
|                 for (i = 0; i < coords.length; i++) { | ||||
|                     points[i] = coords[i][0]; | ||||
|                 } | ||||
|                 coords = points; | ||||
|                 project(coords); | ||||
|                 break; | ||||
|  | ||||
|             case 2: | ||||
|                 for (i = 0; i < coords.length; i++) { | ||||
|                     project(coords[i]); | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|             case 3: | ||||
|                 coords = classifyRings(coords); | ||||
|                 for (i = 0; i < coords.length; i++) { | ||||
|                     for (j = 0; j < coords[i].length; j++) { | ||||
|                         project(coords[i][j]); | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         if (coords.length === 1) { | ||||
|             coords = coords[0]; | ||||
|         } else { | ||||
|             type = 'Multi' + type; | ||||
|         } | ||||
|  | ||||
|         var result = { | ||||
|             type: "Feature", | ||||
|             geometry: { | ||||
|                 type: type, | ||||
|                 coordinates: coords | ||||
|             }, | ||||
|             properties: this.properties | ||||
|         }; | ||||
|  | ||||
|         if ('id' in this) { | ||||
|             result.id = this.id; | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| function readFeature(tag, feature, pbf) { | ||||
|     if (tag == 1) feature.id = pbf.readVarint(); | ||||
|     else if (tag == 2) readTag(pbf, feature); | ||||
|     else if (tag == 3) feature.type = pbf.readVarint(); | ||||
|     else if (tag == 4) feature._geometry = pbf.pos; | ||||
| } | ||||
|  | ||||
| function classifyRings(rings) { | ||||
|     var len = rings.length; | ||||
|  | ||||
|     if (len <= 1) return [rings]; | ||||
|  | ||||
|     var polygons = [], | ||||
|         polygon, | ||||
|         ccw; | ||||
|  | ||||
|     for (var i = 0; i < len; i++) { | ||||
|         var area = signedArea(rings[i]); | ||||
|         if (area === 0) continue; | ||||
|  | ||||
|         if (ccw === undefined) ccw = area < 0; | ||||
|  | ||||
|         if (ccw === area < 0) { | ||||
|             if (polygon) polygons.push(polygon); | ||||
|             polygon = [rings[i]]; | ||||
|  | ||||
|         } else { | ||||
|             polygon.push(rings[i]); | ||||
|         } | ||||
|     } | ||||
|     if (polygon) polygons.push(polygon); | ||||
|  | ||||
|     return polygons; | ||||
| } | ||||
|  | ||||
| function signedArea(ring) { | ||||
|     var sum = 0; | ||||
|     for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { | ||||
|         p1 = ring[i]; | ||||
|         p2 = ring[j]; | ||||
|         sum += (p2.x - p1.x) * (p1.y + p2.y); | ||||
|     } | ||||
|     return sum; | ||||
| } | ||||
|  | ||||
| function readTag(pbf, feature) { | ||||
|     var end = pbf.readVarint() + pbf.pos; | ||||
|  | ||||
|     while (pbf.pos < end) { | ||||
|         var key = feature._keys[pbf.readVarint()], | ||||
|             value = feature._values[pbf.readVarint()]; | ||||
|         feature.properties[key] = value; | ||||
|     } | ||||
| } | ||||
|  | ||||
| class TileLayer { | ||||
|     constructor(pbf, end) { | ||||
|         // Public | ||||
|         this.version = 1; | ||||
|         this.name = null; | ||||
|         this.extent = 4096; | ||||
|         this.length = 0; | ||||
|  | ||||
|         // Private | ||||
|         this._pbf = pbf; | ||||
|         this._keys = []; | ||||
|         this._values = []; | ||||
|         this._features = []; | ||||
|  | ||||
|         pbf.readFields(readLayer, this, end); | ||||
|  | ||||
|         this.length = this._features.length; | ||||
|     } | ||||
|  | ||||
|     feature(i) { | ||||
|         if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds'); | ||||
|  | ||||
|         this._pbf.pos = this._features[i]; | ||||
|  | ||||
|         var end = this._pbf.readVarint() + this._pbf.pos; | ||||
|         return new TileFeature(this._pbf, end, this.extent, this._keys, this._values); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| function readLayer(tag, layer, pbf) { | ||||
|     if (tag === 15) layer.version = pbf.readVarint(); | ||||
|     else if (tag === 1) layer.name = pbf.readString(); | ||||
|     else if (tag === 5) layer.extent = pbf.readVarint(); | ||||
|     else if (tag === 2) layer._features.push(pbf.pos); | ||||
|     else if (tag === 3) layer._keys.push(pbf.readString()); | ||||
|     else if (tag === 4) layer._values.push(readValueMessage(pbf)); | ||||
| } | ||||
|  | ||||
| function readValueMessage(pbf) { | ||||
|     var value = null, | ||||
|         end = pbf.readVarint() + pbf.pos; | ||||
|  | ||||
|     while (pbf.pos < end) { | ||||
|         var tag = pbf.readVarint() >> 3; | ||||
|  | ||||
|         value = tag === 1 ? pbf.readString() : | ||||
|             tag === 2 ? pbf.readFloat() : | ||||
|                 tag === 3 ? pbf.readDouble() : | ||||
|                     tag === 4 ? pbf.readVarint64() : | ||||
|                         tag === 5 ? pbf.readVarint() : | ||||
|                             tag === 6 ? pbf.readSVarint() : | ||||
|                                 tag === 7 ? pbf.readBoolean() : null; | ||||
|     } | ||||
|  | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| class VectorTile { | ||||
|     constructor(pbf_tile) { | ||||
|         this.pbf_tile = pbf_tile | ||||
|         this.layers = {} | ||||
|     } | ||||
|  | ||||
|     features() { | ||||
|         let jsonArray = [] | ||||
|         for (let layersKey in this.layers) { | ||||
|             for (let i = 0; i < tile.layers[layersKey]._features.length; i++) { | ||||
|                 let json = this.layers[layersKey].feature(i).toGeoJSON() | ||||
|                 jsonArray.push(json) | ||||
|             } | ||||
|         } | ||||
|         return jsonArray | ||||
|     } | ||||
|  | ||||
|     async on() { | ||||
|         let res = await fetch(this.pbf_tile) | ||||
|         let buf = await res.arrayBuffer() | ||||
|         buf = new Uint8Array(buf) | ||||
|         let pbf = new Pbf(buf) | ||||
|         this.layers = pbf.readFields(this.readTile, {}) | ||||
|     } | ||||
|  | ||||
|     readTile(tag, layers, pbf) { | ||||
|         if (tag === 3) { | ||||
|             var layer = new TileLayer(pbf, pbf.readVarint() + pbf.pos); | ||||
|             if (layer.length) layers[layer.name] = layer; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										302
									
								
								example/js/video.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								example/js/video.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user