Mercurial > cgi-bin > hgweb.cgi > SimpleResizer
comparison app/src/main/java/com/bartsent/simpleresizer/lib/getScaledInstance.kt @ 13:b1605be35bcc memo.oo
Dumping Bitmap yields 2x improvement!
author | David Barts <n5jrn@me.com> |
---|---|
date | Thu, 18 Feb 2021 14:15:26 -0800 |
parents | e8059b166de1 |
children | 20da616dcda0 |
comparison
equal
deleted
inserted
replaced
11:678adef4774f | 13:b1605be35bcc |
---|---|
7 import kotlin.math.ceil | 7 import kotlin.math.ceil |
8 import kotlin.math.floor | 8 import kotlin.math.floor |
9 | 9 |
10 private data class IndexWeight(var index: Int, var weight: Double) | 10 private data class IndexWeight(var index: Int, var weight: Double) |
11 | 11 |
12 fun Bitmap.getScaledInstance(newWidth: Int, newHeight: Int, kernel: ScalingKernel = LanczosKernel): Bitmap { | 12 fun Bitmap.getScaledInstance(newWidth: Int, newHeight: Int): Bitmap { |
13 if (newWidth <= 0) | 13 if (newWidth <= 0) |
14 throw IllegalArgumentException("invalid width: $newWidth") | 14 throw IllegalArgumentException("invalid width: $newWidth") |
15 if (newHeight <= 0) | 15 if (newHeight <= 0) |
16 throw IllegalArgumentException("invalid height: $newHeight") | 16 throw IllegalArgumentException("invalid height: $newHeight") |
17 if (width == newWidth && height == newHeight) | 17 if (width == newWidth && height == newHeight) |
18 return Bitmap.createBitmap(this) | 18 return Bitmap.createBitmap(this) |
19 val input = if (config == Bitmap.Config.ARGB_8888) | 19 return if (width != newWidth) { |
20 this | 20 Resizer.fromBitmap(this).horizontal(newWidth).let { |
21 else { | 21 if (height == newHeight) it else it.vertical(newHeight) |
22 Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { | |
23 Canvas(it).drawBitmap(this, Matrix(), null) | |
24 } | 22 } |
25 } | |
26 if (width != newWidth) { | |
27 if (height != newHeight) | |
28 return resizeVertical(resizeHorizontal(input, newWidth, kernel), newHeight, kernel) | |
29 else | |
30 return resizeHorizontal(input, newWidth, kernel) | |
31 } else { | 23 } else { |
32 return resizeVertical(input, newHeight, kernel) | 24 Resizer.fromBitmap(this).vertical(newHeight) |
33 } | 25 } .toBitmap() |
34 } | 26 } |
35 | |
36 private fun precomputeWeights(dstSize: Int, srcSize: Int, filter: ScalingKernel): Array<Array<IndexWeight>> { | |
37 val du = srcSize.toDouble() / dstSize.toDouble() | |
38 val scale = maxOf(1.0, du) | |
39 val ru = ceil(scale * filter.size) | |
40 val TEMPLATE = arrayOf<IndexWeight>() | |
41 val out = Array<Array<IndexWeight>>(dstSize) { TEMPLATE } | |
42 val tmp = ArrayList<IndexWeight>((ru.toInt()+2)*2) | |
43 val emax = srcSize - 1 | |
44 | |
45 for (v in 0 until dstSize) { | |
46 val fu = (v.toDouble()+0.5)*du - 0.5 | |
47 val begin = maxOf(0, ceil(fu - ru).toInt()) | |
48 val end = minOf(emax, floor(fu + ru).toInt()) | |
49 var sum: Double = 0.0 | |
50 for (u in begin..end) { | |
51 val w = filter.weight((u.toDouble() - fu) / scale) | |
52 if (w != 0.0) { | |
53 sum += w | |
54 tmp.add(IndexWeight(u, w)) | |
55 } | |
56 } | |
57 if (sum != 0.0) { | |
58 tmp.forEach { | |
59 it.weight /= sum | |
60 } | |
61 } | |
62 out[v] = tmp.toArray(TEMPLATE) | |
63 tmp.clear() | |
64 } | |
65 return out | |
66 } | |
67 | |
68 private fun clamp(v: Double): Int = minOf(255, maxOf(0, (v + 0.5).toInt())) | |
69 | |
70 private fun resample(target: Bitmap, x: Int, y: Int, weights: Array<IndexWeight>, scanLine: IntArray): Unit { | |
71 var r = 0.0; var g = 0.0; var b = 0.0; var a = 0.0 | |
72 weights.forEach { | |
73 val c = scanLine[it.index] | |
74 val aw = Color.alpha(c).toDouble() * it.weight | |
75 r += Color.red(c).toDouble() * aw | |
76 g += Color.green(c).toDouble() * aw | |
77 b += Color.blue(c).toDouble() * aw | |
78 a += aw | |
79 } | |
80 if (a == 0.0) | |
81 return | |
82 target.setPixel(x, y, Color.argb(clamp(a), clamp(r/a), clamp(g/a), clamp(b/a))) | |
83 } | |
84 | |
85 private fun resizeHorizontal(image: Bitmap, newWidth: Int, kernel: ScalingKernel): Bitmap { | |
86 val dst = Bitmap.createBitmap(newWidth, image.height, Bitmap.Config.ARGB_8888) | |
87 val weights = precomputeWeights(newWidth, image.width, kernel) | |
88 val scanLine = IntArray(image.width) | |
89 for (y in 0 until image.height) { | |
90 for (x in 0 until image.width) { | |
91 scanLine[x] = image.getPixel(x, y) | |
92 } | |
93 for (x in weights.indices) { | |
94 resample(dst, x, y, weights[x], scanLine) | |
95 } | |
96 } | |
97 return dst | |
98 } | |
99 | |
100 private fun resizeVertical(image: Bitmap, newHeight: Int, kernel: ScalingKernel): Bitmap { | |
101 val dst = Bitmap.createBitmap(image.width, newHeight, Bitmap.Config.ARGB_8888) | |
102 val weights = precomputeWeights(newHeight, image.height, kernel) | |
103 val scanLine = IntArray(image.height) | |
104 for (x in 0 until image.width) { | |
105 for (y in 0 until image.height) { | |
106 scanLine[y] = image.getPixel(x, y) | |
107 } | |
108 for (y in weights.indices) { | |
109 resample(dst, x, y, weights[y], scanLine) | |
110 } | |
111 } | |
112 return dst | |
113 } |