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