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