Prechádzať zdrojové kódy

Canvas2D: Positioning Engine, ZE Final Fight! + bonus devs

Big boring but necessary work was done to make the positioning engine supporting scale/rotation of primitives... with a little side effect.

The side effect: before the positioning engine would round the aligned position to ensure the rendered primitive to be aligned on the rendering target's pixel. This can't be done anymore due to scaling (an aligned position of 10.5 with a scale of 2 would be aligned correctly). That's why primitive alignment was introduced and is forced for Text2D.

Bonus Developments:

 - Introduced an AOP style of logging system using TypeScript decorators to log the execution of methods and properties getter/setter. Main methods/properties of the Canvas2D rendering pipeline are using it now.
 - Prim2DBase has now an alignToPixel property to align the primitive render to the render target's pixel.
 - Text2D rendering was not using premultiplied alpha as it should be.
 - BaseFontTexture has now a isPremultipliedAlpha property and Text2D is using accordingly.
 - FontTexture.GetCachedFontTexture is now generating a texture using NEAREST_SAMPLINGMODE when SDF is not used.
 - A **Breaking change** is introduced in the constructor of BitmapFontTexture because the premultipliedAlpha argument was inserted before the onLoad one.
nockawa 8 rokov pred
rodič
commit
19ed936670

+ 2 - 1
Tools/Gulp/config.json

@@ -467,7 +467,8 @@
     {
     {
       "files": [
       "files": [
         "../../canvas2D/src/Tools/babylon.math2D.ts",
         "../../canvas2D/src/Tools/babylon.math2D.ts",
-        "../../canvas2D/src/Tools/babylon.IPropertyChanged.ts",
+        "../../canvas2D/src/Tools/babylon.iPropertyChanged.ts",
+        "../../canvas2D/src/Tools/babylon.c2dlogging.ts",
         "../../canvas2D/src/Tools/babylon.observableArray.ts",
         "../../canvas2D/src/Tools/babylon.observableArray.ts",
         "../../canvas2D/src/Tools/babylon.observableStringDictionary.ts",
         "../../canvas2D/src/Tools/babylon.observableStringDictionary.ts",
         "../../canvas2D/src/Engine/babylon.fontTexture.ts",
         "../../canvas2D/src/Engine/babylon.fontTexture.ts",

+ 4 - 0
canvas2D/src/Engine/babylon.bounding2d.ts

@@ -127,6 +127,10 @@
             b._worldAABBDirty = true;
             b._worldAABBDirty = true;
         }
         }
 
 
+        public toString(): string {
+            return `Center: ${this.center}, Extent: ${this.extent}, Radius: ${this.radius}`;
+        }
+
         /**
         /**
          * Duplicate this instance and return a new one
          * Duplicate this instance and return a new one
          * @return the duplicated instance
          * @return the duplicated instance

+ 95 - 60
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -87,20 +87,6 @@
         }) {
         }) {
             super(settings);
             super(settings);
 
 
-            this._drawCallsOpaqueCounter          = new PerfCounter();
-            this._drawCallsAlphaTestCounter       = new PerfCounter();
-            this._drawCallsTransparentCounter     = new PerfCounter();
-            this._groupRenderCounter              = new PerfCounter();
-            this._updateTransparentDataCounter    = new PerfCounter();
-            this._cachedGroupRenderCounter        = new PerfCounter();
-            this._updateCachedStateCounter        = new PerfCounter();
-            this._updateLayoutCounter             = new PerfCounter();
-            this._updatePositioningCounter        = new PerfCounter();
-            this._updateLocalTransformCounter     = new PerfCounter();
-            this._updateGlobalTransformCounter    = new PerfCounter();
-            this._boundingInfoRecomputeCounter    = new PerfCounter();
-            this._layoutBoundingInfoUpdateCounter = new PerfCounter();
-
             this._cachedCanvasGroup = null;
             this._cachedCanvasGroup = null;
 
 
             this._renderingGroupObserver = null;
             this._renderingGroupObserver = null;
@@ -200,18 +186,24 @@
                     this._renderingGroupObserver = this._scene.onRenderingGroupObservable.add((e, s) => {
                     this._renderingGroupObserver = this._scene.onRenderingGroupObservable.add((e, s) => {
                         if ((this._scene.activeCamera === settings.renderingPhase.camera) && (e.renderStage===RenderingGroupInfo.STAGE_POSTTRANSPARENT)) {
                         if ((this._scene.activeCamera === settings.renderingPhase.camera) && (e.renderStage===RenderingGroupInfo.STAGE_POSTTRANSPARENT)) {
                             this._engine.clear(null, false, true, true);
                             this._engine.clear(null, false, true, true);
+                            C2DLogging._startFrameRender();
                             this._render();
                             this._render();
+                            C2DLogging._endFrameRender();
                         }
                         }
                     }, Math.pow(2, settings.renderingPhase.renderingGroupID));
                     }, Math.pow(2, settings.renderingPhase.renderingGroupID));
                 } else {
                 } else {
                     this._afterRenderObserver = this._scene.onAfterRenderObservable.add((d, s) => {
                     this._afterRenderObserver = this._scene.onAfterRenderObservable.add((d, s) => {
                         this._engine.clear(null, false, true, true);
                         this._engine.clear(null, false, true, true);
+                        C2DLogging._startFrameRender();
                         this._render();
                         this._render();
+                        C2DLogging._endFrameRender();
                     });
                     });
                 }
                 }
             } else {
             } else {
                 this._beforeRenderObserver = this._scene.onBeforeRenderObservable.add((d, s) => {
                 this._beforeRenderObserver = this._scene.onBeforeRenderObservable.add((d, s) => {
+                    C2DLogging._startFrameRender();
                     this._render();
                     this._render();
+                    C2DLogging._endFrameRender();
                 });
                 });
             }
             }
 
 
@@ -232,54 +224,93 @@
         }
         }
 
 
         public get drawCallsOpaqueCounter(): PerfCounter {
         public get drawCallsOpaqueCounter(): PerfCounter {
+            if (!this._drawCallsOpaqueCounter) {
+                this._drawCallsOpaqueCounter = new PerfCounter();
+            }
             return this._drawCallsOpaqueCounter;
             return this._drawCallsOpaqueCounter;
         }
         }
 
 
         public get drawCallsAlphaTestCounter(): PerfCounter {
         public get drawCallsAlphaTestCounter(): PerfCounter {
+            if (!this._drawCallsAlphaTestCounter) {
+                this._drawCallsAlphaTestCounter = new PerfCounter();
+            }
             return this._drawCallsAlphaTestCounter;
             return this._drawCallsAlphaTestCounter;
         }
         }
 
 
         public get drawCallsTransparentCounter(): PerfCounter {
         public get drawCallsTransparentCounter(): PerfCounter {
+            if (!this._drawCallsTransparentCounter) {
+                this._drawCallsTransparentCounter = new PerfCounter();
+            }
             return this._drawCallsTransparentCounter;
             return this._drawCallsTransparentCounter;
         }
         }
 
 
         public get groupRenderCounter(): PerfCounter {
         public get groupRenderCounter(): PerfCounter {
+            if (!this._groupRenderCounter) {
+                this._groupRenderCounter = new PerfCounter();
+            }
             return this._groupRenderCounter;
             return this._groupRenderCounter;
         }
         }
 
 
         public get updateTransparentDataCounter(): PerfCounter {
         public get updateTransparentDataCounter(): PerfCounter {
+            if (!this._updateTransparentDataCounter) {
+                this._updateTransparentDataCounter = new PerfCounter();
+            }
             return this._updateTransparentDataCounter;
             return this._updateTransparentDataCounter;
         }
         }
 
 
         public get cachedGroupRenderCounter(): PerfCounter {
         public get cachedGroupRenderCounter(): PerfCounter {
+            if (!this._cachedGroupRenderCounter) {
+                this._cachedGroupRenderCounter = new PerfCounter();
+            }
             return this._cachedGroupRenderCounter;
             return this._cachedGroupRenderCounter;
         }
         }
 
 
         public get updateCachedStateCounter(): PerfCounter {
         public get updateCachedStateCounter(): PerfCounter {
+            if (!this._updateCachedStateCounter) {
+                this._updateCachedStateCounter = new PerfCounter();
+            }
             return this._updateCachedStateCounter;
             return this._updateCachedStateCounter;
         }
         }
 
 
         public get updateLayoutCounter(): PerfCounter {
         public get updateLayoutCounter(): PerfCounter {
+            if (!this._updateLayoutCounter) {
+                this._updateLayoutCounter = new PerfCounter();
+            }
             return this._updateLayoutCounter;
             return this._updateLayoutCounter;
         }
         }
 
 
         public get updatePositioningCounter(): PerfCounter {
         public get updatePositioningCounter(): PerfCounter {
+            if (!this._updatePositioningCounter) {
+                this._updatePositioningCounter = new PerfCounter();
+            }
             return this._updatePositioningCounter;
             return this._updatePositioningCounter;
         }
         }
 
 
         public get updateLocalTransformCounter(): PerfCounter {
         public get updateLocalTransformCounter(): PerfCounter {
+            if (!this._updateLocalTransformCounter) {
+                this._updateLocalTransformCounter = new PerfCounter();
+            }
             return this._updateLocalTransformCounter;
             return this._updateLocalTransformCounter;
         }
         }
 
 
         public get updateGlobalTransformCounter(): PerfCounter {
         public get updateGlobalTransformCounter(): PerfCounter {
+            if (!this._updateGlobalTransformCounter) {
+                this._updateGlobalTransformCounter = new PerfCounter();
+            }
             return this._updateGlobalTransformCounter;
             return this._updateGlobalTransformCounter;
         }
         }
 
 
         public get boundingInfoRecomputeCounter(): PerfCounter {
         public get boundingInfoRecomputeCounter(): PerfCounter {
+            if (!this._boundingInfoRecomputeCounter) {
+                this._boundingInfoRecomputeCounter = new PerfCounter();
+            }
             return this._boundingInfoRecomputeCounter;
             return this._boundingInfoRecomputeCounter;
         }
         }
 
 
         public get layoutBoundingInfoUpdateCounter(): PerfCounter {
         public get layoutBoundingInfoUpdateCounter(): PerfCounter {
+            if (!this._layoutBoundingInfoUpdateCounter) {
+                this._layoutBoundingInfoUpdateCounter = new PerfCounter();
+            }
             return this._layoutBoundingInfoUpdateCounter;
             return this._layoutBoundingInfoUpdateCounter;
         }
         }
 
 
@@ -1129,35 +1160,35 @@
         }
         }
 
 
         private _initPerfMetrics() {
         private _initPerfMetrics() {
-            this._drawCallsOpaqueCounter.fetchNewFrame();
-            this._drawCallsAlphaTestCounter.fetchNewFrame();
-            this._drawCallsTransparentCounter.fetchNewFrame();
-            this._groupRenderCounter.fetchNewFrame();
-            this._updateTransparentDataCounter.fetchNewFrame();
-            this._cachedGroupRenderCounter.fetchNewFrame();
-            this._updateCachedStateCounter.fetchNewFrame();
-            this._updateLayoutCounter.fetchNewFrame();
-            this._updatePositioningCounter.fetchNewFrame();
-            this._updateLocalTransformCounter.fetchNewFrame();
-            this._updateGlobalTransformCounter.fetchNewFrame();
-            this._boundingInfoRecomputeCounter.fetchNewFrame();
-            this._layoutBoundingInfoUpdateCounter.fetchNewFrame();
+            this.drawCallsOpaqueCounter.fetchNewFrame();
+            this.drawCallsAlphaTestCounter.fetchNewFrame();
+            this.drawCallsTransparentCounter.fetchNewFrame();
+            this.groupRenderCounter.fetchNewFrame();
+            this.updateTransparentDataCounter.fetchNewFrame();
+            this.cachedGroupRenderCounter.fetchNewFrame();
+            this.updateCachedStateCounter.fetchNewFrame();
+            this.updateLayoutCounter.fetchNewFrame();
+            this.updatePositioningCounter.fetchNewFrame();
+            this.updateLocalTransformCounter.fetchNewFrame();
+            this.updateGlobalTransformCounter.fetchNewFrame();
+            this.boundingInfoRecomputeCounter.fetchNewFrame();
+            this.layoutBoundingInfoUpdateCounter.fetchNewFrame();
         }
         }
 
 
         private _fetchPerfMetrics() {
         private _fetchPerfMetrics() {
-            this._drawCallsOpaqueCounter.addCount(0, true);
-            this._drawCallsAlphaTestCounter.addCount(0, true);
-            this._drawCallsTransparentCounter.addCount(0, true);
-            this._groupRenderCounter.addCount(0, true);
-            this._updateTransparentDataCounter.addCount(0, true);
-            this._cachedGroupRenderCounter.addCount(0, true);
-            this._updateCachedStateCounter.addCount(0, true);
-            this._updateLayoutCounter.addCount(0, true);
-            this._updatePositioningCounter.addCount(0, true);
-            this._updateLocalTransformCounter.addCount(0, true);
-            this._updateGlobalTransformCounter.addCount(0, true);
-            this._boundingInfoRecomputeCounter.addCount(0, true);
-            this._layoutBoundingInfoUpdateCounter.addCount(0, true);
+            this.drawCallsOpaqueCounter.addCount(0, true);
+            this.drawCallsAlphaTestCounter.addCount(0, true);
+            this.drawCallsTransparentCounter.addCount(0, true);
+            this.groupRenderCounter.addCount(0, true);
+            this.updateTransparentDataCounter.addCount(0, true);
+            this.cachedGroupRenderCounter.addCount(0, true);
+            this.updateCachedStateCounter.addCount(0, true);
+            this.updateLayoutCounter.addCount(0, true);
+            this.updatePositioningCounter.addCount(0, true);
+            this.updateLocalTransformCounter.addCount(0, true);
+            this.updateGlobalTransformCounter.addCount(0, true);
+            this.boundingInfoRecomputeCounter.addCount(0, true);
+            this.layoutBoundingInfoUpdateCounter.addCount(0, true);
         }
         }
 
 
         private _updateProfileCanvas() {
         private _updateProfileCanvas() {
@@ -1455,6 +1486,7 @@
                 this._setRenderingScale(scale);
                 this._setRenderingScale(scale);
             }
             }
         }
         }
+        private static _pCLS = Vector3.Zero();
 
 
         private _updateCanvasState(forceRecompute: boolean) {
         private _updateCanvasState(forceRecompute: boolean) {
             // Check if the update has already been made for this render Frame
             // Check if the update has already been made for this render Frame
@@ -1464,14 +1496,7 @@
 
 
             // Detect a change of HWRendering scale
             // Detect a change of HWRendering scale
             let hwsl = this.engine.getHardwareScalingLevel();
             let hwsl = this.engine.getHardwareScalingLevel();
-            let hwslChanged = this._curHWScale !== hwsl;
-            if (hwslChanged) {
-                this._curHWScale = hwsl;
-                for (let child of this.children) {
-                    child._setFlags(SmartPropertyPrim.flagLocalTransformDirty|SmartPropertyPrim.flagGlobalTransformDirty);
-                }
-                this._setLayoutDirty();
-            }
+            this._curHWScale = hwsl;
 
 
             // Detect a change of rendering size
             // Detect a change of rendering size
             let renderingSizeChanged = false;
             let renderingSizeChanged = false;
@@ -1487,16 +1512,8 @@
             }
             }
             this._renderingSize.height = newHeight;
             this._renderingSize.height = newHeight;
 
 
-            // If the canvas fit the rendering size and it changed, update
-            if (renderingSizeChanged && this._fitRenderingDevice) {
-                this.size = this._renderingSize.clone();
-                if (this._background) {
-                    this._background.size = this.size;
-                }
-
-                // Dirty the Layout at the Canvas level to recompute as the size changed
-                this._setLayoutDirty();
-            }
+            let prevCLS = Canvas2D._pCLS;
+            prevCLS.copyFrom(this._canvasLevelScale);
 
 
             // If there's a design size, update the scale according to the renderingSize
             // If there's a design size, update the scale according to the renderingSize
             if (this._designSize) {
             if (this._designSize) {
@@ -1508,11 +1525,29 @@
                 }
                 }
                 this.size = this._designSize.clone();
                 this.size = this._designSize.clone();
                 this._canvasLevelScale.copyFromFloats(scale, scale, 1);
                 this._canvasLevelScale.copyFromFloats(scale, scale, 1);
-            } else if (this._curHWScale !== 1) {
+            } else {
                 let ratio = 1 / this._curHWScale;
                 let ratio = 1 / this._curHWScale;
                 this._canvasLevelScale.copyFromFloats(ratio, ratio, 1);
                 this._canvasLevelScale.copyFromFloats(ratio, ratio, 1);
             }
             }
 
 
+            if (!prevCLS.equals(this._canvasLevelScale)) {
+                for (let child of this.children) {
+                    child._setFlags(SmartPropertyPrim.flagLocalTransformDirty|SmartPropertyPrim.flagGlobalTransformDirty);
+                }
+                this._setLayoutDirty();
+            }
+
+            // If the canvas fit the rendering size and it changed, update
+            if (renderingSizeChanged && this._fitRenderingDevice) {
+                this.size = this._renderingSize.clone();
+                if (this._background) {
+                    this._background.size = this.size;
+                }
+
+                // Dirty the Layout at the Canvas level to recompute as the size changed
+                this._setLayoutDirty();
+            }
+
             var context = new PrepareRender2DContext();
             var context = new PrepareRender2DContext();
 
 
             ++this._globalTransformProcessStep;
             ++this._globalTransformProcessStep;
@@ -1527,8 +1562,8 @@
         /**
         /**
          * Method that renders the Canvas, you should not invoke
          * Method that renders the Canvas, you should not invoke
          */
          */
+        @logMethod("==========CANVAS RENDER===============")
         private _render() {
         private _render() {
-
             this._initPerfMetrics();
             this._initPerfMetrics();
 
 
             if (this._renderObservable && this._renderObservable.hasObservers()) {
             if (this._renderObservable && this._renderObservable.hasObservers()) {

+ 2 - 0
canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts

@@ -61,6 +61,7 @@
         // A very simple (no) layout computing...
         // A very simple (no) layout computing...
         // The Canvas and its direct children gets the Canvas' size as Layout Area
         // The Canvas and its direct children gets the Canvas' size as Layout Area
         // Indirect children have their Layout Area to the actualSize (margin area) of their parent
         // Indirect children have their Layout Area to the actualSize (margin area) of their parent
+        @logMethod()
         public updateLayout(prim: Prim2DBase) {
         public updateLayout(prim: Prim2DBase) {
 
 
             // If this prim is layoutDiry we update  its layoutArea and also the one of its direct children
             // If this prim is layoutDiry we update  its layoutArea and also the one of its direct children
@@ -73,6 +74,7 @@
 
 
         }
         }
 
 
+        @logMethod()
         private _doUpdate(prim: Prim2DBase) {
         private _doUpdate(prim: Prim2DBase) {
             // Canvas ?
             // Canvas ?
             if (prim instanceof Canvas2D) {
             if (prim instanceof Canvas2D) {

+ 7 - 2
canvas2D/src/Engine/babylon.ellipse2d.ts

@@ -203,6 +203,7 @@
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
+         * - alignToPixel: if true the primitive will be aligned to the target rendering device's pixel
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - zOrder: override the zOrder with the specified value
          * - zOrder: override the zOrder with the specified value
          * - origin: define the normalized origin point location, default [0.5;0.5]
          * - origin: define the normalized origin point location, default [0.5;0.5]
@@ -245,6 +246,7 @@
             scaleX                ?: number,
             scaleX                ?: number,
             scaleY                ?: number,
             scaleY                ?: number,
             dontInheritParentScale?: boolean,
             dontInheritParentScale?: boolean,
+            alignToPixel          ?: boolean,
             opacity               ?: number,
             opacity               ?: number,
             zOrder                ?: number, 
             zOrder                ?: number, 
             origin                ?: Vector2,
             origin                ?: Vector2,
@@ -419,20 +421,23 @@
             return res;
             return res;
         }
         }
 
 
+        private static _riv0 = new Vector2(0,0);
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
             if (!super.refreshInstanceDataPart(part)) {
             if (!super.refreshInstanceDataPart(part)) {
                 return false;
                 return false;
             }
             }
+
+            let s = Ellipse2D._riv0;
+            this.getActualGlobalScaleToRef(s);
+
             if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
             if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
                 let d = <Ellipse2DInstanceData>part;
                 let d = <Ellipse2DInstanceData>part;
                 let size = this.actualSize;
                 let size = this.actualSize;
-                let s = this.actualScale;
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.subdivisions);
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.subdivisions);
             }
             }
             else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
             else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
                 let d = <Ellipse2DInstanceData>part;
                 let d = <Ellipse2DInstanceData>part;
                 let size = this.actualSize;
                 let size = this.actualSize;
-                let s = this.actualScale;
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.subdivisions);
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.subdivisions);
             }
             }
             return true;
             return true;

+ 15 - 4
canvas2D/src/Engine/babylon.fontTexture.ts

@@ -26,12 +26,13 @@
      */
      */
     export abstract class BaseFontTexture extends Texture {
     export abstract class BaseFontTexture extends Texture {
 
 
-        constructor(url: string, scene: Scene, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
+        constructor(url: string, scene: Scene, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, premultipliedAlpha: boolean = false) {
 
 
             super(url, scene, noMipmap, invertY, samplingMode);
             super(url, scene, noMipmap, invertY, samplingMode);
 
 
             this._cachedFontId = null;
             this._cachedFontId = null;
             this._charInfos = new StringDictionary<CharInfo>();
             this._charInfos = new StringDictionary<CharInfo>();
+            this._isPremultipliedAlpha = premultipliedAlpha;
         }
         }
 
 
         /**
         /**
@@ -50,6 +51,13 @@
         }
         }
 
 
         /**
         /**
+         * True if the font was drawn using multiplied alpha
+         */
+        public get isPremultipliedAlpha(): boolean {
+            return this._isPremultipliedAlpha;
+        }
+
+        /**
          * Get the Width (in pixel) of the Space character
          * Get the Width (in pixel) of the Space character
          */
          */
         public get spaceWidth(): number {
         public get spaceWidth(): number {
@@ -148,6 +156,7 @@
         protected _spaceWidth;
         protected _spaceWidth;
         protected _superSample: boolean;
         protected _superSample: boolean;
         protected _signedDistanceField: boolean;
         protected _signedDistanceField: boolean;
+        protected _isPremultipliedAlpha: boolean;
         protected _cachedFontId: string;
         protected _cachedFontId: string;
     }
     }
 
 
@@ -174,11 +183,12 @@
                             textureUrl: string = null,
                             textureUrl: string = null,
                             noMipmap: boolean = false,
                             noMipmap: boolean = false,
                             invertY: boolean = true,
                             invertY: boolean = true,
-                            samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, 
+                            samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
+                            premultipliedAlpha: boolean = false,
                             onLoad: () => void = null,
                             onLoad: () => void = null,
                             onError: (msg: string, code: number) => void = null)
                             onError: (msg: string, code: number) => void = null)
         {
         {
-            super(null, scene, noMipmap, invertY, samplingMode);
+            super(null, scene, noMipmap, invertY, samplingMode, premultipliedAlpha);
 
 
             var xhr = new XMLHttpRequest();
             var xhr = new XMLHttpRequest();
             xhr.onreadystatechange = () => {
             xhr.onreadystatechange = () => {
@@ -361,7 +371,7 @@
                 return ft;
                 return ft;
             }
             }
 
 
-            ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, Texture.BILINEAR_SAMPLINGMODE, supersample, signedDistanceField);
+            ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, signedDistanceField ? Texture.BILINEAR_SAMPLINGMODE : Texture.NEAREST_SAMPLINGMODE, supersample, signedDistanceField);
             ft._cachedFontId = lfn;
             ft._cachedFontId = lfn;
             dic.add(lfn, ft);
             dic.add(lfn, ft);
 
 
@@ -404,6 +414,7 @@
             this._sdfScale = 8;
             this._sdfScale = 8;
             this._signedDistanceField = signedDistanceField;
             this._signedDistanceField = signedDistanceField;
             this._superSample = false;
             this._superSample = false;
+            this._isPremultipliedAlpha = !signedDistanceField;
 
 
             // SDF will use super sample no matter what, the resolution is otherwise too poor to produce correct result
             // SDF will use super sample no matter what, the resolution is otherwise too poor to produce correct result
             if (superSample || signedDistanceField) {
             if (superSample || signedDistanceField) {

+ 17 - 12
canvas2D/src/Engine/babylon.lines2d.ts

@@ -315,9 +315,6 @@
         }
         }
 
 
         protected updateLevelBoundingInfo(): boolean {
         protected updateLevelBoundingInfo(): boolean {
-            if (!this._size) {
-                return false;
-            }
             if (!this._boundingMin) {
             if (!this._boundingMin) {
                 this._computeLines2D();
                 this._computeLines2D();
             }
             }
@@ -336,6 +333,7 @@
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
+         * - alignToPixel: if true the primitive will be aligned to the target rendering device's pixel
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - zOrder: override the zOrder with the specified value
          * - zOrder: override the zOrder with the specified value
          * - origin: define the normalized origin point location, default [0.5;0.5]
          * - origin: define the normalized origin point location, default [0.5;0.5]
@@ -380,6 +378,7 @@
             scaleX                ?: number,
             scaleX                ?: number,
             scaleY                ?: number,
             scaleY                ?: number,
             dontInheritParentScale?: boolean,
             dontInheritParentScale?: boolean,
+            alignToPixel          ?: boolean,
             opacity               ?: number,
             opacity               ?: number,
             zOrder                ?: number, 
             zOrder                ?: number, 
             origin                ?: Vector2,
             origin                ?: Vector2,
@@ -423,8 +422,6 @@
             this._borderVB = null;
             this._borderVB = null;
             this._borderIB = null;
             this._borderIB = null;
 
 
-            this._size = Size.Zero();
-
             this._boundingMin = null;
             this._boundingMin = null;
             this._boundingMax = null;
             this._boundingMax = null;
 
 
@@ -1152,12 +1149,12 @@
                 let pta = this._primTriArray;
                 let pta = this._primTriArray;
                 let l = this.closed ? pl + 1 : pl;
                 let l = this.closed ? pl + 1 : pl;
 
 
-                this.transformPointWithOriginToRef(contour[0], null, Lines2D._prevA);
-                this.transformPointWithOriginToRef(contour[1], null, Lines2D._prevB);
+                Lines2D._prevA.copyFrom(contour[0]);
+                Lines2D._prevB.copyFrom(contour[1]);
                 let si = 0;
                 let si = 0;
                 for (let i = 1; i < l; i++) {
                 for (let i = 1; i < l; i++) {
-                    this.transformPointWithOriginToRef(contour[(i % pl) * 2 + 0], null, Lines2D._curA);
-                    this.transformPointWithOriginToRef(contour[(i % pl) * 2 + 1], null, Lines2D._curB);
+                    Lines2D._curA.copyFrom(contour[(i % pl) * 2 + 0]);
+                    Lines2D._curB.copyFrom(contour[(i % pl) * 2 + 1]);
 
 
                     pta.storeTriangle(si++, Lines2D._prevA, Lines2D._prevB, Lines2D._curA);
                     pta.storeTriangle(si++, Lines2D._prevA, Lines2D._prevB, Lines2D._curA);
                     pta.storeTriangle(si++, Lines2D._curA,  Lines2D._prevB, Lines2D._curB);
                     pta.storeTriangle(si++, Lines2D._curA,  Lines2D._prevB, Lines2D._curB);
@@ -1173,15 +1170,15 @@
                     for (let i = 0; i < l; i += 3) {
                     for (let i = 0; i < l; i += 3) {
                         Lines2D._curA.x = points[tri[i + 0] * 2 + 0];
                         Lines2D._curA.x = points[tri[i + 0] * 2 + 0];
                         Lines2D._curA.y = points[tri[i + 0] * 2 + 1];
                         Lines2D._curA.y = points[tri[i + 0] * 2 + 1];
-                        this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._curB);
+                        Lines2D._curB.copyFrom(Lines2D._curA);
 
 
                         Lines2D._curA.x = points[tri[i + 1] * 2 + 0];
                         Lines2D._curA.x = points[tri[i + 1] * 2 + 0];
                         Lines2D._curA.y = points[tri[i + 1] * 2 + 1];
                         Lines2D._curA.y = points[tri[i + 1] * 2 + 1];
-                        this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._prevA);
+                        Lines2D._prevA.copyFrom(Lines2D._curA);
 
 
                         Lines2D._curA.x = points[tri[i + 2] * 2 + 0];
                         Lines2D._curA.x = points[tri[i + 2] * 2 + 0];
                         Lines2D._curA.y = points[tri[i + 2] * 2 + 1];
                         Lines2D._curA.y = points[tri[i + 2] * 2 + 1];
-                        this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._prevB);
+                        Lines2D._prevB.copyFrom(Lines2D._curA);
 
 
                         pta.storeTriangle(si++, Lines2D._prevA, Lines2D._prevB, Lines2D._curB);
                         pta.storeTriangle(si++, Lines2D._prevA, Lines2D._prevB, Lines2D._curB);
                     }
                     }
@@ -1200,8 +1197,16 @@
             }
             }
 
 
             let bs = this._boundingMax.subtract(this._boundingMin);
             let bs = this._boundingMax.subtract(this._boundingMin);
+
+            // If the size is not size we were computing the first pass to determine the size took by the primitive
+            if (!this._size) {
+                // Set the size and compute a second time to consider the size while computing the points using the origin
+                this._size = new Size(bs.x, bs.y);
+                this._updatePositioningState();
+            }
             this._size.width = bs.x;
             this._size.width = bs.x;
             this._size.height = bs.y;
             this._size.height = bs.y;
+            this._actualSize = null;
         }
         }
 
 
         public get size(): Size {
         public get size(): Size {

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 320 - 258
canvas2D/src/Engine/babylon.prim2dBase.ts


+ 7 - 2
canvas2D/src/Engine/babylon.rectangle2d.ts

@@ -288,6 +288,7 @@
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
+         * - alignToPixel: if true the primitive will be aligned to the target rendering device's pixel
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - zOrder: override the zOrder with the specified value
          * - zOrder: override the zOrder with the specified value
          * - origin: define the normalized origin point location, default [0.5;0.5]
          * - origin: define the normalized origin point location, default [0.5;0.5]
@@ -329,6 +330,7 @@
             scaleX                ?: number,
             scaleX                ?: number,
             scaleY                ?: number,
             scaleY                ?: number,
             dontInheritParentScale?: boolean,
             dontInheritParentScale?: boolean,
+            alignToPixel          ?: boolean,
             opacity               ?: number,
             opacity               ?: number,
             zOrder                ?: number, 
             zOrder                ?: number, 
             origin                ?: Vector2,
             origin                ?: Vector2,
@@ -585,20 +587,23 @@
             return res;
             return res;
         }
         }
 
 
+        private static _riv0 = new Vector2(0,0);
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
             if (!super.refreshInstanceDataPart(part)) {
             if (!super.refreshInstanceDataPart(part)) {
                 return false;
                 return false;
             }
             }
+
+            let s = Rectangle2D._riv0;
+            this.getActualGlobalScaleToRef(s);
+
             if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
             if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
                 let d = <Rectangle2DInstanceData>part;
                 let d = <Rectangle2DInstanceData>part;
                 let size = this.actualSize;
                 let size = this.actualSize;
-                let s = this.actualScale;
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.roundRadius || 0);
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.roundRadius || 0);
             }
             }
             else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
             else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
                 let d = <Rectangle2DInstanceData>part;
                 let d = <Rectangle2DInstanceData>part;
                 let size = this.actualSize;
                 let size = this.actualSize;
-                let s = this.actualScale;
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.roundRadius || 0);
                 d.properties = new Vector3(size.width * s.x, size.height * s.y, this.roundRadius || 0);
             }
             }
             return true;
             return true;

+ 16 - 20
canvas2D/src/Engine/babylon.renderablePrim2d.ts

@@ -284,6 +284,15 @@
         set transformY(value: Vector4) {
         set transformY(value: Vector4) {
         }
         }
 
 
+        // The vector3 is: rendering width, height and 1 if the primitive must be aligned to pixel or 0 otherwise
+        @instanceData()
+        get renderingInfo(): Vector3 {
+            return null;
+        }
+
+        set renderingInfo(val: Vector3) {
+        }
+
         @instanceData()
         @instanceData()
         get opacity(): number {
         get opacity(): number {
             return null;
             return null;
@@ -824,23 +833,7 @@
             return null;
             return null;
         }
         }
 
 
-        /**
-         * Transform a given point using the Primitive's origin setting.
-         * This method requires the Primitive's actualSize to be accurate
-         * @param p the point to transform
-         * @param originOffset an offset applied on the current origin before performing the transformation. Depending on which frame of reference your data is expressed you may have to apply a offset. (if you data is expressed from the bottom/left, no offset is required. If it's expressed from the center the a [-0.5;-0.5] offset has to be applied.
-         * @param res an allocated Vector2 that will receive the transformed content
-         */
-        protected transformPointWithOriginByRef(p: Vector2, originOffset:Vector2, res: Vector2) {
-            let actualSize = this.actualSize;
-            res.x = p.x - ((this.origin.x + (originOffset ? originOffset.x : 0)) * actualSize.width);
-            res.y = p.y - ((this.origin.y + (originOffset ? originOffset.y : 0)) * actualSize.height);
-        }
-
-        protected transformPointWithOriginToRef(p: Vector2, originOffset: Vector2, res: Vector2) {
-            this.transformPointWithOriginByRef(p, originOffset, res);
-            return res;
-        }
+        private static _toz = Size.Zero();
 
 
         /**
         /**
          * Get the info for a given effect based on the dataPart metadata
          * Get the info for a given effect based on the dataPart metadata
@@ -932,6 +925,7 @@
         private static _r = Quaternion.Identity();
         private static _r = Quaternion.Identity();
         private static _t = Vector3.Zero();
         private static _t = Vector3.Zero();
         private static _iV3 = new Vector3(1, 1, 1); // Must stay identity vector3
         private static _iV3 = new Vector3(1, 1, 1); // Must stay identity vector3
+
         /**
         /**
          * Update the instanceDataBase level properties of a part
          * Update the instanceDataBase level properties of a part
          * @param part the part to update
          * @param part the part to update
@@ -944,8 +938,9 @@
             let trn = RenderablePrim2D._t;
             let trn = RenderablePrim2D._t;
             t.decompose(scl, rot, trn);
             t.decompose(scl, rot, trn);
             let pas = this.actualScale;
             let pas = this.actualScale;
-            scl.x = pas.x;
-            scl.y = pas.y;
+            let canvasScale = this.owner._canvasLevelScale;
+            scl.x = pas.x * canvasScale.x * this._postScale.x;
+            scl.y = pas.y * canvasScale.y * this._postScale.y;
             scl.z = 1;
             scl.z = 1;
             t = Matrix.Compose(this.applyActualScaleOnTransform() ? scl : RenderablePrim2D._iV3, rot, trn);
             t = Matrix.Compose(this.applyActualScaleOnTransform() ? scl : RenderablePrim2D._iV3, rot, trn);
 
 
@@ -970,10 +965,11 @@
             let w = size.width;
             let w = size.width;
             let h = size.height;
             let h = size.height;
             let invZBias = 1 / zBias;
             let invZBias = 1 / zBias;
+
             let tx = new Vector4(t.m[0] * 2 / w, t.m[4] * 2 / w, 0, ((t.m[12] + offX) * 2 / w) - 1);
             let tx = new Vector4(t.m[0] * 2 / w, t.m[4] * 2 / w, 0, ((t.m[12] + offX) * 2 / w) - 1);
             let ty = new Vector4(t.m[1] * 2 / h, t.m[5] * 2 / h, 0, ((t.m[13] + offY) * 2 / h) - 1);
             let ty = new Vector4(t.m[1] * 2 / h, t.m[5] * 2 / h, 0, ((t.m[13] + offY) * 2 / h) - 1);
 
 
-
+            part.renderingInfo = new Vector3(w, h, this.alignToPixel ? 1 : 0);
             part.transformX = tx;
             part.transformX = tx;
             part.transformY = ty;
             part.transformY = ty;
             part.opacity = this.actualOpacity;
             part.opacity = this.actualOpacity;

+ 38 - 2
canvas2D/src/Engine/babylon.smartPropertyPrim.ts

@@ -675,6 +675,7 @@
             } 
             } 
         }
         }
 
 
+        @logProp()
         protected _triggerPropertyChanged(propInfo: Prim2DPropInfo, newValue: any) {
         protected _triggerPropertyChanged(propInfo: Prim2DPropInfo, newValue: any) {
             if (this.isDisposed) {
             if (this.isDisposed) {
                 return;
                 return;
@@ -1103,13 +1104,13 @@
         }
         }
 
 
         protected _boundingBoxDirty() {
         protected _boundingBoxDirty() {
-            this._setFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
+            this._setFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty|SmartPropertyPrim.flagLayoutBoundingInfoDirty);
 
 
             // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
             // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
             if (this instanceof Prim2DBase) {
             if (this instanceof Prim2DBase) {
                 let curprim: Prim2DBase = (<any>this);
                 let curprim: Prim2DBase = (<any>this);
                 while (curprim) {
                 while (curprim) {
-                    curprim._setFlags(SmartPropertyPrim.flagBoundingInfoDirty);
+                    curprim._setFlags(SmartPropertyPrim.flagBoundingInfoDirty|SmartPropertyPrim.flagLayoutBoundingInfoDirty);
                     if (curprim.isSizeAuto) {
                     if (curprim.isSizeAuto) {
                         curprim.onPrimitivePropertyDirty(Prim2DBase.sizeProperty.flagId);
                         curprim.onPrimitivePropertyDirty(Prim2DBase.sizeProperty.flagId);
                         if (curprim._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
                         if (curprim._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
@@ -1198,6 +1199,7 @@
         /**
         /**
          * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
          * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
          */
          */
+        @logProp()
         public get levelBoundingInfo(): BoundingInfo2D {
         public get levelBoundingInfo(): BoundingInfo2D {
             if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
             if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
                 if (this.updateLevelBoundingInfo()) {
                 if (this.updateLevelBoundingInfo()) {
@@ -1283,6 +1285,39 @@
             }
             }
         }
         }
 
 
+        public _getFlagsDebug(flags: number): string {
+            let res = "";
+            if (flags & SmartPropertyPrim.flagNoPartOfLayout)          res += "NoPartOfLayout, ";
+            if (flags & SmartPropertyPrim.flagLevelBoundingInfoDirty)  res += "LevelBoundingInfoDirty, ";
+            if (flags & SmartPropertyPrim.flagModelDirty)              res += "ModelDirty, ";
+            if (flags & SmartPropertyPrim.flagLayoutDirty)             res += "LayoutDirty, ";
+            if (flags & SmartPropertyPrim.flagLevelVisible)            res += "LevelVisible, ";
+            if (flags & SmartPropertyPrim.flagBoundingInfoDirty)       res += "BoundingInfoDirty, ";
+            if (flags & SmartPropertyPrim.flagIsPickable)              res += "IsPickable, ";
+            if (flags & SmartPropertyPrim.flagIsVisible)               res += "IsVisible, ";
+            if (flags & SmartPropertyPrim.flagVisibilityChanged)       res += "VisibilityChanged, ";
+            if (flags & SmartPropertyPrim.flagPositioningDirty)        res += "PositioningDirty, ";
+            if (flags & SmartPropertyPrim.flagTrackedGroup)            res += "TrackedGroup, ";
+            if (flags & SmartPropertyPrim.flagWorldCacheChanged)       res += "WorldCacheChanged, ";
+            if (flags & SmartPropertyPrim.flagChildrenFlatZOrder)      res += "ChildrenFlatZOrder, ";
+            if (flags & SmartPropertyPrim.flagZOrderDirty)             res += "ZOrderDirty, ";
+            if (flags & SmartPropertyPrim.flagActualOpacityDirty)      res += "ActualOpacityDirty, ";
+            if (flags & SmartPropertyPrim.flagPrimInDirtyList)         res += "PrimInDirtyList, ";
+            if (flags & SmartPropertyPrim.flagIsContainer)             res += "IsContainer, ";
+            if (flags & SmartPropertyPrim.flagNeedRefresh)             res += "NeedRefresh, ";
+            if (flags & SmartPropertyPrim.flagActualScaleDirty)        res += "ActualScaleDirty, ";
+            if (flags & SmartPropertyPrim.flagDontInheritParentScale)  res += "DontInheritParentScale, ";
+            if (flags & SmartPropertyPrim.flagGlobalTransformDirty)    res += "GlobalTransformDirty, ";
+            if (flags & SmartPropertyPrim.flagLayoutBoundingInfoDirty) res += "LayoutBoundingInfoDirty, ";
+            if (flags & SmartPropertyPrim.flagCollisionActor)          res += "CollisionActor, ";
+            if (flags & SmartPropertyPrim.flagModelUpdate)             res += "ModelUpdate, ";
+            if (flags & SmartPropertyPrim.flagLocalTransformDirty)     res += "LocalTransformDirty, ";
+            if (flags & SmartPropertyPrim.flagUsePositioning)          res += "UsePositioning, ";
+            if (flags & SmartPropertyPrim.flagComputingPositioning)    res += "ComputingPositioning, ";
+
+            return res.slice(0, res.length - 2);
+        }
+
         public static flagNoPartOfLayout          = 0x0000001;    // set if the primitive's position/size must not be computed by Layout Engine
         public static flagNoPartOfLayout          = 0x0000001;    // set if the primitive's position/size must not be computed by Layout Engine
         public static flagLevelBoundingInfoDirty  = 0x0000002;    // set if the primitive's level bounding box (not including children) is dirty
         public static flagLevelBoundingInfoDirty  = 0x0000002;    // set if the primitive's level bounding box (not including children) is dirty
         public static flagModelDirty              = 0x0000004;    // set if the model must be changed
         public static flagModelDirty              = 0x0000004;    // set if the model must be changed
@@ -1310,6 +1345,7 @@
         public static flagLocalTransformDirty     = 0x1000000;    // set if the local transformation matrix must be recomputed
         public static flagLocalTransformDirty     = 0x1000000;    // set if the local transformation matrix must be recomputed
         public static flagUsePositioning          = 0x2000000;    // set if the primitive rely on the positioning engine (padding or margin is used)
         public static flagUsePositioning          = 0x2000000;    // set if the primitive rely on the positioning engine (padding or margin is used)
         public static flagComputingPositioning    = 0x4000000;    // set if the positioning engine is computing the primitive, used to avoid re entrance
         public static flagComputingPositioning    = 0x4000000;    // set if the positioning engine is computing the primitive, used to avoid re entrance
+        public static flagAlignPrimitive          = 0x8000000;    // set if the primitive should be pixel aligned to the render target
 
 
         private   _uid                : string;
         private   _uid                : string;
         private   _flags              : number;
         private   _flags              : number;

+ 2 - 14
canvas2D/src/Engine/babylon.sprite2d.ts

@@ -224,17 +224,6 @@
         //    this._spriteScaleFactor = value;
         //    this._spriteScaleFactor = value;
         //}
         //}
 
 
-        /**
-         * Get/set if the sprite rendering should be aligned to the target rendering device pixel or not
-         */
-        public get alignToPixel(): boolean {
-            return this._alignToPixel;
-        }
-
-        public set alignToPixel(value: boolean) {
-            this._alignToPixel = value;
-        }
-
         protected updateLevelBoundingInfo(): boolean {
         protected updateLevelBoundingInfo(): boolean {
             BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo);
             BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo);
             return true;
             return true;
@@ -273,6 +262,7 @@
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - size: the size of the sprite displayed in the canvas, if not specified the spriteSize will be used
          * - size: the size of the sprite displayed in the canvas, if not specified the spriteSize will be used
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
+         * - alignToPixel: if true the sprite's texels will be aligned to the rendering viewport pixels, ensuring the best rendering quality but slow animations won't be done as smooth as if you set false. If false a texel could lies between two pixels, being blended by the texture sampling mode you choose, the rendering result won't be as good, but very slow animation will be overall better looking. Default is true: content will be aligned.
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - zOrder: override the zOrder with the specified value
          * - zOrder: override the zOrder with the specified value
          * - origin: define the normalized origin point location, default [0.5;0.5]
          * - origin: define the normalized origin point location, default [0.5;0.5]
@@ -281,7 +271,6 @@
          * - spriteScaleFactor: DEPRECATED. Old behavior: say you want to display a sprite twice as big as its bitmap which is 64,64, you set the spriteSize to 128,128 and have to set the spriteScaleFactory to 0.5,0.5 in order to address only the 64,64 pixels of the bitmaps. Default is 1,1.
          * - spriteScaleFactor: DEPRECATED. Old behavior: say you want to display a sprite twice as big as its bitmap which is 64,64, you set the spriteSize to 128,128 and have to set the spriteScaleFactory to 0.5,0.5 in order to address only the 64,64 pixels of the bitmaps. Default is 1,1.
          * - scale9: draw the sprite as a Scale9 sprite, see http://yannickloriot.com/2013/03/9-patch-technique-in-cocos2d/ for more info. x, y, w, z are left, bottom, right, top coordinate of the resizable box
          * - scale9: draw the sprite as a Scale9 sprite, see http://yannickloriot.com/2013/03/9-patch-technique-in-cocos2d/ for more info. x, y, w, z are left, bottom, right, top coordinate of the resizable box
          * - invertY: if true the texture Y will be inverted, default is false.
          * - invertY: if true the texture Y will be inverted, default is false.
-         * - alignToPixel: if true the sprite's texels will be aligned to the rendering viewport pixels, ensuring the best rendering quality but slow animations won't be done as smooth as if you set false. If false a texel could lies between two pixels, being blended by the texture sampling mode you choose, the rendering result won't be as good, but very slow animation will be overall better looking. Default is true: content will be aligned.
          * - isVisible: true if the sprite must be visible, false for hidden. Default is true.
          * - isVisible: true if the sprite must be visible, false for hidden. Default is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
@@ -317,6 +306,7 @@
             scaleX                ?: number,
             scaleX                ?: number,
             scaleY                ?: number,
             scaleY                ?: number,
             dontInheritParentScale?: boolean,
             dontInheritParentScale?: boolean,
+            alignToPixel          ?: boolean,
             opacity               ?: number,
             opacity               ?: number,
             zOrder                ?: number, 
             zOrder                ?: number, 
             origin                ?: Vector2,
             origin                ?: Vector2,
@@ -325,7 +315,6 @@
             spriteScaleFactor     ?: Vector2,
             spriteScaleFactor     ?: Vector2,
             scale9                ?: Vector4,
             scale9                ?: Vector4,
             invertY               ?: boolean,
             invertY               ?: boolean,
-            alignToPixel          ?: boolean,
             isVisible             ?: boolean,
             isVisible             ?: boolean,
             isPickable            ?: boolean,
             isPickable            ?: boolean,
             isContainer           ?: boolean,
             isContainer           ?: boolean,
@@ -552,7 +541,6 @@
         private _spriteFrame: number;
         private _spriteFrame: number;
         private _scale9: Vector4;
         private _scale9: Vector4;
         private _invertY: boolean;
         private _invertY: boolean;
-        private _alignToPixel: boolean;
     }
     }
 
 
     export class Sprite2DInstanceData extends InstanceDataBase {
     export class Sprite2DInstanceData extends InstanceDataBase {

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 16 - 8
canvas2D/src/Engine/babylon.text2d.ts


+ 249 - 0
canvas2D/src/Tools/babylon.c2dlogging.ts

@@ -0,0 +1,249 @@
+module BABYLON {
+    // logging stuffs
+    export class C2DLogging {
+        // Set to true to temporary disable logging.
+        public static snooze = true;
+        public static logFrameRender(frameCount: number) {
+            C2DLogging.snooze = true;
+            C2DLogging._logFramesCount = frameCount;
+        }
+        public static setPostMessage(message: () => string) {
+            if (C2DLoggingInternals.enableLog) {
+                C2DLoggingInternals.postMessages[C2DLoggingInternals.callDepth-1] = message();
+            }
+        }
+
+        public static _startFrameRender() {
+            if (C2DLogging._logFramesCount === 0) {
+                return;
+            }
+            C2DLogging.snooze = false;
+        }
+
+        public static _endFrameRender() {
+            if (C2DLogging._logFramesCount === 0) {
+                return;
+            }
+            C2DLogging.snooze = true;
+            --C2DLogging._logFramesCount;
+        }
+
+        private static _logFramesCount = 0;
+    }
+
+    class C2DLoggingInternals {
+        //-------------FLAG TO CHANGE TO ENABLE/DISABLE LOGGING ACTIVATION--------------
+        // This flag can't be changed at runtime you must manually change it in the code
+        public static enableLog = false;
+
+        public static callDepth = 0;
+
+        public static depths = [
+            "|-", "|--", "|---", "|----", "|-----", "|------", "|-------", "|--------", "|---------", "|----------",
+            "|-----------", "|------------", "|-------------", "|--------------", "|---------------", "|----------------", "|-----------------", "|------------------", "|-------------------", "|--------------------"
+        ];
+        public static postMessages = [];
+
+        public static computeIndent(): string {
+            // Compute the indent
+            let indent: string = null;
+            if (C2DLoggingInternals.callDepth < 20) {
+                indent = C2DLoggingInternals.depths[C2DLoggingInternals.callDepth];
+            } else {
+                indent = "|";
+                for (let i = 0; i <= C2DLoggingInternals.callDepth; i++) {
+                    indent = indent + "-";
+                }
+            }
+            return indent;
+        }
+
+        public static getFormattedValue(a): string {
+            if (a instanceof Prim2DBase) {
+                return a.id;
+            }
+            if (a == null) {
+                return "[null]";
+            }
+            return a.toString();
+        }
+    }
+
+    export function logProp<T>(message: string = "", alsoGet = false, setNoProlog=false, getNoProlog=false): (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => void {
+        return (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => {
+            if (!C2DLoggingInternals.enableLog) {
+                return descriptor;
+            }
+
+            let getter = descriptor.get, setter = descriptor.set;
+
+            if (getter && alsoGet) {
+                descriptor.get = function (): T {
+                    if (C2DLogging.snooze) {
+                        return getter.call(this);
+                    } else {
+                        let indent = C2DLoggingInternals.computeIndent();
+                        let id = this.id || "";
+
+                        if (message !== null && message !== "") {
+                            console.log(message);
+                        }
+
+                        let isSPP = this instanceof SmartPropertyPrim;
+                        let flags = isSPP ? this._flags : 0;
+                        let depth = C2DLoggingInternals.callDepth;
+                        if (!getNoProlog) {
+                            console.log(`${indent} [${id}] (${depth}) ==> get ${propName} property`);
+                        }
+                        ++C2DLoggingInternals.callDepth;
+                        C2DLogging.setPostMessage(() => "[no msg]");
+
+                        // Call the initial getter
+                        let r = getter.call(this);
+
+                        --C2DLoggingInternals.callDepth;
+                        let flagsStr = "";
+                        if (isSPP) {
+                            let nflags = this._flags;
+                            let newFlags = this._getFlagsDebug((nflags & flags) ^ nflags);
+                            let removedFlags = this._getFlagsDebug((nflags & flags) ^ flags);
+                            flagsStr = "";
+                            if (newFlags !== "") {
+                                flagsStr = ` +++[${newFlags}]`;
+                            }
+                            if (removedFlags !== "") {
+                                if (flagsStr !== "") {
+                                    flagsStr += ",";
+                                }
+                                flagsStr += ` ---[${removedFlags}]`;
+                            }
+                        }
+                        console.log(`${indent} [${id}] (${depth})${getNoProlog ? "" : " <=="} get ${propName} property => ${C2DLoggingInternals.getFormattedValue(r)}${flagsStr}, ${C2DLoggingInternals.postMessages[C2DLoggingInternals.callDepth]}`);
+
+                        return r;
+                    }
+                }
+            }
+
+            // Overload the property setter implementation to add our own logic
+            if (setter) {
+                descriptor.set = function (val) {
+                    if (C2DLogging.snooze) {
+                        setter.call(this, val);
+                    } else {
+                        let indent = C2DLoggingInternals.computeIndent();
+                        let id = this.id || "";
+
+                        if (message !== null && message !== "") {
+                            console.log(message);
+                        }
+                        let isSPP = this instanceof SmartPropertyPrim;
+                        let flags = isSPP ? this._flags : 0;
+                        let depth = C2DLoggingInternals.callDepth;
+                        if (!setNoProlog) {
+                            console.log(`${indent} [${id}] (${depth}) ==> set ${propName} property with ${C2DLoggingInternals.getFormattedValue(val)}`);
+                        }
+                        ++C2DLoggingInternals.callDepth;
+                        C2DLogging.setPostMessage(() => "[no msg]");
+
+                        // Change the value
+                        setter.call(this, val);
+
+                        --C2DLoggingInternals.callDepth;
+                        let flagsStr = "";
+                        if (isSPP) {
+                            let nflags = this._flags;
+                            let newFlags = this._getFlagsDebug((nflags & flags) ^ nflags);
+                            let removedFlags = this._getFlagsDebug((nflags & flags) ^ flags);
+                            flagsStr = "";
+                            if (newFlags !== "") {
+                                flagsStr = ` +++[${newFlags}]`;
+                            }
+                            if (removedFlags !== "") {
+                                if (flagsStr !== "") {
+                                    flagsStr += ",";
+                                }
+                                flagsStr += ` ---[${removedFlags}]`;
+                            }
+                        }
+                        console.log(`${indent} [${id}] (${depth})${setNoProlog ? "" : " <=="} set ${propName} property, ${C2DLoggingInternals.postMessages[C2DLoggingInternals.callDepth]}${flagsStr}`);
+                    }
+                }
+            }
+
+            return descriptor;
+        }
+    }
+
+    export function logMethod(message: string = "", noProlog = false) {
+        return (target, key, descriptor) => {
+            if (!C2DLoggingInternals.enableLog) {
+                return descriptor;
+            }
+
+            if (descriptor === undefined) {
+                descriptor = Object.getOwnPropertyDescriptor(target, key);
+            }
+            var originalMethod = descriptor.value;
+
+            //editing the descriptor/value parameter
+            descriptor.value = function () {
+                var args = [];
+                for (var _i = 0; _i < arguments.length; _i++) {
+                    args[_i - 0] = arguments[_i];
+                }
+                if (C2DLogging.snooze) {
+                    return originalMethod.apply(this, args);
+                } else {
+                    var a = args.map(a => C2DLoggingInternals.getFormattedValue(a) + ", ").join();
+                    a = a.slice(0, a.length - 2);
+
+                    let indent = C2DLoggingInternals.computeIndent();
+                    let id = this.id || "";
+
+                    if (message !== null && message !== "") {
+                        console.log(message);
+                    }
+
+                    let isSPP = this instanceof SmartPropertyPrim;
+                    let flags = isSPP ? this._flags : 0;
+                    let depth = C2DLoggingInternals.callDepth;
+                    if (!noProlog) {
+                        console.log(`${indent} [${id}] (${depth}) ==> call: ${key} (${a})`);
+                    }
+                    ++C2DLoggingInternals.callDepth;
+                    C2DLogging.setPostMessage(() => "[no msg]");
+
+                    // Call the method!
+                    var result = originalMethod.apply(this, args);
+
+                    --C2DLoggingInternals.callDepth;
+                    let flagsStr = "";
+                    if (isSPP) {
+                        let nflags = this._flags;
+                        let newFlags = this._getFlagsDebug((nflags & flags) ^ nflags);
+                        let removedFlags = this._getFlagsDebug((nflags & flags) ^ flags);
+                        flagsStr = "";
+                        if (newFlags !== "") {
+                            flagsStr = ` +++[${newFlags}]`;
+                        }
+                        if (removedFlags !== "") {
+                            if (flagsStr !== "") {
+                                flagsStr += ",";
+                            }
+                            flagsStr += ` ---[${removedFlags}]`;
+                        }
+                    }
+                    console.log(`${indent} [${id}] (${depth})${noProlog ? "" : " <=="} call: ${key} (${a}) Res: ${C2DLoggingInternals.getFormattedValue(result)}, ${C2DLoggingInternals.postMessages[C2DLoggingInternals.callDepth]}${flagsStr}`);
+
+                    return result;
+                }
+            };
+
+            // return edited descriptor as opposed to overwriting the descriptor
+            return descriptor;
+        }
+        
+    }
+
+}

+ 10 - 1
canvas2D/src/shaders/ellipse2d.vertex.fx

@@ -9,6 +9,7 @@ attribute float index;
 att vec2 zBias;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformX;
 att vec4 transformY;
 att vec4 transformY;
+att vec3 renderingInfo;
 att float opacity;
 att float opacity;
 
 
 #ifdef Border
 #ifdef Border
@@ -104,6 +105,14 @@ void main(void) {
 	pos.xy = pos2.xy * properties.xy;
 	pos.xy = pos2.xy * properties.xy;
 	pos.z = 1.0;
 	pos.z = 1.0;
 	pos.w = 1.0;
 	pos.w = 1.0;
-	gl_Position = vec4(dot(pos, transformX), dot(pos, transformY), zBias.x, 1);
 
 
+	float rx = dot(pos, transformX);
+	float ry = dot(pos, transformY);
+
+	if (renderingInfo.z == 1.0) {
+		rx = floor((rx / renderingInfo.x) + 0.5) * renderingInfo.x;
+		ry = floor((ry / renderingInfo.y) + 0.5) * renderingInfo.y;
+	}
+
+	gl_Position = vec4(rx, ry, zBias.x, 1);
 }
 }

+ 10 - 1
canvas2D/src/shaders/lines2d.vertex.fx

@@ -9,6 +9,7 @@ attribute vec2 position;
 att vec2 zBias;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformX;
 att vec4 transformY;
 att vec4 transformY;
+att vec3 renderingInfo;
 att float opacity;
 att float opacity;
 
 
 #ifdef FillSolid
 #ifdef FillSolid
@@ -64,6 +65,14 @@ void main(void) {
 	pos.xy = position.xy;
 	pos.xy = position.xy;
 	pos.z = 1.0;
 	pos.z = 1.0;
 	pos.w = 1.0;
 	pos.w = 1.0;
-	gl_Position = vec4(dot(pos, transformX), dot(pos, transformY), zBias.x, 1);
+	
+	float rx = dot(pos, transformX);
+	float ry = dot(pos, transformY);
 
 
+	if (renderingInfo.z == 1.0) {
+		rx = floor((rx / renderingInfo.x) + 0.5) * renderingInfo.x;
+		ry = floor((ry / renderingInfo.y) + 0.5) * renderingInfo.y;
+	}
+
+	gl_Position = vec4(rx, ry, zBias.x, 1);
 }
 }

+ 10 - 1
canvas2D/src/shaders/rect2d.vertex.fx

@@ -9,6 +9,7 @@ attribute float index;
 att vec2 zBias;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformX;
 att vec4 transformY;
 att vec4 transformY;
+att vec3 renderingInfo;
 att float opacity;
 att float opacity;
 
 
 #ifdef Border
 #ifdef Border
@@ -202,6 +203,14 @@ void main(void) {
 	pos.xy = pos2.xy * properties.xy;
 	pos.xy = pos2.xy * properties.xy;
 	pos.z = 1.0;
 	pos.z = 1.0;
 	pos.w = 1.0;
 	pos.w = 1.0;
-	gl_Position = vec4(dot(pos, transformX), dot(pos, transformY), zBias.x, 1);
 
 
+	float rx = dot(pos, transformX);
+	float ry = dot(pos, transformY);
+
+	if (renderingInfo.z == 1.0) {
+		rx = floor((rx / renderingInfo.x) + 0.5) * renderingInfo.x;
+		ry = floor((ry / renderingInfo.y) + 0.5) * renderingInfo.y;
+	}
+
+	gl_Position = vec4(rx, ry, zBias.x, 1);
 }
 }

+ 15 - 1
canvas2D/src/shaders/sprite2d.vertex.fx

@@ -25,6 +25,7 @@ att vec4 scale9;
 att vec2 zBias;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformX;
 att vec4 transformY;
 att vec4 transformY;
+att vec3 renderingInfo;
 att float opacity;
 att float opacity;
 
 
 // Uniforms
 // Uniforms
@@ -101,5 +102,18 @@ void main(void) {
 	vOpacity = opacity;
 	vOpacity = opacity;
 	pos.z = 1.0;
 	pos.z = 1.0;
 	pos.w = 1.0;
 	pos.w = 1.0;
-	gl_Position = vec4(dot(pos, transformX), dot(pos, transformY), zBias.x, 1);
+
+	float x = dot(pos, transformX);
+	float y = dot(pos, transformY);
+	if (renderingInfo.z == 1.0) {
+		float rw = renderingInfo.x;
+		float rh = renderingInfo.y;
+		float irw = 2.0 / rw;
+		float irh = 2.0 / rh;
+
+		x = floor((x / irw) + 0.5) * irw;
+		y = floor((y / irh) + 0.5) * irh;
+	}
+
+	gl_Position = vec4(x, y, zBias.x, 1);
 }	
 }	

+ 15 - 1
canvas2D/src/shaders/text2d.vertex.fx

@@ -11,6 +11,7 @@ att vec2 zBias;
 
 
 att vec4 transformX;
 att vec4 transformX;
 att vec4 transformY;
 att vec4 transformY;
+att vec3 renderingInfo;
 att float opacity;
 att float opacity;
 
 
 att vec2 topLeftUV;
 att vec2 topLeftUV;
@@ -60,5 +61,18 @@ void main(void) {
 	pos.xy = floor(pos2.xy * superSampleFactor * sizeUV * textureSize);	// Align on target pixel to avoid bad interpolation
 	pos.xy = floor(pos2.xy * superSampleFactor * sizeUV * textureSize);	// Align on target pixel to avoid bad interpolation
 	pos.z = 1.0;
 	pos.z = 1.0;
 	pos.w = 1.0;
 	pos.w = 1.0;
-	gl_Position = vec4(dot(pos, transformX), dot(pos, transformY), zBias.x, 1);
+
+	float x = dot(pos, transformX);
+	float y = dot(pos, transformY);
+	if (renderingInfo.z == 1.0) {
+		float rw = renderingInfo.x;
+		float rh = renderingInfo.y;
+		float irw = 2.0 / rw;
+		float irh = 2.0 / rh;
+
+		x = floor((x / irw) + 0.5) * irw;
+		y = floor((y / irh) + 0.5) * irh;
+	}
+
+	gl_Position = vec4(x, y, zBias.x, 1);
 }
 }