diff --git a/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/GifSplitter.kt b/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/GifSplitter.kt new file mode 100644 index 0000000000..14bab5e4f4 --- /dev/null +++ b/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/GifSplitter.kt @@ -0,0 +1,116 @@ +package me.leon.toolsfx.plugin.ext + +import java.awt.* +import java.awt.image.BufferedImage +import java.io.File +import javax.imageio.ImageIO +import javax.imageio.metadata.IIOMetadataNode + +/** + * https://stackoverflow.com/questions/8933893/convert-each-animated-gif-frame-to-a-separate-bufferedimage + */ +fun File.splitGif(): String { + val outputDir = File(parent, nameWithoutExtension) + outputDir.mkdirs() + val reader = + ImageIO.getImageReadersByFormatName("gif").next().apply { + input = ImageIO.createImageInputStream(inputStream()) + } + + var size = Rectangle() + + val metadata = reader.streamMetadata + var backgroundColor: Color? = null + if (metadata != null) { + val globalRoot = metadata.getAsTree(metadata.nativeMetadataFormatName) as IIOMetadataNode + size = globalRoot.parseSize() + backgroundColor = globalRoot.parseBackground() + } + var masterImage: BufferedImage? = null + var frameIndex = 0 + + while (true) { + val image: BufferedImage = + try { + reader.read(frameIndex) + } catch (ignored: Exception) { + break + } + + if (size.isEmpty) { + size.width = image.width + size.height = image.height + } + val root = + reader.getImageMetadata(frameIndex).getAsTree("javax_imageio_gif_image_1.0") + as IIOMetadataNode + val children = root.childNodes + if (masterImage == null) { + masterImage = + BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB).also { + it.createGraphics().apply { + color = backgroundColor + fillRect(0, 0, size.width, size.height) + drawImage(image, 0, 0, null) + } + } + } else { + val currentPoint = Point() + for (nodeIndex in 0 until children.length) { + val nodeItem = children.item(nodeIndex) + if (nodeItem.nodeName.equals("ImageDescriptor")) { + val map = nodeItem.attributes + currentPoint.x = + Integer.valueOf(map.getNamedItem("imageLeftPosition").nodeValue) + currentPoint.y = Integer.valueOf(map.getNamedItem("imageTopPosition").nodeValue) + break + } + } + masterImage.createGraphics()?.drawImage(image, currentPoint.x, currentPoint.y, null) + ImageIO.write(masterImage, "PNG", File(outputDir, "$frameIndex.png")) + } + masterImage.flush() + frameIndex++ + } + + reader.dispose() + return "saved to ${outputDir.absolutePath}" +} + +private fun IIOMetadataNode.parseBackground(backgroundColor: Color? = null): Color? { + var tmp = backgroundColor + val globalColorTable = getElementsByTagName("GlobalColorTable") + if (globalColorTable.length > 0) { + val colorTable = globalColorTable.item(0) as IIOMetadataNode? + if (colorTable != null) { + val bgIndex = colorTable.getAttribute("backgroundColorIndex") + var colorEntry = colorTable.firstChild as IIOMetadataNode? + while (colorEntry != null) { + if (colorEntry.getAttribute("index") == bgIndex) { + tmp = colorEntry.color() + break + } + colorEntry = colorEntry.nextSibling as IIOMetadataNode + } + } + } + return tmp +} + +private fun IIOMetadataNode.parseSize(): Rectangle { + var rect = Rectangle() + val globalScreeDescriptor = getElementsByTagName("LogicalScreenDescriptor") + + if (globalScreeDescriptor.length > 0) { + val screenDescriptor = globalScreeDescriptor.item(0) as IIOMetadataNode + rect = + Rectangle( + screenDescriptor.getAttribute("logicalScreenWidth").toInt(), + screenDescriptor.getAttribute("logicalScreenHeight").toInt() + ) + } + return rect +} + +private fun IIOMetadataNode.color() = + Color(getAttribute("red").toInt(), getAttribute("green").toInt(), getAttribute("blue").toInt()) diff --git a/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/ImageServiceType.kt b/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/ImageServiceType.kt index 89000cd66d..f0977aa3eb 100644 --- a/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/ImageServiceType.kt +++ b/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/ImageServiceType.kt @@ -2,6 +2,7 @@ package me.leon.toolsfx.plugin.ext import me.leon.encode.base.base64 import me.leon.ext.fx.base64Image +import me.leon.ext.toFile enum class ImageServiceType(val type: String) : ImageService { FIX_PNG("fix png") { @@ -28,6 +29,10 @@ enum class ImageServiceType(val type: String) : ImageService { override fun process(raw: String, isFile: Boolean, params: Map) = raw.properString(isFile).rgb() }, + GIF_SPLIT("gif split") { + override fun process(raw: String, isFile: Boolean, params: Map) = + raw.toFile().splitGif() + }, } val serviceTypeMap = ImageServiceType.values().associateBy { it.type } diff --git a/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/Images.kt b/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/Images.kt index dbab46c4b7..aabd34714f 100644 --- a/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/Images.kt +++ b/plugin-image/src/main/kotlin/me/leon/toolsfx/plugin/ext/Images.kt @@ -1,14 +1,14 @@ package me.leon.toolsfx.plugin.ext +import java.awt.image.BufferedImage +import java.math.BigInteger import javafx.scene.image.Image import javafx.scene.paint.Color +import kotlin.math.sqrt import me.leon.ctf.rsa.factor import me.leon.ext.* import me.leon.ext.crypto.crc32 import me.leon.ext.fx.toFxImg -import java.awt.image.BufferedImage -import java.math.BigInteger -import kotlin.math.sqrt /** * @author Leon diff --git a/plugin-image/src/test/kotlin/me/leon/img/ImageFrame.kt b/plugin-image/src/test/kotlin/me/leon/img/ImageFrame.kt new file mode 100644 index 0000000000..d17b1f1d27 --- /dev/null +++ b/plugin-image/src/test/kotlin/me/leon/img/ImageFrame.kt @@ -0,0 +1,11 @@ +package me.leon.img + +import java.awt.image.BufferedImage + +data class ImageFrame( + val image: BufferedImage?, + val delay: Int, + val disposal: String?, + val width: Int, + val height: Int +) diff --git a/plugin-image/src/test/kotlin/me/leon/img/ImageTest.kt b/plugin-image/src/test/kotlin/me/leon/img/ImageTest.kt index 0b28c8a6ec..418a207b6d 100644 --- a/plugin-image/src/test/kotlin/me/leon/img/ImageTest.kt +++ b/plugin-image/src/test/kotlin/me/leon/img/ImageTest.kt @@ -1,6 +1,9 @@ package me.leon.img import kotlin.test.Test +import me.leon.ext.toFile +import me.leon.toolsfx.plugin.ext.splitGif +import org.junit.Ignore import tornadofx.* /** @@ -10,8 +13,14 @@ import tornadofx.* class ImageTest { @Test - // @Ignore + @Ignore fun base64() { launch
() } + + @Test + @Ignore + fun gifSplit() { + "E:\\gitrepo\\ToolsFx\\art\\ctf.gif".toFile().splitGif() + } } diff --git a/plugin-image/src/test/kotlin/me/leon/img/Main.kt b/plugin-image/src/test/kotlin/me/leon/img/Main.kt index ce7d3c9837..a36c48f47f 100644 --- a/plugin-image/src/test/kotlin/me/leon/img/Main.kt +++ b/plugin-image/src/test/kotlin/me/leon/img/Main.kt @@ -33,7 +33,7 @@ class Main : Application() { // val image = File(IMG_DIR,"qr01").readText().binaryImage(false) // val image = File(IMG_DIR, "bin2").readText().binaryImage() - val image = File("E:\\download\\360\\1-raw.png").fixPng().toImage() + val image = File("E:\\download\\360\\1-raw.png").readBytes().fixPng().toImage() // val image = File(IMG_DIR, "base64").readText().base64Image() // val text = File(IMG_DIR, "rgb.txt").readText()