@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.
Files changed (83) hide show
  1. package/README.md +0 -12
  2. package/android/build.gradle +1 -3
  3. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageGlideRegistry.kt +1 -1
  4. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImagePackage.kt +1 -2
  5. package/ios/OneKeyImageCoderBridge.h +0 -4
  6. package/ios/OneKeyImageCoderBridge.m +0 -9
  7. package/lib/module/package.json +1 -6
  8. package/lib/nitrogen/generated/android/onekeyimage+autolinking.cmake +0 -4
  9. package/lib/nitrogen/generated/android/onekeyimageOnLoad.cpp +0 -18
  10. package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +0 -17
  11. package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +15 -32
  12. package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +0 -5
  13. package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.mm +0 -8
  14. package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.swift +0 -12
  15. package/nitro.json +0 -10
  16. package/nitrogen/generated/android/onekeyimage+autolinking.cmake +0 -4
  17. package/nitrogen/generated/android/onekeyimageOnLoad.cpp +0 -18
  18. package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +0 -17
  19. package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +15 -32
  20. package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +0 -5
  21. package/nitrogen/generated/ios/OneKeyImageAutolinking.mm +0 -8
  22. package/nitrogen/generated/ios/OneKeyImageAutolinking.swift +0 -12
  23. package/package.json +7 -40
  24. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyIcon.kt +0 -93
  25. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyNamedSVG.kt +0 -217
  26. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeySVG.kt +0 -31
  27. package/ios/OneKeyIcon.swift +0 -90
  28. package/ios/OneKeyNamedSVG.swift +0 -282
  29. package/ios/tests/OneKeyIconTests.swift +0 -484
  30. package/lib/module/OneKeyIcon.nitro.js +0 -4
  31. package/lib/module/icon/assetUrls.js +0 -7
  32. package/lib/module/icon/dom.js +0 -21
  33. package/lib/module/icon/index.js +0 -29
  34. package/lib/module/icon/index.web.js +0 -32
  35. package/lib/module/icon/props.js +0 -22
  36. package/lib/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.cpp +0 -67
  37. package/lib/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.hpp +0 -66
  38. package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.cpp +0 -79
  39. package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.hpp +0 -54
  40. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyIconSpec.kt +0 -65
  41. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconManager.kt +0 -96
  42. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconStateUpdater.kt +0 -23
  43. package/lib/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.cpp +0 -11
  44. package/lib/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.hpp +0 -89
  45. package/lib/nitrogen/generated/ios/c++/views/HybridOneKeyIconComponent.mm +0 -150
  46. package/lib/nitrogen/generated/ios/swift/HybridOneKeyIconSpec.swift +0 -56
  47. package/lib/nitrogen/generated/ios/swift/HybridOneKeyIconSpec_cxx.swift +0 -193
  48. package/lib/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.cpp +0 -24
  49. package/lib/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.hpp +0 -66
  50. package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.cpp +0 -36
  51. package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.hpp +0 -88
  52. package/lib/nitrogen/generated/shared/json/OneKeyIconConfig.json +0 -11
  53. package/lib/typescript/src/OneKeyIcon.nitro.d.ts +0 -7
  54. package/lib/typescript/src/icon/assetUrls.d.ts +0 -2
  55. package/lib/typescript/src/icon/dom.d.ts +0 -3
  56. package/lib/typescript/src/icon/index.d.ts +0 -140
  57. package/lib/typescript/src/icon/index.web.d.ts +0 -134
  58. package/lib/typescript/src/icon/props.d.ts +0 -8
  59. package/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.cpp +0 -67
  60. package/nitrogen/generated/android/c++/JHybridOneKeyIconSpec.hpp +0 -66
  61. package/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.cpp +0 -79
  62. package/nitrogen/generated/android/c++/views/JHybridOneKeyIconStateUpdater.hpp +0 -54
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyIconSpec.kt +0 -65
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconManager.kt +0 -96
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyIconStateUpdater.kt +0 -23
  66. package/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.cpp +0 -11
  67. package/nitrogen/generated/ios/c++/HybridOneKeyIconSpecSwift.hpp +0 -89
  68. package/nitrogen/generated/ios/c++/views/HybridOneKeyIconComponent.mm +0 -150
  69. package/nitrogen/generated/ios/swift/HybridOneKeyIconSpec.swift +0 -56
  70. package/nitrogen/generated/ios/swift/HybridOneKeyIconSpec_cxx.swift +0 -193
  71. package/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.cpp +0 -24
  72. package/nitrogen/generated/shared/c++/HybridOneKeyIconSpec.hpp +0 -66
  73. package/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.cpp +0 -36
  74. package/nitrogen/generated/shared/c++/views/HybridOneKeyIconComponent.hpp +0 -88
  75. package/nitrogen/generated/shared/json/OneKeyIconConfig.json +0 -11
  76. package/scripts/configure-icon-imports.mjs +0 -7
  77. package/scripts/icon-fixtures.test.mjs +0 -398
  78. package/src/OneKeyIcon.nitro.ts +0 -8
  79. package/src/icon/assetUrls.ts +0 -4
  80. package/src/icon/dom.ts +0 -21
  81. package/src/icon/index.tsx +0 -38
  82. package/src/icon/index.web.tsx +0 -36
  83. 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.412",
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 && node scripts/configure-icon-imports.mjs",
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.412",
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
- }
@@ -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
- }