Gridarta Editor
MapImageCreator.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2023 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.action.exportmap;
21 
22 import java.awt.Graphics2D;
23 import java.awt.Image;
24 import java.awt.image.BufferedImage;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.LinkedHashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Map.Entry;
49 import net.sf.gridarta.utils.Size2D;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
52 
56 public class MapImageCreator<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> {
57 
61  private static final int SCALE = 32;
62 
66  @NotNull
68 
72  @NotNull
74 
80  @NotNull
81  private final Map<MapFile, LayoutInfo> layoutInfo = new LinkedHashMap<>();
82 
88  @NotNull
89  private final Map<MapFile, PendingInfo> pending = new LinkedHashMap<>();
90 
95  @NotNull
96  private final Collection<MapFile> done = new HashSet<>();
97 
101  private int minX;
102 
106  private int minY;
107 
111  private int maxX;
112 
116  private int maxY;
117 
125  this.mapManager = mapManager;
126  this.rendererFactory = rendererFactory;
127  }
128 
136  @Nullable
137  public BufferedImage createImage(@NotNull final MapFile mapFile, final boolean includeTiledMaps, @NotNull final ExportMapProgress progress) {
138  progress.setMax(0);
139 
140  layoutInfo.clear();
141  pending.clear();
142  minX = 0;
143  minY = 0;
144  maxX = 1;
145  maxY = 1;
146  add(mapFile, includeTiledMaps, new PendingInfo(Direction.UP, 0, 0));
147  progress.setMax(done.size());
148 
149  while (true) {
150  final Iterator<Entry<MapFile, PendingInfo>> it = pending.entrySet().iterator();
151  if (!it.hasNext()) {
152  break;
153  }
154  final Entry<MapFile, PendingInfo> mapFile2 = it.next();
155  it.remove();
156 
157  add(mapFile2.getKey(), includeTiledMaps, mapFile2.getValue());
158  progress.setMax(done.size());
159  if (progress.cancel()) {
160  return null;
161  }
162  }
163 
164  final BufferedImage image = new BufferedImage((maxX - minX) / SCALE, (maxY - minY) / SCALE, BufferedImage.TYPE_INT_ARGB);
165  final Graphics2D graphics = image.createGraphics();
166  try {
167  final List<LayoutInfo> tmp = new ArrayList<>(layoutInfo.values());
168  Collections.shuffle(tmp);
169  int done = 0;
170  for (final LayoutInfo layoutInfo : tmp) {
171  final MapControl<G, A, R> mapControl;
172  try {
173  mapControl = mapManager.openMapFile(layoutInfo.getMapFile(), false);
174  } catch (final IOException ex) {
175  System.err.println("cannot load map: " + layoutInfo.getMapFile() + ": " + ex);
176  continue;
177  }
178  try {
179  final MapRenderer mapRenderer = rendererFactory.newSimpleMapRenderer(mapControl.getMapModel());
180  final BufferedImage tmp1Image = mapRenderer.getFullImage();
181  final Image tmp2Image = tmp1Image.getScaledInstance(tmp1Image.getWidth() / SCALE, tmp1Image.getHeight() / SCALE, Image.SCALE_AREA_AVERAGING);
182  graphics.drawImage(tmp2Image, (layoutInfo.getX() - minX) / SCALE, (layoutInfo.getY() - minY) / SCALE, null);
183  } finally {
184  mapManager.release(mapControl);
185  }
186 
187  done++;
188  progress.setValue(done, image);
189  if (progress.cancel()) {
190  return null;
191  }
192  }
193  } finally {
194  graphics.dispose();
195  }
196  return image;
197  }
198 
205  private void add(@NotNull final MapFile mapFile, final boolean includeTiledMaps, @NotNull final PendingInfo pendingInfo) {
206  final MapControl<G, A, R> mapControl;
207  try {
208  mapControl = mapManager.openMapFile(mapFile, false);
209  } catch (final IOException ex) {
210  System.err.println("cannot load map: " + mapFile + ": " + ex);
211  return;
212  }
213  try {
214  final MapRenderer mapRenderer = rendererFactory.newSimpleMapRenderer(mapControl.getMapModel());
215  final Size2D imageSize = mapRenderer.getImageSize();
216  final int width = imageSize.getWidth();
217  final int height = imageSize.getHeight();
218 
219  final int x = pendingInfo.getX(width);
220  final int y = pendingInfo.getY(height);
221  if (x < minX) {
222  minX = x;
223  } else if (x + width > maxX) {
224  maxX = x + width;
225  }
226  if (y < minY) {
227  minY = y;
228  } else if (y + height > maxY) {
229  maxY = y + height;
230  }
231  layoutInfo.put(mapFile, new LayoutInfo(mapFile, x, y));
232 
233  if (includeTiledMaps) {
234  final A mapArchObject = mapControl.getMapModel().getMapArchObject();
235  for (final Direction direction : Direction.values()) {
236  final String tilePath = mapArchObject.getTilePath(direction);
237  if (!tilePath.isEmpty()) {
238  final MapModel<G, A, R> mapModel2 = mapControl.getMapModel();
239  final MapPath mapPath2 = MapPathUtils.newMapPath(tilePath);
240  final MapFile mapFile2;
241  try {
242  mapFile2 = mapModel2.getMapFile(mapPath2);
243  } catch (final SameMapException | UnsavedMapException ignored) {
244  // ignore
245  continue;
246  }
247  if (done.add(mapFile2)) {
248  final int x2;
249  final int y2;
250  switch (direction) {
251  case NORTH:
252  case WEST:
253  x2 = x;
254  y2 = y;
255  break;
256 
257  case EAST:
258  x2 = x + width;
259  y2 = y;
260  break;
261 
262  case SOUTH:
263  x2 = x;
264  y2 = y + height;
265  break;
266 
267  case NORTH_EAST:
268  case SOUTH_EAST:
269  case SOUTH_WEST:
270  case NORTH_WEST:
271  case UP:
272  case DOWN:
273  default:
274  throw new AssertionError("unexpected direction: " + direction);
275  }
276  pending.put(mapFile2, new PendingInfo(direction, x2, y2));
277  }
278  }
279  }
280  }
281  } finally {
282  mapManager.release(mapControl);
283  }
284  }
285 
286 }
net.sf.gridarta.utils.Size2D.getWidth
int getWidth()
Returns the width of the area.
Definition: Size2D.java:96
net.sf.gridarta.model.direction.Direction
A direction.
Definition: Direction.java:28
net.sf.gridarta.model.mapmanager
Definition: AbstractMapManager.java:20
net.sf.gridarta.model.mapmodel.MapModel
A MapModel reflects the data of a map.
Definition: MapModel.java:75
net.sf.gridarta.model.mapmodel.SameMapException
Exception thrown if the destination path points to the source map.
Definition: SameMapException.java:26
net.sf.gridarta.gui.map.renderer.MapRenderer.getImageSize
Size2D getImageSize()
Returns the size of an image of the entire map view in pixels.
net.sf.gridarta.model.mapmodel.MapModel.getMapArchObject
A getMapArchObject()
Returns the Map Arch Object with the meta information about the map.
net.sf.gridarta.model.mapmanager.MapManager
A MapManager manages all opened maps.
Definition: MapManager.java:37
net.sf.gridarta.action.exportmap.MapImageCreator.layoutInfo
final Map< MapFile, LayoutInfo > layoutInfo
Maps MapFile instance to the LayoutInfo for this map.
Definition: MapImageCreator.java:81
net.sf.gridarta
Base package of all Gridarta classes.
net.sf.gridarta.action.exportmap.MapImageCreator.createImage
BufferedImage createImage(@NotNull final MapFile mapFile, final boolean includeTiledMaps, @NotNull final ExportMapProgress progress)
Creates an image from a map file.
Definition: MapImageCreator.java:137
net.sf.gridarta.action.exportmap.MapImageCreator.SCALE
static final int SCALE
The scale factor for the output image.
Definition: MapImageCreator.java:61
net.sf
net.sf.gridarta.gui.map.renderer.RendererFactory.newSimpleMapRenderer
MapRenderer newSimpleMapRenderer(@NotNull MapModel< G, A, R > mapModel)
Creates a new map renderer instance which paints only squares but no grid, cursor,...
net.sf.gridarta.action.exportmap.PendingInfo
Preliminary layout information for a map.
Definition: PendingInfo.java:28
net.sf.gridarta.model.mapmodel
Definition: AboveFloorInsertionMode.java:20
net.sf.gridarta.gui.map.renderer
Definition: AbstractIsoMapRenderer.java:20
net.sf.gridarta.action.exportmap.MapImageCreator
Creates images from maps.
Definition: MapImageCreator.java:56
net.sf.gridarta.model.mapmodel.MapPathUtils.newMapPath
static MapPath newMapPath(@NotNull final String string)
Creates a MapPath instance from string representation.
Definition: MapPathUtils.java:43
net.sf.gridarta.model.archetype
Definition: AbstractArchetype.java:20
net.sf.gridarta.model.gameobject.GameObject
Reflects a game object (object on a map).
Definition: GameObject.java:36
net.sf.gridarta.model.mapmodel.MapPathUtils
Utility class for MapPath related functions.
Definition: MapPathUtils.java:28
net.sf.gridarta.model.mapcontrol
Definition: DefaultMapControl.java:20
net.sf.gridarta.action.exportmap.MapImageCreator.rendererFactory
final RendererFactory< G, A, R > rendererFactory
The RendererFactory for creating images from maps.
Definition: MapImageCreator.java:73
net.sf.gridarta.gui
Graphical User Interface of Gridarta.
net.sf.gridarta.action.exportmap.MapImageCreator.minX
int minX
The minimum x coordinate of all maps in layoutInfo.
Definition: MapImageCreator.java:101
net.sf.gridarta.model.gameobject
GameObjects are the objects based on Archetypes found on maps.
Definition: AbstractGameObject.java:20
net
net.sf.gridarta.utils.Size2D.getHeight
int getHeight()
Returns the height of the area.
Definition: Size2D.java:104
net.sf.gridarta.action.exportmap.MapImageCreator.done
final Collection< MapFile > done
Contains all MapFile instances which have been discovered and which are part of the final image.
Definition: MapImageCreator.java:96
net.sf.gridarta.action.exportmap.ExportMapProgress
Interface for receiving progress information while the map is exported.
Definition: ExportMapProgress.java:28
net.sf.gridarta.action.exportmap.MapImageCreator.pending
final Map< MapFile, PendingInfo > pending
Maps MapFile instance to its preliminary layout position.
Definition: MapImageCreator.java:89
net.sf.gridarta.model.maparchobject.MapArchObject
Interface for MapArchObjects.
Definition: MapArchObject.java:40
net.sf.gridarta.model.mapmanager.MapManager.openMapFile
MapControl< G, A, R > openMapFile(@NotNull MapFile mapFile, boolean interactive)
Loads a map file.
net.sf.gridarta.model.mapmodel.MapPath
Represents a maps directory local map path.
Definition: MapPath.java:31
net.sf.gridarta.action.exportmap.MapImageCreator.MapImageCreator
MapImageCreator(@NotNull final MapManager< G, A, R > mapManager, @NotNull final RendererFactory< G, A, R > rendererFactory)
Creates a new instance.
Definition: MapImageCreator.java:124
net.sf.gridarta.action.exportmap.MapImageCreator.mapManager
final MapManager< G, A, R > mapManager
The MapManager for loading maps.
Definition: MapImageCreator.java:67
net.sf.gridarta.model.mapmodel.UnsavedMapException
Exception thrown if an operation is attempted on an unsaved map.
Definition: UnsavedMapException.java:26
net.sf.gridarta.model.direction.Direction.UP
UP
Up.
Definition: Direction.java:73
net.sf.gridarta.action.exportmap.MapImageCreator.minY
int minY
The minimum y coordinate of all maps in layoutInfo.
Definition: MapImageCreator.java:106
net.sf.gridarta.model.mapmodel.MapFile
The location of a map file with a map directory.
Definition: MapFile.java:31
net.sf.gridarta.model.mapmanager.MapManager.release
void release(@NotNull MapControl< G, A, R > mapControl)
Releases a MapControl instance.
net.sf.gridarta.action.exportmap.MapImageCreator.maxX
int maxX
The maximum x coordinate of all maps in layoutInfo.
Definition: MapImageCreator.java:111
net.sf.gridarta.action.exportmap.MapImageCreator.add
void add(@NotNull final MapFile mapFile, final boolean includeTiledMaps, @NotNull final PendingInfo pendingInfo)
Adds a new map file.
Definition: MapImageCreator.java:205
net.sf.gridarta.model
net.sf.gridarta.model.archetype.Archetype
Reflects an Archetype.
Definition: Archetype.java:41
net.sf.gridarta.gui.map.renderer.RendererFactory
Factory for creating MapRenderer instances.
Definition: RendererFactory.java:33
net.sf.gridarta.gui.map
Base classes for rendering maps.
Definition: AbstractPerMapDialogManager.java:20
net.sf.gridarta.model.mapcontrol.MapControl
Currently nothing more than a marker interface for unification.
Definition: MapControl.java:35
net.sf.gridarta.model.mapcontrol.MapControl.getMapModel
MapModel< G, A, R > getMapModel()
Returns the map model.
net.sf.gridarta.model.maparchobject
Definition: AbstractMapArchObject.java:20
net.sf.gridarta.gui.map.renderer.MapRenderer.getFullImage
BufferedImage getFullImage()
Returns an image of the entire map view.
net.sf.gridarta.gui.map.renderer.MapRenderer
Common interface for renderers of map control instances.
Definition: MapRenderer.java:36
net.sf.gridarta.model.mapmodel.MapModel.getMapFile
MapFile getMapFile()
Returns the map file.
net.sf.gridarta.action.exportmap.LayoutInfo
Stores layout information for maps.
Definition: LayoutInfo.java:28
net.sf.gridarta.utils.Size2D
The class Size2D represents a 2d rectangular area.
Definition: Size2D.java:30
net.sf.gridarta.action.exportmap.MapImageCreator.maxY
int maxY
The maximum y coordinate of all maps in layoutInfo.
Definition: MapImageCreator.java:116
net.sf.gridarta.utils
Definition: ActionBuilderUtils.java:20
net.sf.gridarta.model.direction
Definition: Direction.java:20
it
This document describes some hints and requirements for general development on the CrossfireEditor If you plan to make changes to the editor code or setup please read the following and keep it in derived from a basic editor application called Gridder by Pasi Ker�nen so please communicate with best through the cf devel mailing before considering any fundamental changes About code DO NOT USE TABS No matter what Java development platform you are please configure insert indent Tabs are displayed totally different in every editor and there are millions of different editors out there The insertion of tabs in the source code is messing up the syntax formatting in a way that is UNREPAIRABLE Apart from please keep code indentation accurate This is not just good it helps to keep code readable and in that way dramatically decreases the chance for overlooked bugs Everyone is welcomed to correct indentation errors wherever they are spotted Before you start to do this please double check that your editor is really configured to insert spaces Line feeds may be checked in either in windows or in unix linux style All reasonable text and java editors can deal with both linefeed formats Converting line feeds is but in this case please make sure that only linefeed characters are changed and nothing else is affected Due to the platform independent nature of the editor has the potential to run on almost any given operating system the build process differs greatly between systems as well as java environments In the several people have attempted to add build scripts along with structural changes to optimize the setup on one particular system environment which has led to conflict Please do *not *attempt to change the structure or any directories for the mere purpose of improving a build process or performance in a java environment Build scripts may be placed in the root it would be especially fine if it is just one or two files but the latter is not required Please excuse me for placing such restriction I and many users of the editor greatly appreciate build scripts We just had some real troubles over this issue in the past and I don t want to have them repeated the editor has relatively high performance requirements I ve spent a lot of extra work to keep everything as fast and memory efficient as possible when you add new data fields or calculations in the archetype please make sure they are as efficient as possible and worth both the time and space they consume Now don t be afraid too much No development would be possible without adding calculations and data at all Just bear in mind unlike for many other open source performance does make a difference for the CrossfireEditor The for as many systems as possible In case you are unexperienced with java and note that the graphics look different on every and with every font They also have different sizes proportions and behave different A seemingly trivial and effectless change can wreck havoc for the same GUI run on another system please don t be totally afraid of it
Definition: Developer_README.txt:76