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 /** 196 * Load RLE image data 197 * @param buffer 198 * @param bufSize 199 * @param psInfo 200 * @returns {boolean} 201 */ 202 cc.tgaLoadRLEImageData = function (buffer, bufSize, psInfo) { 203 var mode, total, i, index = 0 , skip = 0, flag = 0; 204 var aux = [], runlength = 0; 205 206 var step = 18; // . size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6; 207 208 // mode equal the number of components for each pixel 209 mode = psInfo.pixelDepth / 8; 210 // total is the number of unsigned chars we'll have to read 211 total = psInfo.height * psInfo.width; 212 213 for (i = 0; i < total; i++) { 214 // if we have a run length pending, run it 215 if (runlength !== 0) { 216 // we do, update the run length count 217 runlength--; 218 skip = (flag !== 0); 219 } else { 220 // otherwise, read in the run length token 221 if (step + 1 > bufSize) 222 break; 223 runlength = buffer[step]; 224 step += 1; 225 226 // see if it's a RLE encoded sequence 227 flag = runlength & 0x80; 228 if (flag) 229 runlength -= 128; 230 skip = 0; 231 } 232 233 // do we need to skip reading this pixel? 234 if (!skip) { 235 // no, read in the pixel data 236 if (step + mode > bufSize) 237 break; 238 aux = cc.__getSubArray(buffer, step, step + mode); 239 step += mode; 240 241 // mode=3 or 4 implies that the image is RGB(A). However TGA 242 // stores it as BGR(A) so we'll have to swap R and B. 243 if (mode >= 3) { 244 var tmp = aux[0]; 245 aux[0] = aux[2]; 246 aux[2] = tmp; 247 } 248 } 249 250 // add the pixel to our image 251 for (var j = 0; j < mode; j++) 252 psInfo.imageData[index + j] = aux[j]; 253 254 index += mode; 255 } 256 257 return true; 258 }; 259 260 /** 261 * ImageTGA Flip 262 * @param {cc.ImageTGA} psInfo 263 */ 264 cc.tgaFlipImage = function (psInfo) { 265 // mode equal the number of components for each pixel 266 var mode = psInfo.pixelDepth / 8; 267 var rowbytes = psInfo.width * mode; 268 269 for (var y = 0; y < (psInfo.height / 2); y++) { 270 var row = cc.__getSubArray(psInfo.imageData, y * rowbytes, y * rowbytes + rowbytes); 271 cc.__setDataToArray(cc.__getSubArray(psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes, rowbytes), psInfo.imageData, y * rowbytes); 272 cc.__setDataToArray(row, psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes); 273 } 274 psInfo.flipped = 0; 275 }; 276 277 cc.__getSubArray = function (array, start, end) { 278 if (array instanceof Array) 279 return array.slice(start, end); 280 else 281 return array.subarray(start, end); 282 }; 283 284 cc.__setDataToArray = function (sourceData, destArray, startIndex) { 285 for (var i = 0; i < sourceData.length; i++) 286 destArray[startIndex + i] = sourceData[i]; 287 }; 288 289 /** 290 * Binary Stream Reader 291 * 292 * @class 293 * @param binaryData 294 */ 295 cc.BinaryStreamReader = cc.Class.extend({ 296 _binaryData:null, 297 _offset:0, 298 299 /** 300 * <p>The cc.BinaryStreamReader's constructor. <br/> 301 * This function will automatically be invoked when you create a node using new construction: "var node = new cc.BinaryStreamReader()".<br/> 302 * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.</p> 303 * @param binaryData 304 */ 305 ctor:function (binaryData) { 306 this._binaryData = binaryData; 307 }, 308 309 /** 310 * Set the binaryData. 311 * @param binaryData 312 */ 313 setBinaryData:function (binaryData) { 314 this._binaryData = binaryData; 315 this._offset = 0; 316 }, 317 318 /** 319 * Gets the binaryData. 320 * @returns {Object} 321 */ 322 getBinaryData:function () { 323 return this._binaryData; 324 }, 325 326 _checkSize:function (neededBits) { 327 if (!(this._offset + Math.ceil(neededBits / 8) < this._data.length)) 328 throw new Error("Index out of bound"); 329 }, 330 331 _decodeFloat:function (precisionBits, exponentBits) { 332 var length = precisionBits + exponentBits + 1; 333 var size = length >> 3; 334 this._checkSize(length); 335 336 var bias = Math.pow(2, exponentBits - 1) - 1; 337 var signal = this._readBits(precisionBits + exponentBits, 1, size); 338 var exponent = this._readBits(precisionBits, exponentBits, size); 339 var significand = 0; 340 var divisor = 2; 341 var curByte = 0; //length + (-precisionBits >> 3) - 1; 342 do { 343 var byteValue = this._readByte(++curByte, size); 344 var startBit = precisionBits % 8 || 8; 345 var mask = 1 << startBit; 346 while (mask >>= 1) { 347 if (byteValue & mask) 348 significand += 1 / divisor; 349 divisor *= 2; 350 } 351 } while (precisionBits -= startBit); 352 353 this._offset += size; 354 355 return exponent === (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity 356 : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand 357 : Math.pow(2, exponent - bias) * (1 + significand) : 0); 358 }, 359 360 _readByte:function (i, size) { 361 return this._data[this._offset + size - i - 1]; 362 }, 363 364 _decodeInt:function (bits, signed) { 365 var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits); 366 var result = signed && x >= max / 2 ? x - max : x; 367 368 this._offset += bits / 8; 369 return result; 370 }, 371 372 _shl:function (a, b) { 373 for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) === 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1){}; 374 return a; 375 }, 376 377 _readBits:function (start, length, size) { 378 var offsetLeft = (start + length) % 8; 379 var offsetRight = start % 8; 380 var curByte = size - (start >> 3) - 1; 381 var lastByte = size + (-(start + length) >> 3); 382 var diff = curByte - lastByte; 383 384 var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1); 385 386 if (diff && offsetLeft) 387 sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight; 388 389 while (diff) 390 sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight); 391 392 return sum; 393 }, 394 395 readInteger:function () { 396 return this._decodeInt(32, true); 397 }, 398 399 readUnsignedInteger:function () { 400 return this._decodeInt(32, false); 401 }, 402 403 readSingle:function () { 404 return this._decodeFloat(23, 8); 405 }, 406 407 readShort:function () { 408 return this._decodeInt(16, true); 409 }, 410 411 readUnsignedShort:function () { 412 return this._decodeInt(16, false); 413 }, 414 415 readByte:function () { 416 var readByte = this._data[this._offset]; 417 this._offset += 1; 418 return readByte; 419 }, 420 421 readData:function (start, end) { 422 if (this._binaryData instanceof Array) { 423 return this._binaryData.slice(start, end); 424 } else { 425 //typed array 426 return this._binaryData.subarray(start, end); 427 } 428 }, 429 430 setOffset:function (offset) { 431 this._offset = offset; 432 }, 433 434 getOffset:function () { 435 return this._offset; 436 } 437 }); 438