1 /**************************************************************************** 2 Copyright (c) 2008-2010 Ricardo Quesada 3 Copyright (c) 2011-2012 cocos2d-x.org 4 Copyright (c) 2013-2014 Chukong Technologies Inc. 5 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ****************************************************************************/ 26 27 /** 28 * @constant 29 * @type Number 30 */ 31 cc.TGA_OK = 0; 32 33 /** 34 * @constant 35 * @type Number 36 */ 37 cc.TGA_ERROR_FILE_OPEN = 1; 38 39 /** 40 * @constant 41 * @type Number 42 */ 43 cc.TGA_ERROR_READING_FILE = 2; 44 45 /** 46 * @constant 47 * @type Number 48 */ 49 cc.TGA_ERROR_INDEXED_COLOR = 3; 50 51 /** 52 * @constant 53 * @type Number 54 */ 55 cc.TGA_ERROR_MEMORY = 4; 56 57 /** 58 * @constant 59 * @type Number 60 */ 61 cc.TGA_ERROR_COMPRESSED_FILE = 5; 62 63 /** 64 * TGA format 65 * @param {Number} status 66 * @param {Number} type 67 * @param {Number} pixelDepth 68 * @param {Number} width map width 69 * @param {Number} height map height 70 * @param {Array} imageData raw data 71 * @param {Number} flipped 72 * @constructor 73 */ 74 cc.ImageTGA = function (status, type, pixelDepth, width, height, imageData, flipped) { 75 this.status = status || 0; 76 this.type = type || 0; 77 this.pixelDepth = pixelDepth || 0; 78 this.width = width || 0; 79 this.height = height || 0; 80 this.imageData = imageData || []; 81 this.flipped = flipped || 0; 82 }; 83 84 /** 85 * load the image header field from stream. We only keep those that matter! 86 * @param {Array} buffer 87 * @param {Number} bufSize 88 * @param {cc.ImageTGA} psInfo 89 * @return {Boolean} 90 */ 91 cc.tgaLoadHeader = function (buffer, bufSize, psInfo) { 92 var step = 2; 93 if (step + 1 > bufSize) 94 return false; 95 96 var binaryReader = new cc.BinaryStreamReader(buffer); 97 98 binaryReader.setOffset(step); 99 psInfo.type = binaryReader.readByte(); 100 step += 10; // . step += sizeof(unsigned char) * 2; step += sizeof(signed short) * 4; 101 102 if (step + 4 + 1 > bufSize) 103 return false; 104 binaryReader.setOffset(step); 105 psInfo.width = binaryReader.readUnsignedShort(); 106 psInfo.height = binaryReader.readUnsignedInteger(); 107 psInfo.pixelDepth = binaryReader.readByte(); 108 109 step += 5; // . step += sizeof(unsigned char); step += sizeof(signed short) * 2; 110 if (step + 1 > bufSize) 111 return false; 112 113 var garbage = binaryReader.readByte(); 114 psInfo.flipped = 0; 115 if (garbage & 0x20) 116 psInfo.flipped = 1; 117 return true; 118 }; 119 120 /** 121 * loads the image pixels. You shouldn't call this function directly 122 * @param {Array} buffer 123 * @param {Number} bufSize 124 * @param {cc.ImageTGA} psInfo 125 * @return {Boolean} 126 */ 127 cc.tgaLoadImageData = function (buffer, bufSize, psInfo) { 128 var mode, total, i, aux; 129 var step = 18; // .size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6; 130 131 // mode equal the number of components for each pixel 132 mode = 0 | (psInfo.pixelDepth / 2); 133 // total is the number of unsigned chars we'll have to read 134 total = psInfo.height * psInfo.width * mode; 135 136 if (step + total > bufSize) 137 return false; 138 139 psInfo.imageData = cc.__getSubArray(buffer, step, step + total); 140 141 // mode=3 or 4 implies that the image is RGB(A). However TGA 142 // stores it as BGR(A) so we'll have to swap R and B. 143 if (mode >= 3) { 144 for (i = 0; i < total; i += mode) { 145 aux = psInfo.imageData[i]; 146 psInfo.imageData[i] = psInfo.imageData[i + 2]; 147 psInfo.imageData[i + 2] = aux; 148 } 149 } 150 return true; 151 }; 152 153 /** 154 * converts RGB to grayscale 155 * @param {cc.ImageTGA} psInfo 156 */ 157 cc.tgaRGBtogreyscale = function (psInfo) { 158 var i, j; 159 160 // if the image is already grayscale do nothing 161 if (psInfo.pixelDepth === 8) 162 return; 163 164 // compute the number of actual components 165 var mode = psInfo.pixelDepth / 8; 166 167 // allocate an array for the new image data 168 var newImageData = new Uint8Array(psInfo.height * psInfo.width); 169 if (newImageData === null) 170 return; 171 172 // convert pixels: grayscale = o.30 * R + 0.59 * G + 0.11 * B 173 for (i = 0, j = 0; j < psInfo.width * psInfo.height; i += mode, j++) 174 newImageData[j] = (0.30 * psInfo.imageData[i] + 0.59 * psInfo.imageData[i + 1] + 0.11 * psInfo.imageData[i + 2]); 175 176 // reassign pixelDepth and type according to the new image type 177 psInfo.pixelDepth = 8; 178 psInfo.type = 3; 179 // reassigning imageData to the new array. 180 psInfo.imageData = newImageData; 181 }; 182 183 /** 184 * releases the memory used for the image 185 * @param {cc.ImageTGA} psInfo 186 */ 187 cc.tgaDestroy = function (psInfo) { 188 if (!psInfo) 189 return; 190 191 psInfo.imageData = null; 192 psInfo = null; 193 }; 194 195 cc.tgaLoadRLEImageData = function (buffer, bufSize, psInfo) { 196 var mode, total, i, index = 0 , skip = 0, flag = 0; 197 var aux = [], runlength = 0; 198 199 var step = 18; // . size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6; 200 201 // mode equal the number of components for each pixel 202 mode = psInfo.pixelDepth / 8; 203 // total is the number of unsigned chars we'll have to read 204 total = psInfo.height * psInfo.width; 205 206 for (i = 0; i < total; i++) { 207 // if we have a run length pending, run it 208 if (runlength != 0) { 209 // we do, update the run length count 210 runlength--; 211 skip = (flag != 0); 212 } else { 213 // otherwise, read in the run length token 214 if (step + 1 > bufSize) 215 break; 216 runlength = buffer[step]; 217 step += 1; 218 219 // see if it's a RLE encoded sequence 220 flag = runlength & 0x80; 221 if (flag) 222 runlength -= 128; 223 skip = 0; 224 } 225 226 // do we need to skip reading this pixel? 227 if (!skip) { 228 // no, read in the pixel data 229 if (step + mode > bufSize) 230 break; 231 aux = cc.__getSubArray(buffer, step, step + mode); 232 step += mode; 233 234 // mode=3 or 4 implies that the image is RGB(A). However TGA 235 // stores it as BGR(A) so we'll have to swap R and B. 236 if (mode >= 3) { 237 var tmp = aux[0]; 238 aux[0] = aux[2]; 239 aux[2] = tmp; 240 } 241 } 242 243 // add the pixel to our image 244 for (var j = 0; j < mode; j++) 245 psInfo.imageData[index + j] = aux[j]; 246 247 index += mode; 248 } 249 250 return true; 251 }; 252 253 cc.tgaFlipImage = function (psInfo) { 254 // mode equal the number of components for each pixel 255 var mode = psInfo.pixelDepth / 8; 256 var rowbytes = psInfo.width * mode; 257 258 for (var y = 0; y < (psInfo.height / 2); y++) { 259 var row = cc.__getSubArray(psInfo.imageData, y * rowbytes, y * rowbytes + rowbytes); 260 cc.__setDataToArray(cc.__getSubArray(psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes, rowbytes), psInfo.imageData, y * rowbytes); 261 cc.__setDataToArray(row, psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes); 262 } 263 psInfo.flipped = 0; 264 }; 265 266 cc.__getSubArray = function (array, start, end) { 267 if (array instanceof Array) 268 return array.slice(start, end); 269 else 270 return array.subarray(start, end); 271 }; 272 273 cc.__setDataToArray = function (sourceData, destArray, startIndex) { 274 for (var i = 0; i < sourceData.length; i++) 275 destArray[startIndex + i] = sourceData[i]; 276 }; 277 278 279 cc.BinaryStreamReader = cc.Class.extend({ 280 _binaryData:null, 281 _offset:0, 282 283 ctor:function (binaryData) { 284 this._binaryData = binaryData; 285 }, 286 287 setBinaryData:function (binaryData) { 288 this._binaryData = binaryData; 289 this._offset = 0; 290 }, 291 292 getBinaryData:function () { 293 return this._binaryData; 294 }, 295 296 _checkSize:function (neededBits) { 297 if (!(this._offset + Math.ceil(neededBits / 8) < this._data.length)) 298 throw new Error("Index out of bound"); 299 }, 300 301 _decodeFloat:function (precisionBits, exponentBits) { 302 var length = precisionBits + exponentBits + 1; 303 var size = length >> 3; 304 this._checkSize(length); 305 306 var bias = Math.pow(2, exponentBits - 1) - 1; 307 var signal = this._readBits(precisionBits + exponentBits, 1, size); 308 var exponent = this._readBits(precisionBits, exponentBits, size); 309 var significand = 0; 310 var divisor = 2; 311 var curByte = 0; //length + (-precisionBits >> 3) - 1; 312 do { 313 var byteValue = this._readByte(++curByte, size); 314 var startBit = precisionBits % 8 || 8; 315 var mask = 1 << startBit; 316 while (mask >>= 1) { 317 if (byteValue & mask) 318 significand += 1 / divisor; 319 divisor *= 2; 320 } 321 } while (precisionBits -= startBit); 322 323 this._offset += size; 324 325 return exponent == (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity 326 : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand 327 : Math.pow(2, exponent - bias) * (1 + significand) : 0); 328 }, 329 330 _readByte:function (i, size) { 331 return this._data[this._offset + size - i - 1]; 332 }, 333 334 _decodeInt:function (bits, signed) { 335 var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits); 336 var result = signed && x >= max / 2 ? x - max : x; 337 338 this._offset += bits / 8; 339 return result; 340 }, 341 342 _shl:function (a, b) { 343 for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1){}; 344 return a; 345 }, 346 347 _readBits:function (start, length, size) { 348 var offsetLeft = (start + length) % 8; 349 var offsetRight = start % 8; 350 var curByte = size - (start >> 3) - 1; 351 var lastByte = size + (-(start + length) >> 3); 352 var diff = curByte - lastByte; 353 354 var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1); 355 356 if (diff && offsetLeft) 357 sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight; 358 359 while (diff) 360 sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight); 361 362 return sum; 363 }, 364 365 readInteger:function () { 366 return this._decodeInt(32, true); 367 }, 368 369 readUnsignedInteger:function () { 370 return this._decodeInt(32, false); 371 }, 372 373 readSingle:function () { 374 return this._decodeFloat(23, 8); 375 }, 376 377 readShort:function () { 378 return this._decodeInt(16, true); 379 }, 380 381 readUnsignedShort:function () { 382 return this._decodeInt(16, false); 383 }, 384 385 readByte:function () { 386 var readByte = this._data[this._offset]; 387 this._offset += 1; 388 return readByte; 389 }, 390 391 readData:function (start, end) { 392 if (this._binaryData instanceof Array) { 393 return this._binaryData.slice(start, end); 394 } else { 395 //typed array 396 return this._binaryData.subarray(start, end); 397 } 398 }, 399 400 setOffset:function (offset) { 401 this._offset = offset; 402 }, 403 404 getOffset:function () { 405 return this._offset; 406 } 407 }); 408