mirror of https://github.com/FreeCol/freecol.git
2053 lines
77 KiB
Java
2053 lines
77 KiB
Java
/**
|
|
* Copyright (C) 2002-2022 The FreeCol Team
|
|
*
|
|
* This file is part of FreeCol.
|
|
*
|
|
* FreeCol is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* FreeCol is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package net.sf.freecol.client.gui;
|
|
|
|
import static net.sf.freecol.common.util.CollectionUtils.makeUnmodifiableList;
|
|
import static net.sf.freecol.common.util.StringUtils.getEnumKey;
|
|
|
|
import java.awt.Color;
|
|
import java.awt.Cursor;
|
|
import java.awt.Dimension;
|
|
import java.awt.Font;
|
|
import java.awt.FontMetrics;
|
|
import java.awt.Graphics;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.Image;
|
|
import java.awt.Point;
|
|
import java.awt.Toolkit;
|
|
import java.awt.image.BufferedImage;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.logging.Logger;
|
|
import java.util.stream.Collectors;
|
|
|
|
import javax.swing.ImageIcon;
|
|
import javax.swing.JLabel;
|
|
|
|
import net.sf.freecol.client.gui.images.ImageCreators;
|
|
import net.sf.freecol.common.io.sza.SimpleZippedAnimation;
|
|
import net.sf.freecol.common.model.Ability;
|
|
import net.sf.freecol.common.model.BuildableType;
|
|
import net.sf.freecol.common.model.Building;
|
|
import net.sf.freecol.common.model.BuildingType;
|
|
import net.sf.freecol.common.model.Colony;
|
|
import net.sf.freecol.common.model.Direction;
|
|
import net.sf.freecol.common.model.FoundingFather;
|
|
import net.sf.freecol.common.model.FreeColObject;
|
|
import net.sf.freecol.common.model.GoodsType;
|
|
import net.sf.freecol.common.model.IndianSettlement;
|
|
import net.sf.freecol.common.model.Location;
|
|
import net.sf.freecol.common.model.LostCityRumour;
|
|
import net.sf.freecol.common.model.Nation;
|
|
import net.sf.freecol.common.model.Player;
|
|
import net.sf.freecol.common.model.Resource;
|
|
import net.sf.freecol.common.model.ResourceType;
|
|
import net.sf.freecol.common.model.Role;
|
|
import net.sf.freecol.common.model.Settlement;
|
|
import net.sf.freecol.common.model.SettlementType;
|
|
import net.sf.freecol.common.model.Tension;
|
|
import net.sf.freecol.common.model.Tile;
|
|
import net.sf.freecol.common.model.TileImprovementStyle;
|
|
import net.sf.freecol.common.model.TileType;
|
|
import net.sf.freecol.common.model.Unit;
|
|
import net.sf.freecol.common.model.UnitType;
|
|
import net.sf.freecol.common.resources.ImageCache;
|
|
import net.sf.freecol.common.resources.ImageResource;
|
|
import net.sf.freecol.common.resources.ResourceManager;
|
|
import net.sf.freecol.common.resources.StringResource;
|
|
import net.sf.freecol.common.resources.Video;
|
|
import net.sf.freecol.common.util.ImageUtils;
|
|
|
|
|
|
/**
|
|
* Holds various images that can be called upon by others in order to display
|
|
* certain things.
|
|
*/
|
|
public final class ImageLibrary {
|
|
|
|
private static final Logger logger = Logger.getLogger(ImageLibrary.class.getName());
|
|
|
|
/**
|
|
* Canonical sizes of images GUI elements and map are expecting
|
|
* and current image files have.
|
|
* ICON_SIZE, TILE_SIZE, TILE_OVERLAY_SIZE and TILE_FOREST_SIZE constants
|
|
* are used in this way already, allowing them to tolerate changing sizes
|
|
* in the files.
|
|
* Most other images are currently still shown in a size of image size
|
|
* times scaling factor times requested size.
|
|
*/
|
|
public static final Dimension ICON_SIZE = new Dimension(32, 32),
|
|
BUILDING_SIZE = new Dimension(128, 96),
|
|
TILE_SIZE = new Dimension(128, 64),
|
|
TILE_OVERLAY_SIZE = new Dimension(128, 96),
|
|
TILE_FOREST_SIZE = new Dimension(128, 84);
|
|
|
|
/**
|
|
* Constants for the current "named" scales (tiny, smaller, small) plus
|
|
* a trivial value for unscaled.
|
|
*/
|
|
public static final float
|
|
TINY_SCALE = 0.25f,
|
|
SMALLER_SCALE = 0.5f,
|
|
SMALL_SCALE = 0.75f,
|
|
NORMAL_SCALE = 1f,
|
|
MIN_SCALE = 0.25f, // Minimum of the above
|
|
MAX_SCALE = 4.0f, // Maximum of the above
|
|
SCALE_STEP = 0.25f; // Steps between scales
|
|
|
|
// TODO: should these be hidden?
|
|
public static final String DELETE = "image.miscicon.delete",
|
|
PLOWED = "image.tile.model.improvement.plow",
|
|
UNIT_SELECT = "image.tile.unitSelect",
|
|
TILE_SELECT = "image.tile.tileSelect",
|
|
TILE_TAKEN = "image.tile.tileTaken",
|
|
TILE_OWNED_BY_INDIANS = "image.tileitem.nativeLand",
|
|
LOST_CITY_RUMOUR = "image.tileitem.lostCityRumour",
|
|
DARKNESS = "image.halo.dark",
|
|
ICON_LOCK = "image.icon.lock",
|
|
ICON_COIN = "image.icon.coin",
|
|
BELLS = "image.icon.model.goods.bells";
|
|
private static String RIVER_STYLE_PREFIX
|
|
= "image.tile.model.improvement.river.s";
|
|
|
|
/** The action button key prefixes. */
|
|
private static final List<String> buttonKeys
|
|
= makeUnmodifiableList("image.miscicon.button.normal.",
|
|
"image.miscicon.button.highlighted.",
|
|
"image.miscicon.button.pressed.",
|
|
"image.miscicon.button.disabled.");
|
|
|
|
/** Helper to distinguish different types of paths. */
|
|
public enum PathType {
|
|
NAVAL,
|
|
WAGON,
|
|
HORSE,
|
|
FOOT;
|
|
|
|
/**
|
|
* Get a key for this path type.
|
|
*
|
|
* @return A key.
|
|
*/
|
|
private String getKey() {
|
|
return getEnumKey(this);
|
|
}
|
|
|
|
public String getImageKey() {
|
|
return "image.tileitem.path." + getKey();
|
|
}
|
|
|
|
public String getNextTurnImageKey() {
|
|
return "image.tileitem.path." + getKey() + ".nextTurn";
|
|
}
|
|
|
|
/**
|
|
* Get the broad class of image to show along unit paths.
|
|
*
|
|
* @param u A {@code Unit} to classify.
|
|
* @return A suitable {@code PathType}.
|
|
*/
|
|
public static PathType getPathType(Unit u) {
|
|
return (u == null) ? PathType.FOOT
|
|
: (u.isNaval()) ? PathType.NAVAL
|
|
: (u.isMounted()) ? PathType.HORSE
|
|
: (u.isPerson()) ? PathType.FOOT
|
|
: PathType.WAGON;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* The scale factor used when creating this library. The value
|
|
* {@code 1} is used if this object is not a result of a scaling
|
|
* operation.
|
|
*/
|
|
private float scaleFactor;
|
|
|
|
/** Fixed tile dimensions. */
|
|
private Dimension tileSize, tileOverlaySize, tileForestSize;
|
|
|
|
/** Cache for images. */
|
|
private final ImageCache imageCache;
|
|
|
|
/** Cache for the string images. */
|
|
private Map<String,BufferedImage> stringImageCache;
|
|
|
|
private final ImageCreators imageCreators;
|
|
|
|
|
|
/**
|
|
* The constructor to use for an unscaled {@code ImageLibrary}.
|
|
*
|
|
* @param imageCache An {@code ImageCache} to hold images..
|
|
*/
|
|
public ImageLibrary(ImageCache imageCache) {
|
|
this(NORMAL_SCALE, imageCache);
|
|
}
|
|
|
|
/**
|
|
* The constructor to use for a scaled {@code ImageLibrary}.
|
|
*
|
|
* Please avoid using too many different scaling factors, as this will
|
|
* lead to wasted memory for caching images in ResourceManager!
|
|
* Currently, 0.25, 0.5, ..., 2.0 are used for the map. Colony tiles and
|
|
* the GUI use 1.0 here (maybe also 1.25, 1.5, 1.75 and 2.0 in future),
|
|
* but this gets multiplied for tiny 0.25(rarely, candidate for removal),
|
|
* smaller 0.5, small 0.75 and normal 1.0 image retrieval methods.
|
|
*
|
|
* @param scaleFactor The factor used when scaling. 2 is twice
|
|
* the size of the original images and 0.5 is half
|
|
* @param imageCache An {@code ImageCache} to hold images..
|
|
*/
|
|
public ImageLibrary(float scaleFactor, ImageCache imageCache) {
|
|
changeScaleFactor(scaleFactor);
|
|
this.imageCache = imageCache;
|
|
this.imageCreators = new ImageCreators(this, imageCache);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the scaling factor used when creating this {@code ImageLibrary}.
|
|
*
|
|
* It is 1f if the constructor without scaling factor was used to create
|
|
* this object.
|
|
*
|
|
* @return The scaling factor of this ImageLibrary.
|
|
*/
|
|
public float getScaleFactor() {
|
|
return this.scaleFactor;
|
|
}
|
|
|
|
/**
|
|
* Change the scale factor for this image library.
|
|
*
|
|
* All the other variables depend on the scale factor.
|
|
*
|
|
* @param scaleFactor The factor used when scaling. 2 is twice
|
|
*/
|
|
public void changeScaleFactor(float scaleFactor) {
|
|
this.scaleFactor = scaleFactor;
|
|
this.tileSize = scale(TILE_SIZE);
|
|
this.tileOverlaySize = scale(TILE_OVERLAY_SIZE);
|
|
this.tileForestSize = scale(TILE_FOREST_SIZE);
|
|
this.stringImageCache = new HashMap<>();
|
|
}
|
|
|
|
/**
|
|
* Scale a pixel distance the current internal scale.
|
|
*
|
|
* @param n The pixels to scale.
|
|
* @return The scaled pixels.
|
|
*/
|
|
public int scaleInt(int n) {
|
|
return (int)(n * this.scaleFactor);
|
|
}
|
|
|
|
/**
|
|
* Scale a dimension with the current internal scale.
|
|
*
|
|
* @param size The {@code Dimension} to scale.
|
|
* @return The scaled {@code Dimension}.
|
|
*/
|
|
public Dimension scale(Dimension size) {
|
|
return scaleDimension(size, this.scaleFactor);
|
|
}
|
|
|
|
/**
|
|
* Scale a dimension with the current internal scale and an extra override.
|
|
*
|
|
* @param size The {@code Dimension} to scale.
|
|
* @param extraFactor An extra scaling.
|
|
* @return The scaled {@code Dimension}.
|
|
*/
|
|
public Dimension scale(Dimension size, float extraFactor) {
|
|
return scaleDimension(size, this.scaleFactor * extraFactor);
|
|
}
|
|
|
|
/**
|
|
* Absolute dimenion scaling helper routine.
|
|
*
|
|
* @param size The {@code Dimension} to scale.
|
|
* @param scaleFactor The scale to use.
|
|
* @return The scaled {@code Dimension}.
|
|
*/
|
|
public static Dimension scaleDimension(Dimension size, float scaleFactor) {
|
|
return new Dimension(Math.round(size.width * scaleFactor),
|
|
Math.round(size.height * scaleFactor));
|
|
}
|
|
|
|
/**
|
|
* Get the scaled size of a tile.
|
|
*
|
|
* @return The tile size.
|
|
*/
|
|
public Dimension getTileSize() {
|
|
return this.tileSize;
|
|
}
|
|
|
|
/**
|
|
* Get the scaled size of a forested tile.
|
|
*
|
|
* @return The forested tile size.
|
|
*/
|
|
public Dimension getForestedTileSize() {
|
|
return this.tileForestSize;
|
|
}
|
|
|
|
/**
|
|
* Calculates a seed for reliably generating the same "random" number.
|
|
*
|
|
* This is useful to select different images for the same tile
|
|
* type in order to prevent big stripes or a checker-board effect.
|
|
*
|
|
* @param x The tile x coordinate.
|
|
* @param y The tile y coordinate.
|
|
* @return The seed.
|
|
*/
|
|
public static int variationSeedUsing(int x, int y) {
|
|
return x * 6841 + y * 7919;
|
|
}
|
|
|
|
// Animation handling
|
|
|
|
public static SimpleZippedAnimation getSZA(String key, float scale) {
|
|
return (ResourceManager.getSZAResource(key, false) == null) ? null
|
|
: ResourceManager.getSZA(key, scale);
|
|
}
|
|
|
|
|
|
// Color handling
|
|
|
|
/**
|
|
* Derive a suitable foreground color from a background color.
|
|
*
|
|
* Our eyes have different sensitivity towards red, green and
|
|
* blue. We want a foreground color with the inverse brightness.
|
|
*
|
|
* @param background The background {@code Color} to complement.
|
|
* @return A suitable foreground {@code Color}.
|
|
*/
|
|
public static Color makeForegroundColor(Color background) {
|
|
return (background == null
|
|
|| (background.getRed() * 0.3 + background.getGreen() * 0.59
|
|
+ background.getBlue() * 0.11 >= 126))
|
|
? Color.BLACK
|
|
: Color.WHITE;
|
|
}
|
|
|
|
/**
|
|
* Derive a string border color from the string color. Black
|
|
* unless the color of the string is really dark.
|
|
*
|
|
* @param color The {@code Color} to complement.
|
|
* @return A suitable border {@code Color}.
|
|
*/
|
|
private static Color makeStringBorderColor(Color color) {
|
|
return (color.getRed() * 0.3
|
|
+ color.getGreen() * 0.59
|
|
+ color.getBlue() * 0.11 < 10) ? Color.WHITE
|
|
: Color.BLACK;
|
|
}
|
|
|
|
/**
|
|
* Get a color.
|
|
*
|
|
* @param key The color name.
|
|
* @return The {@code Color} found by the resource manager.
|
|
*/
|
|
public static Color getColor(final String key) {
|
|
return getColor(key, null);
|
|
}
|
|
|
|
/**
|
|
* Get a color.
|
|
*
|
|
* @param key The color name.
|
|
* @param replacement A replacement {@code Color} to use if the named color
|
|
* can not be found by the resource manager.
|
|
* @return The {@code Color} found by the resource manager.
|
|
*/
|
|
public static Color getColor(final String key, final Color replacement) {
|
|
return ResourceManager.getColor(key, replacement);
|
|
}
|
|
|
|
/**
|
|
* Get a foreground color for a given goods type and amount in a
|
|
* given location.
|
|
*
|
|
* @param goodsType The {@code GoodsType} to use.
|
|
* @param amount The amount of goods.
|
|
* @param location The {@code Location} for the goods.
|
|
* @param whiteForeground If the color should be white (or bright) so that
|
|
* it works on a black background.
|
|
* @return A suitable {@code Color}.
|
|
*/
|
|
public static Color getGoodsColor(GoodsType goodsType, int amount,
|
|
Location location, boolean whiteForeground) {
|
|
final String foreground = (whiteForeground) ? "whiteForeground" : "foreground";
|
|
final String prefix = "color." + foreground + ".GoodsLabel.";
|
|
final String key = (!goodsType.limitIgnored()
|
|
&& location instanceof Colony
|
|
&& ((Colony)location).getWarehouseCapacity() < amount)
|
|
? prefix + "capacityExceeded"
|
|
: (location instanceof Colony && goodsType.isStorable()
|
|
&& ((Colony)location).getExportData(goodsType).getExported())
|
|
? prefix + "exported"
|
|
: (amount == 0)
|
|
? prefix + "zeroAmount"
|
|
: (amount < 0)
|
|
? prefix + "negativeAmount"
|
|
: prefix + "positiveAmount";
|
|
|
|
if (whiteForeground) {
|
|
return getColor(key, Color.WHITE);
|
|
}
|
|
return getColor(key, Color.BLACK);
|
|
}
|
|
|
|
public static Color getMinimapBackgroundColor() {
|
|
return getColor("color.background.MiniMap");
|
|
}
|
|
|
|
public static Color getMinimapBorderColor() {
|
|
return getColor("color.border.MiniMap");
|
|
}
|
|
|
|
public static Color getMinimapEconomicColor(TileType type) {
|
|
final String key = "color.economic.MiniMap." + type.getId();
|
|
return getColor(key);
|
|
}
|
|
|
|
public static Color getMinimapPoliticsColor(TileType type) {
|
|
final String key = "color.politics.MiniMap." + type.getId();
|
|
return getColor(key);
|
|
}
|
|
|
|
/**
|
|
* Get the road color.
|
|
*
|
|
* @return The road color.
|
|
*/
|
|
public static Color getRoadColor() {
|
|
return getColor("color.map.road");
|
|
}
|
|
|
|
|
|
// Scaled font wrapper
|
|
|
|
/**
|
|
* Get a scaled font with a simple text specification.
|
|
*
|
|
* The <code>Font</code> is scaled with the scaleFactor from this
|
|
* <code>ImageLibrary</code> instead of the global scaleFactor in
|
|
* {@link FontLibrary}.
|
|
*
|
|
* This method is meant to be used for font that should scale
|
|
* perfectly with the graphics, for example text on the map.
|
|
*
|
|
* @param spec The font specification.
|
|
* @param text Optional text that the font must be able to represent.
|
|
* @return The {@code Font} found.
|
|
*/
|
|
public Font getScaledFont(String spec, String text) {
|
|
return FontLibrary.getScaledFont(spec, this.scaleFactor, text);
|
|
}
|
|
|
|
|
|
// Fundamental image retrieval
|
|
|
|
/**
|
|
* Just get an image without any scaling.
|
|
*
|
|
* Note, that this routine and its callers can be static, but anything
|
|
* that is scaled or sized goes through the image cache and thus can
|
|
* not be static.
|
|
*
|
|
* @param key The image key.
|
|
* @return The image found.
|
|
*/
|
|
public static BufferedImage getUnscaledImage(String key) {
|
|
return ResourceManager.getImage(key);
|
|
}
|
|
|
|
/**
|
|
* Get the image for the given identifier, using the current scaling.
|
|
*
|
|
* @param key The image key.
|
|
* @return The {@code BufferedImage} found by the {@code ResourceManager}.
|
|
*/
|
|
public BufferedImage getScaledImage(String key) {
|
|
return this.imageCache.getScaledImage(key, this.scaleFactor, false);
|
|
}
|
|
|
|
public BufferedImage getRangedTargetCrosshair() {
|
|
return getScaledImage("image.rangedTarget");
|
|
}
|
|
|
|
/**
|
|
* Get the image for the given identifier, using a given size.
|
|
*
|
|
* @param key The image key.
|
|
* @param size The image size required.
|
|
* @return The {@code BufferedImage} found by the {@code ResourceManager}.
|
|
*/
|
|
public BufferedImage getSizedImage(String key, Dimension size) {
|
|
return this.imageCache.getSizedImage(key, size, false);
|
|
}
|
|
|
|
|
|
// Miscellaneous image handling
|
|
|
|
public static BufferedImage getButtonBackground() {
|
|
return getUnscaledImage("image.background.FreeColButton");
|
|
}
|
|
|
|
/**
|
|
* Get the button images for a given key.
|
|
*
|
|
* @param key The key to look up.
|
|
* @return The list of button images found.
|
|
*/
|
|
public List<BufferedImage> getButtonImages(final String key) {
|
|
List<BufferedImage> ret = new ArrayList<>();
|
|
for (String base : buttonKeys) {
|
|
String k = base + key;
|
|
final BufferedImage image = imageCache.getScaledImage(k, scaleFactor, false);
|
|
if (image != null) {
|
|
ret.add(image);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public static BufferedImage getBrightPanelBackground() {
|
|
return getUnscaledImage("image.background.FreeColBrightPanel");
|
|
}
|
|
|
|
public static BufferedImage getCanvasBackgroundImage() {
|
|
final String key = "image.flavor.Canvas.map";
|
|
return (ResourceManager.getImageResource(key, false) == null) ? null
|
|
: getUnscaledImage(key);
|
|
}
|
|
|
|
public BufferedImage getColopediaCellImage(boolean expanded) {
|
|
final String key = "image.icon.Colopedia."
|
|
+ ((expanded) ? "open" : "closed") + "Section";
|
|
return getScaledImage(key);
|
|
}
|
|
|
|
public BufferedImage getColopediaConceptImage() {
|
|
return getScaledImage("image.icon.Colopedia.idea");
|
|
}
|
|
|
|
public static BufferedImage getColorCellRendererBackground() {
|
|
return getUnscaledImage("image.background.ColorCellRenderer");
|
|
}
|
|
|
|
public JLabel getCompassRose() {
|
|
return new JLabel(new ImageIcon(getScaledImage("image.skin.compass")));
|
|
}
|
|
|
|
/**
|
|
* Get the standard cursor.
|
|
*
|
|
* @return A suitable default {@code Cursor}.
|
|
*/
|
|
public static Cursor getCursor() {
|
|
String key = "image.icon.cursor.go";
|
|
if (ResourceManager.getImageResource(key, false) != null) {
|
|
Image im = getUnscaledImage(key);
|
|
return Toolkit.getDefaultToolkit().createCustomCursor(im,
|
|
new Point(im.getWidth(null)/2, im.getHeight(null)/2), "go");
|
|
}
|
|
return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
|
|
}
|
|
|
|
/**
|
|
* Returns the portrait of this Founding Father.
|
|
*
|
|
* @param father The {@code FoundingFather} to look for.
|
|
* @param grayscale True if the image should be grayscale.
|
|
* @return The {@code BufferedImage} found.
|
|
*/
|
|
public BufferedImage getFoundingFatherImage(FoundingFather father,
|
|
boolean grayscale) {
|
|
final String key = "image.flavor." + father.getId();
|
|
return this.imageCache.getScaledImage(key, scaleFactor, grayscale);
|
|
}
|
|
|
|
public BufferedImage getInformationPanelSkin(Player player) {
|
|
String key = determineInformationPanelSkinKey(player);
|
|
return getScaledImage(key);
|
|
}
|
|
|
|
private String determineInformationPanelSkinKey(Player player) {
|
|
String key = (player == null) ? "image.skin.InformationPanel"
|
|
: (player.isRebel()) ? "image.skin.InformationPanel.rebel"
|
|
: "image.skin.InformationPanel." + player.getNationResourceKey();
|
|
if (ResourceManager.getImageResource(key, false) == null) {
|
|
key = "image.skin.InformationPanel";
|
|
}
|
|
return key;
|
|
}
|
|
|
|
public int getInformationPanelSkinTopInset(Player player) {
|
|
final String key = determineInformationPanelSkinKey(player) + ".insets.top";
|
|
final StringResource s = ResourceManager.getStringResource(key, false);
|
|
return (s != null) ? Integer.parseInt(s.getString()) : 0;
|
|
}
|
|
|
|
public BufferedImage getLCRImage(Dimension size) {
|
|
return this.imageCache.getSizedImage(LOST_CITY_RUMOUR, size, false);
|
|
}
|
|
|
|
public BufferedImage getLibertyImage() {
|
|
return this.imageCache.getSizedImage(BELLS, scale(ICON_SIZE), false);
|
|
}
|
|
|
|
public static BufferedImage getListBackground() {
|
|
return getUnscaledImage("image.background.FreeColList");
|
|
}
|
|
|
|
public JLabel getLockLabel() {
|
|
BufferedImage img = this.imageCache.getScaledImage(ICON_LOCK,
|
|
this.scaleFactor * SMALLER_SCALE, false);
|
|
return new JLabel(new ImageIcon(img));
|
|
}
|
|
|
|
public static BufferedImage getMeetingImage(Player meet) {
|
|
final String base = "image.flavor.event.meeting.";
|
|
String key = base + meet.getNationResourceKey();
|
|
if (ResourceManager.getImageResource(key, false) == null) {
|
|
key = base + "natives";
|
|
}
|
|
return getUnscaledImage(key);
|
|
}
|
|
|
|
public static BufferedImage getMenuBarBackground() {
|
|
return getUnscaledImage("image.background.FreeColMenuBar");
|
|
}
|
|
|
|
public static BufferedImage getMenuBackground() {
|
|
return getUnscaledImage("image.background.FreeColMenu");
|
|
}
|
|
|
|
public static BufferedImage getMiniMapBackground() {
|
|
return getUnscaledImage("image.background.MiniMap");
|
|
}
|
|
|
|
public BufferedImage getMiniMapSkin() {
|
|
final String key = "image.skin.MiniMap";
|
|
return imageCache.getScaledImage(key, scaleFactor, false);
|
|
}
|
|
|
|
/**
|
|
* Get the appropriate BufferedImage for a FreeColObject.
|
|
*
|
|
* @param display The {@code FreeColObject} to display.
|
|
* @param size The image size.
|
|
* @return The appropriate {@code BufferedImage}.
|
|
*/
|
|
private BufferedImage getObjectImageInternal(FreeColObject display,
|
|
Dimension size) {
|
|
final FreeColObject derived = display.getDisplayObject();
|
|
// Not all types have a meaningful image.
|
|
BufferedImage image = (derived instanceof BuildingType)
|
|
? getBuildingTypeImage((BuildingType)derived, size)
|
|
: (derived instanceof GoodsType)
|
|
? getGoodsTypeImage((GoodsType)derived, size)
|
|
: (derived instanceof LostCityRumour)
|
|
? getLCRImage(size)
|
|
: (derived instanceof Nation)
|
|
? getNationImage((Nation)derived, size)
|
|
: (derived instanceof ResourceType)
|
|
? getResourceTypeImage((ResourceType)derived, size, false)
|
|
: (derived instanceof Settlement)
|
|
? getSettlementImage((Settlement)derived, size)
|
|
: (derived instanceof TileType)
|
|
? getTerrainImage((TileType)derived, 0, 0, size)
|
|
: (derived instanceof UnitType)
|
|
? getUnitTypeImage((UnitType)derived, size)
|
|
: null;
|
|
if (image == null) {
|
|
logger.warning("Could not find image for " + display);
|
|
return null;
|
|
}
|
|
return image;
|
|
}
|
|
|
|
/**
|
|
* Get the appropriate BufferedImage for a FreeColObject.
|
|
*
|
|
* Please use a more specific method!
|
|
*
|
|
* @param display The {@code FreeColObject} to display.
|
|
* @return The appropriate {@code BufferedImage}.
|
|
*/
|
|
public BufferedImage getObjectImage(FreeColObject display) {
|
|
return getObjectImageInternal(display, scale(ICON_SIZE));
|
|
}
|
|
|
|
/**
|
|
* Get the appropriate BufferedImage for a FreeColObject.
|
|
*
|
|
* Please use a more specific method!
|
|
*
|
|
* @param display The {@code FreeColObject} to display.
|
|
* @param size The image size.
|
|
* @return The appropriate {@code BufferedImage}.
|
|
*/
|
|
public BufferedImage getObjectImage(FreeColObject display, Dimension size) {
|
|
return getObjectImageInternal(display, size);
|
|
}
|
|
|
|
/**
|
|
* Get an ImageIcon for a FreeColObject.
|
|
*
|
|
* @param display The {@code FreeColObject} to find an icon for.
|
|
* @return The {@code ImageIcon} found.
|
|
*/
|
|
public ImageIcon getObjectImageIcon(FreeColObject display) {
|
|
if (display == null) return null;
|
|
BufferedImage image = getObjectImage(display, scale(ICON_SIZE, 2));
|
|
return (image == null) ? null : new ImageIcon(image);
|
|
}
|
|
|
|
public static BufferedImage getOptionPaneBackground() {
|
|
return getUnscaledImage("image.background.FreeColOptionPane");
|
|
}
|
|
|
|
/**
|
|
* Gets the default background for panels (dialogs/windows).
|
|
* @return The default background image.
|
|
* @see #getPanelBackground(Class)
|
|
*/
|
|
public static BufferedImage getPanelBackground() {
|
|
return getUnscaledImage("image.background.FreeColPanel");
|
|
}
|
|
|
|
/**
|
|
* Gets the background for the given panel.
|
|
*
|
|
* @param clazz The {@code Class} of the panel.
|
|
* @return The background for the given panel, or the first matching
|
|
* superclass.
|
|
*/
|
|
public static BufferedImage getPanelBackground(Class<?> clazz) {
|
|
while (clazz != null) {
|
|
final ImageResource ir = ResourceManager.getImageResource("image.background." + clazz.getSimpleName(), false);
|
|
if (ir != null) {
|
|
return ir.getImage();
|
|
}
|
|
clazz = clazz.getSuperclass();
|
|
if (!clazz.getName().startsWith("net.sf.freecol")) {
|
|
clazz = null;
|
|
}
|
|
}
|
|
return getPanelBackground();
|
|
}
|
|
|
|
/**
|
|
* Get the generic placeholder image.
|
|
*
|
|
* @return The placeholder {@code BufferedImage}.
|
|
*/
|
|
public BufferedImage getPlaceholderImage() {
|
|
return this.imageCache.getSizedImage("image.unit.placeholder",
|
|
ICON_SIZE, false);
|
|
}
|
|
|
|
public static BufferedImage getPopupMenuBackground() {
|
|
return getUnscaledImage("image.background.FreeColPopupMenu");
|
|
}
|
|
|
|
public static BufferedImage getProgressBarBackground() {
|
|
return getUnscaledImage("image.background.FreeColProgressBar");
|
|
}
|
|
|
|
public static BufferedImage getTextAreaBackground() {
|
|
return getUnscaledImage("image.background.FreeColTextArea");
|
|
}
|
|
|
|
public static BufferedImage getTextFieldBackground() {
|
|
return getUnscaledImage("image.background.FreeColTextField");
|
|
}
|
|
|
|
public static BufferedImage getToolTipBackground() {
|
|
return getUnscaledImage("image.background.FreeColToolTip");
|
|
}
|
|
|
|
|
|
// BuildingType/Building/Buildable handling
|
|
|
|
private static String getBuildingTypeKey(BuildingType buildingType) {
|
|
return "image.buildingicon." + buildingType.getId();
|
|
}
|
|
|
|
public BufferedImage getBuildableTypeImage(BuildableType buildable,
|
|
Dimension size) {
|
|
return (buildable instanceof BuildingType)
|
|
? getBuildingTypeImage((BuildingType)buildable, size)
|
|
: getUnitTypeImage((UnitType)buildable, size);
|
|
}
|
|
|
|
public BufferedImage getBuildableTypeImage(BuildableType buildable, Player player, Dimension size) {
|
|
return (buildable instanceof BuildingType)
|
|
? getBuildingTypeImage((BuildingType)buildable, player, size)
|
|
: getUnitTypeImage((UnitType)buildable, size);
|
|
}
|
|
|
|
public BufferedImage getSmallBuildableTypeImage(BuildableType buildable,
|
|
Player player) {
|
|
float scale = this.scaleFactor * SMALL_SCALE;
|
|
return (buildable instanceof BuildingType)
|
|
? getScaledBuildingTypeImage((BuildingType)buildable, player, scale)
|
|
: getUnitTypeImage((UnitType)buildable, scale);
|
|
}
|
|
|
|
public BufferedImage getSmallBuildableTypeImageWithWithSize(BuildableType buildable, Player player, Dimension maxSize) {
|
|
final BufferedImage image = getSmallBuildableTypeImageWithMaxSize(buildable, player, maxSize);
|
|
return ImageUtils.createCenteredImage(image, maxSize);
|
|
}
|
|
|
|
public BufferedImage getSmallBuildableTypeImageWithMaxSize(BuildableType buildable, Player player, Dimension maxSize) {
|
|
final BufferedImage image = getSmallBuildableTypeImage(buildable, player);
|
|
final double widthProportion = image.getWidth() / maxSize.width;
|
|
final double heightProportion = image.getHeight() / maxSize.height;
|
|
if (widthProportion >= heightProportion) {
|
|
if (image.getWidth() > maxSize.width) {
|
|
return getBuildableTypeImage(buildable, player, new Dimension(maxSize.width, 0));
|
|
} else {
|
|
return image;
|
|
}
|
|
} else {
|
|
if (image.getHeight() > maxSize.height) {
|
|
return getBuildableTypeImage(buildable, player, new Dimension(0, maxSize.height));
|
|
} else {
|
|
return image;
|
|
}
|
|
}
|
|
}
|
|
|
|
private BufferedImage getBuildingTypeImage(BuildingType buildingType, Player player, Dimension size) {
|
|
String key = getBuildingTypeKey(buildingType);
|
|
final String extraKey = key + "." + player.getNationResourceKey();
|
|
if (ResourceManager.getImageResource(extraKey, false) != null) {
|
|
key = extraKey;
|
|
}
|
|
return this.imageCache.getSizedImage(key, size, false);
|
|
}
|
|
|
|
public BufferedImage getBuildingTypeImage(BuildingType buildingType,
|
|
Dimension size) {
|
|
final String key = getBuildingTypeKey(buildingType);
|
|
return this.imageCache.getSizedImage(key, size, false);
|
|
}
|
|
|
|
public BufferedImage getScaledBuildingTypeImage(BuildingType buildingType,
|
|
float scale) {
|
|
final String key = getBuildingTypeKey(buildingType);
|
|
return this.imageCache.getScaledImage(key, scale, false);
|
|
}
|
|
|
|
private BufferedImage getScaledBuildingTypeImage(BuildingType buildingType,
|
|
Player player,
|
|
float scale) {
|
|
String key = getBuildingTypeKey(buildingType);
|
|
final String extraKey = key + "." + player.getNationResourceKey();
|
|
if (ResourceManager.getImageResource(extraKey, false) != null) {
|
|
key = extraKey;
|
|
}
|
|
return this.imageCache.getScaledImage(key, scale, false);
|
|
}
|
|
|
|
public Dimension determineMaxSizeUsingSizeFromAllLevels(BuildingType buildingType, Player player) {
|
|
int maxWidth = 0;
|
|
int maxHeight = 0;
|
|
while (buildingType.getUpgradesFrom() != null) {
|
|
buildingType = buildingType.getUpgradesFrom();
|
|
}
|
|
do {
|
|
final Image buildingImage = getScaledBuildingTypeImage(buildingType, player, getScaleFactor());
|
|
if (buildingImage.getWidth(null) > maxWidth) {
|
|
maxWidth = buildingImage.getWidth(null);
|
|
}
|
|
if (buildingImage.getHeight(null) > maxHeight) {
|
|
maxHeight = buildingImage.getHeight(null);
|
|
}
|
|
buildingType = buildingType.getUpgradesTo();
|
|
} while (buildingType != null);
|
|
|
|
return new Dimension(maxWidth, maxHeight);
|
|
}
|
|
|
|
public BufferedImage getScaledBuildingImage(Building building) {
|
|
return getScaledBuildingTypeImage(building.getType(),
|
|
building.getOwner(),
|
|
this.scaleFactor);
|
|
}
|
|
|
|
public BufferedImage getSmallBuildingImage(Building building) {
|
|
return getScaledBuildingTypeImage(building.getType(),
|
|
building.getOwner(),
|
|
this.scaleFactor * SMALL_SCALE);
|
|
}
|
|
|
|
public BufferedImage getScaledBuildingEmptyLandImage() {
|
|
final String key = "image.buildingicon.model.building.BuildingSite";
|
|
return this.imageCache.getScaledImage(key, this.scaleFactor, false);
|
|
}
|
|
|
|
/**
|
|
* Gets an UNCACHED image of the "outside colony"-background based on the given defensive building.
|
|
*
|
|
* The result have to be cached by the caller of this method as long as the image is needed.
|
|
*
|
|
* @param defensiveBuilding The building to get the graphics for.
|
|
* @param size The size of the background.
|
|
* @return The image, resized to the given {@code size} without honoring the aspect ratio.
|
|
*/
|
|
public static BufferedImage getUncachedOutsideColonyBackground(BuildingType defensiveBuilding, Dimension size) {
|
|
final String key;
|
|
if (defensiveBuilding == null) {
|
|
key = "image.buildingOutside.background.default";
|
|
} else {
|
|
key = "image.buildingOutside.background." + defensiveBuilding.getId();
|
|
}
|
|
final ImageResource ir = ResourceManager.getImageResource(key, false);
|
|
if (ir == null) {
|
|
return null;
|
|
}
|
|
return ir.getSizedImageIgnoringProportions(size);
|
|
}
|
|
|
|
// Colony Panel
|
|
|
|
public BufferedImage getScaledCargoHold(boolean available) {
|
|
final String key = "image.cargohold." + (available ? "available" : "unavailable");
|
|
return this.imageCache.getScaledImage(key, this.scaleFactor, false);
|
|
}
|
|
|
|
public BufferedImage getColonyDocks() {
|
|
final String key = "image.colony.docks.background";
|
|
return this.imageCache.getScaledImage(key, this.scaleFactor, false);
|
|
}
|
|
|
|
public BufferedImage getColonyDocksSky() {
|
|
final String key = "image.colony.docks.sky.background";
|
|
return this.imageCache.getScaledImage(key, this.scaleFactor, false);
|
|
}
|
|
|
|
public BufferedImage getColonyUpperRightBackground() {
|
|
final String key = "image.colony.upperRight.background";
|
|
return this.imageCache.getScaledImage(key, this.scaleFactor, false);
|
|
}
|
|
|
|
public BufferedImage getColonyWarehouseBackground() {
|
|
final String key = "image.colony.warehouse.background";
|
|
return this.imageCache.getScaledImage(key, this.scaleFactor, false);
|
|
}
|
|
|
|
// Goods image handling
|
|
|
|
private static String getGoodsTypeKey(GoodsType gt) {
|
|
return "image.icon." + gt.getId();
|
|
}
|
|
|
|
public BufferedImage getGoodsTypeImage(GoodsType gt, Dimension size) {
|
|
return this.imageCache.getSizedImage(getGoodsTypeKey(gt), size, false);
|
|
}
|
|
|
|
public BufferedImage getScaledGoodsTypeImage(GoodsType gt) {
|
|
return getGoodsTypeImage(gt, scale(ICON_SIZE));
|
|
}
|
|
|
|
public BufferedImage getSmallGoodsTypeImage(GoodsType gt) {
|
|
return getGoodsTypeImage(gt, scale(ICON_SIZE, SMALL_SCALE));
|
|
}
|
|
|
|
public BufferedImage getSmallerGoodsTypeImage(GoodsType gt) {
|
|
return getGoodsTypeImage(gt, scale(ICON_SIZE, SMALLER_SCALE));
|
|
}
|
|
|
|
|
|
// Nation image handling
|
|
|
|
private static String getMercenaryLeaderKey(int n) {
|
|
return "image.flavor.model.mercenaries." + n;
|
|
}
|
|
|
|
private static String getMonarchKey(String nationId) {
|
|
return "image.flavor.monarch." + nationId;
|
|
}
|
|
|
|
/**
|
|
* Returns the monarch-image for the given tile.
|
|
*
|
|
* @param nation The nation this monarch rules.
|
|
* @return the monarch-image for the given nation.
|
|
*/
|
|
public static BufferedImage getMonarchImage(Nation nation) {
|
|
final String key = getMonarchKey(nation.getId());
|
|
return getUnscaledImage(key);
|
|
}
|
|
|
|
/**
|
|
* Get the "monarch" image from a key.
|
|
*
|
|
* The key may be a nation identifier, or if an integer it is a
|
|
* mercenary leader.
|
|
*
|
|
* @param monarchKey The key to examine.
|
|
* @return A suitable {@code BufferedImage}.
|
|
*/
|
|
public static BufferedImage getMonarchImage(String monarchKey) {
|
|
String key;
|
|
try {
|
|
int n = Integer.parseInt(monarchKey);
|
|
key = getMercenaryLeaderKey(n);
|
|
} catch (Exception e) {
|
|
key = getMonarchKey(monarchKey);
|
|
}
|
|
return getUnscaledImage(key);
|
|
}
|
|
|
|
|
|
public static String getNationKey(Nation nation) {
|
|
return "image.miscicon." + nation.getId();
|
|
}
|
|
|
|
public BufferedImage getNationImage(Nation nation, Dimension size) {
|
|
return this.imageCache.getSizedImage(getNationKey(nation), size, false);
|
|
}
|
|
|
|
public BufferedImage getNationImage(Nation nation, float scale) {
|
|
return this.imageCache.getScaledImage(getNationKey(nation), scale, false);
|
|
}
|
|
|
|
public BufferedImage getScaledNationImage(Nation nation) {
|
|
return getNationImage(nation, this.scaleFactor);
|
|
}
|
|
|
|
public BufferedImage getSmallNationImage(Nation nation) {
|
|
return getNationImage(nation, this.scaleFactor * SMALL_SCALE);
|
|
}
|
|
|
|
public BufferedImage getSmallerNationImage(Nation nation) {
|
|
return getNationImage(nation, this.scaleFactor * SMALLER_SCALE);
|
|
}
|
|
|
|
public BufferedImage getUnscaledSmallerNationImage(Nation nation) {
|
|
return getNationImage(nation, SMALLER_SCALE);
|
|
}
|
|
|
|
|
|
// Path type image handling
|
|
|
|
/**
|
|
* Gets an image to represent the path of given path type.
|
|
*
|
|
* @param pt The {@code PathType}
|
|
* @return The {@code BufferedImage}.
|
|
*/
|
|
public BufferedImage getPathImage(PathType pt) {
|
|
return (pt == null) ? null
|
|
: this.imageCache.getScaledImage(pt.getImageKey(), this.scaleFactor, false);
|
|
}
|
|
|
|
/**
|
|
* Gets an image to represent the path of the given {@code Unit}.
|
|
*
|
|
* @param u The {@code Unit}
|
|
* @return The {@code BufferedImage}.
|
|
*/
|
|
public BufferedImage getPathImage(Unit u) {
|
|
return (u == null) ? null
|
|
: getPathImage(PathType.getPathType(u));
|
|
}
|
|
|
|
/**
|
|
* Gets an image to represent the path of the given {@code Unit}.
|
|
*
|
|
* @param pt The {@code PathType}
|
|
* @return The {@code BufferedImage}.
|
|
*/
|
|
private BufferedImage getPathNextTurnImage(PathType pt) {
|
|
return (pt == null) ? null
|
|
: this.imageCache.getScaledImage(pt.getNextTurnImageKey(), this.scaleFactor, false);
|
|
}
|
|
|
|
/**
|
|
* Gets an image to represent the path of the given {@code Unit}.
|
|
*
|
|
* @param u The {@code Unit}
|
|
* @return The {@code BufferedImage}.
|
|
*/
|
|
public BufferedImage getPathNextTurnImage(Unit u) {
|
|
return (u == null) ? null
|
|
: getPathNextTurnImage(PathType.getPathType(u));
|
|
}
|
|
|
|
|
|
// Terrain image handling
|
|
|
|
/**
|
|
* Returns the beach corner image at the given index.
|
|
*
|
|
* @param index The index of the image to return.
|
|
* @param x an {@code int} value
|
|
* @param y an {@code int} value
|
|
* @return The image at the given index.
|
|
*/
|
|
public BufferedImage getBeachCornerImage(int index, int x, int y) {
|
|
final String key = "image.tile.model.tile.beach.corner" + index;
|
|
return this.imageCache.getSizedImage(key, this.tileSize, false, variationSeedUsing(x, y));
|
|
}
|
|
|
|
/**
|
|
* Returns the beach edge image at the given index.
|
|
*
|
|
* @param index The index of the image to return.
|
|
* @param x an {@code int} value
|
|
* @param y an {@code int} value
|
|
* @return The image at the given index.
|
|
*/
|
|
public BufferedImage getBeachEdgeImage(int index, int x, int y) {
|
|
final String key = "image.tile.model.tile.beach.edge" + index;
|
|
return this.imageCache.getSizedImage(key, this.tileSize, false, variationSeedUsing(x, y));
|
|
}
|
|
|
|
public static ImageResource getBeachCenterImageResource() {
|
|
final String key = "image.tile.model.tile.beach";
|
|
return ImageCache.getImageResource(key);
|
|
}
|
|
|
|
/**
|
|
* Returns a transparent image for making a transition between
|
|
* the given tiles.
|
|
*
|
|
* @param tile The tile that should get a transition.
|
|
* @param direction The direction to get the bordering tile from..
|
|
* @param useNiceCorners Determines if the corners of the base transitions
|
|
* should be rendered nicely (takes more time).
|
|
* @param useVariations Uses variations of the transition.
|
|
* @return The image, or {@code null} if there is no transition that
|
|
* should be drawn.
|
|
*/
|
|
public BufferedImage getBaseTileTransitionImage(Tile tile, Direction direction, boolean useNiceCorners, boolean useVariations) {
|
|
return imageCreators.getBaseTileTransitionImageCreator().getBaseTileTransitionImage(tile, direction, useNiceCorners, useVariations);
|
|
}
|
|
|
|
/**
|
|
* Returns the border terrain-image for the given type.
|
|
*
|
|
* @param type The type of the terrain-image to return.
|
|
* @param direction a {@code Direction} value
|
|
* @param x The x-coordinate of the tile that is being drawn.
|
|
* @param y The x-coordinate of the tile that is being drawn.
|
|
* @return The terrain-image at the given index.
|
|
*/
|
|
public BufferedImage getBorderImage(TileType type, Direction direction,
|
|
int x, int y) {
|
|
final String key = "image.tile."
|
|
+ ((type==null) ? "model.tile.unexplored" : type.getId())
|
|
+ ".border." + direction;
|
|
return this.imageCache.getSizedImage(key, this.tileSize, false, variationSeedUsing(x, y));
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the forest image for a terrain type.
|
|
*
|
|
* @param type The type of the terrain-image to return.
|
|
* @param riverStyle An optional river style to apply.
|
|
* @param size The image size.
|
|
* @return The image at the given index.
|
|
*/
|
|
private BufferedImage getForestImageInternal(TileType type,
|
|
TileImprovementStyle riverStyle,
|
|
Dimension size) {
|
|
String key;
|
|
if (riverStyle != null) {
|
|
String mask = riverStyle.getMask();
|
|
// Ensure unconnected river tiles are visible in map editor
|
|
key = "image.tileforest." + type.getId() + ".s"
|
|
+ (("0000".equals(mask)) ? "0100" : mask);
|
|
// Safety check providing fallback for incomplete mods
|
|
if (ResourceManager.getImageResource(key, false) != null) {
|
|
return this.imageCache.getSizedImage(key, size, false);
|
|
}
|
|
}
|
|
key = "image.tileforest." + type.getId();
|
|
return this.imageCache.getSizedImage(key, size, false);
|
|
}
|
|
|
|
public BufferedImage getForestImage(TileType type, Dimension size) {
|
|
return getForestImageInternal(type, null, size);
|
|
}
|
|
|
|
public BufferedImage getScaledForestImage(TileType type) {
|
|
return getForestImageInternal(type, null, this.tileForestSize);
|
|
}
|
|
|
|
public BufferedImage getScaledForestImage(TileType type,
|
|
TileImprovementStyle riverStyle) {
|
|
return getForestImageInternal(type, riverStyle, this.tileForestSize);
|
|
}
|
|
|
|
public BufferedImage getForestCornerImage(TileType type, Direction corner) {
|
|
final String key = "image.tileforest." + type.getId() + "." + corner.toString().toLowerCase();
|
|
final ImageResource ir = ResourceManager.getImageResource(key, false);
|
|
if (ir == null) {
|
|
return null;
|
|
}
|
|
return imageCache.getSizedImage(key, this.tileForestSize, false, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the overlay-image for the given type and scale.
|
|
*
|
|
* @param type The type of the terrain-image to return.
|
|
* @param seed A seed for the tile instance that needs a random image.
|
|
* @param size The size of the image to return.
|
|
* @return A stable (with respect to id) random overlay image.
|
|
*/
|
|
private BufferedImage getOverlayImageInternal(TileType type, int seed,
|
|
Dimension size) {
|
|
final String key = "image.tileoverlay." + type.getId();
|
|
final ImageResource ir = ResourceManager.getImageResource(key, false);
|
|
if (ir == null) {
|
|
return null;
|
|
}
|
|
return imageCache.getSizedImage(key, size, false, seed);
|
|
}
|
|
|
|
/**
|
|
* Get the overlay-image for the given type and scale. To be
|
|
* placed above units.
|
|
*
|
|
* @param type The type of the terrain-image to return.
|
|
* @param seed A seed for the tile instance that needs a random image.
|
|
* @param size The size of the image to return.
|
|
* @return A stable (with respect to id) random overlay image.
|
|
*/
|
|
private BufferedImage getAboveTileImageInternal(TileType type, int seed,
|
|
Dimension size) {
|
|
final String key = "image.abovetile." + type.getId();
|
|
final ImageResource ir = ResourceManager.getImageResource(key, false);
|
|
if (ir == null) {
|
|
return null;
|
|
}
|
|
return imageCache.getSizedImage(key, size, false, seed);
|
|
}
|
|
|
|
public BufferedImage getScaledOverlayImage(Tile tile) {
|
|
return getOverlayImageInternal(tile.getType(), variationSeedUsing(tile.getX(), tile.getY()),
|
|
this.tileOverlaySize);
|
|
}
|
|
|
|
public BufferedImage getScaledAboveTileImage(Tile tile) {
|
|
return getAboveTileImageInternal(tile.getType(), variationSeedUsing(tile.getX(), tile.getY()),
|
|
this.tileOverlaySize);
|
|
}
|
|
|
|
public BufferedImage getSizedOverlayImage(TileType type, Dimension size) {
|
|
return getOverlayImageInternal(type, type.getId().hashCode(), size);
|
|
}
|
|
|
|
private static String getResourceTypeKey(ResourceType rt) {
|
|
return "image.tileitem." + rt.getId();
|
|
}
|
|
|
|
public BufferedImage getResourceTypeImage(ResourceType rt, Dimension size,
|
|
boolean grayscale) {
|
|
return this.imageCache.getSizedImage(getResourceTypeKey(rt), size, grayscale);
|
|
}
|
|
|
|
private BufferedImage getResourceTypeImage(ResourceType rt, float scale,
|
|
boolean grayscale) {
|
|
return this.imageCache.getScaledImage(getResourceTypeKey(rt), scale, grayscale);
|
|
}
|
|
|
|
public BufferedImage getScaledResourceTypeImage(ResourceType rt) {
|
|
return getResourceTypeImage(rt, this.scaleFactor, false);
|
|
}
|
|
|
|
public BufferedImage getSmallResourceTypeImage(ResourceType rt) {
|
|
return getResourceTypeImage(rt,
|
|
this.scaleFactor * SMALL_SCALE, false);
|
|
}
|
|
|
|
public BufferedImage getScaledResourceImage(Resource resource) {
|
|
return getResourceTypeImage(resource.getType(),
|
|
this.scaleFactor, false);
|
|
}
|
|
|
|
|
|
private static String getRiverStyleKey(String style) {
|
|
return RIVER_STYLE_PREFIX + style;
|
|
}
|
|
|
|
/**
|
|
* Returns the river image with the given style.
|
|
*
|
|
* @param style the style code
|
|
* @param size the image size
|
|
* @return The image with the given style.
|
|
*/
|
|
private BufferedImage getRiverImageInternal(String style, Dimension size) {
|
|
return this.imageCache.getSizedImage(getRiverStyleKey(style), size, false);
|
|
}
|
|
|
|
public BufferedImage getRiverImage(String style, Dimension size) {
|
|
return getRiverImageInternal(style, size);
|
|
}
|
|
|
|
/**
|
|
* Returns the river image with the given style and scale.
|
|
*
|
|
* @param style The improvement style identifier.
|
|
* @param scale A scale factor.
|
|
* @return The image with the given style.
|
|
*/
|
|
private BufferedImage getScaledRiverImage(String style, float scale) {
|
|
return getRiverImageInternal(style,
|
|
scaleDimension(this.tileSize, scale));
|
|
}
|
|
|
|
/**
|
|
* Returns the river image with the given style.
|
|
*
|
|
* @param style a {@code TileImprovementStyle} value
|
|
* @return The image with the given style.
|
|
*/
|
|
public BufferedImage getScaledRiverImage(TileImprovementStyle style) {
|
|
return getRiverImageInternal(style.getString(), this.tileSize);
|
|
}
|
|
|
|
public BufferedImage getScaledRiverImage(String style) {
|
|
return getScaledRiverImage(style, this.scaleFactor);
|
|
}
|
|
|
|
public BufferedImage getSmallerRiverImage(String style) {
|
|
return getScaledRiverImage(style, this.scaleFactor * SMALLER_SCALE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the river mouth terrain-image for the direction and magnitude.
|
|
*
|
|
* @param direction a {@code Direction} value
|
|
* @param magnitude an {@code int} value
|
|
* @param x The x-coordinate of the location of the tile that is being
|
|
* drawn (ignored).
|
|
* @param y The x-coordinate of the location of the tile that is being
|
|
* drawn (ignored).
|
|
* @return The terrain-image at the given index.
|
|
*/
|
|
public BufferedImage getRiverMouthImage(Direction direction, int magnitude,
|
|
int x, int y) {
|
|
final String key = "image.tile.model.tile.delta." + direction
|
|
+ ((magnitude == 1) ? ".small" : ".large");
|
|
return this.imageCache.getSizedImage(key, this.tileSize, false);
|
|
}
|
|
|
|
/**
|
|
* Get a list of river style image keys.
|
|
*
|
|
* @param all If true accept all non-0000 keys.
|
|
* @return A list of river style image keys.
|
|
*/
|
|
public static List<String> getRiverStyleKeys(final boolean all) {
|
|
return ResourceManager.getImageKeys(RIVER_STYLE_PREFIX)
|
|
.stream()
|
|
.map(key -> key.substring(RIVER_STYLE_PREFIX.length()))
|
|
.filter(style ->
|
|
(all || !style.contains("1")) && !"0000".equals(style))
|
|
.sorted().collect(Collectors.toList());
|
|
}
|
|
|
|
|
|
private static String getSettlementTypeKey(SettlementType settlementType) {
|
|
return "image.tileitem." + settlementType.getId();
|
|
}
|
|
|
|
private BufferedImage getSettlementTypeImage(SettlementType settlementType,
|
|
float scale) {
|
|
final String key = getSettlementTypeKey(settlementType);
|
|
return this.imageCache.getScaledImage(key, scale, false);
|
|
}
|
|
|
|
public BufferedImage getSettlementTypeImage(SettlementType settlementType,
|
|
Dimension size) {
|
|
final String key = getSettlementTypeKey(settlementType);
|
|
return this.imageCache.getSizedImage(key, size, false);
|
|
}
|
|
|
|
/**
|
|
* Returns the graphics that will represent the given settlement.
|
|
*
|
|
* @param settlementType The type of settlement whose graphics
|
|
* type is needed.
|
|
* @return The graphics that will represent the given settlement.
|
|
*/
|
|
public BufferedImage getScaledSettlementTypeImage(SettlementType settlementType) {
|
|
return getSettlementTypeImage(settlementType, this.scaleFactor);
|
|
}
|
|
|
|
public BufferedImage getSmallerSettlementTypeImage(SettlementType settlementType) {
|
|
return getSettlementTypeImage(settlementType,
|
|
this.scaleFactor * SMALLER_SCALE);
|
|
}
|
|
|
|
/**
|
|
* Get an image key for a settlement.
|
|
*
|
|
* @param settlement The {@code Settlement} to examine.
|
|
* @return An image key.
|
|
*/
|
|
public static String getSettlementKey(Settlement settlement) {
|
|
String key = getSettlementTypeKey(settlement.getType());
|
|
if (settlement instanceof Colony) {
|
|
Colony colony = (Colony)settlement;
|
|
if (colony.isUndead()) {
|
|
key += ".undead";
|
|
} else {
|
|
int count = colony.getApparentUnitCount();
|
|
key += (count <= 3) ? ".small"
|
|
: (count <= 7) ? ".medium"
|
|
: ".large";
|
|
String stockade = colony.getStockadeKey();
|
|
if (stockade != null) key += "." + stockade;
|
|
}
|
|
final String extraKey = key + "." + settlement.getOwner().getNationResourceKey();
|
|
if (ResourceManager.getImageResource(extraKey, false) != null) {
|
|
key = extraKey;
|
|
}
|
|
} else if (settlement instanceof IndianSettlement) {
|
|
IndianSettlement is = (IndianSettlement)settlement;
|
|
if (is.hasMissionary()) key += ".mission";
|
|
}
|
|
return key;
|
|
}
|
|
|
|
/**
|
|
* Returns the graphics that will represent the given settlement.
|
|
*
|
|
* @param settlement The settlement whose graphics type is needed.
|
|
* @param scale a {@code double} value
|
|
* @return The graphics that will represent the given settlement.
|
|
*/
|
|
public BufferedImage getSettlementImage(Settlement settlement,
|
|
float scale) {
|
|
return this.imageCache.getScaledImage(getSettlementKey(settlement), scale, false);
|
|
}
|
|
|
|
public BufferedImage getSettlementImage(Settlement settlement,
|
|
Dimension size) {
|
|
return this.imageCache.getSizedImage(getSettlementKey(settlement), size, false);
|
|
}
|
|
|
|
/**
|
|
* Returns the graphics that will represent the given settlement.
|
|
*
|
|
* @param settlement The settlement whose graphics type is needed.
|
|
* @return The graphics that will represent the given settlement.
|
|
*/
|
|
public BufferedImage getScaledSettlementImage(Settlement settlement) {
|
|
return getSettlementImage(settlement, this.scaleFactor);
|
|
}
|
|
|
|
public BufferedImage getSmallSettlementImage(Settlement settlement) {
|
|
return getSettlementImage(settlement, this.scaleFactor * SMALL_SCALE);
|
|
}
|
|
|
|
public BufferedImage getSmallerSettlementImage(Settlement settlement) {
|
|
return getSettlementImage(settlement, this.scaleFactor * SMALLER_SCALE);
|
|
}
|
|
|
|
|
|
public static String getTerrainImageKey(TileType type) {
|
|
return "image.tile."
|
|
+ ((type == null) ? "model.tile.unexplored" : type.getId())
|
|
+ ".center";
|
|
}
|
|
|
|
/**
|
|
* Gets the terrain-image for the given type.
|
|
*
|
|
* @param type The type of the terrain-image to return.
|
|
* @param x The x-coordinate of the location of the tile that is being
|
|
* drawn.
|
|
* @param y The x-coordinate of the location of the tile that is being
|
|
* drawn.
|
|
* @param size The image size.
|
|
* @return The terrain-image at the given index.
|
|
*/
|
|
private BufferedImage getTerrainImageInternal(TileType type,
|
|
int x, int y,
|
|
Dimension size) {
|
|
return this.imageCache.getSizedImage(getTerrainImageKey(type),
|
|
size, false, variationSeedUsing(x, y));
|
|
}
|
|
|
|
public ImageResource getTerrainMaskResource(Direction direction) {
|
|
return ResourceManager.getImageResource(getTerrainMaskKey(direction), true);
|
|
}
|
|
|
|
public BufferedImage getTerrainMask(Direction direction) {
|
|
final String key = getTerrainMaskKey(direction);
|
|
return this.imageCache.getSizedImage(key, this.tileSize, false);
|
|
}
|
|
|
|
private String getTerrainMaskKey(Direction direction) {
|
|
return (direction != null) ? "image.mask." + direction.toString().toLowerCase() : "image.mask";
|
|
}
|
|
|
|
public BufferedImage getTerrainImage(TileType type, int x, int y,
|
|
Dimension size) {
|
|
return getTerrainImageInternal(type, x, y, size);
|
|
}
|
|
|
|
public BufferedImage getScaledTerrainImage(TileType type, int x, int y) {
|
|
return getTerrainImageInternal(type, x, y, this.tileSize);
|
|
}
|
|
|
|
public BufferedImage getAnimatedScaledTerrainImage(TileType type, long ticks) {
|
|
final ImageResource imageResource = ImageCache.getImageResource(getTerrainImageKey(type));
|
|
if (imageResource == null) {
|
|
return null;
|
|
}
|
|
|
|
return imageCache.getCachedImage(imageResource,
|
|
this.tileSize,
|
|
false,
|
|
imageResource.getVariationNumberForTick(ticks));
|
|
}
|
|
|
|
/**
|
|
* Gets the combined animated image for river
|
|
*
|
|
* @param tile The tile.
|
|
* @param ticks The number of ticks to get the correct animation frame.
|
|
* @return A cached, generated image.
|
|
*/
|
|
public BufferedImage getAnimatedScaledRiverTerrainImage(Tile tile, long ticks) {
|
|
return imageCreators.getRiverAnimationImageCreator().getAnimatedScaledRiverTerrainImage(tile, ticks);
|
|
}
|
|
|
|
/**
|
|
* Gets the combined animated image for ocean and beach.
|
|
*
|
|
* @param type The tile type.
|
|
* @param directionsWithLand All directions where there are neighbouring land tiles.
|
|
* @param ticks The number of ticks to get the correct animation frame.
|
|
* @return A cached, genereated image.
|
|
*/
|
|
public BufferedImage getAnimatedScaledWaterAndBeachTerrainImage(TileType type, List<Direction> directionsWithLand, long ticks) {
|
|
return imageCreators.getBeachTileAnimationImageCreator().getAnimatedScaledWaterAndBeachTerrainImage(type, directionsWithLand, ticks);
|
|
}
|
|
|
|
/**
|
|
* Gets the combined animated image for the river delta.
|
|
*
|
|
* @param direction The direction to where the river is coming from.
|
|
* @param ticks The number of ticks to get the correct animation frame.
|
|
* @return A cached, genereated image.
|
|
*/
|
|
public BufferedImage getAnimatedScaledRiverDeltaTerrainImage(Direction direction, long ticks) {
|
|
return imageCreators.getDeltaAnimationImageCreator().getAnimatedScaledRiverDeltaTerrainImage(direction, ticks);
|
|
}
|
|
|
|
/**
|
|
* Get the tile improvement image with for a given identifier.
|
|
*
|
|
* @param id The tile improvement identifier.
|
|
* @return The image found, or null if it does not exist.
|
|
*/
|
|
public BufferedImage getTileImprovementImage(String id) {
|
|
final String key = "image.tile." + id;
|
|
return (ResourceManager.getImageResource(key, false) == null) ? null
|
|
// Has its own Overlay Image in Misc, use it
|
|
: this.imageCache.getSizedImage(key, this.tileSize, false);
|
|
}
|
|
|
|
/**
|
|
* Get a scaled terrain-image for a terrain type (and position 0, 0).
|
|
*
|
|
* Called from MapeEditorTransformPanel.buildList
|
|
*
|
|
* @param type The type of the terrain-image to return.
|
|
* @param size The maximum size of the terrain image to return.
|
|
* @return The terrain-image
|
|
*/
|
|
public BufferedImage getTileImageWithOverlayAndForest(TileType type,
|
|
Dimension size) {
|
|
int width = (size.width > 0) ? size.width
|
|
: ((2 * TILE_SIZE.width * size.height
|
|
+ (TILE_OVERLAY_SIZE.height+1))
|
|
/ (2 * ImageLibrary.TILE_OVERLAY_SIZE.height));
|
|
Dimension size2 = new Dimension(width, -1);
|
|
BufferedImage terrainImage = getTerrainImage(type, 0, 0, size2);
|
|
BufferedImage overlayImage = getSizedOverlayImage(type, size2);
|
|
BufferedImage forestImage = (type.isForested())
|
|
? getForestImage(type, size2)
|
|
: null;
|
|
if (overlayImage == null && forestImage == null) return terrainImage;
|
|
|
|
width = terrainImage.getWidth();
|
|
int height = terrainImage.getHeight();
|
|
if (overlayImage != null) {
|
|
height = Math.max(height, overlayImage.getHeight());
|
|
}
|
|
if (forestImage != null) {
|
|
height = Math.max(height, forestImage.getHeight());
|
|
}
|
|
final BufferedImage compositeImage = ImageUtils.createBufferedImage(width, height);
|
|
Graphics2D g = compositeImage.createGraphics();
|
|
g.drawImage(terrainImage, 0, height - terrainImage.getHeight(), null);
|
|
if (overlayImage != null) {
|
|
g.drawImage(overlayImage, 0, height - overlayImage.getHeight(), null);
|
|
}
|
|
if (forestImage != null) {
|
|
g.drawImage(forestImage, 0, height - forestImage.getHeight(), null);
|
|
}
|
|
g.dispose();
|
|
return compositeImage;
|
|
}
|
|
|
|
// Unit image handling
|
|
|
|
/**
|
|
* Get the unit image key for the given parameters.
|
|
*
|
|
* @param unitType The type of unit to be represented.
|
|
* @param owner An optional owning {@code Player}.
|
|
* @param roleId The id of the unit role.
|
|
* @param nativeEthnicity If true the unit is a former native.
|
|
* @return A suitable key.
|
|
*/
|
|
private static String getUnitTypeImageKey(UnitType unitType, Player owner,
|
|
String roleId,
|
|
boolean nativeEthnicity) {
|
|
// Units that can only be native don't need the .native key part
|
|
if (nativeEthnicity
|
|
&& unitType.hasAbility(Ability.BORN_IN_INDIAN_SETTLEMENT)) {
|
|
nativeEthnicity = false;
|
|
}
|
|
// Try to get an image matching the key
|
|
String roleQual = (Role.isDefaultRoleId(roleId)) ? ""
|
|
: "." + Role.getRoleIdSuffix(roleId);
|
|
String key = "image.unit." + unitType.getId() + roleQual
|
|
+ ((nativeEthnicity) ? ".native" : "");
|
|
if (nativeEthnicity
|
|
&& ResourceManager.getImageResource(key, false) == null) {
|
|
key = "image.unit." + unitType.getId() + roleQual;
|
|
}
|
|
if (owner != null) {
|
|
final String extraKey = key + "." + owner.getNationResourceKey();
|
|
if (ResourceManager.getImageResource(extraKey, false) != null) {
|
|
key = extraKey;
|
|
}
|
|
}
|
|
return key;
|
|
}
|
|
|
|
/**
|
|
* Fundamental unit image accessor.
|
|
*
|
|
* @param unitType The type of unit to be represented.
|
|
* @param owner An optional owning {@code Player}.
|
|
* @param roleId The id of the unit role.
|
|
* @param nativeEthnicity If true the unit is a former native.
|
|
* @param grayscale If true draw in inactive/disabled-looking state.
|
|
* @param scale How much the image is scaled.
|
|
* @return A suitable {@code BufferedImage}.
|
|
*/
|
|
private BufferedImage getUnitTypeImage(UnitType unitType, Player owner,
|
|
String roleId,
|
|
boolean nativeEthnicity,
|
|
boolean grayscale, float scale) {
|
|
final String key = getUnitTypeImageKey(unitType, owner, roleId,
|
|
nativeEthnicity);
|
|
return this.imageCache.getScaledImage(key, scale, grayscale);
|
|
}
|
|
|
|
private BufferedImage getUnitTypeImage(UnitType unitType,
|
|
float scale) {
|
|
return getUnitTypeImage(unitType, null, unitType.getDisplayRoleId(),
|
|
false, false, scale);
|
|
}
|
|
|
|
private BufferedImage getUnitTypeImage(UnitType unitType,
|
|
String roleId,
|
|
boolean nativeEthnicity,
|
|
Dimension size) {
|
|
final String key = getUnitTypeImageKey(unitType, null, roleId,
|
|
nativeEthnicity);
|
|
return this.imageCache.getSizedImage(key, size, false);
|
|
}
|
|
|
|
private BufferedImage getUnitTypeImage(UnitType unitType,
|
|
Dimension size) {
|
|
return getUnitTypeImage(unitType, unitType.getDisplayRoleId(),
|
|
false, size);
|
|
}
|
|
|
|
public BufferedImage getScaledUnitTypeImage(UnitType unitType) {
|
|
return getUnitTypeImage(unitType, null, unitType.getDisplayRoleId(),
|
|
false, false, this.scaleFactor);
|
|
}
|
|
|
|
public BufferedImage getSmallUnitTypeImage(UnitType unitType, String roleId,
|
|
boolean grayscale) {
|
|
return getUnitTypeImage(unitType, null, roleId, false,
|
|
grayscale, this.scaleFactor * SMALL_SCALE);
|
|
}
|
|
|
|
public BufferedImage getSmallUnitTypeImage(UnitType unitType,
|
|
boolean grayscale) {
|
|
return getSmallUnitTypeImage(unitType, unitType.getDisplayRoleId(),
|
|
grayscale);
|
|
}
|
|
|
|
public BufferedImage getSmallUnitTypeImage(UnitType unitType) {
|
|
return getSmallUnitTypeImage(unitType, unitType.getDisplayRoleId(),
|
|
false);
|
|
}
|
|
|
|
public BufferedImage getSmallerUnitTypeImage(UnitType unitType) {
|
|
return getUnitTypeImage(unitType, null, unitType.getDisplayRoleId(),
|
|
false, false,
|
|
this.scaleFactor * SMALLER_SCALE);
|
|
}
|
|
|
|
public BufferedImage getTinyUnitTypeImage(UnitType unitType,
|
|
boolean grayscale) {
|
|
return getUnitTypeImage(unitType, null, unitType.getDisplayRoleId(),
|
|
false, grayscale,
|
|
this.scaleFactor * TINY_SCALE);
|
|
}
|
|
|
|
public BufferedImage getTinyUnitTypeImage(UnitType unitType) {
|
|
return getTinyUnitTypeImage(unitType, false);
|
|
}
|
|
|
|
private BufferedImage getUnitImage(Unit unit, boolean grayscale,
|
|
float scale) {
|
|
return getUnitTypeImage(unit.getType(), unit.getOwner(),
|
|
unit.getRole().getId(),
|
|
unit.hasNativeEthnicity(), grayscale, scale);
|
|
}
|
|
|
|
public BufferedImage getScaledUnitImage(Unit unit, boolean grayscale) {
|
|
return getUnitImage(unit, grayscale, this.scaleFactor);
|
|
}
|
|
|
|
public BufferedImage getScaledUnitImage(Unit unit) {
|
|
return getScaledUnitImage(unit, false);
|
|
}
|
|
|
|
public BufferedImage getSmallUnitImage(Unit unit, boolean grayscale) {
|
|
return getUnitImage(unit, grayscale,
|
|
this.scaleFactor * SMALL_SCALE);
|
|
}
|
|
|
|
public BufferedImage getSmallUnitImage(Unit unit) {
|
|
return getSmallUnitImage(unit, false);
|
|
}
|
|
|
|
public BufferedImage getSmallerUnitImage(Unit unit) {
|
|
return getUnitImage(unit, false,
|
|
this.scaleFactor * SMALLER_SCALE);
|
|
}
|
|
|
|
public BufferedImage getTinyUnitImage(Unit unit) {
|
|
return getUnitImage(unit, false,
|
|
this.scaleFactor * TINY_SCALE);
|
|
}
|
|
|
|
|
|
// Accessors for small "chip" images.
|
|
// TODO: cache these too?
|
|
|
|
/**
|
|
* Gets a chip image for the alarm at an Indian settlement.
|
|
* The background is either the native owner's, or that of the
|
|
* most-hatednation, if any.
|
|
*
|
|
* @param g Graphics2D for getting the FontMetrics.
|
|
* @param is The {@code IndianSettlement} to check.
|
|
* @param player The observing {@code Player}.
|
|
* @return An alarm chip, or null if none suitable.
|
|
*/
|
|
public BufferedImage getAlarmChip(Graphics2D g, IndianSettlement is,
|
|
Player player) {
|
|
if (player == null || !is.hasContacted(player)) return null;
|
|
Color ownerColor = is.getOwner().getNationColor();
|
|
Player enemy = is.getMostHated();
|
|
Color enemyColor = (enemy == null) ? Nation.UNKNOWN_NATION_COLOR
|
|
: enemy.getNationColor();
|
|
// Set amount to [0-4] corresponding to HAPPY, CONTENT,
|
|
// DISPLEASED, ANGRY, HATEFUL but only if the player is the
|
|
// most hated, because other nation alarm is not nor should be
|
|
// serialized to the client.
|
|
int amount = 4;
|
|
if (enemy == null) {
|
|
amount = 0;
|
|
} else if (player == enemy) {
|
|
Tension alarm = is.getAlarm(enemy);
|
|
amount = (alarm == null) ? 4 : alarm.getLevel().ordinal();
|
|
if (amount == 0) amount = 1; // Show *something*!
|
|
}
|
|
Color foreground = makeForegroundColor(enemyColor);
|
|
final String text = ResourceManager.getString((is.worthScouting(player))
|
|
? "indianAlarmChip.contacted" : "indianAlarmChip.scouted");
|
|
return createChip(g, text, Color.BLACK,
|
|
ownerColor, amount/4.0, enemyColor, foreground, true);
|
|
}
|
|
|
|
/**
|
|
* Gets the owner chip for the settlement.
|
|
*
|
|
* @param g Graphics2D for getting the FontMetrics.
|
|
* @param is The {@code IndianSettlement} to check.
|
|
* @return A chip.
|
|
*/
|
|
public BufferedImage getIndianSettlementChip(Graphics2D g,
|
|
IndianSettlement is) {
|
|
final String key = ResourceManager.getString("indianSettlementChip."
|
|
+ ((is.getType().isCapital()) ? "capital" : "normal"));
|
|
Color background = is.getOwner().getNationColor();
|
|
return createChip(g, key, Color.BLACK, background, 0, Color.BLACK,
|
|
makeForegroundColor(background), true);
|
|
}
|
|
|
|
/**
|
|
* Gets the mission chip for a native settlement.
|
|
*
|
|
* @param g Graphics2D for getting the FontMetrics.
|
|
* @param owner The player that owns the mission.
|
|
* @param expert True if the unit is an expert.
|
|
* @return A suitable chip, or null if no mission is present.
|
|
*/
|
|
public BufferedImage getMissionChip(Graphics2D g,
|
|
Player owner, boolean expert) {
|
|
final Color background = owner.getNationColor();
|
|
final String key = "color.foreground.mission."
|
|
+ ((expert) ? "expert" : "normal");
|
|
final Color foreground = getColor(key,
|
|
(expert) ? Color.BLACK : Color.GRAY);
|
|
return createChip(g, ResourceManager.getString("cross"), Color.BLACK,
|
|
background, 0, Color.BLACK, foreground, true);
|
|
}
|
|
|
|
/**
|
|
* Gets a chip for an occupation indicator, i.e. a small image with a
|
|
* single letter or symbol that indicates the Unit's state.
|
|
*
|
|
* @param g Graphics2D for getting the FontMetrics.
|
|
* @param unit The {@code Unit} with the occupation.
|
|
* @param text The text for the chip.
|
|
* @return A suitable chip.
|
|
*/
|
|
public BufferedImage getOccupationIndicatorChip(Graphics2D g,
|
|
Unit unit, String text) {
|
|
final Color background = unit.getOwner().getNationColor();
|
|
final Color foreground = (unit.getState() == Unit.UnitState.FORTIFIED)
|
|
? Color.GRAY : makeForegroundColor(background);
|
|
return createChip(g, text, Color.BLACK,
|
|
background, 0, Color.BLACK, foreground, true);
|
|
}
|
|
|
|
/**
|
|
* Create a "chip" with the given text and colors.
|
|
*
|
|
* @param g Graphics2D for getting the FontMetrics.
|
|
* @param text The text to display.
|
|
* @param border The border {@code Color}.
|
|
* @param background The background {@code Color}.
|
|
* @param amount How much to fill the chip with the fill color
|
|
* @param fill The fill {@code Color}.
|
|
* @param foreground The foreground {@code Color}.
|
|
* @param filled Whether the chip is filled or not
|
|
* @return A chip.
|
|
*/
|
|
private BufferedImage createChip(Graphics2D g, String text,
|
|
Color border, Color background,
|
|
double amount, Color fill,
|
|
Color foreground,
|
|
boolean filled) {
|
|
final Font font = getScaledFont("simple-bold-tiny", null);
|
|
final FontMetrics fm = g.getFontMetrics(font);
|
|
final int padding = scaleInt(6);
|
|
final int width = fm.stringWidth(text) + padding;
|
|
final int height = fm.getMaxAscent() + fm.getMaxDescent() + padding;
|
|
final BufferedImage bi = ImageUtils.createBufferedImage(width, height);
|
|
final Graphics2D g2 = bi.createGraphics();
|
|
g2.setFont(font);
|
|
g2.setColor(border);
|
|
g2.fillRect(0, 0, width, height);
|
|
g2.setColor(background);
|
|
g2.fillRect(1, 1, width - 2, height - 2);
|
|
if (!filled) {
|
|
if (amount > 0.0 && amount <= 1.0) {
|
|
g2.setColor(fill);
|
|
g2.fillRect(1, 1, width - 2, (int)((height - 2) * amount));
|
|
}
|
|
}
|
|
g2.setColor(foreground);
|
|
g2.drawString(text, padding/2, fm.getMaxAscent() + padding/2);
|
|
g2.dispose();
|
|
return bi;
|
|
}
|
|
|
|
|
|
// Get special images for strings with particular color and font
|
|
|
|
/**
|
|
* Gets an image with a string of a given color and with
|
|
* a black border around the glyphs.
|
|
*
|
|
* @param g A {@code Graphics}-object for getting the font metrics.
|
|
* @param text The {@code String} to make an image of.
|
|
* @param color The {@code Color} to use for the text.
|
|
* @param font The {@code Font} to display the text with.
|
|
* @return The {@code BufferedImage} found or created.
|
|
*/
|
|
public BufferedImage getStringImage(Graphics g, String text, Color color,
|
|
Font font) {
|
|
if (color == null) {
|
|
logger.warning("getStringImage(" + text + ") called with color null");
|
|
color = Color.WHITE;
|
|
}
|
|
|
|
// Check the cache
|
|
final String key = text
|
|
+ "." + font.getFontName().replace(' ', '-')
|
|
+ "." + Integer.toString(font.getSize())
|
|
+ "." + Integer.toHexString(color.getRGB());
|
|
BufferedImage img = this.stringImageCache.get(key);
|
|
if (img != null) return img;
|
|
|
|
img = createStringImage(text, color, font, g.getFontMetrics(font));
|
|
this.stringImageCache.put(key, img);
|
|
return img;
|
|
}
|
|
|
|
/**
|
|
* Create a string image.
|
|
*
|
|
* @param text The {@code String} to make an image of.
|
|
* @param color The {@code Color} to use for the text.
|
|
* @param font The {@code Font} to display the text with.
|
|
* @param fm The {@code FontMetrics} to use with the font.
|
|
* @return The image that was created.
|
|
*/
|
|
private BufferedImage createStringImage(String text, Color color,
|
|
Font font, FontMetrics fm) {
|
|
final int width = fm.stringWidth(text) + 4;
|
|
final int height = fm.getMaxAscent() + fm.getMaxDescent();
|
|
if (width <= 0 || height <= 0) {
|
|
return ImageUtils.createBufferedImage(1, 1);
|
|
}
|
|
final BufferedImage img = ImageUtils.createBufferedImage(width, height);
|
|
// draw the string with selected color
|
|
Graphics2D g = img.createGraphics();
|
|
g.setColor(makeStringBorderColor(color));
|
|
g.setFont(font);
|
|
g.drawString(text, 2, fm.getMaxAscent());
|
|
|
|
// draw the border around letters
|
|
int borderWidth = 1;
|
|
int borderColor = makeStringBorderColor(color).getRGB();
|
|
int srcRGB, dstRGB, srcA;
|
|
for (int biY = 0; biY < height; biY++) {
|
|
for (int biX = borderWidth; biX < width - borderWidth; biX++) {
|
|
int biXI = width - biX - 1;
|
|
for (int d = 1; d <= borderWidth; d++) {
|
|
// left to right
|
|
srcRGB = img.getRGB(biX, biY);
|
|
srcA = (srcRGB >> 24) & 0xFF;
|
|
dstRGB = img.getRGB(biX - d, biY);
|
|
if (dstRGB != borderColor) {
|
|
if (srcA > 0) {
|
|
img.setRGB(biX, biY, borderColor);
|
|
img.setRGB(biX - d, biY, srcRGB);
|
|
}
|
|
}
|
|
// right to left
|
|
srcRGB = img.getRGB(biXI, biY);
|
|
srcA = (srcRGB >> 24) & 0xFF;
|
|
dstRGB = img.getRGB(biXI + d, biY);
|
|
if (dstRGB != borderColor) {
|
|
if (srcA > 0) {
|
|
img.setRGB(biXI, biY, borderColor);
|
|
img.setRGB(biXI + d, biY, srcRGB);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (int biX = 0; biX < width; biX++) {
|
|
for (int biY = borderWidth; biY < height - borderWidth; biY++) {
|
|
int biYI = height - biY - 1;
|
|
for (int d = 1; d <= borderWidth; d++) {
|
|
// top to bottom
|
|
srcRGB = img.getRGB(biX, biY);
|
|
srcA = (srcRGB >> 24) & 0xFF;
|
|
dstRGB = img.getRGB(biX, biY - d);
|
|
if (dstRGB != borderColor) {
|
|
if (srcA > 0) {
|
|
img.setRGB(biX, biY, borderColor);
|
|
img.setRGB(biX, biY - d, srcRGB);
|
|
}
|
|
}
|
|
// bottom to top
|
|
srcRGB = img.getRGB(biX, biYI);
|
|
srcA = (srcRGB >> 24) & 0xFF;
|
|
dstRGB = img.getRGB(biX, biYI + d);
|
|
if (dstRGB != borderColor) {
|
|
if (srcA > 0) {
|
|
img.setRGB(biX, biYI, borderColor);
|
|
img.setRGB(biX, biYI + d, srcRGB);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
g.setColor(color);
|
|
g.drawString(text, 2, fm.getMaxAscent());
|
|
g.dispose();
|
|
return img;
|
|
}
|
|
|
|
|
|
// Video
|
|
|
|
public static Video getVideo(String key) {
|
|
return ResourceManager.getVideo(key);
|
|
}
|
|
}
|