1 /**
  2  * The MIT License (MIT)
  3  *
  4  * Copyright (c) 2016 DeNA Co., Ltd.
  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 THE
 22  * SOFTWARE.
 23  */
 24 
 25 /// <reference path="base.js"/>
 26 /// <reference path="object.js"/>
 27 /// <reference path="shadow.js"/>
 28 /// <reference path="bounding_box.js"/>
 29 /// <reference path="color.js"/>
 30 /// <reference path="rectangle.js"/>
 31 /// <reference path="transform.js"/>
 32 /// <reference path="point.js"/>
 33 
 34 /**
 35  * An abstract class that renders shapes and objects to an HTMLCanvasElement
 36  * object.
 37  * @param {HTMLCanvasElement} canvas
 38  * @param {number} width
 39  * @param {number} height
 40  * @constructor
 41  */
 42 createjs.Renderer = function(canvas, width, height) {
 43   /// <param type="HTMLCanvasElement" name="canvas"/>
 44   /// <param type="number" name="width"/>
 45   /// <param type="number" name="height"/>
 46   /**
 47    * The output <canvas> element.
 48    * @type {HTMLCanvasElement}
 49    * @private
 50    */
 51   this.canvas_ = canvas;
 52 
 53   /**
 54    * The current width of the output <canvas> element.
 55    * @type {number}
 56    * @private
 57    */
 58   this.width_ = width;
 59 
 60   /**
 61    * The current height of the output <canvas> element.
 62    * @type {number}
 63    * @private
 64    */
 65   this.height_ = height;
 66 };
 67 
 68 /**
 69  * Whether this renderer applies workarounds for Android browsers.
 70  * @type {number}
 71  * @private
 72  */
 73 createjs.Renderer.prototype.useAndroidWorkarounds_ = -1;
 74 
 75 /**
 76  * The ratio of a point in this renderer to one of CSS.
 77  * @type {createjs.Point}
 78  * @private
 79  */
 80 createjs.Renderer.prototype.cssRatio_ = null;
 81 
 82 /**
 83  * Composite operations.
 84  * @enum {number}
 85  */
 86 createjs.Renderer.Composition = {
 87   SOURCE_OVER: 0,
 88   SOURCE_ATOP: 1,
 89   SOURCE_IN: 2,
 90   SOURCE_OUT: 3,
 91   DESTINATION_OVER: 4,
 92   DESTINATION_ATOP: 5,
 93   DESTINATION_IN: 6,
 94   DESTINATION_OUT: 7,
 95   LIGHTER: 8,
 96   COPY: 9,
 97   XOR: 10,
 98   DARKER: 11,
 99   MULTIPLY: 12,
100   MASK: 15
101 };
102 
103 /**
104  * Mask methods.
105  * @enum {number}
106  */
107 createjs.Renderer.Mask = {
108   SCISSOR: 1,
109   COMPOSE: 2,
110   SHOW: 3,
111   HIDE: 4
112 };
113 
114 /**
115  * Color formats used by WebGLTextures.
116  * @enum {number}
117  */
118 createjs.Renderer.Format = {
119   RGBA8888: 0,
120   RGBA4444: 1
121 };
122 
123 /**
124  * Extensions.
125  * @enum {number}
126  */
127 createjs.Renderer.Extension = {
128   VIDEO: 1 << 0
129 };
130 
131 /**
132  * Returns the composition ID associated with the specified name.
133  * @param {string} name
134  * @return {number}
135  */
136 createjs.Renderer.getCompositionKey = function(name) {
137   var KEYS = {
138     'source-over': createjs.Renderer.Composition.SOURCE_OVER,
139     'source-atop': createjs.Renderer.Composition.SOURCE_ATOP,
140     'source-in': createjs.Renderer.Composition.SOURCE_IN,
141     'source-out': createjs.Renderer.Composition.SOURCE_OUT,
142     'destination-over': createjs.Renderer.Composition.DESTINATION_OVER,
143     'destination-atop': createjs.Renderer.Composition.DESTINATION_ATOP,
144     'destination-in': createjs.Renderer.Composition.DESTINATION_IN,
145     'destination-out': createjs.Renderer.Composition.DESTINATION_OUT,
146     'lighter': createjs.Renderer.Composition.LIGHTER,
147     'copy': createjs.Renderer.Composition.COPY,
148     'xor': createjs.Renderer.Composition.XOR,
149     'darker': createjs.Renderer.Composition.DARKER
150   };
151   return KEYS[name] || createjs.Renderer.Composition.SOURCE_OVER;
152 };
153 
154 /**
155  * Returns the composition name from the specified ID.
156  * @param {number} key
157  * @return {string}
158  */
159 createjs.Renderer.getCompositionName = function(key) {
160   var NAMES = [
161     'source-over',
162     'source-atop',
163     'source-in',
164     'source-out',
165     'destination-over',
166     'destination-atop',
167     'destination-in',
168     'destination-out',
169     'lighter',
170     'copy',
171     'xor',
172     'darker',
173     'multiply'
174 ];
175   return NAMES[key];
176 };
177 
178 /**
179  * An inner class used by createjs.Renderer objects to clip RenderObject
180  * objects.
181  * @param {createjs.Transform} transform
182  * @param {createjs.BoundingBox} rectangle
183  * @param {createjs.BoundingBox} box
184  * @param {number} method
185  * @param {number} composition
186  * @param {createjs.Renderer.RenderObject} shape
187  * @constructor
188  */
189 createjs.Renderer.Clip =
190     function(transform, rectangle, box, method, composition, shape) {
191   /// <param type="createjs.Transform" name="transform"/>
192   /// <param type="createjs.BoundingBox" name="rectangle"/>
193   /// <param type="createjs.BoundingBox" name="box"/>
194   /// <param type="number" name="method"/>
195   /// <param type="number" name="composition"/>
196   /// <param type="createjs.Renderer.RenderObject" name="shape"/>
197   /**
198    * An affine transformation applied to a clipping rectangle.
199    * @const {createjs.Transform}
200    * @private
201    */
202   this.transform_ = transform;
203 
204   /**
205    * A clipping rectangle in the local coordinate.
206    * @const {createjs.Rectangle}
207    * @private
208    */
209   this.rectangle_ = new createjs.Rectangle(rectangle.getLeft(),
210                                            rectangle.getTop(),
211                                            rectangle.getWidth(),
212                                            rectangle.getHeight());
213 
214   /**
215    * A clipping rectangle in the global coordinate.
216    * @const {createjs.BoundingBox}
217    * @private
218    */
219   this.box_ = createjs.Renderer.Clip.cloneBox(box);
220 
221   /**
222    * A method used for applying this clip to a render object.
223    * rectangle.
224    * @const {number}
225    * @private
226    */
227   this.method_ = method;
228 
229   /**
230    * A composition operation used in rendering the clipped image.
231    * @const {number}
232    * @private
233    */
234   this.composition_ = composition;
235 
236   /**
237    * A render object that renders this clip.
238    * @const {createjs.Renderer.RenderObject}
239    * @private
240    */
241   this.shape_ =
242       (method > createjs.Renderer.Mask.SCISSOR) ? shape : null;
243 };
244 
245 /**
246  * Returns a clone of the specified box.
247  * @param {createjs.BoundingBox} box
248  * @return {createjs.BoundingBox}
249  */
250 createjs.Renderer.Clip.cloneBox = function(box) {
251   /// <param type="createjs.BoundingBox" name="box"/>
252   /// <returns type="createjs.BoundingBox"/>
253   var clone = new createjs.BoundingBox();
254   clone.minX = createjs.truncate(box.minX) + 1;
255   clone.minY = createjs.truncate(box.minY);
256   clone.maxX = createjs.truncate(box.maxX);
257   clone.maxY = createjs.truncate(box.maxY);
258   return clone;
259 };
260 
261 /**
262  * Returns the affine transformation.
263  * @return {createjs.Transform}
264  * @const
265  */
266 createjs.Renderer.Clip.prototype.getTransform = function() {
267   /// <returns type="createjs.Transform"/>
268   return this.transform_;
269 };
270 
271 /**
272  * Returns the clipping rectangle.
273  * @return {createjs.Rectangle}
274  * @const
275  */
276 createjs.Renderer.Clip.prototype.getRectangle = function() {
277   /// <returns type="createjs.Rectangle"/>
278   return this.rectangle_;
279 };
280 
281 /**
282  * Returns the bounding box.
283  * @return {createjs.BoundingBox}
284  * @const
285  */
286 createjs.Renderer.Clip.prototype.getBox = function() {
287   /// <returns type="createjs.BoundingBox"/>
288   return this.box_;
289 };
290 
291 /**
292  * Returns the clipping method.
293  * @return {number}
294  * @const
295  */
296 createjs.Renderer.Clip.prototype.getMethod = function() {
297   /// <returns type="number"/>
298   // Disable this clip when it is invisible to avoid a crash in rendering an
299   // invisible shape. (A tween may set an empty shape to this mask.)
300   if (this.shape_ && !this.shape_.isVisible()) {
301     return 0;
302   }
303   return this.method_;
304 };
305 
306 /**
307  * Returns whether a renderer has to compose this clip to a render object.
308  * @return {boolean}
309  * @const
310  */
311 createjs.Renderer.Clip.prototype.isCompose = function() {
312   /// <returns type="boolean"/>
313   return this.method_ == createjs.Renderer.Mask.COMPOSE;
314 };
315 
316 /**
317  * Returns whether a renderer should show a render object without applying this
318  * clip.
319  * @return {boolean}
320  * @const
321  */
322 createjs.Renderer.Clip.prototype.isShow = function() {
323   /// <returns type="boolean"/>
324   return this.method_ == createjs.Renderer.Mask.SHOW;
325 };
326 
327 /**
328  * Returns whether this clip can use its bounding box as a scissor box.
329  * @return {number}
330  * @const
331  */
332 createjs.Renderer.Clip.prototype.getComposition = function() {
333   /// <returns type="number"/>
334   return this.composition_;
335 };
336 
337 /**
338  * Returns a display object used as a mask.
339  * @return {createjs.Renderer.RenderObject}
340  * @const
341  */
342 createjs.Renderer.Clip.prototype.getShape = function() {
343   /// <returns type="createjs.Renderer.RenderObject"/>
344   return this.shape_;
345 };
346 
347 /**
348  * An interface used by a renderer object to draw objects registered with the
349  * paintObject() method.
350  * @interface
351  */
352 createjs.Renderer.RenderObject = function() {};
353 
354 /**
355  * Returns whether this object needs a redraw.
356  * @param {createjs.BoundingBox} box
357  * @return {boolean}
358  */
359 createjs.Renderer.RenderObject.prototype.isDirtyObject = function(box) {
360   /// <returns type="createjs.BoundingBox"/>
361 };
362 
363 /**
364  * Returns the bounding box of this object in the global coordinate system.
365  * @return {createjs.BoundingBox}
366  */
367 createjs.Renderer.RenderObject.prototype.getRenderBox = function() {
368   /// <returns type="createjs.BoundingBox"/>
369 };
370 
371 /**
372  * Returns a createjs.Renderer.Clip object associated with this object.
373  * @return {createjs.Renderer.Clip}
374  */
375 createjs.Renderer.RenderObject.prototype.getClip = function() {
376   /// <returns type="createjs.Renderer.Clip"/>
377 };
378 
379 /**
380  * Starts painting this object.
381  * @param {createjs.Renderer} renderer
382  */
383 createjs.Renderer.RenderObject.prototype.beginPaintObject =
384     function(renderer) {
385   /// <param type="createjs.Renderer" name="renderer"/>
386 };
387 
388 /**
389  * Paints an object.
390  * @param {createjs.Renderer} renderer
391  */
392 createjs.Renderer.RenderObject.prototype.paintObject = function(renderer) {
393   /// <param type="createjs.Renderer" name="renderer"/>
394 };
395 
396 /**
397  * Calculates the ratio of a point in this renderer to one of CSS. When this
398  * renderer has its <canvas> element applied CSS transforms, this ratio does not
399  * become (1, 1) to (1, 1). This method retrieves the computed rectangle from a
400  * browser and calculates it.
401  * @private
402  */
403 createjs.Renderer.prototype.calculateRatio_ = function() {
404   var canvas = this.getCanvas();
405   var bounds = canvas.getBoundingClientRect();
406   var zoom = window.getComputedStyle(canvas).zoom;
407   this.cssRatio_ = new createjs.Point(
408       bounds.width * zoom / this.width_, bounds.height * zoom / this.height_);
409 };
410 
411 /**
412  * Synchronizes the width property of this renderer with the specified one.
413  * @param {number} width
414  * @protected
415  * @const
416  */
417 createjs.Renderer.prototype.setWidth = function(width) {
418   /// <param type="number" name="width"/>
419   this.width_ = width;
420 };
421 
422 /**
423  * Synchronizes the height properties of this renderer with the specified one.
424  * @param {number} height
425  * @protected
426  * @const
427  */
428 createjs.Renderer.prototype.setHeight = function(height) {
429   /// <param type="number" name="height"/>
430   this.height_ = height;
431 };
432 
433 /**
434  * Resets the HTMLCanvasElement object attached to this renderer.
435  * @protected
436  * @const
437  */
438 createjs.Renderer.prototype.resetCanvas = function() {
439   if (this.canvas_) {
440     this.canvas_.width = 0;
441     this.canvas_ = null;
442   }
443 };
444 
445 /**
446  * Updates the layout of the HTMLCanvasElement object attached to this renderer.
447  * @param {string} context
448  * @suppress {uselessCode}
449  * @protected
450  * @const
451  */
452 createjs.Renderer.prototype.updateCanvas = function(context) {
453   /// <param type="string" name="context"/>
454   if (this.useAndroidWorkarounds_ < 0) {
455     this.useAndroidWorkarounds_ =
456         createjs.Config.useAndroidWorkarounds(context);
457   }
458   var canvas = this.getCanvas();
459   if (this.useAndroidWorkarounds_ == 1) {
460     var display = canvas.style.display;
461     if (display != 'none') {
462       canvas.style.display = 'none';
463       canvas.offsetHeight;
464       canvas.style.display = display;
465     }
466   } else if (this.useAndroidWorkarounds_) {
467     canvas.width = canvas.width;
468   }
469 };
470 
471 /**
472  * Destroys the resources attached to this renderer.
473  */
474 createjs.Renderer.prototype.destroy = function() {
475   createjs.notReached();
476 };
477 
478 /**
479  * Returns the HTMLCanvasElement object attached to this renderer.
480  * @return {HTMLCanvasElement}
481  * @const
482  */
483 createjs.Renderer.prototype.getCanvas = function() {
484   /// <returns type="HTMLCanvasElement"/>
485   return this.canvas_;
486 };
487 
488 /**
489  * Returns the width.
490  * @return {number}
491  * @const
492  */
493 createjs.Renderer.prototype.getWidth = function() {
494   /// <returns type="number"/>
495   return this.width_;
496 };
497 
498 /**
499  * Returns the height.
500  * @return {number}
501  * @const
502  */
503 createjs.Renderer.prototype.getHeight = function() {
504   /// <returns type="number"/>
505   return this.height_;
506 };
507 
508 /**
509  * Returns the ratio of a point in this renderer to one of CSS.
510  * @return {createjs.Point}
511  * @const
512  */
513 createjs.Renderer.prototype.getCSSRatio = function() {
514   /// <returns type="createjs.Point"/>
515   if (!this.cssRatio_) {
516     this.calculateRatio_();
517   }
518   return this.cssRatio_;
519 };
520 
521 /**
522  * Sets an affine transformation.
523  * @param {number} a
524  * @param {number} b
525  * @param {number} c
526  * @param {number} d
527  * @param {number} tx
528  * @param {number} ty
529  */
530 createjs.Renderer.prototype.setTransformation = function(a, b, c, d, tx, ty) {
531   /// <param type="number" name="a"/>
532   /// <param type="number" name="b"/>
533   /// <param type="number" name="c"/>
534   /// <param type="number" name="d"/>
535   /// <param type="number" name="tx"/>
536   /// <param type="number" name="ty"/>
537   createjs.notReached();
538 };
539 
540 /**
541  * Sets the alpha value.
542  * @param {number} alpha
543  */
544 createjs.Renderer.prototype.setAlpha = function(alpha) {
545   /// <param type="number" name="alpha"/>
546   createjs.notReached();
547 };
548 
549 /**
550  * Sets the color matrix.
551  * @param {Array.<number>} matrix
552  */
553 createjs.Renderer.prototype.setColorMatrix = function(matrix) {
554   /// <param type="Array" elementType="number" name="matrix"/>
555 };
556 
557 /**
558  * Sets the color-composition operation.
559  * @param {number} operation
560  */
561 createjs.Renderer.prototype.setComposition = function(operation) {
562   /// <param type="number" name="operation"/>
563   createjs.notReached();
564 };
565 
566 /**
567  * Draws an HTMLCanvasElement object to the specified rectangle.
568  * @param {HTMLCanvasElement} canvas
569  * @param {number} x
570  * @param {number} y
571  * @param {number} width
572  * @param {number} height
573  */
574 createjs.Renderer.prototype.drawCanvas = function(canvas, x, y, width, height) {
575   /// <param type="HTMLCanvasElement" name="canvas"/>
576   /// <param type="number" name="x"/>
577   /// <param type="number" name="y"/>
578   /// <param type="number" name="width"/>
579   /// <param type="number" name="height"/>
580   createjs.notReached();
581 };
582 
583 /**
584  * Returns a bit-mask representing the extensions supported by this renderer.
585  * @return {number}
586  */
587 createjs.Renderer.prototype.getExtensions = function() {
588   /// <returns type="number"/>
589   return createjs.Renderer.Extension.VIDEO;
590 };
591 
592 /**
593  * Draws an HTMLVideoElement object to the specified rectangle.
594  * @param {HTMLVideoElement} video
595  * @param {number} x
596  * @param {number} y
597  * @param {number} width
598  * @param {number} height
599  */
600 createjs.Renderer.prototype.drawVideo = function(video, x, y, width, height) {
601   /// <param type="HTMLVideoElement" name="video"/>
602   /// <param type="number" name="x"/>
603   /// <param type="number" name="y"/>
604   /// <param type="number" name="width"/>
605   /// <param type="number" name="height"/>
606   createjs.notReached();
607 };
608 
609 /**
610  * Draws a part of an HTMLImageElement object to the specified rectangle.
611  * @param {HTMLImageElement} image
612  * @param {number} srcX
613  * @param {number} srcY
614  * @param {number} srcWidth
615  * @param {number} srcHeight
616  * @param {number} x
617  * @param {number} y
618  * @param {number} width
619  * @param {number} height
620  */
621 createjs.Renderer.prototype.drawPartial =
622     function(image, srcX, srcY, srcWidth, srcHeight, x, y, width, height) {
623   /// <param type="HTMLImageElement" name="image"/>
624   /// <param type="number" name="srcX"/>
625   /// <param type="number" name="srcY"/>
626   /// <param type="number" name="srcWidth"/>
627   /// <param type="number" name="srcHeight"/>
628   /// <param type="number" name="x"/>
629   /// <param type="number" name="y"/>
630   /// <param type="number" name="width"/>
631   /// <param type="number" name="height"/>
632   createjs.notReached();
633 };
634 
635 /**
636  * Adds an object to the rendering queue.
637  * @param {createjs.Renderer.RenderObject} object
638  */
639 createjs.Renderer.prototype.addObject = function(object) {
640   /// <param type="createjs.Renderer.RenderObject" name="object"/>
641   // This method is used for calculating clipping rectangles, i.e. this method
642   // should not call createjs.notReached().
643 };
644 
645 /**
646  * Deletes the cache for the specified image.
647  * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image
648  */
649 createjs.Renderer.prototype.uncache = function(image) {
650   /// <signature>
651   ///   <param type="HTMLImageElement" name="image"/>
652   /// </signature>
653   /// <signature>
654   ///   <param type="HTMLCanvasElement" name="canvas"/>
655   /// </signature>
656   /// <signature>
657   ///   <param type="HTMLVideoElement" name="video"/>
658   /// </signature>
659 };
660 
661 /**
662  * Starts paints objects.
663  */
664 createjs.Renderer.prototype.begin = function() {
665 };
666 
667 /**
668  * Paints objects added to this renderer. This method returns true if this
669  * renderer actually draws objects.
670  * @param {number} time
671  */
672 createjs.Renderer.prototype.paint = function(time) {
673   /// <param type="number" name="time"/>
674   createjs.notReached();
675 };
676 
677 if (createjs.DEBUG) {
678   /**
679    * Adds a dirty object to another renderer.
680    * @param {createjs.Renderer.RenderObject} object
681    */
682   createjs.Renderer.prototype.addDirtyObject = function(object) {
683     /// <param type="createjs.Renderer.RenderObject" name="object"/>
684   };
685 }
686