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 //CONSTANTS:
 28 
 29 /**
 30  * Horizontal center and vertical center.
 31  * @constant
 32  * @type Number
 33  */
 34 cc.ALIGN_CENTER = 0x33;
 35 
 36 /**
 37  * Horizontal center and vertical top.
 38  * @constant
 39  * @type Number
 40  */
 41 cc.ALIGN_TOP = 0x13;
 42 
 43 /**
 44  * Horizontal right and vertical top.
 45  * @constant
 46  * @type Number
 47  */
 48 cc.ALIGN_TOP_RIGHT = 0x12;
 49 
 50 /**
 51  * Horizontal right and vertical center.
 52  * @constant
 53  * @type Number
 54  */
 55 cc.ALIGN_RIGHT = 0x32;
 56 
 57 /**
 58  * Horizontal right and vertical bottom.
 59  * @constant
 60  * @type Number
 61  */
 62 cc.ALIGN_BOTTOM_RIGHT = 0x22;
 63 
 64 /**
 65  * Horizontal center and vertical bottom.
 66  * @constant
 67  * @type Number
 68  */
 69 cc.ALIGN_BOTTOM = 0x23;
 70 
 71 /**
 72  * Horizontal left and vertical bottom.
 73  * @constant
 74  * @type Number
 75  */
 76 cc.ALIGN_BOTTOM_LEFT = 0x21;
 77 
 78 /**
 79  * Horizontal left and vertical center.
 80  * @constant
 81  * @type Number
 82  */
 83 cc.ALIGN_LEFT = 0x31;
 84 
 85 /**
 86  * Horizontal left and vertical top.
 87  * @constant
 88  * @type Number
 89  */
 90 cc.ALIGN_TOP_LEFT = 0x11;
 91 //----------------------Possible texture pixel formats----------------------------
 92 
 93 
 94 // By default PVR images are treated as if they don't have the alpha channel premultiplied
 95 cc.PVRHaveAlphaPremultiplied_ = false;
 96 
 97 //cc.Texture2DWebGL move to TextureWebGL.js
 98 
 99 cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () {
100 
101     if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) {
102 
103         var proto = {
104             _contentSize: null,
105             _textureLoaded: false,
106             _htmlElementObj: null,
107             url: null,
108             _pattern: null,
109 
110             ctor: function () {
111                 this._contentSize = cc.size(0, 0);
112                 this._textureLoaded = false;
113                 this._htmlElementObj = null;
114                 this._pattern = "";
115             },
116 
117             /**
118              * get width in pixels
119              * @return {Number}
120              */
121             getPixelsWide: function () {
122                 return this._contentSize.width;
123             },
124 
125             /**
126              * get height of in pixels
127              * @return {Number}
128              */
129             getPixelsHigh: function () {
130                 return this._contentSize.height;
131             },
132 
133             /**
134              * get content size
135              * @returns {cc.Size}
136              */
137             getContentSize: function () {
138                 var locScaleFactor = cc.contentScaleFactor();
139                 return cc.size(this._contentSize.width / locScaleFactor, this._contentSize.height / locScaleFactor);
140             },
141 
142             _getWidth: function () {
143                 return this._contentSize.width / cc.contentScaleFactor();
144             },
145             _getHeight: function () {
146                 return this._contentSize.height / cc.contentScaleFactor();
147             },
148 
149             /**
150              * get content size in pixels
151              * @returns {cc.Size}
152              */
153             getContentSizeInPixels: function () {
154                 return this._contentSize;
155             },
156 
157             /**
158              * init with HTML element
159              * @param {HTMLImageElement|HTMLCanvasElement} element
160              */
161             initWithElement: function (element) {
162                 if (!element)
163                     return;
164                 this._htmlElementObj = element;
165                 this._contentSize.width = element.width;
166                 this._contentSize.height = element.height;
167                 this._textureLoaded = true;
168             },
169 
170             /**
171              * HTMLElement Object getter
172              * @return {HTMLImageElement|HTMLCanvasElement}
173              */
174             getHtmlElementObj: function () {
175                 return this._htmlElementObj;
176             },
177 
178             /**
179              * check whether texture is loaded
180              * @returns {boolean}
181              */
182             isLoaded: function () {
183                 return this._textureLoaded;
184             },
185 
186             /**
187              * handle loaded texture
188              */
189             handleLoadedTexture: function () {
190                 var self = this;
191                 if (self._textureLoaded) return;
192                 if (!self._htmlElementObj) {
193                     var img = cc.loader.getRes(self.url);
194                     if (!img) return;
195                     self.initWithElement(img);
196                 }
197 
198                 var locElement = self._htmlElementObj;
199                 self._contentSize.width = locElement.width;
200                 self._contentSize.height = locElement.height;
201 
202                 //dispatch load event to listener.
203                 self.dispatchEvent("load");
204             },
205 
206             /**
207              * description of cc.Texture2D
208              * @returns {string}
209              */
210             description: function () {
211                 return "<cc.Texture2D | width = " + this._contentSize.width + " height " + this._contentSize.height + ">";
212             },
213 
214             initWithData: function (data, pixelFormat, pixelsWide, pixelsHigh, contentSize) {
215                 //support only in WebGl rendering mode
216                 return false;
217             },
218 
219             initWithImage: function (uiImage) {
220                 //support only in WebGl rendering mode
221                 return false;
222             },
223 
224             initWithString: function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) {
225                 //support only in WebGl rendering mode
226                 return false;
227             },
228 
229             releaseTexture: function () {
230                 cc.loader.release(this.url);
231             },
232 
233             getName: function () {
234                 //support only in WebGl rendering mode
235                 return null;
236             },
237 
238             getMaxS: function () {
239                 //support only in WebGl rendering mode
240                 return 1;
241             },
242 
243             setMaxS: function (maxS) {
244                 //support only in WebGl rendering mode
245             },
246 
247             getMaxT: function () {
248                 return 1;
249             },
250 
251             setMaxT: function (maxT) {
252                 //support only in WebGl rendering mode
253             },
254 
255             getPixelFormat: function () {
256                 //support only in WebGl rendering mode
257                 return null;
258             },
259 
260             getShaderProgram: function () {
261                 //support only in WebGl rendering mode
262                 return null;
263             },
264 
265             setShaderProgram: function (shaderProgram) {
266                 //support only in WebGl rendering mode
267             },
268 
269             hasPremultipliedAlpha: function () {
270                 //support only in WebGl rendering mode
271                 return false;
272             },
273 
274             hasMipmaps: function () {
275                 //support only in WebGl rendering mode
276                 return false;
277             },
278 
279             releaseData: function (data) {
280                 //support only in WebGl rendering mode
281                 data = null;
282             },
283 
284             keepData: function (data, length) {
285                 //support only in WebGl rendering mode
286                 return data;
287             },
288 
289             drawAtPoint: function (point) {
290                 //support only in WebGl rendering mode
291             },
292 
293             drawInRect: function (rect) {
294                 //support only in WebGl rendering mode
295             },
296 
297             /**
298              * init with ETC file
299              * @warning does not support on HTML5
300              */
301             initWithETCFile: function (file) {
302                 cc.log(cc._LogInfos.Texture2D_initWithETCFile);
303                 return false;
304             },
305 
306             /**
307              * init with PVR file
308              * @warning does not support on HTML5
309              */
310             initWithPVRFile: function (file) {
311                 cc.log(cc._LogInfos.Texture2D_initWithPVRFile);
312                 return false;
313             },
314 
315             /**
316              * init with PVRTC data
317              * @warning does not support on HTML5
318              */
319             initWithPVRTCData: function (data, level, bpp, hasAlpha, length, pixelFormat) {
320                 cc.log(cc._LogInfos.Texture2D_initWithPVRTCData);
321                 return false;
322             },
323 
324             setTexParameters: function (texParams, magFilter, wrapS, wrapT) {
325                 if(magFilter !== undefined)
326                     texParams = {minFilter: texParams, magFilter: magFilter, wrapS: wrapS, wrapT: wrapT};
327 
328                 if(texParams.wrapS === cc.REPEAT && texParams.wrapT === cc.REPEAT){
329                     this._pattern = "repeat";
330                     return;
331                 }
332 
333                 if(texParams.wrapS === cc.REPEAT ){
334                     this._pattern = "repeat-x";
335                     return;
336                 }
337 
338                 if(texParams.wrapT === cc.REPEAT){
339                     this._pattern = "repeat-y";
340                     return;
341                 }
342 
343                 this._pattern = "";
344             },
345 
346             setAntiAliasTexParameters: function () {
347                 //support only in WebGl rendering mode
348             },
349 
350             setAliasTexParameters: function () {
351                 //support only in WebGl rendering mode
352             },
353 
354             generateMipmap: function () {
355                 //support only in WebGl rendering mode
356             },
357 
358             stringForFormat: function () {
359                 //support only in WebGl rendering mode
360                 return "";
361             },
362 
363             bitsPerPixelForFormat: function (format) {
364                 //support only in WebGl rendering mode
365                 return -1;
366             },
367 
368             /**
369              * add listener for loaded event
370              * @param {Function} callback
371              * @param {cc.Node} target
372              * @deprecated since 3.1, please use addEventListener instead
373              */
374             addLoadedEventListener: function (callback, target) {
375                 this.addEventListener("load", callback, target);
376             },
377 
378             /**
379              * remove listener from listeners by target
380              * @param {cc.Node} target
381              */
382             removeLoadedEventListener: function (target) {
383                 this.removeEventListener("load", target);
384             },
385 
386             _generateColorTexture: function(){/*overide*/},
387             _generateTextureCacheForColor: function(){
388                 if (this.channelCache)
389                     return this.channelCache;
390 
391                 var textureCache = [
392                     document.createElement("canvas"),
393                     document.createElement("canvas"),
394                     document.createElement("canvas"),
395                     document.createElement("canvas")
396                 ];
397                 //todo texture onload
398                 renderToCache(this._htmlElementObj, textureCache);
399                 return this.channelCache = textureCache;
400             },
401 
402             //hack for gray effect
403             _grayElementObj: null,
404             _backupElement: null,
405             _isGray: false,
406             _switchToGray: function(toGray){
407                 if(!this._textureLoaded || this._isGray === toGray)
408                     return;
409                 this._isGray = toGray;
410                 if(this._isGray){
411                     this._backupElement = this._htmlElementObj;
412                     if(!this._grayElementObj)
413                         this._grayElementObj = cc.Texture2D._generateGrayTexture(this._htmlElementObj);
414                     this._htmlElementObj = this._grayElementObj;
415                 } else {
416                     if(this._backupElement !== null)
417                         this._htmlElementObj = this._backupElement;
418                 }
419             }
420         };
421 
422         var renderToCache = function(image, cache){
423             var w = image.width;
424             var h = image.height;
425 
426             cache[0].width = w;
427             cache[0].height = h;
428             cache[1].width = w;
429             cache[1].height = h;
430             cache[2].width = w;
431             cache[2].height = h;
432             cache[3].width = w;
433             cache[3].height = h;
434 
435             var cacheCtx = cache[3].getContext("2d");
436             cacheCtx.drawImage(image, 0, 0);
437             var pixels = cacheCtx.getImageData(0, 0, w, h).data;
438 
439             var ctx;
440             for (var rgbI = 0; rgbI < 4; rgbI++) {
441                 ctx = cache[rgbI].getContext("2d");
442 
443                 var to = ctx.getImageData(0, 0, w, h);
444                 var data = to.data;
445                 for (var i = 0; i < pixels.length; i += 4) {
446                     data[i  ] = (rgbI === 0) ? pixels[i  ] : 0;
447                     data[i + 1] = (rgbI === 1) ? pixels[i + 1] : 0;
448                     data[i + 2] = (rgbI === 2) ? pixels[i + 2] : 0;
449                     data[i + 3] = pixels[i + 3];
450                 }
451                 ctx.putImageData(to, 0, 0);
452             }
453             image.onload = null;
454         };
455 
456         //change color function
457         if(cc.sys._supportCanvasNewBlendModes){
458             //multiply mode
459             //Primary afferent, Draw a new texture based on rect
460             proto._generateColorTexture = function(r, g, b, rect, canvas){
461                 var onlyCanvas = false;
462                 if(canvas)
463                     onlyCanvas = true;
464                 else
465                     canvas = document.createElement("canvas");
466                 var textureImage = this._htmlElementObj;
467                 if(!rect)
468                     rect = cc.rect(0, 0, textureImage.width, textureImage.height);
469 
470                 canvas.width = rect.width;
471                 canvas.height = rect.height;
472 
473                 var context = canvas.getContext("2d");
474                 context.globalCompositeOperation = "source-over";
475                 context.fillStyle = "rgb(" + (r|0) + "," + (g|0) + "," + (b|0) + ")";
476                 context.fillRect(0, 0, rect.width, rect.height);
477                 context.globalCompositeOperation = "multiply";
478                 context.drawImage(
479                     textureImage,
480                     rect.x, rect.y, rect.width, rect.height,
481                     0, 0, rect.width, rect.height
482                 );
483                 context.globalCompositeOperation = "destination-atop";
484                 context.drawImage(
485                     textureImage,
486                     rect.x, rect.y, rect.width, rect.height,
487                     0, 0, rect.width, rect.height
488                 );
489                 if(onlyCanvas)
490                     return canvas;
491                 var newTexture = new cc.Texture2D();
492                 newTexture.initWithElement(canvas);
493                 newTexture.handleLoadedTexture();
494                 return newTexture;
495             };
496         }else{
497             //Four color map overlay
498             proto._generateColorTexture = function(r, g, b, rect, canvas){
499                 var onlyCanvas = false;
500                 if(canvas)
501                     onlyCanvas = true;
502                 else
503                     canvas = document.createElement("canvas");
504 
505                 var textureImage = this._htmlElementObj;
506                 if(!rect)
507                     rect = cc.rect(0, 0, textureImage.width, textureImage.height);
508                 var x, y, w, h;
509                 x = rect.x; y = rect.y; w = rect.width; h = rect.height;
510                 if(!w || !h)
511                     return;
512 
513                 canvas.width = w;
514                 canvas.height = h;
515                 
516                 var context = canvas.getContext("2d");
517                 var tintedImgCache = cc.textureCache.getTextureColors(this);
518                 context.globalCompositeOperation = 'lighter';
519                 context.drawImage(
520                     tintedImgCache[3],
521                     x, y, w, h,
522                     0, 0, w, h
523                 );
524                 if (r > 0) {
525                     context.globalAlpha = r / 255;
526                     context.drawImage(
527                         tintedImgCache[0],
528                         x, y, w, h,
529                         0, 0, w, h
530                     );
531                 }
532                 if (g > 0) {
533                     context.globalAlpha = g / 255;
534                     context.drawImage(
535                         tintedImgCache[1],
536                         x, y, w, h,
537                         0, 0, w, h
538                     );
539                 }
540                 if (b > 0) {
541                     context.globalAlpha = b / 255;
542                     context.drawImage(
543                         tintedImgCache[2],
544                         x, y, w, h,
545                         0, 0, w, h
546                     );
547                 }
548                 if(onlyCanvas)
549                     return canvas;
550 
551                 var newTexture = new cc.Texture2D();
552                 newTexture.initWithElement(canvas);
553                 newTexture.handleLoadedTexture();
554                 return newTexture;
555             };
556         }
557 
558         /**
559          * <p>
560          * This class allows to easily create OpenGL or Canvas 2D textures from images, text or raw data.                                    <br/>
561          * The created cc.Texture2D object will always have power-of-two dimensions.                                                <br/>
562          * Depending on how you create the cc.Texture2D object, the actual image area of the texture might be smaller than the texture dimensions <br/>
563          *  i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0).                                           <br/>
564          * Be aware that the content of the generated textures will be upside-down! </p>
565          * @name cc.Texture2D
566          * @class
567          * @extends cc.Class
568          *
569          * @property {WebGLTexture}     name            - <@readonly> WebGLTexture Object
570          * @property {Number}           pixelFormat     - <@readonly> Pixel format of the texture
571          * @property {Number}           pixelsWidth     - <@readonly> Width in pixels
572          * @property {Number}           pixelsHeight    - <@readonly> Height in pixels
573          * @property {Number}           width           - Content width in points
574          * @property {Number}           height          - Content height in points
575          * @property {cc.GLProgram}     shaderProgram   - The shader program used by drawAtPoint and drawInRect
576          * @property {Number}           maxS            - Texture max S
577          * @property {Number}           maxT            - Texture max T
578          */
579         cc.Texture2D = cc.Class.extend(/** @lends cc.Texture2D# */proto);
580 
581         cc.Texture2D._generateGrayTexture = function(texture, rect, renderCanvas){
582             if (texture === null)
583                 return null;
584             renderCanvas = renderCanvas || document.createElement("canvas");
585             rect = rect || cc.rect(0, 0, texture.width, texture.height);
586             renderCanvas.width = rect.width;
587             renderCanvas.height = rect.height;
588 
589             var context = renderCanvas.getContext("2d");
590             context.drawImage(texture, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height);
591             var imgData = context.getImageData(0, 0, rect.width, rect.height);
592             var data = imgData.data;
593             for (var i = 0, len = data.length; i < len; i += 4) {
594                 data[i] = data[i + 1] = data[i + 2] = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
595             }
596             context.putImageData(imgData, 0, 0);
597             return renderCanvas;
598         };
599 
600     } else if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) {
601         cc.assert(cc.isFunction(cc._tmp.WebGLTexture2D), cc._LogInfos.MissingFile, "TexturesWebGL.js");
602         cc._tmp.WebGLTexture2D();
603         delete cc._tmp.WebGLTexture2D;
604     }
605 
606     cc.EventHelper.prototype.apply(cc.Texture2D.prototype);
607 
608     cc.assert(cc.isFunction(cc._tmp.PrototypeTexture2D), cc._LogInfos.MissingFile, "TexturesPropertyDefine.js");
609     cc._tmp.PrototypeTexture2D();
610     delete cc._tmp.PrototypeTexture2D;
611 });
612