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 * this is the function to call when we want to load an image 153 * @param filename 154 * @return {cc.ImageTGA} 155 */ 156 cc.tgaLoad = function (filename) { 157 var buffer = cc.FileUtils.getInstance().getFileData(filename, "rb"); 158 var size = buffer.length; 159 160 if (buffer == null) 161 return null; 162 163 var info = new cc.ImageTGA(); 164 // get the file header info 165 if (!cc.tgaLoadHeader(buffer, size, info)) { 166 info.status = cc.TGA_ERROR_MEMORY; 167 return info; 168 } 169 170 // check if the image is color indexed 171 if (info.type === 1) { 172 info.status = cc.TGA_ERROR_INDEXED_COLOR; 173 return info; 174 } 175 176 // check for other types (compressed images) 177 if ((info.type != 2) && (info.type != 3) && (info.type != 10)) { 178 info.status = cc.TGA_ERROR_COMPRESSED_FILE; 179 return info; 180 } 181 182 var bLoadImage = false; 183 // finally load the image pixels 184 if (info.type == 10) 185 bLoadImage = cc.tgaLoadRLEImageData(buffer, size, info); 186 else 187 bLoadImage = cc.tgaLoadImageData(buffer, size, info); 188 189 // check for errors when reading the pixels 190 if (!bLoadImage) { 191 info.status = cc.TGA_ERROR_READING_FILE; 192 return info; 193 } 194 195 info.status = cc.TGA_OK; 196 if (info.flipped) { 197 cc.tgaFlipImage(info); 198 if (info.flipped) 199 info.status = cc.TGA_ERROR_MEMORY; 200 } 201 buffer = null; 202 return info; 203 }; 204 205 /** 206 * converts RGB to grayscale 207 * @param {cc.ImageTGA} psInfo 208 */ 209 cc.tgaRGBtogreyscale = function (psInfo) { 210 var i, j; 211 212 // if the image is already grayscale do nothing 213 if (psInfo.pixelDepth === 8) 214 return; 215 216 // compute the number of actual components 217 var mode = psInfo.pixelDepth / 8; 218 219 // allocate an array for the new image data 220 var newImageData = new Uint8Array(psInfo.height * psInfo.width); 221 if (newImageData === null) 222 return; 223 224 // convert pixels: grayscale = o.30 * R + 0.59 * G + 0.11 * B 225 for (i = 0, j = 0; j < psInfo.width * psInfo.height; i += mode, j++) 226 newImageData[j] = (0.30 * psInfo.imageData[i] + 0.59 * psInfo.imageData[i + 1] + 0.11 * psInfo.imageData[i + 2]); 227 228 // reassign pixelDepth and type according to the new image type 229 psInfo.pixelDepth = 8; 230 psInfo.type = 3; 231 // reassigning imageData to the new array. 232 psInfo.imageData = newImageData; 233 }; 234 235 /** 236 * releases the memory used for the image 237 * @param {cc.ImageTGA} psInfo 238 */ 239 cc.tgaDestroy = function (psInfo) { 240 if (!psInfo) 241 return; 242 243 psInfo.imageData = null; 244 psInfo = null; 245 }; 246 247 cc.tgaLoadRLEImageData = function (buffer, bufSize, psInfo) { 248 var mode, total, i, index = 0 , skip = 0, flag = 0; 249 var aux = [], runlength = 0; 250 251 var step = 18; // . size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6; 252 253 // mode equal the number of components for each pixel 254 mode = psInfo.pixelDepth / 8; 255 // total is the number of unsigned chars we'll have to read 256 total = psInfo.height * psInfo.width; 257 258 for (i = 0; i < total; i++) { 259 // if we have a run length pending, run it 260 if (runlength != 0) { 261 // we do, update the run length count 262 runlength--; 263 skip = (flag != 0); 264 } else { 265 // otherwise, read in the run length token 266 if (step + 1 > bufSize) 267 break; 268 runlength = buffer[step]; 269 step += 1; 270 271 // see if it's a RLE encoded sequence 272 flag = runlength & 0x80; 273 if (flag) 274 runlength -= 128; 275 skip = 0; 276 } 277 278 // do we need to skip reading this pixel? 279 if (!skip) { 280 // no, read in the pixel data 281 if (step + mode > bufSize) 282 break; 283 aux = cc.__getSubArray(buffer, step, step + mode); 284 step += mode; 285 286 // mode=3 or 4 implies that the image is RGB(A). However TGA 287 // stores it as BGR(A) so we'll have to swap R and B. 288 if (mode >= 3) { 289 var tmp = aux[0]; 290 aux[0] = aux[2]; 291 aux[2] = tmp; 292 } 293 } 294 295 // add the pixel to our image 296 for (var j = 0; j < mode; j++) 297 psInfo.imageData[index + j] = aux[j]; 298 299 index += mode; 300 } 301 302 return true; 303 }; 304 305 cc.tgaFlipImage = function (psInfo) { 306 // mode equal the number of components for each pixel 307 var mode = psInfo.pixelDepth / 8; 308 var rowbytes = psInfo.width * mode; 309 310 for (var y = 0; y < (psInfo.height / 2); y++) { 311 var row = cc.__getSubArray(psInfo.imageData, y * rowbytes, y * rowbytes + rowbytes); 312 cc.__setDataToArray(cc.__getSubArray(psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes, rowbytes), psInfo.imageData, y * rowbytes); 313 cc.__setDataToArray(row, psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes); 314 } 315 psInfo.flipped = 0; 316 }; 317 318 cc.__getSubArray = function (array, start, end) { 319 if (array instanceof Array) 320 return array.slice(start, end); 321 else 322 return array.subarray(start, end); 323 }; 324 325 cc.__setDataToArray = function (sourceData, destArray, startIndex) { 326 for (var i = 0; i < sourceData.length; i++) 327 destArray[startIndex + i] = sourceData[i]; 328 }; 329 330 331 cc.BinaryStreamReader = cc.Class.extend({ 332 _binaryData:null, 333 _offset:0, 334 335 ctor:function (binaryData) { 336 this._binaryData = binaryData; 337 }, 338 339 setBinaryData:function (binaryData) { 340 this._binaryData = binaryData; 341 this._offset = 0; 342 }, 343 344 getBinaryData:function () { 345 return this._binaryData; 346 }, 347 348 _checkSize:function (neededBits) { 349 if (!(this._offset + Math.ceil(neededBits / 8) < this._data.length)) 350 throw new Error("Index out of bound"); 351 }, 352 353 _decodeFloat:function (precisionBits, exponentBits) { 354 var length = precisionBits + exponentBits + 1; 355 var size = length >> 3; 356 this._checkSize(length); 357 358 var bias = Math.pow(2, exponentBits - 1) - 1; 359 var signal = this._readBits(precisionBits + exponentBits, 1, size); 360 var exponent = this._readBits(precisionBits, exponentBits, size); 361 var significand = 0; 362 var divisor = 2; 363 var curByte = 0; //length + (-precisionBits >> 3) - 1; 364 do { 365 var byteValue = this._readByte(++curByte, size); 366 var startBit = precisionBits % 8 || 8; 367 var mask = 1 << startBit; 368 while (mask >>= 1) { 369 if (byteValue & mask) 370 significand += 1 / divisor; 371 divisor *= 2; 372 } 373 } while (precisionBits -= startBit); 374 375 this._offset += size; 376 377 return exponent == (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity 378 : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand 379 : Math.pow(2, exponent - bias) * (1 + significand) : 0); 380 }, 381 382 _readByte:function (i, size) { 383 return this._data[this._offset + size - i - 1]; 384 }, 385 386 _decodeInt:function (bits, signed) { 387 var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits); 388 var result = signed && x >= max / 2 ? x - max : x; 389 390 this._offset += bits / 8; 391 return result; 392 }, 393 394 _shl:function (a, b) { 395 for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1){}; 396 return a; 397 }, 398 399 _readBits:function (start, length, size) { 400 var offsetLeft = (start + length) % 8; 401 var offsetRight = start % 8; 402 var curByte = size - (start >> 3) - 1; 403 var lastByte = size + (-(start + length) >> 3); 404 var diff = curByte - lastByte; 405 406 var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1); 407 408 if (diff && offsetLeft) 409 sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight; 410 411 while (diff) 412 sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight); 413 414 return sum; 415 }, 416 417 readInteger:function () { 418 return this._decodeInt(32, true); 419 }, 420 421 readUnsignedInteger:function () { 422 return this._decodeInt(32, false); 423 }, 424 425 readSingle:function () { 426 return this._decodeFloat(23, 8); 427 }, 428 429 readShort:function () { 430 return this._decodeInt(16, true); 431 }, 432 433 readUnsignedShort:function () { 434 return this._decodeInt(16, false); 435 }, 436 437 readByte:function () { 438 var readByte = this._data[this._offset]; 439 this._offset += 1; 440 return readByte; 441 }, 442 443 readData:function (start, end) { 444 if (this._binaryData instanceof Array) { 445 return this._binaryData.slice(start, end); 446 } else { 447 //typed array 448 return this._binaryData.subarray(start, end); 449 } 450 }, 451 452 setOffset:function (offset) { 453 this._offset = offset; 454 }, 455 456 getOffset:function () { 457 return this._offset; 458 } 459 }); 460