やりたかったこと
とある案件で大量の重めのpng
画像サンプルが必要になりました。
一方、単純に四角をfill
するような形で画像を生成した場合、圧縮されてしまって高々数KB程度になってしまうなど、容量が確保できませんでした。
必要な画像のフォーマットも決まっていたため、単純に面積を増やして容量を稼ぐことも難しかったです。
方針
圧縮というものは大概以下2点に当てはまる場合強く作用します。
- 同じパターンが連続する
- 同じ色が連続する
よって、「ランダムな色のランダムな線を沢山引けば圧縮しにくいだろう」というノリでやりました。
目標は1940 x 500のサイズで1枚2MBです。
それを達成していれば一旦構わないため、更なる大容量化や処理時間に関しては気にせずにやっていきます。
実装
${プロジェクトルート}/build/generated
ディレクトリに2000枚の画像を生成するサンプルです。
lines
が100000有れば安定して2MBを超えてくれました。
parallelStream()
しているのは「ちょっとは早くなるんじゃね」程度のノリで、実際に早くなってるのかは確認していません。
import java.awt.Color
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import kotlin.random.Random
const val width = 1940
const val height = 500
const val lines = 100000 // ライン数
const val count = 2000
fun main() {
val generateTarget = System.getProperty("user.dir") + "/build/generated/"
List(count) { it }.parallelStream().forEach { index ->
val img = BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR)
(img.graphics as Graphics2D).apply {
repeat(lines) {
// 色は少しでも大容量化することを狙って透過度も指定
this.color = Random.nextBytes(4).let {
Color(it[0] + 128, it[1] + 128, it[2] + 128, it[3] + 128)
}
this.drawLine(Random.nextInt(width), Random.nextInt(height), Random.nextInt(width), Random.nextInt(height))
}
dispose()
}
ImageIO.write(img, "png", File("$generateTarget${index}.png"))
}
}
実行結果
2000枚で4.81GB、平均2.4MBでした。
負荷は高く実行時間はそれなりにかかったので、暇なときに回しておく位がちょうどいいと思います。