changeset 7:9374d044a132 concur

Concurrency attempt, NOT WORKING.
author David Barts <n5jrn@me.com>
date Wed, 17 Feb 2021 07:24:26 -0800
parents e8059b166de1
children 884092efe31a
files app/src/main/java/com/bartsent/simpleresizer/lib/Channel.kt app/src/main/java/com/bartsent/simpleresizer/lib/Resizer.kt app/src/main/java/com/bartsent/simpleresizer/lib/getScaledInstance.kt
diffstat 3 files changed, 190 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/src/main/java/com/bartsent/simpleresizer/lib/Channel.kt	Wed Feb 17 07:24:26 2021 -0800
@@ -0,0 +1,41 @@
+package com.bartsent.simpleresizer.lib
+
+import java.util.concurrent.Semaphore
+
+/**
+ * A typed, buffered communications channel a'la C.A.R. Hoare's communicating sequential
+ * processes (CSP).
+ */
+class Channel<T: Any>(capacity: Int) {
+    private val buffer = Array<Any?>(capacity) { null }
+    private var start = 0
+    private val wSem = Semaphore(capacity)
+    private val rSem = Semaphore(0)
+
+    /**
+     * Write a single item to this channel.
+     * @param item      Item to write
+     */
+    fun write(item: T): Unit {
+        wSem.acquire()
+        synchronized(this) {
+            buffer[(start + rSem.availablePermits()) % buffer.size] = item
+            rSem.release()
+        }
+    }
+
+    /**
+     * Read a single item from this channel.
+     * @return          The item read
+     */
+    fun read(): T {
+        rSem.acquire()
+        synchronized(this) {
+            val ret = buffer[start]!! as T
+            buffer[start] = null // unref
+            start = (start + 1) % buffer.size
+            wSem.release()
+            return ret
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/src/main/java/com/bartsent/simpleresizer/lib/Resizer.kt	Wed Feb 17 07:24:26 2021 -0800
@@ -0,0 +1,140 @@
+package com.bartsent.simpleresizer.lib
+
+import android.graphics.Bitmap
+import android.graphics.Color
+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 {
+        for (i in 0 until NWORKERS)
+            workers.write(Worker(workers))
+    }
+
+    override fun close() {
+        for (i in 0 until NWORKERS)
+            workers.read().run {
+                interrupt()
+                join()
+            }
+    }
+
+    private fun drain(): Unit {
+        val saved = Array<Worker>(NWORKERS) { workers.read() }
+        saved.forEach { workers.write(it) }
+    }
+
+    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 data class WorkUnit(val target: Bitmap, val x: Int, val y: Int,
+                                val weights: Array<IndexWeight>, val scanLine: IntArray)
+
+    private class Worker(private val workers: Channel<Worker>): Thread() {
+        private val _input = Channel<WorkUnit>(1)
+
+        override fun run() {
+            while (true) {
+                val data = _input.read()
+                if (isInterrupted)
+                    return
+                resample(data.target, data.x, data.y, data.weights, data.scanLine)
+                workers.write(this)
+            }
+        }
+
+        fun send(data: WorkUnit) {
+            _input.write(data)
+        }
+
+        private fun clamp(v: Double): Int = minOf(255, maxOf(0, (v + 0.5).toInt()))
+
+        private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit {
+            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)
+                return
+            val argb = Color.argb(clamp(a), clamp(r/a), clamp(g/a), clamp(b/a))
+            synchronized(target) {
+                target.setPixel(x, y, argb)
+            }
+        }
+    }
+
+    private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit {
+        workers.read().send(WorkUnit(target, x, y, weights, scanLine))
+    }
+
+    fun horizontal(image: Bitmap, newWidth: Int, kernel: ScalingKernel): Bitmap {
+        val dst = Bitmap.createBitmap(newWidth, image.height, Bitmap.Config.ARGB_8888)
+        val weights = precomputeWeights(newWidth, image.width, kernel)
+        val scanLine = IntArray(image.width)
+        for (y in 0 until image.height) {
+            for (x in 0 until image.width) {
+                scanLine[x] = image.getPixel(x, y)
+            }
+            for (x in weights.indices) {
+                resample(dst, x, y, weights[x], scanLine)
+            }
+        }
+        drain()
+        return dst
+    }
+
+    fun vertical(image: Bitmap, newHeight: Int, kernel: ScalingKernel): Bitmap {
+        val dst = Bitmap.createBitmap(image.width, newHeight, Bitmap.Config.ARGB_8888)
+        val weights = precomputeWeights(newHeight, image.height, kernel)
+        val scanLine = IntArray(image.height)
+        for (x in 0 until image.width) {
+            for (y in 0 until image.height) {
+                scanLine[y] = image.getPixel(x, y)
+            }
+            for (y in weights.indices) {
+                resample(dst, x, y, weights[y], scanLine)
+            }
+        }
+        drain()
+        return dst
+    }
+}
\ No newline at end of file
--- a/app/src/main/java/com/bartsent/simpleresizer/lib/getScaledInstance.kt	Tue Feb 16 17:29:52 2021 -0800
+++ b/app/src/main/java/com/bartsent/simpleresizer/lib/getScaledInstance.kt	Wed Feb 17 07:24:26 2021 -0800
@@ -2,10 +2,7 @@
 
 import android.graphics.Bitmap
 import android.graphics.Canvas
-import android.graphics.Color
 import android.graphics.Matrix
-import kotlin.math.ceil
-import kotlin.math.floor
 
 private data class IndexWeight(var index: Int, var weight: Double)
 
@@ -23,91 +20,14 @@
             Canvas(it).drawBitmap(this, Matrix(), null)
         }
     }
-    if (width != newWidth) {
-        if (height != newHeight)
-            return resizeVertical(resizeHorizontal(input, newWidth, kernel), newHeight, kernel)
-        else
-            return resizeHorizontal(input, newWidth, kernel)
-    } else {
-        return resizeVertical(input, newHeight, kernel)
+    Resizer().use { resize ->
+        if (width != newWidth) {
+            if (height != newHeight)
+                return resize.vertical(resize.horizontal(input, newWidth, kernel), newHeight, kernel)
+            else
+                return resize.horizontal(input, newWidth, kernel)
+        } else {
+            return resize.vertical(input, newHeight, kernel)
+        }
     }
 }
-
-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 fun clamp(v: Double): Int = minOf(255, maxOf(0, (v + 0.5).toInt()))
-
-private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit {
-    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)
-        return
-    target.setPixel(x, y, Color.argb(clamp(a), clamp(r/a), clamp(g/a), clamp(b/a)))
-}
-
-private fun resizeHorizontal(image: Bitmap, newWidth: Int, kernel: ScalingKernel): Bitmap {
-    val dst = Bitmap.createBitmap(newWidth, image.height, Bitmap.Config.ARGB_8888)
-    val weights = precomputeWeights(newWidth, image.width, kernel)
-    val scanLine = IntArray(image.width)
-    for (y in 0 until image.height) {
-        for (x in 0 until image.width) {
-            scanLine[x] = image.getPixel(x, y)
-        }
-        for (x in weights.indices) {
-            resample(dst, x, y, weights[x], scanLine)
-        }
-    }
-    return dst
-}
-
-private fun resizeVertical(image: Bitmap, newHeight: Int, kernel: ScalingKernel): Bitmap {
-    val dst = Bitmap.createBitmap(image.width, newHeight, Bitmap.Config.ARGB_8888)
-    val weights = precomputeWeights(newHeight, image.height, kernel)
-    val scanLine = IntArray(image.height)
-    for (x in 0 until image.width) {
-        for (y in 0 until image.height) {
-            scanLine[y] = image.getPixel(x, y)
-        }
-        for (y in weights.indices) {
-            resample(dst, x, y, weights[y], scanLine)
-        }
-    }
-    return dst
-}