Mercurial > cgi-bin > hgweb.cgi > SimpleResizer
comparison app/src/main/java/com/bartsent/simpleresizer/lib/Resizer.kt @ 7:9374d044a132 concur
Concurrency attempt, NOT WORKING.
author | David Barts <n5jrn@me.com> |
---|---|
date | Wed, 17 Feb 2021 07:24:26 -0800 |
parents | |
children | 884092efe31a |
comparison
equal
deleted
inserted
replaced
6:e8059b166de1 | 7:9374d044a132 |
---|---|
1 package com.bartsent.simpleresizer.lib | |
2 | |
3 import android.graphics.Bitmap | |
4 import android.graphics.Color | |
5 import java.io.Closeable | |
6 import kotlin.math.ceil | |
7 import kotlin.math.floor | |
8 | |
9 class Resizer: Closeable { | |
10 private val NWORKERS = maxOf(1, Runtime.getRuntime().availableProcessors() - 2) | |
11 private val workers = Channel<Worker>(NWORKERS) | |
12 init { | |
13 for (i in 0 until NWORKERS) | |
14 workers.write(Worker(workers)) | |
15 } | |
16 | |
17 override fun close() { | |
18 for (i in 0 until NWORKERS) | |
19 workers.read().run { | |
20 interrupt() | |
21 join() | |
22 } | |
23 } | |
24 | |
25 private fun drain(): Unit { | |
26 val saved = Array<Worker>(NWORKERS) { workers.read() } | |
27 saved.forEach { workers.write(it) } | |
28 } | |
29 | |
30 private data class IndexWeight(var index: Int, var weight: Double) | |
31 | |
32 private fun precomputeWeights(dstSize: Int, srcSize: Int, filter: ScalingKernel): Array<Array<IndexWeight>> { | |
33 val du = srcSize.toDouble() / dstSize.toDouble() | |
34 val scale = maxOf(1.0, du) | |
35 val ru = ceil(scale * filter.size) | |
36 val TEMPLATE = arrayOf<IndexWeight>() | |
37 val out = Array<Array<IndexWeight>>(dstSize) { TEMPLATE } | |
38 val tmp = ArrayList<IndexWeight>((ru.toInt()+2)*2) | |
39 val emax = srcSize - 1 | |
40 | |
41 for (v in 0 until dstSize) { | |
42 val fu = (v.toDouble()+0.5)*du - 0.5 | |
43 val begin = maxOf(0, ceil(fu - ru).toInt()) | |
44 val end = minOf(emax, floor(fu + ru).toInt()) | |
45 var sum: Double = 0.0 | |
46 for (u in begin..end) { | |
47 val w = filter.weight((u.toDouble() - fu) / scale) | |
48 if (w != 0.0) { | |
49 sum += w | |
50 tmp.add(IndexWeight(u, w)) | |
51 } | |
52 } | |
53 if (sum != 0.0) { | |
54 tmp.forEach { | |
55 it.weight /= sum | |
56 } | |
57 } | |
58 out[v] = tmp.toArray(TEMPLATE) | |
59 tmp.clear() | |
60 } | |
61 return out | |
62 } | |
63 | |
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() { | |
68 private val _input = Channel<WorkUnit>(1) | |
69 | |
70 override fun run() { | |
71 while (true) { | |
72 val data = _input.read() | |
73 if (isInterrupted) | |
74 return | |
75 resample(data.target, data.x, data.y, data.weights, data.scanLine) | |
76 workers.write(this) | |
77 } | |
78 } | |
79 | |
80 fun send(data: WorkUnit) { | |
81 _input.write(data) | |
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 } | |
103 } | |
104 | |
105 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)) | |
107 } | |
108 | |
109 fun horizontal(image: Bitmap, newWidth: Int, kernel: ScalingKernel): Bitmap { | |
110 val dst = Bitmap.createBitmap(newWidth, image.height, Bitmap.Config.ARGB_8888) | |
111 val weights = precomputeWeights(newWidth, image.width, kernel) | |
112 val scanLine = IntArray(image.width) | |
113 for (y in 0 until image.height) { | |
114 for (x in 0 until image.width) { | |
115 scanLine[x] = image.getPixel(x, y) | |
116 } | |
117 for (x in weights.indices) { | |
118 resample(dst, x, y, weights[x], scanLine) | |
119 } | |
120 } | |
121 drain() | |
122 return dst | |
123 } | |
124 | |
125 fun vertical(image: Bitmap, newHeight: Int, kernel: ScalingKernel): Bitmap { | |
126 val dst = Bitmap.createBitmap(image.width, newHeight, Bitmap.Config.ARGB_8888) | |
127 val weights = precomputeWeights(newHeight, image.height, kernel) | |
128 val scanLine = IntArray(image.height) | |
129 for (x in 0 until image.width) { | |
130 for (y in 0 until image.height) { | |
131 scanLine[y] = image.getPixel(x, y) | |
132 } | |
133 for (y in weights.indices) { | |
134 resample(dst, x, y, weights[y], scanLine) | |
135 } | |
136 } | |
137 drain() | |
138 return dst | |
139 } | |
140 } |