1 /**************************************************************************** 2 Copyright (c) 2011 Devon Govett 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011-2012 cocos2d-x.org 5 Copyright (c) 2013-2014 Chukong Technologies Inc. 6 7 http://www.cocos2d-x.org 8 9 10 Permission is hereby granted, free of charge, to any person obtaining a copy 11 of this software and associated documentation files (the "Software"), to deal 12 in the Software without restriction, including without limitation the rights 13 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 copies of the Software, and to permit persons to whom the Software is 15 furnished to do so, subject to the following conditions: 16 17 The above copyright notice and this permission notice shall be included in 18 all copies or substantial portions of the Software. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 THE SOFTWARE. 27 ****************************************************************************/ 28 29 /** 30 * A png file reader 31 * @name cc.tiffReader 32 */ 33 cc.PNGReader = cc.Class.extend({ 34 ctor:function(data){ 35 var chunkSize, colors, delayDen, delayNum, frame, i, index, key, section, ccshort, text, _i, _j, _ref; 36 this.data = data; 37 this.pos = 8; 38 this.palette = []; 39 this.imgData = []; 40 this.transparency = {}; 41 this.animation = null; 42 this.text = {}; 43 frame = null; 44 while (true) { 45 chunkSize = this.readUInt32(); 46 section = ((function() { 47 var _i, _results; 48 _results = []; 49 for (i = _i = 0; _i < 4; i = ++_i) { 50 _results.push(String.fromCharCode(this.data[this.pos++])); 51 } 52 return _results; 53 }).call(this)).join(''); 54 switch (section) { 55 case 'IHDR': 56 this.width = this.readUInt32(); 57 this.height = this.readUInt32(); 58 this.bits = this.data[this.pos++]; 59 this.colorType = this.data[this.pos++]; 60 this.compressionMethod = this.data[this.pos++]; 61 this.filterMethod = this.data[this.pos++]; 62 this.interlaceMethod = this.data[this.pos++]; 63 break; 64 case 'acTL': 65 this.animation = { 66 numFrames: this.readUInt32(), 67 numPlays: this.readUInt32() || Infinity, 68 frames: [] 69 }; 70 break; 71 case 'PLTE': 72 this.palette = this.read(chunkSize); 73 break; 74 case 'fcTL': 75 if (frame) { 76 this.animation.frames.push(frame); 77 } 78 this.pos += 4; 79 frame = { 80 width: this.readUInt32(), 81 height: this.readUInt32(), 82 xOffset: this.readUInt32(), 83 yOffset: this.readUInt32() 84 }; 85 delayNum = this.readUInt16(); 86 delayDen = this.readUInt16() || 100; 87 frame.delay = 1000 * delayNum / delayDen; 88 frame.disposeOp = this.data[this.pos++]; 89 frame.blendOp = this.data[this.pos++]; 90 frame.data = []; 91 break; 92 case 'IDAT': 93 case 'fdAT': 94 if (section === 'fdAT') { 95 this.pos += 4; 96 chunkSize -= 4; 97 } 98 data = (frame != null ? frame.data : void 0) || this.imgData; 99 for (i = _i = 0; 0 <= chunkSize ? _i < chunkSize : _i > chunkSize; i = 0 <= chunkSize ? ++_i : --_i) { 100 data.push(this.data[this.pos++]); 101 } 102 break; 103 case 'tRNS': 104 this.transparency = {}; 105 switch (this.colorType) { 106 case 3: 107 this.transparency.indexed = this.read(chunkSize); 108 ccshort = 255 - this.transparency.indexed.length; 109 if (ccshort > 0) { 110 for (i = _j = 0; 0 <= ccshort ? _j < ccshort : _j > ccshort; i = 0 <= ccshort ? ++_j : --_j) { 111 this.transparency.indexed.push(255); 112 } 113 } 114 break; 115 case 0: 116 this.transparency.grayscale = this.read(chunkSize)[0]; 117 break; 118 case 2: 119 this.transparency.rgb = this.read(chunkSize); 120 } 121 break; 122 case 'tEXt': 123 text = this.read(chunkSize); 124 index = text.indexOf(0); 125 key = String.fromCharCode.apply(String, text.slice(0, index)); 126 this.text[key] = String.fromCharCode.apply(String, text.slice(index + 1)); 127 break; 128 case 'IEND': 129 if (frame) { 130 this.animation.frames.push(frame); 131 } 132 this.colors = (function() { 133 switch (this.colorType) { 134 case 0: 135 case 3: 136 case 4: 137 return 1; 138 case 2: 139 case 6: 140 return 3; 141 } 142 }).call(this); 143 this.hasAlphaChannel = (_ref = this.colorType) === 4 || _ref === 6; 144 colors = this.colors + (this.hasAlphaChannel ? 1 : 0); 145 this.pixelBitlength = this.bits * colors; 146 this.colorSpace = (function() { 147 switch (this.colors) { 148 case 1: 149 return 'DeviceGray'; 150 case 3: 151 return 'DeviceRGB'; 152 } 153 }).call(this); 154 if(Uint8Array != Array) 155 this.imgData = new Uint8Array(this.imgData); 156 return; 157 default: 158 this.pos += chunkSize; 159 } 160 this.pos += 4; 161 if (this.pos > this.data.length) { 162 throw new Error("Incomplete or corrupt PNG file"); 163 } 164 } 165 }, 166 read:function(bytes){ 167 var i, _i, _results; 168 _results = []; 169 for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) { 170 _results.push(this.data[this.pos++]); 171 } 172 return _results; 173 }, 174 readUInt32:function(){ 175 var b1, b2, b3, b4; 176 b1 = this.data[this.pos++] << 24; 177 b2 = this.data[this.pos++] << 16; 178 b3 = this.data[this.pos++] << 8; 179 b4 = this.data[this.pos++]; 180 return b1 | b2 | b3 | b4; 181 }, 182 readUInt16:function(){ 183 var b1, b2; 184 b1 = this.data[this.pos++] << 8; 185 b2 = this.data[this.pos++]; 186 return b1 | b2; 187 }, 188 decodePixels:function(data){ 189 var ccbyte, c, col, i, left, length, p, pa, paeth, pb, pc, pixelBytes, pixels, pos, row, scanlineLength, upper, upperLeft, _i, _j, _k, _l, _m; 190 if (data == null) { 191 data = this.imgData; 192 } 193 if (data.length === 0) { 194 return new Uint8Array(0); 195 } 196 var inflate = new Zlib.Inflate(data,{index:0, verify:false}); 197 data = inflate.decompress(); 198 199 pixelBytes = this.pixelBitlength / 8; 200 scanlineLength = pixelBytes * this.width; 201 pixels = new Uint8Array(scanlineLength * this.height); 202 length = data.length; 203 row = 0; 204 pos = 0; 205 c = 0; 206 while (pos < length) { 207 switch (data[pos++]) { 208 case 0: 209 for (i = _i = 0; _i < scanlineLength; i = _i += 1) { 210 pixels[c++] = data[pos++]; 211 } 212 break; 213 case 1: 214 for (i = _j = 0; _j < scanlineLength; i = _j += 1) { 215 ccbyte = data[pos++]; 216 left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; 217 pixels[c++] = (ccbyte + left) % 256; 218 } 219 break; 220 case 2: 221 for (i = _k = 0; _k < scanlineLength; i = _k += 1) { 222 ccbyte = data[pos++]; 223 col = (i - (i % pixelBytes)) / pixelBytes; 224 upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; 225 pixels[c++] = (upper + ccbyte) % 256; 226 } 227 break; 228 case 3: 229 for (i = _l = 0; _l < scanlineLength; i = _l += 1) { 230 ccbyte = data[pos++]; 231 col = (i - (i % pixelBytes)) / pixelBytes; 232 left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; 233 upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; 234 pixels[c++] = (ccbyte + Math.floor((left + upper) / 2)) % 256; 235 } 236 break; 237 case 4: 238 for (i = _m = 0; _m < scanlineLength; i = _m += 1) { 239 ccbyte = data[pos++]; 240 col = (i - (i % pixelBytes)) / pixelBytes; 241 left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; 242 if (row === 0) { 243 upper = upperLeft = 0; 244 } else { 245 upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; 246 upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + (i % pixelBytes)]; 247 } 248 p = left + upper - upperLeft; 249 pa = Math.abs(p - left); 250 pb = Math.abs(p - upper); 251 pc = Math.abs(p - upperLeft); 252 if (pa <= pb && pa <= pc) { 253 paeth = left; 254 } else if (pb <= pc) { 255 paeth = upper; 256 } else { 257 paeth = upperLeft; 258 } 259 pixels[c++] = (ccbyte + paeth) % 256; 260 } 261 break; 262 default: 263 throw new Error("Invalid filter algorithm: " + data[pos - 1]); 264 } 265 row++; 266 } 267 return pixels; 268 }, 269 copyToImageData:function(imageData,pixels){ 270 var alpha, colors, data, i, input, j, k, length, palette, v, _ref; 271 colors = this.colors; 272 palette = null; 273 alpha = this.hasAlphaChannel; 274 if (this.palette.length) { 275 palette = (_ref = this._decodedPalette) != null ? _ref : this._decodedPalette = this.decodePalette(); 276 colors = 4; 277 alpha = true; 278 } 279 data = imageData.data || imageData; 280 length = data.length; 281 input = palette || pixels; 282 i = j = 0; 283 if (colors === 1) { 284 while (i < length) { 285 k = palette ? pixels[i / 4] * 4 : j; 286 v = input[k++]; 287 data[i++] = v; 288 data[i++] = v; 289 data[i++] = v; 290 data[i++] = alpha ? input[k++] : 255; 291 j = k; 292 } 293 } else { 294 while (i < length) { 295 k = palette ? pixels[i / 4] * 4 : j; 296 data[i++] = input[k++]; 297 data[i++] = input[k++]; 298 data[i++] = input[k++]; 299 data[i++] = alpha ? input[k++] : 255; 300 j = k; 301 } 302 } 303 }, 304 decodePalette:function(){ 305 var c, i, palette, pos, ret, transparency, _i, _ref, _ref1; 306 palette = this.palette; 307 transparency = this.transparency.indexed || []; 308 ret = new Uint8Array((transparency.length || 0) + palette.length); 309 pos = 0; 310 c = 0; 311 for (i = _i = 0, _ref = palette.length; _i < _ref; i = _i += 3) { 312 ret[pos++] = palette[i]; 313 ret[pos++] = palette[i + 1]; 314 ret[pos++] = palette[i + 2]; 315 ret[pos++] = (_ref1 = transparency[c++]) != null ? _ref1 : 255; 316 } 317 return ret; 318 }, 319 render: function (canvas) { 320 var ctx, data; 321 canvas.width = this.width; 322 canvas.height = this.height; 323 ctx = canvas.getContext("2d"); 324 data = ctx.createImageData(this.width, this.height); 325 this.copyToImageData(data, this.decodePixels()); 326 return ctx.putImageData(data, 0, 0); 327 328 } 329 }); 330 331