@onekeyfe/react-native-image 6.7.0-alpha.412 → 6.7.0-alpha.414
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -12
- package/android/build.gradle +1 -3
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageGlideRegistry.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImagePackage.kt +1 -2
- package/ios/OneKeyImageCoderBridge.h +0 -4
- package/ios/OneKeyImageCoderBridge.m +0 -9
- package/lib/module/package.json +1 -6
- package/lib/nitrogen/generated/android/onekeyimage+autolinking.cmake +0 -4
- package/lib/nitrogen/generated/android/onekeyimageOnLoad.cpp +0 -18
- package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +0 -17
- package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +15 -32
- package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +0 -5
- package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.mm +0 -8
- package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.swift +0 -12
- package/nitro.json +0 -10
- package/nitrogen/generated/android/onekeyimage+autolinking.cmake +0 -4
- package/nitrogen/generated/android/onekeyimageOnLoad.cpp +0 -18
- package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +0 -17
- package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +15 -32
- package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +0 -5
- package/nitrogen/generated/ios/OneKeyImageAutolinking.mm +0 -8
- package/nitrogen/generated/ios/OneKeyImageAutolinking.swift +0 -12
- package/package.json +7 -40
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyIcon.kt +0 -93
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyNamedSVG.kt +0 -217
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeySVG.kt +0 -31
- package/ios/OneKeyIcon.swift +0 -90
- package/ios/OneKeyNamedSVG.swift +0 -282
- package/ios/tests/OneKeyIconTests.swift +0 -484
- package/lib/module/OneKeyIcon.nitro.js +0 -4
- package/lib/module/icon/assetUrls.js +0 -7
- package/lib/module/icon/dom.js +0 -21
- package/lib/module/icon/index.js +0 -29
- package/lib/module/icon/index.web.js +0 -32
- package/lib/module/icon/props.js +0 -22
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.cpp +0 -67
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.hpp +0 -66
- package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.cpp +0 -79
- package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.hpp +0 -54
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyIconSpec.kt +0 -65
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconManager.kt +0 -96
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconStateUpdater.kt +0 -23
- package/lib/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.cpp +0 -11
- package/lib/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.hpp +0 -89
- package/lib/nitrogen/generated/ios/c++/views/HybridOneKeyIconComponent.mm +0 -150
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyIconSpec.swift +0 -56
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyIconSpec_cxx.swift +0 -193
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.cpp +0 -24
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.hpp +0 -66
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.cpp +0 -36
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.hpp +0 -88
- package/lib/nitrogen/generated/shared/json/OneKeyIconConfig.json +0 -11
- package/lib/typescript/src/OneKeyIcon.nitro.d.ts +0 -7
- package/lib/typescript/src/icon/assetUrls.d.ts +0 -2
- package/lib/typescript/src/icon/dom.d.ts +0 -3
- package/lib/typescript/src/icon/index.d.ts +0 -140
- package/lib/typescript/src/icon/index.web.d.ts +0 -134
- package/lib/typescript/src/icon/props.d.ts +0 -8
- package/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.cpp +0 -67
- package/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.hpp +0 -66
- package/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.cpp +0 -79
- package/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.hpp +0 -54
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyIconSpec.kt +0 -65
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconManager.kt +0 -96
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconStateUpdater.kt +0 -23
- package/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.cpp +0 -11
- package/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.hpp +0 -89
- package/nitrogen/generated/ios/c++/views/HybridOneKeyIconComponent.mm +0 -150
- package/nitrogen/generated/ios/swift/HybridOneKeyIconSpec.swift +0 -56
- package/nitrogen/generated/ios/swift/HybridOneKeyIconSpec_cxx.swift +0 -193
- package/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.cpp +0 -24
- package/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.hpp +0 -66
- package/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.cpp +0 -36
- package/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.hpp +0 -88
- package/nitrogen/generated/shared/json/OneKeyIconConfig.json +0 -11
- package/scripts/configure-icon-imports.mjs +0 -7
- package/scripts/icon-fixtures.test.mjs +0 -398
- package/src/OneKeyIcon.nitro.ts +0 -8
- package/src/icon/assetUrls.ts +0 -4
- package/src/icon/dom.ts +0 -21
- package/src/icon/index.tsx +0 -38
- package/src/icon/index.web.tsx +0 -36
- package/src/icon/props.ts +0 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-image",
|
|
3
|
-
"version": "6.7.0-alpha.
|
|
3
|
+
"version": "6.7.0-alpha.414",
|
|
4
4
|
"description": "High-performance native image view for OneKey",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -10,27 +10,7 @@
|
|
|
10
10
|
"types": "./lib/typescript/src/index.d.ts",
|
|
11
11
|
"default": "./lib/module/index.js"
|
|
12
12
|
},
|
|
13
|
-
"./package.json": "./package.json"
|
|
14
|
-
"./icon": {
|
|
15
|
-
"browser": {
|
|
16
|
-
"source": "./src/icon/index.web.tsx",
|
|
17
|
-
"types": "./lib/typescript/src/icon/index.d.ts",
|
|
18
|
-
"default": "./lib/module/icon/index.web.js"
|
|
19
|
-
},
|
|
20
|
-
"react-native": {
|
|
21
|
-
"source": "./src/icon/index.tsx",
|
|
22
|
-
"types": "./lib/typescript/src/icon/index.d.ts",
|
|
23
|
-
"default": "./lib/module/icon/index.js"
|
|
24
|
-
},
|
|
25
|
-
"types": "./lib/typescript/src/icon/index.d.ts",
|
|
26
|
-
"source": "./src/icon/index.tsx",
|
|
27
|
-
"default": "./lib/module/icon/index.js"
|
|
28
|
-
},
|
|
29
|
-
"./icon/dom": {
|
|
30
|
-
"source": "./src/icon/dom.ts",
|
|
31
|
-
"types": "./lib/typescript/src/icon/dom.d.ts",
|
|
32
|
-
"default": "./lib/module/icon/dom.js"
|
|
33
|
-
}
|
|
13
|
+
"./package.json": "./package.json"
|
|
34
14
|
},
|
|
35
15
|
"files": [
|
|
36
16
|
"src",
|
|
@@ -50,19 +30,16 @@
|
|
|
50
30
|
"!android/local.properties",
|
|
51
31
|
"!**/__tests__",
|
|
52
32
|
"!**/.*",
|
|
53
|
-
"!**/*.map"
|
|
54
|
-
"scripts"
|
|
33
|
+
"!**/*.map"
|
|
55
34
|
],
|
|
56
35
|
"scripts": {
|
|
57
36
|
"clean": "del-cli android/build lib",
|
|
58
|
-
"prepare": "rm -rf lib && bob build && cp -r nitrogen lib/nitrogen
|
|
37
|
+
"prepare": "rm -rf lib && bob build && cp -r nitrogen lib/nitrogen",
|
|
59
38
|
"nitrogen": "nitrogen",
|
|
60
39
|
"typecheck": "tsc",
|
|
61
40
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
62
41
|
"test": "jest",
|
|
63
|
-
"release": "yarn nitrogen && yarn prepare && npm whoami && npm publish --access public"
|
|
64
|
-
"icons:check": "node scripts/icon-fixtures.test.mjs",
|
|
65
|
-
"icons:test": "node scripts/icon-fixtures.test.mjs"
|
|
42
|
+
"release": "yarn nitrogen && yarn prepare && npm whoami && npm publish --access public"
|
|
66
43
|
},
|
|
67
44
|
"keywords": [
|
|
68
45
|
"react-native",
|
|
@@ -108,7 +85,7 @@
|
|
|
108
85
|
"typescript": "^5.9.2"
|
|
109
86
|
},
|
|
110
87
|
"peerDependencies": {
|
|
111
|
-
"@onekeyfe/react-native-skeleton": "6.7.0-alpha.
|
|
88
|
+
"@onekeyfe/react-native-skeleton": "6.7.0-alpha.414",
|
|
112
89
|
"react": "*",
|
|
113
90
|
"react-native": "*",
|
|
114
91
|
"react-native-nitro-modules": "0.37.0"
|
|
@@ -150,16 +127,6 @@
|
|
|
150
127
|
"preset": "react-native",
|
|
151
128
|
"modulePathIgnorePatterns": [
|
|
152
129
|
"<rootDir>/lib/"
|
|
153
|
-
]
|
|
154
|
-
"moduleNameMapper": {
|
|
155
|
-
"^#onekey-icon-assets$": "<rootDir>/../react-native-image/src/icon/assetUrls.ts"
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
"imports": {
|
|
159
|
-
"#onekey-icon-assets": {
|
|
160
|
-
"types": "./src/icon/assetUrls.ts",
|
|
161
|
-
"source": "./src/icon/assetUrls.ts",
|
|
162
|
-
"default": "./lib/module/icon/assetUrls.js"
|
|
163
|
-
}
|
|
130
|
+
]
|
|
164
131
|
}
|
|
165
132
|
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.onekeyimage
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.content.res.Configuration
|
|
5
|
-
import android.graphics.PorterDuff
|
|
6
|
-
import android.graphics.PorterDuffColorFilter
|
|
7
|
-
import android.widget.ImageView
|
|
8
|
-
import com.facebook.react.uimanager.ThemedReactContext
|
|
9
|
-
import com.margelo.nitro.views.RecyclableView
|
|
10
|
-
import java.lang.ref.WeakReference
|
|
11
|
-
|
|
12
|
-
private class OneKeyIconHost(context: Context) : ImageView(context) {
|
|
13
|
-
var changed: (() -> Unit)? = null
|
|
14
|
-
var attached = false
|
|
15
|
-
private set
|
|
16
|
-
init { scaleType = ScaleType.FIT_CENTER }
|
|
17
|
-
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
18
|
-
super.onSizeChanged(w, h, oldw, oldh)
|
|
19
|
-
changed?.invoke()
|
|
20
|
-
}
|
|
21
|
-
override fun onAttachedToWindow() { super.onAttachedToWindow(); attached = true; changed?.invoke() }
|
|
22
|
-
override fun onDetachedFromWindow() { attached = false; super.onDetachedFromWindow(); changed?.invoke() }
|
|
23
|
-
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig); changed?.invoke() }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
class HybridOneKeyIcon(context: ThemedReactContext) : HybridOneKeyIconSpec(), RecyclableView {
|
|
27
|
-
private val host = OneKeyIconHost(context)
|
|
28
|
-
private val owner = Any()
|
|
29
|
-
private var generation = 0L
|
|
30
|
-
private var identity: String? = null
|
|
31
|
-
private var displayedGeometry: String? = null
|
|
32
|
-
private var displayed: OneKeyNamedSVG.RenderResult? = null
|
|
33
|
-
override val view: ImageView get() = host
|
|
34
|
-
override var iconName: String? = null
|
|
35
|
-
override var resolvedColor: String? = null
|
|
36
|
-
init { host.changed = { refresh() } }
|
|
37
|
-
|
|
38
|
-
override fun afterUpdate() { super.afterUpdate(); refresh() }
|
|
39
|
-
|
|
40
|
-
private fun clearImage() {
|
|
41
|
-
displayed = null; displayedGeometry = null
|
|
42
|
-
host.colorFilter = null; host.setImageDrawable(null)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private fun applyTint(result: OneKeyNamedSVG.RenderResult, color: String) {
|
|
46
|
-
host.colorFilter = result.tintColor(color)?.let { PorterDuffColorFilter(it, PorterDuff.Mode.SRC_IN) }
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private fun refresh() {
|
|
50
|
-
val scale = host.resources.displayMetrics.density
|
|
51
|
-
val next = "$iconName|$resolvedColor|${host.width}|${host.height}|$scale|${host.attached}"
|
|
52
|
-
if (identity == next) return
|
|
53
|
-
identity = next; generation++
|
|
54
|
-
val revision = generation
|
|
55
|
-
OneKeyNamedSVG.cancel(owner)
|
|
56
|
-
val name = iconName
|
|
57
|
-
val color = resolvedColor
|
|
58
|
-
if (!host.attached || host.width !in 1..4096 || host.height !in 1..4096 ||
|
|
59
|
-
host.width.toLong() * host.height > 4194304 || name.isNullOrEmpty() || color == null ||
|
|
60
|
-
!OneKeyNamedSVG.isValidColor(color)) { clearImage(); return }
|
|
61
|
-
val geometry = "$name|${host.width}|${host.height}|$scale"
|
|
62
|
-
val ready = displayed
|
|
63
|
-
if (displayedGeometry == geometry && ready != null && host.drawable != null) {
|
|
64
|
-
if (ready.canTint(color)) { applyTint(ready, color); return }
|
|
65
|
-
} else clearImage()
|
|
66
|
-
OneKeyNamedSVG.cachedRender(name, color, host.width, host.height, scale)?.let { hit ->
|
|
67
|
-
displayed = hit; displayedGeometry = geometry
|
|
68
|
-
applyTint(hit, color)
|
|
69
|
-
host.setImageBitmap(hit.bitmap)
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
val weakHost = WeakReference(host)
|
|
73
|
-
val weakSelf = WeakReference(this)
|
|
74
|
-
OneKeyNamedSVG.requestRender(owner, host.context.applicationContext.assets, name, color,
|
|
75
|
-
host.width, host.height, scale) { result ->
|
|
76
|
-
val current = weakSelf.get()
|
|
77
|
-
val target = weakHost.get()
|
|
78
|
-
if (current?.generation != revision || target?.attached != true) return@requestRender
|
|
79
|
-
if (result == null) { current.clearImage(); return@requestRender }
|
|
80
|
-
current.displayed = result; current.displayedGeometry = geometry
|
|
81
|
-
current.applyTint(result, color)
|
|
82
|
-
target.setImageBitmap(result.bitmap)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
override fun prepareForRecycle() {
|
|
87
|
-
iconName = null
|
|
88
|
-
resolvedColor = null
|
|
89
|
-
refresh()
|
|
90
|
-
}
|
|
91
|
-
override fun onDropView() { prepareForRecycle(); super.onDropView() }
|
|
92
|
-
override fun dispose() { prepareForRecycle(); host.changed = null; super.dispose() }
|
|
93
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.onekeyimage
|
|
2
|
-
|
|
3
|
-
import android.content.res.AssetManager
|
|
4
|
-
import android.graphics.Bitmap
|
|
5
|
-
import android.graphics.Canvas
|
|
6
|
-
import android.graphics.Color
|
|
7
|
-
import android.graphics.Paint
|
|
8
|
-
import android.graphics.PorterDuff
|
|
9
|
-
import android.graphics.PorterDuffColorFilter
|
|
10
|
-
import android.os.Handler
|
|
11
|
-
import android.os.Looper
|
|
12
|
-
import android.util.Log
|
|
13
|
-
import org.json.JSONObject
|
|
14
|
-
import java.util.concurrent.Executors
|
|
15
|
-
import kotlin.math.roundToInt
|
|
16
|
-
|
|
17
|
-
/** Process-owned named resources; no React runtime or Activity is retained. */
|
|
18
|
-
object OneKeyNamedSVG {
|
|
19
|
-
enum class RecolorMode { FIXED, SINGLE, MULTIPLE, RASTER }
|
|
20
|
-
data class IconMetadata(
|
|
21
|
-
val width: Float,
|
|
22
|
-
val height: Float,
|
|
23
|
-
val mode: RecolorMode,
|
|
24
|
-
)
|
|
25
|
-
data class RenderResult(val bitmap: Bitmap, val mode: RecolorMode,
|
|
26
|
-
val sourceIdentity: String, val maskAlpha: Int) {
|
|
27
|
-
fun canTint(color: String): Boolean {
|
|
28
|
-
val value = parseColor(color) ?: return false
|
|
29
|
-
return mode == RecolorMode.FIXED || mode == RecolorMode.SINGLE ||
|
|
30
|
-
(mode == RecolorMode.MULTIPLE && Color.alpha(value) == maskAlpha)
|
|
31
|
-
}
|
|
32
|
-
fun tintColor(color: String): Int? {
|
|
33
|
-
val value = parseColor(color) ?: return null
|
|
34
|
-
return when(mode) {
|
|
35
|
-
RecolorMode.SINGLE -> value
|
|
36
|
-
RecolorMode.MULTIPLE -> value or (0xff shl 24)
|
|
37
|
-
else -> null
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
internal fun finalBitmap(color: String): Bitmap {
|
|
41
|
-
val tint = tintColor(color) ?: return bitmap
|
|
42
|
-
if (tint == Color.BLACK) return bitmap
|
|
43
|
-
val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
|
|
44
|
-
val paint = Paint().apply { colorFilter = PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_IN) }
|
|
45
|
-
Canvas(output).drawBitmap(bitmap, 0f, 0f, paint)
|
|
46
|
-
return output
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private data class Request(val assets: AssetManager, val name: String, val color: String,
|
|
51
|
-
val width: Int, val height: Int, val scale: Float, val finalColor: Boolean, val complete: (RenderResult?) -> Unit)
|
|
52
|
-
private val lock = Any()
|
|
53
|
-
private val pending = LinkedHashMap<Any, Request>()
|
|
54
|
-
private val cache = LinkedHashMap<String, RenderResult>(128, 0.75f, true)
|
|
55
|
-
private var cacheBytes = 0L
|
|
56
|
-
private var activeWorkers = 0
|
|
57
|
-
private var manifest: JSONObject? = null
|
|
58
|
-
private val manifestLock = Any()
|
|
59
|
-
private val workers = Executors.newFixedThreadPool(2)
|
|
60
|
-
private val main = Handler(Looper.getMainLooper())
|
|
61
|
-
private val colorPattern = Regex("rgba\\(\\d{1,3},\\d{1,3},\\d{1,3},(?:0(?:\\.\\d+)?|1(?:\\.0+)?)\\)")
|
|
62
|
-
|
|
63
|
-
private var manifestMissingLogged = false
|
|
64
|
-
|
|
65
|
-
fun cancel(owner: Any) { synchronized(lock) { pending.remove(owner) } }
|
|
66
|
-
|
|
67
|
-
/** Caller holds manifestLock. Hosts own the resources, so a miss is logged once. */
|
|
68
|
-
private fun loadManifestLocked(assets: AssetManager): JSONObject? {
|
|
69
|
-
manifest?.let { return it }
|
|
70
|
-
return try {
|
|
71
|
-
assets.open("icons/manifest.json").bufferedReader().use { JSONObject(it.readText()) }
|
|
72
|
-
.also { manifest = it }
|
|
73
|
-
} catch (error: Exception) {
|
|
74
|
-
if (!manifestMissingLogged) {
|
|
75
|
-
manifestMissingLogged = true
|
|
76
|
-
Log.w("OneKeyIcon", "assets/icons/manifest.json is missing or invalid; named icons will not render", error)
|
|
77
|
-
}
|
|
78
|
-
null
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private fun modeOf(metadata: JSONObject): RecolorMode = try {
|
|
83
|
-
RecolorMode.valueOf(metadata.optString("recolorMode", "raster").uppercase())
|
|
84
|
-
} catch (_: IllegalArgumentException) {
|
|
85
|
-
RecolorMode.RASTER
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private data class RenderKey(val identity: String, val key: String, val maskAlpha: Int)
|
|
89
|
-
|
|
90
|
-
private fun renderKey(name: String, digest: String, width: Int, height: Int, scale: Float,
|
|
91
|
-
mode: RecolorMode, requestColor: String, color: Int): RenderKey {
|
|
92
|
-
val identity = "$name|$digest|$width|$height|$scale"
|
|
93
|
-
val maskAlpha = if (mode == RecolorMode.MULTIPLE) Color.alpha(color) else 255
|
|
94
|
-
val colorKey = when(mode) {
|
|
95
|
-
RecolorMode.RASTER -> requestColor
|
|
96
|
-
RecolorMode.MULTIPLE -> maskAlpha.toString()
|
|
97
|
-
else -> ""
|
|
98
|
-
}
|
|
99
|
-
return RenderKey(identity, "$identity|$mode|$colorKey", maskAlpha)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Main-thread fast path: returns an already-decoded result without asset I/O or
|
|
104
|
-
* manifest loading, so remounted and recycled views paint in the same frame.
|
|
105
|
-
*/
|
|
106
|
-
fun cachedRender(name: String, color: String, width: Int, height: Int, scale: Float): RenderResult? {
|
|
107
|
-
val value = parseColor(color) ?: return null
|
|
108
|
-
val metadata = synchronized(manifestLock) { manifest?.optJSONObject(name) } ?: return null
|
|
109
|
-
val digest = metadata.optString("digest").takeIf { it.isNotEmpty() } ?: return null
|
|
110
|
-
val key = renderKey(name, digest, width, height, scale, modeOf(metadata), color, value).key
|
|
111
|
-
return synchronized(lock) { cache[key] }
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
fun iconMetadata(assets: AssetManager, name: String): IconMetadata? {
|
|
115
|
-
val trimmed = name.trim()
|
|
116
|
-
if (trimmed.isEmpty()) return null
|
|
117
|
-
val metadata = try {
|
|
118
|
-
synchronized(manifestLock) {
|
|
119
|
-
loadManifestLocked(assets)?.optJSONObject(trimmed)
|
|
120
|
-
}
|
|
121
|
-
} catch (_: Exception) {
|
|
122
|
-
null
|
|
123
|
-
} ?: return null
|
|
124
|
-
val mode = modeOf(metadata)
|
|
125
|
-
val viewBox = metadata.optJSONArray("viewBox")
|
|
126
|
-
val vbWidth = if (viewBox != null && viewBox.length() >= 4) viewBox.optDouble(2, 24.0).toFloat() else 24f
|
|
127
|
-
val vbHeight = if (viewBox != null && viewBox.length() >= 4) viewBox.optDouble(3, 24.0).toFloat() else 24f
|
|
128
|
-
val width = if (vbWidth > 0f) vbWidth else 24f
|
|
129
|
-
val height = if (vbHeight > 0f) vbHeight else 24f
|
|
130
|
-
return IconMetadata(width = width, height = height, mode = mode)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
fun request(owner: Any, assets: AssetManager, name: String, color: String,
|
|
134
|
-
width: Int, height: Int, scale: Float, complete: (Bitmap?) -> Unit) {
|
|
135
|
-
enqueue(owner, assets, name, color, width, height, scale, true) { complete(it?.bitmap) }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
fun requestRender(owner: Any, assets: AssetManager, name: String, color: String,
|
|
139
|
-
width: Int, height: Int, scale: Float, complete: (RenderResult?) -> Unit) {
|
|
140
|
-
enqueue(owner, assets, name, color, width, height, scale, false, complete)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private fun enqueue(owner: Any, assets: AssetManager, name: String, color: String,
|
|
144
|
-
width: Int, height: Int, scale: Float, finalColor: Boolean, complete: (RenderResult?) -> Unit) {
|
|
145
|
-
synchronized(lock) {
|
|
146
|
-
pending[owner] = Request(assets, name, color, width, height, scale, finalColor, complete)
|
|
147
|
-
while (activeWorkers < 2 && activeWorkers < pending.size) {
|
|
148
|
-
activeWorkers++
|
|
149
|
-
workers.execute { drain() }
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private fun parseColor(color: String): Int? {
|
|
155
|
-
if (!colorPattern.matches(color)) return null
|
|
156
|
-
val channels = color.removePrefix("rgba(").removeSuffix(")").split(',')
|
|
157
|
-
val rgb = channels.take(3).map { it.toInt() }
|
|
158
|
-
if (rgb.any { it !in 0..255 }) return null
|
|
159
|
-
val alpha = (channels[3].toDouble() * 255).roundToInt()
|
|
160
|
-
return Color.argb(alpha, rgb[0], rgb[1], rgb[2])
|
|
161
|
-
}
|
|
162
|
-
fun isValidColor(color: String): Boolean = parseColor(color) != null
|
|
163
|
-
|
|
164
|
-
private fun drain() {
|
|
165
|
-
while (true) {
|
|
166
|
-
val request = synchronized(lock) {
|
|
167
|
-
val first = pending.entries.firstOrNull()
|
|
168
|
-
if (first == null) { activeWorkers--; null }
|
|
169
|
-
else { pending.remove(first.key); first.value }
|
|
170
|
-
} ?: return
|
|
171
|
-
val bitmap = try {
|
|
172
|
-
decode(request)?.let { result ->
|
|
173
|
-
if (request.finalColor) result.copy(bitmap = result.finalBitmap(request.color), mode = RecolorMode.RASTER)
|
|
174
|
-
else result
|
|
175
|
-
}
|
|
176
|
-
} catch (_: Throwable) { null } // OOM must not strand activeWorkers.
|
|
177
|
-
main.post { request.complete(bitmap) }
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private fun decode(request: Request): RenderResult? {
|
|
182
|
-
if (request.width !in 1..4096 || request.height !in 1..4096 ||
|
|
183
|
-
request.width.toLong() * request.height > 4194304 || !request.scale.isFinite() ||
|
|
184
|
-
request.scale <= 0) return null
|
|
185
|
-
val color = parseColor(request.color) ?: return null
|
|
186
|
-
val metadata = synchronized(manifestLock) {
|
|
187
|
-
loadManifestLocked(request.assets)?.optJSONObject(request.name)
|
|
188
|
-
} ?: return null
|
|
189
|
-
val mode = modeOf(metadata)
|
|
190
|
-
val (sourceIdentity, key, maskAlpha) = renderKey(request.name, metadata.getString("digest"),
|
|
191
|
-
request.width, request.height, request.scale, mode, request.color, color)
|
|
192
|
-
val paintColor = when(mode) {
|
|
193
|
-
RecolorMode.FIXED, RecolorMode.SINGLE -> Color.BLACK
|
|
194
|
-
RecolorMode.MULTIPLE -> Color.argb(maskAlpha, 0, 0, 0)
|
|
195
|
-
RecolorMode.RASTER -> color
|
|
196
|
-
}
|
|
197
|
-
synchronized(lock) { cache[key]?.let { return it } }
|
|
198
|
-
// AndroidSVG rgba quantizes with 256; SVG hex alpha preserves the exact CSS 8-bit channel.
|
|
199
|
-
val svgColor = listOf(Color.red(paintColor), Color.green(paintColor), Color.blue(paintColor), Color.alpha(paintColor))
|
|
200
|
-
.joinToString(separator = "", prefix = "#") { it.toString(16).padStart(2, '0') }
|
|
201
|
-
val xml = request.assets.open("icons/${request.name}.svg").bufferedReader().use { it.readText() }
|
|
202
|
-
.replace(Regex("((?:fill|stroke)\\s*=\\s*[\"'])currentColor([\"'])"), "$1${svgColor}$2")
|
|
203
|
-
val bitmap = OneKeySVG.decode(xml.byteInputStream()).render(request.width, request.height)
|
|
204
|
-
val result = RenderResult(bitmap, mode, sourceIdentity, maskAlpha)
|
|
205
|
-
val bytes = bitmap.allocationByteCount.toLong()
|
|
206
|
-
if (bytes <= 8388608) synchronized(lock) {
|
|
207
|
-
cache.put(key, result)?.let { cacheBytes -= it.bitmap.allocationByteCount }
|
|
208
|
-
cacheBytes += bytes
|
|
209
|
-
while (cache.size > 128 || cacheBytes > 8388608) {
|
|
210
|
-
val first = cache.entries.first()
|
|
211
|
-
cacheBytes -= first.value.bitmap.allocationByteCount
|
|
212
|
-
cache.remove(first.key)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return result
|
|
216
|
-
}
|
|
217
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.onekeyimage
|
|
2
|
-
|
|
3
|
-
import android.graphics.Bitmap
|
|
4
|
-
import android.graphics.Canvas
|
|
5
|
-
import com.caverock.androidsvg.SVG
|
|
6
|
-
import java.io.InputStream
|
|
7
|
-
|
|
8
|
-
/** Synchronous per-request document; loaders retain scheduling and cancellation. */
|
|
9
|
-
object OneKeySVG {
|
|
10
|
-
fun decode(source: InputStream): OneKeySVGDocument = OneKeySVGDocument(parse(source))
|
|
11
|
-
internal fun parse(source: InputStream): SVG = SVG.getFromInputStream(source)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class OneKeySVGDocument internal constructor(private val svg: SVG) {
|
|
15
|
-
val viewBoxWidth: Float? = svg.documentViewBox?.width()
|
|
16
|
-
val viewBoxHeight: Float? = svg.documentViewBox?.height()
|
|
17
|
-
val intrinsicWidth: Float = svg.documentWidth
|
|
18
|
-
val intrinsicHeight: Float = svg.documentHeight
|
|
19
|
-
|
|
20
|
-
fun render(width: Int, height: Int): Bitmap {
|
|
21
|
-
require(width > 0 && height > 0)
|
|
22
|
-
if (svg.documentViewBox == null && intrinsicWidth > 0 && intrinsicHeight > 0) {
|
|
23
|
-
svg.setDocumentViewBox(0f, 0f, intrinsicWidth, intrinsicHeight)
|
|
24
|
-
}
|
|
25
|
-
svg.setDocumentWidth(width.toFloat())
|
|
26
|
-
svg.setDocumentHeight(height.toFloat())
|
|
27
|
-
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
28
|
-
svg.renderToCanvas(Canvas(bitmap))
|
|
29
|
-
return bitmap
|
|
30
|
-
}
|
|
31
|
-
}
|
package/ios/OneKeyIcon.swift
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import NitroModules
|
|
2
|
-
import UIKit
|
|
3
|
-
|
|
4
|
-
private final class OneKeyIconHost: UIImageView {
|
|
5
|
-
var changed: (() -> Void)?
|
|
6
|
-
override func layoutSubviews() { super.layoutSubviews(); changed?() }
|
|
7
|
-
override func didMoveToWindow() { super.didMoveToWindow(); changed?() }
|
|
8
|
-
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
9
|
-
super.traitCollectionDidChange(previousTraitCollection)
|
|
10
|
-
if previousTraitCollection?.displayScale != traitCollection.displayScale { changed?() }
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
final class HybridOneKeyIcon: HybridOneKeyIconSpec, RecyclableView {
|
|
15
|
-
private let host = OneKeyIconHost()
|
|
16
|
-
private let owner = NSObject()
|
|
17
|
-
private var generation: UInt64 = 0
|
|
18
|
-
private var identity: String?
|
|
19
|
-
private var displayedGeometry: String?
|
|
20
|
-
private var displayed: OneKeyNamedSVG.RenderResult?
|
|
21
|
-
var iconName: String?
|
|
22
|
-
var resolvedColor: String?
|
|
23
|
-
var view: UIView { host }
|
|
24
|
-
|
|
25
|
-
override init() {
|
|
26
|
-
super.init()
|
|
27
|
-
host.contentMode = .scaleAspectFit
|
|
28
|
-
host.changed = { [weak self] in self?.refresh() }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
func afterUpdate() { refresh() }
|
|
32
|
-
|
|
33
|
-
private func clearImage() {
|
|
34
|
-
displayed = nil; displayedGeometry = nil; host.image = nil
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private func apply(_ result: OneKeyNamedSVG.RenderResult, color: String) {
|
|
38
|
-
displayed = result
|
|
39
|
-
if let tint = result.tintColor(color) {
|
|
40
|
-
host.image = result.image.withRenderingMode(.alwaysTemplate)
|
|
41
|
-
host.tintColor = tint
|
|
42
|
-
} else {
|
|
43
|
-
host.image = result.image.withRenderingMode(.alwaysOriginal)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private func refresh() {
|
|
48
|
-
let scale = host.window?.screen.scale ?? 1
|
|
49
|
-
let width = ceil(host.bounds.width * scale), height = ceil(host.bounds.height * scale)
|
|
50
|
-
let attached = host.window != nil
|
|
51
|
-
let next = "\(iconName ?? "")|\(resolvedColor ?? "")|\(width)|\(height)|\(scale)|\(attached)"
|
|
52
|
-
guard next != identity else { return }
|
|
53
|
-
identity = next; generation &+= 1
|
|
54
|
-
let revision = generation
|
|
55
|
-
OneKeyNamedSVG.shared.cancel(owner: owner)
|
|
56
|
-
guard attached, width.isFinite, height.isFinite,
|
|
57
|
-
width > 0, height > 0, width <= 4096, height <= 4096, width * height <= 4_194_304,
|
|
58
|
-
let name = iconName, !name.isEmpty,
|
|
59
|
-
let color = resolvedColor, OneKeyNamedSVG.isValidColor(color) else { clearImage(); return }
|
|
60
|
-
let geometry = "\(name)|\(width)|\(height)|\(scale)"
|
|
61
|
-
if displayedGeometry == geometry, let result = displayed, host.image != nil {
|
|
62
|
-
if result.canTint(color) {
|
|
63
|
-
if let tint = result.tintColor(color) { host.tintColor = tint }
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
// Keep only a successful same-source/geometry raster while new color decodes.
|
|
67
|
-
} else { clearImage() }
|
|
68
|
-
if let hit = OneKeyNamedSVG.shared.cachedRender(name: name, color: color,
|
|
69
|
-
width: Int(width), height: Int(height), scale: scale) {
|
|
70
|
-
displayedGeometry = geometry
|
|
71
|
-
apply(hit, color: color)
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
OneKeyNamedSVG.shared.requestRender(owner: owner, name: name, color: color,
|
|
75
|
-
width: Int(width), height: Int(height), scale: scale) { [weak self] result in
|
|
76
|
-
guard let self, self.generation == revision, self.host.window != nil else { return }
|
|
77
|
-
guard let result else { self.clearImage(); return }
|
|
78
|
-
self.displayedGeometry = geometry
|
|
79
|
-
self.apply(result, color: color)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
func prepareForRecycle() {
|
|
84
|
-
iconName = nil; resolvedColor = nil; refresh()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
func onDropView() { prepareForRecycle() }
|
|
88
|
-
|
|
89
|
-
deinit { OneKeyNamedSVG.shared.cancel(owner: owner) }
|
|
90
|
-
}
|