diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/asset/model/Asset.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/asset/model/Asset.kt index 0e63926f..48114882 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/asset/model/Asset.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/asset/model/Asset.kt @@ -15,4 +15,8 @@ abstract class Asset(directory: ObjectProperty, val uid: String, val sourc val file by fileProperty override fun toString() = "${this.javaClass.simpleName}[name=$name, uid=$uid]" + + open fun delete() { + file.delete() + } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/asset/model/GraphicAsset.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/asset/model/GraphicAsset.kt new file mode 100644 index 00000000..534efb93 --- /dev/null +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/asset/model/GraphicAsset.kt @@ -0,0 +1,27 @@ +package com.bartlomiejpluta.base.editor.asset.model + +import javafx.beans.binding.Bindings.createObjectBinding +import javafx.beans.property.ObjectProperty +import tornadofx.getValue +import java.io.File + +abstract class GraphicAsset( + assetDirectory: ObjectProperty, + graphicDirectory: ObjectProperty, + uid: String, + assetSource: String, + val graphicSource: String, + name: String +) : Asset(assetDirectory, uid, assetSource, name) { + + constructor(directory: ObjectProperty, uid: String, source: String, name: String) + : this(directory, directory, uid, source, source, name) + + val graphicFileProperty = createObjectBinding({ File(graphicDirectory.value, graphicSource) }, graphicDirectory) + val graphicFile by graphicFileProperty + + override fun delete() { + super.delete() + graphicFile.delete() + } +} \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/image/asset/ImageAsset.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/image/asset/ImageAsset.kt index 1354d501..d8e6b251 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/image/asset/ImageAsset.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/image/asset/ImageAsset.kt @@ -1,7 +1,7 @@ package com.bartlomiejpluta.base.editor.image.asset -import com.bartlomiejpluta.base.editor.asset.model.Asset +import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset import com.bartlomiejpluta.base.editor.project.model.Project class ImageAsset(project: Project, uid: String, source: String, name: String) : - Asset(project.imagesDirectoryProperty, uid, source, name) \ No newline at end of file + GraphicAsset(project.imagesDirectoryProperty, uid, source, name) \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/asset/GameMapAsset.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/asset/GameMapAsset.kt index 59b15954..d88d5754 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/asset/GameMapAsset.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/asset/GameMapAsset.kt @@ -1,7 +1,7 @@ package com.bartlomiejpluta.base.editor.map.asset -import com.bartlomiejpluta.base.editor.asset.model.Asset +import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset import com.bartlomiejpluta.base.editor.project.model.Project class GameMapAsset(project: Project, uid: String, name: String) : - Asset(project.mapsDirectoryProperty, uid, "$uid.dat", name) \ No newline at end of file + GraphicAsset(project.mapsDirectoryProperty, project.mapsDirectoryProperty, uid, "$uid.dat", "$uid.png", name) \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapCanvas.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapCanvas.kt index e04f5bd9..66ff2e0c 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapCanvas.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/canvas/MapCanvas.kt @@ -70,6 +70,11 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr override fun render(gc: GraphicsContext) { gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height) + if (editorStateVM.takingSnapshot) { + renderForPhoto(gc) + return + } + renderBackground(gc) renderUnderlyingLayers(gc) renderCover(gc) @@ -78,12 +83,16 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr painter.render(gc) } + private fun renderForPhoto(gc: GraphicsContext) { + map.layers.forEach { dispatchLayerRender(gc, it) } + } + private fun renderSelectedLayer(gc: GraphicsContext) { map.layers.getOrNull(editorStateVM.selectedLayerIndex)?.let { dispatchLayerRender(gc, it) } } private fun renderCover(gc: GraphicsContext) { - if(!editorStateVM.coverUnderlyingLayers) { + if (!editorStateVM.coverUnderlyingLayers) { return } diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/controller/MapController.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/controller/MapController.kt index edd3e95c..3a2496b2 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/controller/MapController.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/controller/MapController.kt @@ -2,6 +2,7 @@ package com.bartlomiejpluta.base.editor.map.controller import com.bartlomiejpluta.base.editor.map.model.map.GameMap import com.bartlomiejpluta.base.editor.project.context.ProjectContext +import javafx.scene.image.Image import org.springframework.stereotype.Component import tornadofx.Controller @@ -9,7 +10,7 @@ import tornadofx.Controller class MapController : Controller() { private val projectContext: ProjectContext by di() - fun saveMap(map: GameMap) { - projectContext.saveMap(map) + fun saveMap(map: GameMap, image: Image) { + projectContext.saveMap(map, image) } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapFragment.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapFragment.kt index e06ee45c..235a85b9 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapFragment.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapFragment.kt @@ -1,8 +1,10 @@ package com.bartlomiejpluta.base.editor.map.view.editor import com.bartlomiejpluta.base.editor.command.context.UndoableScope +import com.bartlomiejpluta.base.editor.map.controller.MapController import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM +import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM import com.bartlomiejpluta.base.editor.tileset.view.editor.TileSetView import javafx.beans.binding.Bindings import tornadofx.* @@ -11,12 +13,15 @@ import tornadofx.* class MapFragment : Fragment() { override val scope = super.scope as UndoableScope + private val mapController: MapController by di() + private val editorStateVM = find() + private val mapVM = find() private val mapView = find() private val layersView = find() private val tileSetView = find() - private val toolbarView = find() + private val toolbarView = find(MapToolbarView::onSaveMap to this::saveMap) private val statusBarView = find() private val layerParameters = find() @@ -25,6 +30,10 @@ class MapFragment : Fragment() { editorStateVM.selectedLayerProperty ) + private fun saveMap() { + mapController.saveMap(mapVM.item, mapView.snapshot()) + } + override val root = borderpane { top = toolbarView.root diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapToolbarView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapToolbarView.kt index e7fda69e..ed72f249 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapToolbarView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapToolbarView.kt @@ -19,6 +19,8 @@ class MapToolbarView : View() { private val undoRedoService: UndoRedoService by di() private val mapController: MapController by di() + val onSaveMap: () -> Unit by param() + override val scope = super.scope as UndoableScope private val mapVM = find() @@ -45,7 +47,7 @@ class MapToolbarView : View() { button(graphic = FontIcon("fa-floppy-o")) { shortcut("Ctrl+S") action { - mapController.saveMap(mapVM.item) + onSaveMap() } } diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapView.kt index 8e4b86e5..a08c9213 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/view/editor/MapView.kt @@ -7,6 +7,7 @@ import com.bartlomiejpluta.base.editor.map.component.MapPane import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM +import javafx.scene.image.Image import javafx.scene.input.MouseButton import javafx.scene.input.MouseEvent import javafx.scene.transform.Scale @@ -41,6 +42,16 @@ class MapView : View() { subscribe { mapPane.render() } } + fun snapshot(): Image { + editorStateVM.takingSnapshot = true + mapPane.render() + + return mapPane.snapshot(null, null).also { + editorStateVM.takingSnapshot = false + mapPane.render() + } + } + override val root = scrollpane { prefWidth = 640.0 prefHeight = 480.0 diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/EditorStateVM.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/EditorStateVM.kt index 9cd79882..ba7386ce 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/EditorStateVM.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/map/viewmodel/EditorStateVM.kt @@ -30,4 +30,7 @@ class EditorStateVM : ViewModel() { val cursorColumnProperty = SimpleIntegerProperty(-1) val cursorColumn by cursorColumnProperty + + val takingSnapshotProperty = SimpleBooleanProperty(false) + var takingSnapshot by takingSnapshotProperty } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt index 4234ff68..03b574ae 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt @@ -13,6 +13,7 @@ import com.bartlomiejpluta.base.editor.project.serial.ProjectSerializer import com.bartlomiejpluta.base.editor.tileset.asset.TileSetAsset import com.bartlomiejpluta.base.editor.tileset.asset.TileSetAssetData import com.bartlomiejpluta.base.editor.tileset.model.TileSet +import com.bartlomiejpluta.base.editor.util.fx.ImageUtil import com.bartlomiejpluta.base.editor.util.uid.UID import javafx.beans.property.ObjectProperty import javafx.beans.property.SimpleObjectProperty @@ -24,6 +25,7 @@ import tornadofx.setValue import java.io.File import java.io.FileInputStream import java.io.FileOutputStream +import kotlin.math.min @Component class DefaultProjectContext : ProjectContext { @@ -88,11 +90,14 @@ class DefaultProjectContext : ProjectContext { File(it.mapsDirectory, asset.source).inputStream().use { fis -> mapDeserializer.deserialize(fis) } } ?: throw IllegalStateException("There is no open project in the context") - override fun saveMap(map: GameMap) { + override fun saveMap(map: GameMap, image: Image) { project?.let { val asset = it.maps.firstOrNull { asset -> asset.uid == map.uid } ?: throw IllegalStateException("The map with uid [${map.uid}] does not exist ") + val thumbnail = ImageUtil.scale(image, min(200, map.width.toInt()), min(200, map.height.toInt()), true) + + ImageUtil.save(thumbnail, asset.graphicFile) File(it.mapsDirectory, asset.source).outputStream().use { fos -> mapSerializer.serialize(map, fos) } } } @@ -151,7 +156,7 @@ class DefaultProjectContext : ProjectContext { it.assetLists.firstOrNull { assets -> assets.remove(asset) } ?: throw IllegalStateException("The asset does not belong to any of the assets lists") - asset.file.delete() + asset.delete() } ?: throw IllegalStateException("There is no open project in the context") } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt index a213f333..f23ee9df 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt @@ -20,7 +20,7 @@ interface ProjectContext { fun importMap(name: String, map: GameMap) fun loadMap(uid: String): GameMap - fun saveMap(map: GameMap) + fun saveMap(map: GameMap, image: Image) fun importTileSet(data: TileSetAssetData) fun loadTileSet(uid: String): TileSet diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/asset/TileSetAsset.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/asset/TileSetAsset.kt index 31759808..2d33c1f0 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/asset/TileSetAsset.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/tileset/asset/TileSetAsset.kt @@ -1,7 +1,7 @@ package com.bartlomiejpluta.base.editor.tileset.asset -import com.bartlomiejpluta.base.editor.asset.model.Asset +import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset import com.bartlomiejpluta.base.editor.project.model.Project class TileSetAsset(project: Project, uid: String, source: String, name: String, val rows: Int, val columns: Int) : - Asset(project.tileSetsDirectoryProperty, uid, source, name) + GraphicAsset(project.tileSetsDirectoryProperty, uid, source, name) diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/util/fx/ImageUtil.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/util/fx/ImageUtil.kt new file mode 100644 index 00000000..293ae331 --- /dev/null +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/util/fx/ImageUtil.kt @@ -0,0 +1,28 @@ +package com.bartlomiejpluta.base.editor.util.fx + +import javafx.scene.image.Image +import javafx.scene.image.ImageView +import java.awt.image.BufferedImage +import java.io.File +import javax.imageio.ImageIO + +object ImageUtil { + fun scale(source: Image, targetWidth: Int, targetHeight: Int, preserveRatio: Boolean) = ImageView(source).apply { + this.isSmooth = false + this.isPreserveRatio = preserveRatio + this.fitWidth = targetWidth.toDouble() + this.fitHeight = targetHeight.toDouble() + }.snapshot(null, null) + + fun save(image: Image, file: File) { + val buffered = BufferedImage(image.width.toInt(), image.height.toInt(), BufferedImage.TYPE_INT_ARGB) + val reader = image.pixelReader + for (x in 0 until image.width.toInt()) { + for (y in 0 until image.height.toInt()) { + buffered.setRGB(x, y, reader.getArgb(x, y)) + } + } + + ImageIO.write(buffered, file.extension, file) + } +} \ No newline at end of file