Skip to content

Commit

Permalink
fix: make gif-offset configurable for non integer-aspect-ratios
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Oct 28, 2024
1 parent c41185c commit 25936ed
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 33 deletions.
3 changes: 0 additions & 3 deletions core/src/main/kotlin/com/mineinabyss/emojy/EmojyGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ object EmojyGenerator {

resourcePack.packMeta(48, "")

File("C:\\Users\\Sivert\\AppData\\Roaming\\ModrinthApp\\profiles\\MineInAbyss - 1.21\\resourcepacks\\pack").deleteRecursively()
MinecraftResourcePackWriter.minecraft().writeToDirectory(File("C:\\Users\\Sivert\\AppData\\Roaming\\ModrinthApp\\profiles\\MineInAbyss - 1.21\\resourcepacks\\pack"), resourcePack)

MinecraftResourcePackWriter.minecraft().writeToZipFile(emojy.plugin.dataFolder.resolve("pack.zip"), resourcePack)
}

Expand Down
48 changes: 18 additions & 30 deletions core/src/main/kotlin/com/mineinabyss/emojy/config/Gif.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.mineinabyss.emojy.EmojyGenerator.gifFolder
import com.mineinabyss.emojy.emojy
import com.mineinabyss.emojy.emojyConfig
import com.mineinabyss.emojy.spaceComponent
import com.mineinabyss.idofront.messaging.*
import com.mineinabyss.idofront.messaging.broadcastVal
import com.mineinabyss.idofront.serialization.KeySerializer
import com.mineinabyss.idofront.textcomponents.miniMsg
import com.mineinabyss.idofront.textcomponents.serialize
Expand All @@ -31,6 +31,7 @@ import java.awt.AlphaComposite
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import kotlin.math.absoluteValue
import kotlin.math.ceil
import kotlin.math.roundToInt
import kotlin.math.sqrt
Expand All @@ -39,14 +40,13 @@ import kotlin.properties.Delegates
@Serializable
data class Gif(
val id: String,
@Transient var bitmapRows: Int = 0,
@Transient var bitmapColumns: Int = 0,
@EncodeDefault(NEVER) var frameCount: Int = 0,
@EncodeDefault(NEVER) @SerialName("framePath") val _framePath: @Serializable(KeySerializer::class) Key = Key.key(
"${emojyConfig.defaultNamespace}:${emojyConfig.defaultFolder}/$id"
),
@EncodeDefault(NEVER) val ascent: Int = 8,
@EncodeDefault(NEVER) val height: Int = 8,
@EncodeDefault(NEVER) var offset: Int? = null,
@EncodeDefault(NEVER) val type: GifType = emojyConfig.defaultGifType
) {
@Transient val framePath = Key.key(_framePath.asString().removeSuffix("/"))
Expand Down Expand Up @@ -76,15 +76,16 @@ data class Gif(
}


private fun frameCount(): Int {
private fun calculateFramecount(): Int {
if (frameCount <= 0) frameCount = runCatching {
val reader = ImageIO.getImageReadersByFormatName("gif").next()
reader.input = ImageIO.createImageInputStream(gifFile)
aspectRatio = reader.getAspectRatio(0)
reader.getNumImages(true).also { frameCount ->
bitmapColumns = ceil(sqrt(frameCount.toDouble())).toInt()
bitmapRows = ceil(frameCount / bitmapColumns.toDouble()).toInt()
if (aspectRatio % 1 != 0f && offset == null) {
emojy.logger.w("AspectRatio for $id is not 1:1, and the offset (${-(height * aspectRatio).roundToInt() - 1}) between frames will likely be wrong")
emojy.logger.w("Either modify your GIF's resolution to have an aspect-ration of 1:1 or tweak the offset-property of your glyph")
}
reader.getNumImages(true)
}.onFailure {
emojy.logger.d("Could not get frame count for ${id}.gif")
}.getOrNull()?.apply { aspectRatio = 1f } ?: 0
Expand All @@ -94,19 +95,11 @@ data class Gif(

fun font() = Font.font(font, fontProvider(), gifAdvance())
private fun gifAdvance() =
FontProvider.space().advance(unicode(frameCount), -(ascent * aspectRatio).roundToInt())
FontProvider.space().advance(unicode(frameCount), offset ?:(-(height * aspectRatio).roundToInt() - 1))
.build()

private fun fontProvider(): FontProvider {
// Construct the `chars` mapping in `["xyz", "xyz", "xyz"]` format
val charsGrid = (0 until bitmapRows).map { row ->
(0 until bitmapColumns).joinToString("") { col ->
unicode((row * bitmapColumns + col).apply { if (id.contains("neco")) println(this) })
}
}

return FontProvider.bitMap(Key.key("${framePath.asString()}.png"), height, ascent, charsGrid)
}
private fun fontProvider() =
FontProvider.bitMap(Key.key("${framePath.asString()}.png"), height, ascent, (0 until frameCount).map(::unicode))

fun checkPermission(player: Player?) =
!emojyConfig.requirePermissions || player == null || player.hasPermission(permission)
Expand All @@ -126,36 +119,31 @@ data class Gif(
fun generateSplitGif(resourcePack: ResourcePack) {
runCatching {
val gifFolder = gifFolder.resolve(id)
gifFolder.deleteRecursively()

GifConverter.splitGif(gifFile, frameCount()) // Keep individual frame creation
GifConverter.splitGif(gifFile, calculateFramecount())
createSpritesheet(gifFolder)

Texture.texture(Key.key("${framePath.asString()}.png"), Writable.file(gifSpriteSheet)).addTo(resourcePack)
gifFolder.deleteRecursively()
}.onFailure {
emojy.logger.d("Could not generate split gif for ${id}.gif: ${it.message}")
}
}

private fun createSpritesheet(gifFolder: File) {
// List all frame files in the folder, ensuring they are sorted by filename
val frames = gifFolder.listFiles()
?.filter { it.isFile && it.extension == "png" }
?.sortedBy { it.nameWithoutExtension.toIntOrNull() } // Assumes frames are named in order (e.g., 1.png, 2.png)
?.sortedBy { it.nameWithoutExtension.toIntOrNull() }
?: throw IllegalStateException("No frame files found in $gifFolder")

// Determine dimensions based on the first frame
val (frameWidth, frameHeight) = ImageIO.read(frames.first()).let { it.width to it.height }

// Create the spritesheet image
val spritesheet = BufferedImage(bitmapColumns * frameWidth, bitmapRows * frameHeight, BufferedImage.TYPE_INT_ARGB)
val spritesheet = BufferedImage(frameWidth, frameCount * frameHeight, BufferedImage.TYPE_INT_ARGB)
val graphics = spritesheet.createGraphics()
graphics.composite = AlphaComposite.Src // Ensure correct alpha handling
graphics.composite = AlphaComposite.Src

frames.forEachIndexed { index, frameFile ->
val x = (index % bitmapColumns) * frameWidth
val y = (index / bitmapColumns) * frameHeight
graphics.drawImage(ImageIO.read(frameFile), x, y, null)
val y = index * frameHeight
graphics.drawImage(ImageIO.read(frameFile), 0, y, null)
}
graphics.dispose()

Expand Down

0 comments on commit 25936ed

Please sign in to comment.