LoginSignup
1
0

More than 3 years have passed since last update.

【Kotlin】面積当たりの容量が大きいpngを生成したかった【Java】

Last updated at Posted at 2020-05-30

やりたかったこと

とある案件で大量の重めの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でした。
負荷は高く実行時間はそれなりにかかったので、暇なときに回しておく位がちょうどいいと思います。

こんな感じの画像が生成されます。
0.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0