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