diff --git a/core/src/com/unciv/ui/components/fonts/FontRulesetIcons.kt b/core/src/com/unciv/ui/components/fonts/FontRulesetIcons.kt index f3df0f7686..8fcf3165dd 100644 --- a/core/src/com/unciv/ui/components/fonts/FontRulesetIcons.kt +++ b/core/src/com/unciv/ui/components/fonts/FontRulesetIcons.kt @@ -116,8 +116,12 @@ object FontRulesetIcons { fun getPixmapFromActor(actor: Actor): Pixmap { val (boxWidth, boxHeight) = scaleAndPositionActor(actor) + return getPixmapFromActorBase(actor, boxWidth, boxHeight) + } + + // Also required for dynamically generating pixmaps for pixmappacker + fun getPixmapFromActorBase(actor: Actor, boxWidth: Int, boxHeight: Int): Pixmap { val pixmap = Pixmap(boxWidth, boxHeight, Pixmap.Format.RGBA8888) - frameBuffer.begin() Gdx.gl.glClearColor(0f,0f,0f,0f) diff --git a/core/src/com/unciv/ui/components/tilegroups/YieldGroup.kt b/core/src/com/unciv/ui/components/tilegroups/YieldGroup.kt index 37b12920a8..0f1604c627 100644 --- a/core/src/com/unciv/ui/components/tilegroups/YieldGroup.kt +++ b/core/src/com/unciv/ui/components/tilegroups/YieldGroup.kt @@ -4,12 +4,14 @@ import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.g2d.Batch import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup +import com.badlogic.gdx.scenes.scene2d.ui.Image import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Align import com.unciv.models.stats.Stats import com.unciv.models.translations.tr import com.unciv.ui.images.ImageGetter import com.unciv.ui.components.extensions.addToCenter +import com.unciv.ui.components.extensions.setSize import com.unciv.ui.components.extensions.surroundWithCircle import com.unciv.ui.components.extensions.toLabel @@ -24,6 +26,7 @@ class YieldGroup : HorizontalGroup() { if (currentStats.equals(stats)) return // don't need to update - this is a memory and time saver! currentStats = stats clearChildren() + for ((stat, amount) in stats) { if (amount > 0f) // Defense against upstream bugs - negatives would show as "lots" addActor(getStatIconsTable(stat.name, amount.toInt())) @@ -32,33 +35,31 @@ class YieldGroup : HorizontalGroup() { } fun getIcon(statName: String) = - ImageGetter.getStatIcon(statName).surroundWithCircle(12f) - .apply { circle.color = ImageGetter.CHARCOAL; circle.color.a = 0.5f } + Image(ImageGetter.getStatWithBackground(statName)) private fun getStatIconsTable(statName: String, number: Int): Table { val table = Table() when (number) { - 1 -> table.add(getIcon(statName)) + 1 -> table.add(getIcon(statName)).size(12f) 2 -> { - table.add(getIcon(statName)).row() - table.add(getIcon(statName)) + table.add(getIcon(statName)).size(12f).row() + table.add(getIcon(statName)).size(12f) } 3 -> { - table.add(getIcon(statName)).colspan(2).row() - table.add(getIcon(statName)) - table.add(getIcon(statName)) + table.add(getIcon(statName)).size(12f).colspan(2).row() + table.add(getIcon(statName)).size(12f) + table.add(getIcon(statName)).size(12f) } 4 -> { - table.add(getIcon(statName)) - table.add(getIcon(statName)).row() - table.add(getIcon(statName)) - table.add(getIcon(statName)) + table.add(getIcon(statName)).size(12f) + table.add(getIcon(statName)).size(12f).row() + table.add(getIcon(statName)).size(12f) + table.add(getIcon(statName)).size(12f) } else -> { val group = Group().apply { setSize(22f, 22f) } - val largeImage = ImageGetter.getStatIcon(statName).surroundWithCircle(22f) - .apply { circle.color = ImageGetter.CHARCOAL;circle.color.a = 0.5f } + val largeImage = getIcon(statName).apply { setSize(22f) } group.addToCenter(largeImage) if (number > 5) { diff --git a/core/src/com/unciv/ui/images/IconCircleGroup.kt b/core/src/com/unciv/ui/images/IconCircleGroup.kt index 6c1b1bad56..6644c990bf 100644 --- a/core/src/com/unciv/ui/images/IconCircleGroup.kt +++ b/core/src/com/unciv/ui/images/IconCircleGroup.kt @@ -3,8 +3,8 @@ package com.unciv.ui.images import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.g2d.Batch import com.badlogic.gdx.scenes.scene2d.Actor +import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.utils.Align -import com.unciv.ui.components.NonTransformGroup import com.unciv.ui.components.extensions.center open class IconCircleGroup( @@ -13,7 +13,11 @@ open class IconCircleGroup( resizeActor: Boolean = true, color: Color = Color.WHITE, circleImage: String = "OtherIcons/Circle" -): NonTransformGroup() { +): Group() { // can't be nonTransformGroup because we need to dynamically pack yield images + + init { + isTransform = false + } val circle = ImageGetter.getImage(circleImage).apply { setSize(size, size) diff --git a/core/src/com/unciv/ui/images/ImageGetter.kt b/core/src/com/unciv/ui/images/ImageGetter.kt index ab42883a43..a6c1a25549 100644 --- a/core/src/com/unciv/ui/images/ImageGetter.kt +++ b/core/src/com/unciv/ui/images/ImageGetter.kt @@ -3,10 +3,12 @@ package com.unciv.ui.images import com.badlogic.gdx.Gdx import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture.TextureFilter import com.badlogic.gdx.graphics.g2d.Batch import com.badlogic.gdx.graphics.g2d.NinePatch +import com.badlogic.gdx.graphics.g2d.PixmapPacker import com.badlogic.gdx.graphics.g2d.TextureAtlas import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.scenes.scene2d.Actor @@ -25,6 +27,7 @@ import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.nation.Nation import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.skins.SkinCache +import com.unciv.models.stats.Stat import com.unciv.models.tilesets.TileSetCache import com.unciv.ui.components.NonTransformGroup import com.unciv.ui.components.extensions.center @@ -54,6 +57,30 @@ object ImageGetter { lateinit var atlas: TextureAtlas private val atlases = HashMap() var ruleset = Ruleset() + + // Performance improvement - "pack" the stat images together with the circle to the same texture + // This allows modded stat icons to not require texture swaps when rendering leading to it being ~50x faster + private var yieldPixmapPacker = PixmapPacker(2048, 2048, Pixmap.Format.RGBA8888, 2, false).apply { packToTexture = true } + private var yieldAtlas = yieldPixmapPacker.generateTextureAtlas(TextureFilter.MipMapLinearLinear, TextureFilter.MipMapLinearLinear, true) + fun getStatWithBackground(statName: String): TextureRegionDrawable? { + if (textureRegionDrawables.containsKey(statName)) return textureRegionDrawables[statName] + + for (stat in Stat.entries) { // pack all images + val actor = + getStatIcon(stat.name).surroundWithCircle(100f) + .apply { circle.color = CHARCOAL; circle.color.a = 0.5f } + .apply { isTransform = true; setScale(1f, -1f); setPosition(0f, height) } // flip Y axis + // By flipping the y axis when *generating the pixmap* we can ensure that when *rendering* we can have isTransform=false :) + yieldPixmapPacker.pack(stat.name, FontRulesetIcons.getPixmapFromActorBase(actor, 100, 100)) + } + yieldAtlas = yieldPixmapPacker.generateTextureAtlas(TextureFilter.MipMapLinearLinear, TextureFilter.MipMapLinearLinear, true) + for (region in yieldAtlas.regions) { + val drawable = TextureRegionDrawable(region) + textureRegionDrawables[region.name] = drawable + } + return textureRegionDrawables[statName] + } + // We then shove all the drawables into a hashmap, because the atlas specifically tells us // that the search on it is inefficient @@ -65,6 +92,8 @@ object ImageGetter { fun resetAtlases() { atlases.values.forEach { it.dispose() } atlases.clear() + yieldPixmapPacker.dispose() + yieldPixmapPacker = PixmapPacker(2048, 2048, Pixmap.Format.RGBA8888, 2, false).apply { packToTexture = true } } fun reloadImages() = setNewRuleset(ruleset)