Mercurial > cgi-bin > hgweb.cgi > SimpleResizer
view 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 |
line wrap: on
line source
package com.bartsent.simpleresizer.lib import android.graphics.Bitmap import android.graphics.Color import android.util.Log import java.io.Closeable import kotlin.math.ceil import kotlin.math.floor class Resizer: Closeable { private val NWORKERS = maxOf(1, Runtime.getRuntime().availableProcessors() - 2) private val workers = Channel<Worker>(NWORKERS) init { Log.d("Resizer", "workers channel is ${workers.hashCode()}") Log.d("Resizer", "writing workers…") for (i in 0 until NWORKERS) workers.write(Worker(workers).apply { start() }) Log.d("Resizer", "writing done") } override fun close() { Log.d("Resizer", "closing, stack trace follows…", Exception("dummy exception")) for (i in 0 until NWORKERS) workers.read().run { interrupt() join() } Log.d("Resizer", "closing done") } private fun drain(): Unit { Log.d("Resizer", "draining workers…") val saved = Array<Worker>(NWORKERS) { workers.read() } saved.forEach { workers.write(it) } Log.d("Resizer", "draining done") } private data class IndexWeight(var index: Int, var weight: Double) private fun precomputeWeights(dstSize: Int, srcSize: Int, filter: ScalingKernel): Array<Array<IndexWeight>> { val du = srcSize.toDouble() / dstSize.toDouble() val scale = maxOf(1.0, du) val ru = ceil(scale * filter.size) val TEMPLATE = arrayOf<IndexWeight>() val out = Array<Array<IndexWeight>>(dstSize) { TEMPLATE } val tmp = ArrayList<IndexWeight>((ru.toInt()+2)*2) val emax = srcSize - 1 for (v in 0 until dstSize) { val fu = (v.toDouble()+0.5)*du - 0.5 val begin = maxOf(0, ceil(fu - ru).toInt()) val end = minOf(emax, floor(fu + ru).toInt()) var sum: Double = 0.0 for (u in begin..end) { val w = filter.weight((u.toDouble() - fu) / scale) if (w != 0.0) { sum += w tmp.add(IndexWeight(u, w)) } } if (sum != 0.0) { tmp.forEach { it.weight /= sum } } out[v] = tmp.toArray(TEMPLATE) tmp.clear() } return out } private class Worker(private val workers: Channel<Worker>): Thread() { private val _input = Channel<() -> Unit>(1) override fun run() { while (true) { val (block: () -> Unit, interrupted: Boolean) = try { Pair(_input.read(), isInterrupted) } catch (e: InterruptedException) { Pair({}, true) } if (interrupted) return block() workers.write(this) } } fun send(block: () -> Unit) { _input.write(block) } } private fun clamp(v: Double): Int = minOf(255, maxOf(0, (v + 0.5).toInt())) fun parallel(block: () -> Unit) = workers.read().send(block) private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit { // Log.d("Resizer", "resample: x=$x, y=$y") var r = 0.0; var g = 0.0; var b = 0.0; var a = 0.0 weights.forEach { val c = scanLine[it.index] val aw = Color.alpha(c).toDouble() * it.weight r += Color.red(c).toDouble() * aw g += Color.green(c).toDouble() * aw b += Color.blue(c).toDouble() * aw a += aw } if (a == 0.0) { Log.d("Resizer", "resample: a=0.0, done") return } val argb = Color.argb(clamp(a), clamp(r/a), clamp(g/a), clamp(b/a)) // Log.d("Resizer", "setting pixel…") synchronized(target) { target.setPixel(x, y, argb) } // Log.d("Resizer", "resample: a=$a, done") } fun horizontal(image: Bitmap, newWidth: Int, kernel: ScalingKernel): Bitmap { Log.d("Resizer", "horizontal…") val dst = Bitmap.createBitmap(newWidth, image.height, Bitmap.Config.ARGB_8888) val weights = precomputeWeights(newWidth, image.width, kernel) for (y in 0 until image.height) { val scanLine = IntArray(image.width) { image.getPixel(it, y) } parallel { Log.d("Resizer", "horizontal: y=$y") for (x in weights.indices) { resample(dst, x, y, weights[x], scanLine) } } } drain() Log.d("Resizer", "horizontal done") return dst } fun vertical(image: Bitmap, newHeight: Int, kernel: ScalingKernel): Bitmap { Log.d("Resizer", "vertical…") val dst = Bitmap.createBitmap(image.width, newHeight, Bitmap.Config.ARGB_8888) val weights = precomputeWeights(newHeight, image.height, kernel) for (x in 0 until image.width) { val scanLine = IntArray(image.height) { image.getPixel(x, it) } parallel { Log.d("Resizer", "vertical: x=$x") for (y in weights.indices) { resample(dst, x, y, weights[y], scanLine) } } } drain() Log.d("Resizer", "vertical done") return dst } }