|
| 1 | +import { Canvas, CanvasView } from '@nativescript-community/ui-canvas'; |
| 2 | +import { File, ImageAsset, Length, Screen, Utils } from '@nativescript/core'; |
| 3 | +import { SVG as SVGBase, xfermodeFromString } from './canvas.common'; |
| 4 | +import { getSVG } from './index.android'; |
| 5 | +export { CanvasSVG } from './canvas.common'; |
| 6 | + |
| 7 | +let bgdImagePaint: android.graphics.Paint; |
| 8 | + |
| 9 | +export class SVG extends SVGBase { |
| 10 | + _svg: com.caverock.androidsvg.SVG; |
| 11 | + _src: string | File | ImageAsset; |
| 12 | + _cachedImage: android.graphics.Bitmap; |
| 13 | + private renderOptions = new com.caverock.androidsvg.RenderOptions(); |
| 14 | + |
| 15 | + getWidth(availableWidth, availableHeight) { |
| 16 | + if (this.width) { |
| 17 | + return super.getWidth(availableWidth, availableHeight); |
| 18 | + } |
| 19 | + const svg = this._svg; |
| 20 | + if (!svg) { |
| 21 | + return 0; |
| 22 | + } |
| 23 | + const viewRect = svg.getDocumentViewBox(); |
| 24 | + if (viewRect) { |
| 25 | + const nativeWidth = viewRect.width(); |
| 26 | + const nativeHeight = viewRect.height(); |
| 27 | + const width = Math.min(nativeWidth, availableWidth); |
| 28 | + const height = this.height ? this.getHeight(availableWidth, availableHeight) : Math.min(nativeHeight, availableHeight); |
| 29 | + let paintedWidth = width; |
| 30 | + let paintedHeight = height; |
| 31 | + const nativeAspectRatio = nativeWidth / nativeHeight; |
| 32 | + const boundedAspectRatio = width / height; |
| 33 | + if (this._stretch === 'aspectFit') { |
| 34 | + if (nativeAspectRatio >= boundedAspectRatio) { |
| 35 | + // blank space on top and bottom |
| 36 | + paintedHeight = paintedWidth / nativeAspectRatio; |
| 37 | + } else { |
| 38 | + paintedWidth = paintedHeight * nativeAspectRatio; |
| 39 | + } |
| 40 | + return paintedWidth; |
| 41 | + } else if (this._stretch === 'aspectFill') { |
| 42 | + if (nativeAspectRatio <= boundedAspectRatio) { |
| 43 | + // blank space on top and bottom |
| 44 | + paintedHeight = paintedWidth / nativeAspectRatio; |
| 45 | + } else { |
| 46 | + paintedWidth = paintedHeight * nativeAspectRatio; |
| 47 | + } |
| 48 | + return paintedWidth; |
| 49 | + } |
| 50 | + return paintedWidth; |
| 51 | + } |
| 52 | + |
| 53 | + return svg.getDocumentWidth(); |
| 54 | + } |
| 55 | + getHeight(availableWidth: number, availableHeight: number) { |
| 56 | + if (this.height) { |
| 57 | + return super.getHeight(availableWidth, availableHeight); |
| 58 | + } |
| 59 | + const svg = this._svg; |
| 60 | + if (!svg) { |
| 61 | + return 0; |
| 62 | + } |
| 63 | + const viewRect = svg.getDocumentViewBox(); |
| 64 | + if (viewRect) { |
| 65 | + const nativeWidth = viewRect.width(); |
| 66 | + const nativeHeight = viewRect.height(); |
| 67 | + const height = Math.min(nativeHeight, availableHeight); |
| 68 | + const width = this.width ? this.getWidth(availableWidth, availableHeight) : Math.min(nativeHeight, availableHeight); |
| 69 | + let paintedWidth = width; |
| 70 | + let paintedHeight = height; |
| 71 | + const nativeAspectRatio = nativeWidth / nativeHeight; |
| 72 | + const boundedAspectRatio = width / height; |
| 73 | + if (this._stretch === 'aspectFit') { |
| 74 | + if (nativeAspectRatio >= boundedAspectRatio) { |
| 75 | + // blank space on top and bottom |
| 76 | + paintedHeight = paintedWidth / nativeAspectRatio; |
| 77 | + } else { |
| 78 | + paintedWidth = paintedHeight * nativeAspectRatio; |
| 79 | + } |
| 80 | + return paintedHeight; |
| 81 | + } else if (this._stretch === 'aspectFill') { |
| 82 | + if (nativeAspectRatio <= boundedAspectRatio) { |
| 83 | + // blank space on top and bottom |
| 84 | + paintedHeight = paintedWidth / nativeAspectRatio; |
| 85 | + } else { |
| 86 | + paintedWidth = paintedHeight * nativeAspectRatio; |
| 87 | + } |
| 88 | + return paintedHeight; |
| 89 | + } |
| 90 | + return paintedHeight; |
| 91 | + } |
| 92 | + |
| 93 | + return svg.getDocumentHeight(); |
| 94 | + } |
| 95 | + drawOnCanvas(canvas: Canvas, parent: CanvasView) { |
| 96 | + const svg = this._svg; |
| 97 | + if (svg) { |
| 98 | + // const startTime = new Date().valueOf(); |
| 99 | + // const wasCached = !!this._cachedImage; |
| 100 | + const availableWidth = Utils.layout.toDevicePixels(canvas.getWidth()); |
| 101 | + const availableHeight = Utils.layout.toDevicePixels(canvas.getHeight()); |
| 102 | + let options = this.renderOptions; |
| 103 | + const width = this.getWidth(availableWidth, availableHeight); |
| 104 | + const height = this.getHeight(availableWidth, availableHeight); |
| 105 | + // const box = svg.getDocumentViewBox(); |
| 106 | + // const nativeWidth = box ? box.width() : width; |
| 107 | + // const nativeHeight = box ? box.height() : height; |
| 108 | + |
| 109 | + // const nativeAspectRatio = nativeWidth / nativeHeight; |
| 110 | + // const boundedAspectRatio = width / height; |
| 111 | + |
| 112 | + const paintedWidth = width; |
| 113 | + const paintedHeight = height; |
| 114 | + // does not seem like we need this. Handled by options? |
| 115 | + // if (nativeAspectRatio >= boundedAspectRatio) { |
| 116 | + // paintedHeight = paintedWidth / nativeAspectRatio; |
| 117 | + // } else { |
| 118 | + // paintedWidth = paintedHeight * nativeAspectRatio; |
| 119 | + // } |
| 120 | + const xOrigin = (width - paintedWidth) / 2.0; |
| 121 | + const yOrigin = (height - paintedHeight) / 2.0; |
| 122 | + options = options.preserveAspectRatio(this._preserveAspectRatio); |
| 123 | + |
| 124 | + if (this.blendingMode || this.cache) { |
| 125 | + let newBitmap: android.graphics.Bitmap = this._cachedImage; |
| 126 | + const scale = Screen.mainScreen.scale; |
| 127 | + if (!this.cache || !this._cachedImage) { |
| 128 | + newBitmap = android.graphics.Bitmap.createBitmap(width * scale, height * scale, android.graphics.Bitmap.Config.ARGB_8888); |
| 129 | + const bmcanvas = new android.graphics.Canvas(newBitmap); |
| 130 | + bmcanvas.setDensity(Math.round(scale * 160)); |
| 131 | + svg.renderToCanvas(bmcanvas, options); |
| 132 | + if (this.cache) { |
| 133 | + this._cachedImage = newBitmap; |
| 134 | + } |
| 135 | + } |
| 136 | + if (this.blendingMode) { |
| 137 | + if (!bgdImagePaint) { |
| 138 | + bgdImagePaint = new android.graphics.Paint(); |
| 139 | + } |
| 140 | + bgdImagePaint.setXfermode(new android.graphics.PorterDuffXfermode(xfermodeFromString(this.blendingMode))); |
| 141 | + } |
| 142 | + canvas.drawBitmap(newBitmap, new android.graphics.Rect(0, 0, width * scale, height * scale), new android.graphics.Rect(xOrigin, yOrigin, width, height), bgdImagePaint as any); |
| 143 | + } else { |
| 144 | + options.viewPort( |
| 145 | + -xOrigin + Utils.layout.toDeviceIndependentPixels(Length.toDevicePixels(this.left)), |
| 146 | + -yOrigin + Utils.layout.toDeviceIndependentPixels(Length.toDevicePixels(this.top)), |
| 147 | + width, |
| 148 | + height |
| 149 | + ); |
| 150 | + svg.renderToCanvas((canvas as any).getNative(), options); |
| 151 | + } |
| 152 | + // console.log('drawSvg', wasCached, Date.now() - startTime, 'ms'); |
| 153 | + } |
| 154 | + } |
| 155 | + set src(value: string | File | ImageAsset) { |
| 156 | + this._src = value; |
| 157 | + this._svg = getSVG(value); |
| 158 | + if (this._svg) { |
| 159 | + this._svg.setDocumentWidth('100%'); |
| 160 | + this._svg.setDocumentHeight('100%'); |
| 161 | + } |
| 162 | + } |
| 163 | + get src(): string | File | ImageAsset { |
| 164 | + return this._src; |
| 165 | + } |
| 166 | + |
| 167 | + _stretch: 'fill' | 'aspectFill' | 'aspectFit'; |
| 168 | + _preserveAspectRatio: com.caverock.androidsvg.PreserveAspectRatio = com.caverock.androidsvg.PreserveAspectRatio.LETTERBOX; |
| 169 | + set stretch(value: 'fill' | 'aspectFill' | 'aspectFit') { |
| 170 | + this._stretch = value; |
| 171 | + switch (value) { |
| 172 | + case 'aspectFill': |
| 173 | + this._preserveAspectRatio = com.caverock.androidsvg.PreserveAspectRatio.FULLSCREEN; |
| 174 | + break; |
| 175 | + case 'fill': |
| 176 | + this._preserveAspectRatio = com.caverock.androidsvg.PreserveAspectRatio.STRETCH; |
| 177 | + break; |
| 178 | + case 'aspectFit': |
| 179 | + this._preserveAspectRatio = com.caverock.androidsvg.PreserveAspectRatio.LETTERBOX; |
| 180 | + break; |
| 181 | + } |
| 182 | + } |
| 183 | + get stretch(): 'fill' | 'aspectFill' | 'aspectFit' { |
| 184 | + return this._stretch; |
| 185 | + } |
| 186 | +} |
0 commit comments