comparison app/src/main/java/com/bartsent/simpleresizer/lib/Resizer.kt @ 9:884092efe31a concur

Gets gratuitously killed. WTF?
author David Barts <n5jrn@me.com>
date Wed, 17 Feb 2021 13:20:25 -0800
parents 9374d044a132
children
comparison
equal deleted inserted replaced
7:9374d044a132 9:884092efe31a
1 package com.bartsent.simpleresizer.lib 1 package com.bartsent.simpleresizer.lib
2 2
3 import android.graphics.Bitmap 3 import android.graphics.Bitmap
4 import android.graphics.Color 4 import android.graphics.Color
5 import android.util.Log
5 import java.io.Closeable 6 import java.io.Closeable
6 import kotlin.math.ceil 7 import kotlin.math.ceil
7 import kotlin.math.floor 8 import kotlin.math.floor
8 9
9 class Resizer: Closeable { 10 class Resizer: Closeable {
10 private val NWORKERS = maxOf(1, Runtime.getRuntime().availableProcessors() - 2) 11 private val NWORKERS = maxOf(1, Runtime.getRuntime().availableProcessors() - 2)
11 private val workers = Channel<Worker>(NWORKERS) 12 private val workers = Channel<Worker>(NWORKERS)
12 init { 13 init {
14 Log.d("Resizer", "workers channel is ${workers.hashCode()}")
15 Log.d("Resizer", "writing workers…")
13 for (i in 0 until NWORKERS) 16 for (i in 0 until NWORKERS)
14 workers.write(Worker(workers)) 17 workers.write(Worker(workers).apply { start() })
18 Log.d("Resizer", "writing done")
15 } 19 }
16 20
17 override fun close() { 21 override fun close() {
22 Log.d("Resizer", "closing, stack trace follows…", Exception("dummy exception"))
18 for (i in 0 until NWORKERS) 23 for (i in 0 until NWORKERS)
19 workers.read().run { 24 workers.read().run {
20 interrupt() 25 interrupt()
21 join() 26 join()
22 } 27 }
28 Log.d("Resizer", "closing done")
23 } 29 }
24 30
25 private fun drain(): Unit { 31 private fun drain(): Unit {
32 Log.d("Resizer", "draining workers…")
26 val saved = Array<Worker>(NWORKERS) { workers.read() } 33 val saved = Array<Worker>(NWORKERS) { workers.read() }
27 saved.forEach { workers.write(it) } 34 saved.forEach { workers.write(it) }
35 Log.d("Resizer", "draining done")
28 } 36 }
29 37
30 private data class IndexWeight(var index: Int, var weight: Double) 38 private data class IndexWeight(var index: Int, var weight: Double)
31 39
32 private fun precomputeWeights(dstSize: Int, srcSize: Int, filter: ScalingKernel): Array<Array<IndexWeight>> { 40 private fun precomputeWeights(dstSize: Int, srcSize: Int, filter: ScalingKernel): Array<Array<IndexWeight>> {
59 tmp.clear() 67 tmp.clear()
60 } 68 }
61 return out 69 return out
62 } 70 }
63 71
64 private data class WorkUnit(val target: Bitmap, val x: Int, val y: Int,
65 val weights: Array<IndexWeight>, val scanLine: IntArray)
66
67 private class Worker(private val workers: Channel<Worker>): Thread() { 72 private class Worker(private val workers: Channel<Worker>): Thread() {
68 private val _input = Channel<WorkUnit>(1) 73 private val _input = Channel<() -> Unit>(1)
69 74
70 override fun run() { 75 override fun run() {
71 while (true) { 76 while (true) {
72 val data = _input.read() 77 val (block: () -> Unit, interrupted: Boolean) = try {
73 if (isInterrupted) 78 Pair(_input.read(), isInterrupted)
79 } catch (e: InterruptedException) {
80 Pair({}, true)
81 }
82 if (interrupted)
74 return 83 return
75 resample(data.target, data.x, data.y, data.weights, data.scanLine) 84 block()
76 workers.write(this) 85 workers.write(this)
77 } 86 }
78 } 87 }
79 88
80 fun send(data: WorkUnit) { 89 fun send(block: () -> Unit) {
81 _input.write(data) 90 _input.write(block)
82 }
83
84 private fun clamp(v: Double): Int = minOf(255, maxOf(0, (v + 0.5).toInt()))
85
86 private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit {
87 var r = 0.0; var g = 0.0; var b = 0.0; var a = 0.0
88 weights.forEach {
89 val c = scanLine[it.index]
90 val aw = Color.alpha(c).toDouble() * it.weight
91 r += Color.red(c).toDouble() * aw
92 g += Color.green(c).toDouble() * aw
93 b += Color.blue(c).toDouble() * aw
94 a += aw
95 }
96 if (a == 0.0)
97 return
98 val argb = Color.argb(clamp(a), clamp(r/a), clamp(g/a), clamp(b/a))
99 synchronized(target) {
100 target.setPixel(x, y, argb)
101 }
102 } 91 }
103 } 92 }
104 93
94 private fun clamp(v: Double): Int = minOf(255, maxOf(0, (v + 0.5).toInt()))
95
96 fun parallel(block: () -> Unit) = workers.read().send(block)
97
105 private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit { 98 private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit {
106 workers.read().send(WorkUnit(target, x, y, weights, scanLine)) 99 // Log.d("Resizer", "resample: x=$x, y=$y")
100 var r = 0.0; var g = 0.0; var b = 0.0; var a = 0.0
101 weights.forEach {
102 val c = scanLine[it.index]
103 val aw = Color.alpha(c).toDouble() * it.weight
104 r += Color.red(c).toDouble() * aw
105 g += Color.green(c).toDouble() * aw
106 b += Color.blue(c).toDouble() * aw
107 a += aw
108 }
109 if (a == 0.0) {
110 Log.d("Resizer", "resample: a=0.0, done")
111 return
112 }
113 val argb = Color.argb(clamp(a), clamp(r/a), clamp(g/a), clamp(b/a))
114 // Log.d("Resizer", "setting pixel…")
115 synchronized(target) {
116 target.setPixel(x, y, argb)
117 }
118 // Log.d("Resizer", "resample: a=$a, done")
107 } 119 }
108 120
109 fun horizontal(image: Bitmap, newWidth: Int, kernel: ScalingKernel): Bitmap { 121 fun horizontal(image: Bitmap, newWidth: Int, kernel: ScalingKernel): Bitmap {
122 Log.d("Resizer", "horizontal…")
110 val dst = Bitmap.createBitmap(newWidth, image.height, Bitmap.Config.ARGB_8888) 123 val dst = Bitmap.createBitmap(newWidth, image.height, Bitmap.Config.ARGB_8888)
111 val weights = precomputeWeights(newWidth, image.width, kernel) 124 val weights = precomputeWeights(newWidth, image.width, kernel)
112 val scanLine = IntArray(image.width)
113 for (y in 0 until image.height) { 125 for (y in 0 until image.height) {
114 for (x in 0 until image.width) { 126 val scanLine = IntArray(image.width) { image.getPixel(it, y) }
115 scanLine[x] = image.getPixel(x, y) 127 parallel {
116 } 128 Log.d("Resizer", "horizontal: y=$y")
117 for (x in weights.indices) { 129 for (x in weights.indices) {
118 resample(dst, x, y, weights[x], scanLine) 130 resample(dst, x, y, weights[x], scanLine)
131 }
119 } 132 }
120 } 133 }
121 drain() 134 drain()
135 Log.d("Resizer", "horizontal done")
122 return dst 136 return dst
123 } 137 }
124 138
125 fun vertical(image: Bitmap, newHeight: Int, kernel: ScalingKernel): Bitmap { 139 fun vertical(image: Bitmap, newHeight: Int, kernel: ScalingKernel): Bitmap {
140 Log.d("Resizer", "vertical…")
126 val dst = Bitmap.createBitmap(image.width, newHeight, Bitmap.Config.ARGB_8888) 141 val dst = Bitmap.createBitmap(image.width, newHeight, Bitmap.Config.ARGB_8888)
127 val weights = precomputeWeights(newHeight, image.height, kernel) 142 val weights = precomputeWeights(newHeight, image.height, kernel)
128 val scanLine = IntArray(image.height)
129 for (x in 0 until image.width) { 143 for (x in 0 until image.width) {
130 for (y in 0 until image.height) { 144 val scanLine = IntArray(image.height) { image.getPixel(x, it) }
131 scanLine[y] = image.getPixel(x, y) 145 parallel {
132 } 146 Log.d("Resizer", "vertical: x=$x")
133 for (y in weights.indices) { 147 for (y in weights.indices) {
134 resample(dst, x, y, weights[y], scanLine) 148 resample(dst, x, y, weights[y], scanLine)
149 }
135 } 150 }
136 } 151 }
137 drain() 152 drain()
153 Log.d("Resizer", "vertical done")
138 return dst 154 return dst
139 } 155 }
140 } 156 }