Mercurial > cgi-bin > hgweb.cgi > SimpleResizer
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 } |