From 53a699b50be0bc6228bb4b799e35eefe2257e737 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Fri, 24 Jun 2016 14:49:11 +0300 Subject: [PATCH 1/3] Background performance optimizations --- .../org.nativescript.widgets.d.ts | 4 - .../ui/styling/background.android.ts | 137 ++++++++++++------ 2 files changed, 92 insertions(+), 49 deletions(-) diff --git a/tns-core-modules/org.nativescript.widgets.d.ts b/tns-core-modules/org.nativescript.widgets.d.ts index 8d2766208..686a76dac 100644 --- a/tns-core-modules/org.nativescript.widgets.d.ts +++ b/tns-core-modules/org.nativescript.widgets.d.ts @@ -10,8 +10,6 @@ clipPath: string, backgroundColor: number, backgroundImage: android.graphics.Bitmap, - backgroundImageWidth: number, - backgroundImageHeight: number, backgroundRepeat: string, backgroundPosition: string, backgroundPositionParsedCSSValues: native.Array, @@ -24,8 +22,6 @@ public getClipPath(): string; public getBackgroundColor(): number; public getBackgroundImage(): android.graphics.Bitmap; - public getBackgroundImageWidth(): number; - public getBackgroundImageHeight(): number; public getBackgroundRepeat(): string; public getBackgroundPosition(): string; public getBackgroundSize(): string; diff --git a/tns-core-modules/ui/styling/background.android.ts b/tns-core-modules/ui/styling/background.android.ts index 1e8261ee2..95beca09e 100644 --- a/tns-core-modules/ui/styling/background.android.ts +++ b/tns-core-modules/ui/styling/background.android.ts @@ -8,8 +8,8 @@ import { CacheLayerType } from "utils/utils"; import cssValue = require("css-value"); import background = require("ui/styling/background"); -var button: typeof buttonModule; -var style: typeof styleModule; +let button: typeof buttonModule; +let style: typeof styleModule; function ensureLazyRequires() { if (!button) { @@ -25,7 +25,7 @@ global.moduleMerge(common, exports); // We are using "ad" here to avoid namespace collision with the global android object export module ad { - var SDK: number; + let SDK: number; function getSDK() { if (!SDK) { SDK = android.os.Build.VERSION.SDK_INT; @@ -34,62 +34,46 @@ export module ad { return SDK; } - var _defaultBackgrounds = new Map(); + let _defaultBackgrounds = new Map(); export function onBackgroundOrBorderPropertyChanged(v: view.View) { - var nativeView = v._nativeView; - var cache = v._nativeView; - + let nativeView = v._nativeView; if (!nativeView) { return; } - + ensureLazyRequires(); - var clipPathValue = v.style._getValue(style.clipPathProperty); - - var backgroundValue = v.style._getValue(style.backgroundInternalProperty); - var borderWidth = v.borderWidth; - var bkg = nativeView.getBackground(); - - var density = utils.layout.getDisplayDensity(); - - if (v instanceof button.Button && !types.isNullOrUndefined(bkg) && types.isFunction(bkg.setColorFilter) && - v.borderWidth === 0 && v.borderRadius === 0 && !clipPathValue && + let clipPath = v.style._getValue(style.clipPathProperty); + let background = v.style._getValue(style.backgroundInternalProperty); + let borderWidth = v.borderWidth; + let backgroundDrawable = nativeView.getBackground(); + let density = utils.layout.getDisplayDensity(); + let cache = v._nativeView; + if (v instanceof button.Button && !types.isNullOrUndefined(backgroundDrawable) && types.isFunction(backgroundDrawable.setColorFilter) && + v.borderWidth === 0 && v.borderRadius === 0 && !clipPath && types.isNullOrUndefined(v.style._getValue(style.backgroundImageProperty)) && !types.isNullOrUndefined(v.style._getValue(style.backgroundColorProperty))) { - let backgroundColor = (bkg).backgroundColor = v.style._getValue(style.backgroundColorProperty).android; - bkg.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); - (bkg).backgroundColor = backgroundColor; + let backgroundColor = (backgroundDrawable).backgroundColor = v.style._getValue(style.backgroundColorProperty).android; + backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); + (backgroundDrawable).backgroundColor = backgroundColor; } - else if (v.borderWidth || v.borderRadius || clipPathValue || !backgroundValue.isEmpty()) { - if (!(bkg instanceof org.nativescript.widgets.BorderDrawable)) { - bkg = new org.nativescript.widgets.BorderDrawable(density); + else if (v.borderWidth || v.borderRadius || clipPath || !background.isEmpty()) { + if (!(backgroundDrawable instanceof org.nativescript.widgets.BorderDrawable)) { let viewClass = types.getClass(v); if (!(v instanceof button.Button) && !_defaultBackgrounds.has(viewClass)) { _defaultBackgrounds.set(viewClass, nativeView.getBackground()); } - - nativeView.setBackground(bkg); + + backgroundDrawable = new org.nativescript.widgets.BorderDrawable(density); + refreshBorderDrawable(v, backgroundDrawable); + nativeView.setBackground(backgroundDrawable); + } + else { + refreshBorderDrawable(v, backgroundDrawable); } - (bkg).refresh( - v.borderWidth, - v.borderColor ? v.borderColor.android : 0, - v.borderRadius, - clipPathValue, - (backgroundValue.color && backgroundValue.color.android) ? backgroundValue.color.android : 0, - (backgroundValue.image && backgroundValue.image.android) ? backgroundValue.image.android : null, - (backgroundValue.image && backgroundValue.image.android) ? backgroundValue.image.width : 0, - (backgroundValue.image && backgroundValue.image.android) ? backgroundValue.image.height : 0, - backgroundValue.repeat, - backgroundValue.position, - backgroundValue.position ? createNativeCSSValueArray(backgroundValue.position) : null, - backgroundValue.size, - backgroundValue.size ? createNativeCSSValueArray(backgroundValue.size) : null - ); - - if ((v.borderWidth || v.borderRadius || clipPathValue) && getSDK() < 18) { + if ((v.borderWidth || v.borderRadius || clipPath) && getSDK() < 18) { // Switch to software because of unsupported canvas methods if hardware acceleration is on: // http://developer.android.com/guide/topics/graphics/hardware-accel.html cache.layerType = cache.getLayerType(); @@ -99,7 +83,7 @@ export module ad { else { // reset the value with the default native value if (v instanceof button.Button) { - var nativeButton = new android.widget.Button(nativeView.getContext()); + let nativeButton = new android.widget.Button(nativeView.getContext()); nativeView.setBackground(nativeButton.getBackground()); } else { @@ -124,7 +108,70 @@ export module ad { } } -function createNativeCSSValueArray(css: string): any{ +function refreshBorderDrawable(view: view.View, borderDrawable: org.nativescript.widgets.BorderDrawable){ + let background = view.style._getValue(style.backgroundInternalProperty); + let borderWidth: number = view.borderWidth; + let borderColor: number = 0; + if (view.borderColor && view.borderColor.android){ + borderColor = view.borderColor.android; + } + let borderRadius: number = view.borderRadius; + let clipPath: string = view.style._getValue(style.clipPathProperty); + let backgroundColor: number = 0; + let backgroundImage: android.graphics.Bitmap = null; + let backgroundRepeat: string = null; + let backgroundPosition: string = null; + let backgroundPositionParsedCSSValues: native.Array = null; + let backgroundSize: string = null; + let backgroundSizeParsedCSSValues: native.Array = null; + if (background){ + if (background.color && background.color.android){ + backgroundColor = background.color.android; + } + if (background.image && background.image.android){ + backgroundImage = background.image.android; + } + if (background.position){ + backgroundPosition = background.position; + backgroundPositionParsedCSSValues = createNativeCSSValueArray(background.position); + } + if (background.size){ + backgroundSize = background.size; + backgroundSizeParsedCSSValues = createNativeCSSValueArray(background.size); + } + } + + let newBackground = JSON.stringify({ + w: borderWidth, + c: borderColor, + r: borderRadius, + cp: clipPath, + bc: backgroundColor, + bi: backgroundImage ? backgroundImage.hashCode() : "", + br: backgroundRepeat, + bp: backgroundPosition, + bs: backgroundSize + }); + + if (newBackground !== view["android-backround"]){ + borderDrawable.refresh( + borderWidth, + borderColor, + borderRadius, + clipPath, + backgroundColor, + backgroundImage, + backgroundRepeat, + backgroundPosition, + backgroundPositionParsedCSSValues, + backgroundSize, + backgroundSizeParsedCSSValues + ); + view["android-backround"] = newBackground; + } +} + +function createNativeCSSValueArray(css: string): native.Array{ if (!css){ return null; } From 05a3f3c846935e75047e1d85051f99c1a5293c30 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Fri, 24 Jun 2016 16:23:01 +0300 Subject: [PATCH 2/3] Remove optimisation --- .../ui/styling/background.android.ts | 41 ++++++------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/tns-core-modules/ui/styling/background.android.ts b/tns-core-modules/ui/styling/background.android.ts index 95beca09e..2b66ff184 100644 --- a/tns-core-modules/ui/styling/background.android.ts +++ b/tns-core-modules/ui/styling/background.android.ts @@ -141,34 +141,19 @@ function refreshBorderDrawable(view: view.View, borderDrawable: org.nativescript } } - let newBackground = JSON.stringify({ - w: borderWidth, - c: borderColor, - r: borderRadius, - cp: clipPath, - bc: backgroundColor, - bi: backgroundImage ? backgroundImage.hashCode() : "", - br: backgroundRepeat, - bp: backgroundPosition, - bs: backgroundSize - }); - - if (newBackground !== view["android-backround"]){ - borderDrawable.refresh( - borderWidth, - borderColor, - borderRadius, - clipPath, - backgroundColor, - backgroundImage, - backgroundRepeat, - backgroundPosition, - backgroundPositionParsedCSSValues, - backgroundSize, - backgroundSizeParsedCSSValues - ); - view["android-backround"] = newBackground; - } + borderDrawable.refresh( + borderWidth, + borderColor, + borderRadius, + clipPath, + backgroundColor, + backgroundImage, + backgroundRepeat, + backgroundPosition, + backgroundPositionParsedCSSValues, + backgroundSize, + backgroundSizeParsedCSSValues + ); } function createNativeCSSValueArray(css: string): native.Array{ From 7d3fb56def330fbde407b81853e2a4db255e4ef3 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Mon, 27 Jun 2016 09:41:53 +0300 Subject: [PATCH 3/3] Move border properties to Background to avoid unnecessary refreshes. --- tns-core-modules/ui/core/view.android.ts | 16 ++-- .../ui/styling/background-common.ts | 64 +++++++++++--- tns-core-modules/ui/styling/background.d.ts | 18 +++- tns-core-modules/ui/styling/style.ts | 86 +++++++++++++------ 4 files changed, 129 insertions(+), 55 deletions(-) diff --git a/tns-core-modules/ui/core/view.android.ts b/tns-core-modules/ui/core/view.android.ts index 1061e0d76..d87698ead 100644 --- a/tns-core-modules/ui/core/view.android.ts +++ b/tns-core-modules/ui/core/view.android.ts @@ -455,11 +455,11 @@ export class CustomLayoutView extends View implements viewDefinition.CustomLayou export class ViewStyler implements style.Styler { // Background and borders methods - private static setBackgroundBorderProperty(view: View, newValue: any, defaultValue?: any) { + private static setBackgroundAndBorder(view: View, newValue: any, defaultValue?: any) { background.ad.onBackgroundOrBorderPropertyChanged(view); } - private static resetBackgroundBorderProperty(view: View, nativeValue: any) { + private static resetBackgroundAndBorder(view: View, nativeValue: any) { background.ad.onBackgroundOrBorderPropertyChanged(view); } @@ -705,15 +705,11 @@ export class ViewStyler implements style.Styler { // Use the same handler for all background/border properties // Note: There is no default value getter - the default value is handled in background.ad.onBackgroundOrBorderPropertyChanged - var borderHandler = new style.StylePropertyChangedHandler( - ViewStyler.setBackgroundBorderProperty, - ViewStyler.resetBackgroundBorderProperty); + var backgroundAndBorderHandler = new style.StylePropertyChangedHandler( + ViewStyler.setBackgroundAndBorder, + ViewStyler.resetBackgroundAndBorder); - style.registerHandler(style.backgroundInternalProperty, borderHandler); - style.registerHandler(style.borderWidthProperty, borderHandler); - style.registerHandler(style.borderColorProperty, borderHandler); - style.registerHandler(style.borderRadiusProperty, borderHandler); - style.registerHandler(style.clipPathProperty, borderHandler); + style.registerHandler(style.backgroundInternalProperty, backgroundAndBorderHandler); style.registerHandler(style.nativeLayoutParamsProperty, new style.StylePropertyChangedHandler( ViewStyler.setNativeLayoutParamsProperty, diff --git a/tns-core-modules/ui/styling/background-common.ts b/tns-core-modules/ui/styling/background-common.ts index 228260930..c20d95244 100644 --- a/tns-core-modules/ui/styling/background-common.ts +++ b/tns-core-modules/ui/styling/background-common.ts @@ -21,46 +21,74 @@ interface CSSValue { } export class Background implements definition.Background { - public static default = new Background(undefined, undefined, undefined, undefined, undefined); + public static default = new Background(undefined, undefined, undefined, undefined, undefined, 0, undefined, 0, undefined); color: colorModule.Color; image: imageSource.ImageSource; repeat: string; position: string; size: string; + borderWidth: number = 0; + borderColor: colorModule.Color; + borderRadius: number = 0; + clipPath: string; constructor( color: colorModule.Color, image: imageSource.ImageSource, repeat: string, position: string, - size: string) { - + size: string, + borderWidth: number, + borderColor: colorModule.Color, + borderRadius: number, + clipPath: string + ) { this.color = color; this.image = image; this.repeat = repeat; this.position = position; this.size = size; + this.borderWidth = borderWidth; + this.borderColor = borderColor; + this.borderRadius = borderRadius; + this.clipPath = clipPath; } public withColor(value: colorModule.Color): Background { - return new Background(value, this.image, this.repeat, this.position, this.size); + return new Background(value, this.image, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath); } public withImage(value: imageSource.ImageSource): Background { - return new Background(this.color, value, this.repeat, this.position, this.size); + return new Background(this.color, value, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath); } public withRepeat(value: string): Background { - return new Background(this.color, this.image, value, this.position, this.size); + return new Background(this.color, this.image, value, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath); } public withPosition(value: string): Background { - return new Background(this.color, this.image, this.repeat, value, this.size); + return new Background(this.color, this.image, this.repeat, value, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath); } public withSize(value: string): Background { - return new Background(this.color, this.image, this.repeat, this.position, value); + return new Background(this.color, this.image, this.repeat, this.position, value, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath); + } + + public withBorderWidth(value: number): Background { + return new Background(this.color, this.image, this.repeat, this.position, this.size, value, this.borderColor, this.borderRadius, this.clipPath); + } + + public withBorderColor(value: colorModule.Color): Background { + return new Background(this.color, this.image, this.repeat, this.position, this.size, this.borderWidth, value, this.borderRadius, this.clipPath); + } + + public withBorderRadius(value: number): Background { + return new Background(this.color, this.image, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, value, this.clipPath); + } + + public withClipPath(value: string): Background { + return new Background(this.color, this.image, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, value); } public getDrawParams(width: number, height: number): definition.BackgroundDrawParams { @@ -222,7 +250,11 @@ export class Background implements definition.Background { public isEmpty(): boolean { ensureTypes(); - return types.isNullOrUndefined(this.image) && types.isNullOrUndefined(this.color); + return types.isNullOrUndefined(this.image) + && types.isNullOrUndefined(this.color) + && !this.borderWidth + && !this.borderRadius + && !this.clipPath; } public static equals(value1: Background, value2: Background): boolean { @@ -236,11 +268,15 @@ export class Background implements definition.Background { return false; } - return value1.image === value2.image && - value1.position === value2.position && - value1.repeat === value2.repeat && - value1.size === value2.size && - colorModule.Color.equals(value1.color, value2.color); + return value1.image === value2.image + && value1.position === value2.position + && value1.repeat === value2.repeat + && value1.size === value2.size + && colorModule.Color.equals(value1.color, value2.color) + && value1.borderWidth === value2.borderWidth + && colorModule.Color.equals(value1.borderColor, value2.borderColor) + && value1.borderRadius === value2.borderRadius + && value1.clipPath === value2.clipPath; } } diff --git a/tns-core-modules/ui/styling/background.d.ts b/tns-core-modules/ui/styling/background.d.ts index de680e1e0..752393f82 100644 --- a/tns-core-modules/ui/styling/background.d.ts +++ b/tns-core-modules/ui/styling/background.d.ts @@ -19,22 +19,32 @@ declare module "ui/styling/background" { repeat: string; position: string; size: string; + borderWidth: number; + borderColor: colorModule.Color; + borderRadius: number; + clipPath: string; constructor( color: colorModule.Color, image: imageSource.ImageSource, repeat: string, position: string, - size: string); + size: string, + borderWidth: number, + borderColor: colorModule.Color, + borderRadius: number, + clipPath: string + ); public withColor(value: colorModule.Color): Background; public withImage(value: imageSource.ImageSource): Background; - public withRepeat(value: string): Background; - public withPosition(value: string): Background; - public withSize(value: string): Background; + public withBorderWidth(value: number): Background; + public withBorderColor(value: colorModule.Color): Background; + public withBorderRadius(value: number): Background; + public withClipPath(value: string): Background; public getDrawParams(width: number, height: number): BackgroundDrawParams; diff --git a/tns-core-modules/ui/styling/style.ts b/tns-core-modules/ui/styling/style.ts index d934c4df2..1bee04e98 100644 --- a/tns-core-modules/ui/styling/style.ts +++ b/tns-core-modules/ui/styling/style.ts @@ -273,6 +273,14 @@ function isMinWidthHeightValid(value: number): boolean { return !isNaN(value) && value >= 0.0 && isFinite(value); } +function onBackgroundColorPropertyChanged(data: PropertyChangeData) { + var style =