From d008548306dad3df8530da6975974d23f2d6b107 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Fri, 12 Aug 2011 14:44:49 +0800 Subject: [PATCH] Add support for JPEG encoding option for HDMaps --- src/main/java/org/dynmap/DynmapWorld.java | 15 +++++++----- src/main/java/org/dynmap/MapType.java | 18 +++++++++++---- src/main/java/org/dynmap/flat/FlatMap.java | 1 - src/main/java/org/dynmap/hdmap/HDMap.java | 23 ++++++++++++------- src/main/java/org/dynmap/hdmap/HDMapTile.java | 13 ++++++----- .../org/dynmap/hdmap/IsoHDPerspective.java | 10 ++++---- src/main/java/org/dynmap/kzedmap/KzedMap.java | 1 - .../org/dynmap/utils/FileLockManager.java | 18 ++++++++++++++- .../org/dynmap/web/handlers/FileHandler.java | 1 + web/js/dynmaputils.js | 3 ++- web/js/hdmap.js | 2 +- 11 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/dynmap/DynmapWorld.java b/src/main/java/org/dynmap/DynmapWorld.java index ecaf64ba..72b9f37b 100644 --- a/src/main/java/org/dynmap/DynmapWorld.java +++ b/src/main/java/org/dynmap/DynmapWorld.java @@ -107,9 +107,10 @@ public class DynmapWorld { private static class PNGFileFilter implements FilenameFilter { String prefix; - public PNGFileFilter(String pre) { prefix = pre; } + String suffix; + public PNGFileFilter(String pre, MapType.ImageFormat fmt) { prefix = pre; suffix = "." + fmt.getFileExt(); } public boolean accept(File f, String n) { - if(n.endsWith(".png") && n.startsWith(prefix)) { + if(n.endsWith(suffix) && n.startsWith(prefix)) { File fn = new File(f, n); return fn.isFile(); } @@ -147,6 +148,7 @@ public class DynmapWorld { String zfnprefix; int bigworldshift; boolean isbigmap; + MapType.ImageFormat fmt; } public boolean freshenZoomOutFilesByLevel(int zoomlevel) { @@ -253,6 +255,7 @@ public class DynmapWorld { pd.zoomprefix = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz".substring(0, zoomlevel); pd.bigworldshift = bigworldshift; pd.isbigmap = mt.isBigWorldMap(this); + pd.fmt = mt.getImageFormat(); if(pd.isbigmap) { if(zoomlevel > 0) { pd.zoomprefix += "_"; @@ -283,15 +286,15 @@ public class DynmapWorld { private String makeFilePath(PrefixData pd, int x, int y, boolean zoomed) { if(pd.isbigmap) - return pd.baseprefix + "/" + (x >> pd.bigworldshift) + "_" + (y >> pd.bigworldshift) + "/" + (zoomed?pd.zfnprefix:pd.fnprefix) + x + "_" + y + ".png"; + return pd.baseprefix + "/" + (x >> pd.bigworldshift) + "_" + (y >> pd.bigworldshift) + "/" + (zoomed?pd.zfnprefix:pd.fnprefix) + x + "_" + y + "." + pd.fmt.getFileExt(); else - return (zoomed?pd.zfnprefix:pd.fnprefix) + "_" + x + "_" + y + ".png"; + return (zoomed?pd.zfnprefix:pd.fnprefix) + "_" + x + "_" + y + "." + pd.fmt.getFileExt(); } private int processZoomDirectory(File dir, PrefixData pd) { Debug.debug("processZoomDirectory(" + dir.getPath() + "," + pd.baseprefix + ")"); HashMap toprocess = new HashMap(); - String[] files = dir.list(new PNGFileFilter(pd.fnprefix)); + String[] files = dir.list(new PNGFileFilter(pd.fnprefix, pd.fmt)); if(files == null) return 0; for(String fn : files) { @@ -435,7 +438,7 @@ public class DynmapWorld { try { if(!zf.getParentFile().exists()) zf.getParentFile().mkdirs(); - FileLockManager.imageIOWrite(zIm, "png", zf); + FileLockManager.imageIOWrite(zIm, pd.fmt.getFileExt(), zf); Debug.debug("Saved zoom-out tile at " + zf.getPath()); } catch (IOException e) { Debug.error("Failed to save zoom-out tile: " + zf.getName(), e); diff --git a/src/main/java/org/dynmap/MapType.java b/src/main/java/org/dynmap/MapType.java index 3515147e..ba1d9642 100644 --- a/src/main/java/org/dynmap/MapType.java +++ b/src/main/java/org/dynmap/MapType.java @@ -1,20 +1,27 @@ package org.dynmap; -import java.io.File; import java.util.List; import org.bukkit.Location; -import org.dynmap.utils.MapChunkCache; import org.json.simple.JSONObject; public abstract class MapType { + public enum ImageFormat { + FORMAT_PNG("png"), + FORMAT_JPG("jpg"); + + String ext; + ImageFormat(String ext) { + this.ext = ext; + } + public String getFileExt() { return ext; } + }; + public abstract MapTile[] getTiles(Location l); public abstract MapTile[] getAdjecentTiles(MapTile tile); public abstract List getRequiredChunks(MapTile tile); - - public abstract boolean render(MapChunkCache cache, MapTile tile, File outputFile); public void buildClientConfiguration(JSONObject worldObject, DynmapWorld w) { } @@ -41,6 +48,9 @@ public abstract class MapType { public abstract boolean isBigWorldMap(DynmapWorld w); /* Return number of zoom levels needed by this map (before extra levels from extrazoomout) */ public int getMapZoomOutLevels() { return 0; } + + public ImageFormat getImageFormat() { return ImageFormat.FORMAT_PNG; } + /** * Step sequence for creating zoomed file: first index is top-left, second top-right, third bottom-left, forth bottom-right * Values correspond to tile X,Y (0), X+step,Y (1), X,Y+step (2), X+step,Y+step (3) diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index e8f501b3..762a72da 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -124,7 +124,6 @@ public class FlatMap extends MapType { return result; } - @Override public boolean render(MapChunkCache cache, MapTile tile, File outputFile) { FlatMapTile t = (FlatMapTile) tile; World w = t.getWorld(); diff --git a/src/main/java/org/dynmap/hdmap/HDMap.java b/src/main/java/org/dynmap/hdmap/HDMap.java index 66d3ba63..3a672990 100644 --- a/src/main/java/org/dynmap/hdmap/HDMap.java +++ b/src/main/java/org/dynmap/hdmap/HDMap.java @@ -19,6 +19,7 @@ import org.dynmap.utils.MapChunkCache; import org.json.simple.JSONObject; public class HDMap extends MapType { + private String name; private String prefix; private HDPerspective perspective; @@ -26,6 +27,11 @@ public class HDMap extends MapType { private HDLighting lighting; private ConfigurationNode configuration; private int mapzoomout; + private MapType.ImageFormat imgformat; + + public static final String IMGFORMAT_PNG = "png"; + public static final String IMGFORMAT_JPG = "jpg"; + public HDMap(ConfigurationNode configuration) { name = configuration.getString("name", null); @@ -83,6 +89,12 @@ public class HDMap extends MapType { mapzoomout++; scale = scale / 2.0; } + String fmt = configuration.getString("image-format", "png"); + /* Only allow png or jpg */ + if(fmt.equals(IMGFORMAT_PNG)) + imgformat = ImageFormat.FORMAT_PNG; + else + imgformat = ImageFormat.FORMAT_JPG; } public HDShader getShader() { return shader; } @@ -104,14 +116,6 @@ public class HDMap extends MapType { return perspective.getRequiredChunks(tile); } - @Override - public boolean render(MapChunkCache cache, MapTile tile, File bogus) { - if(tile instanceof HDMapTile) - return perspective.render(cache, (HDMapTile)tile); - else - return false; - } - @Override public List baseZoomFilePrefixes() { ArrayList s = new ArrayList(); @@ -181,6 +185,8 @@ public class HDMap extends MapType { return lst; } + @Override + public ImageFormat getImageFormat() { return imgformat; } @Override public void buildClientConfiguration(JSONObject worldObject, DynmapWorld world) { @@ -197,6 +203,7 @@ public class HDMap extends MapType { s(o, "bigmap", true); s(o, "mapzoomout", (world.getExtraZoomOutLevels()+mapzoomout)); s(o, "mapzoomin", c.getInteger("mapzoomin", 2)); + s(o, "image-format", imgformat.getFileExt()); perspective.addClientConfiguration(o); shader.addClientConfiguration(o); lighting.addClientConfiguration(o); diff --git a/src/main/java/org/dynmap/hdmap/HDMapTile.java b/src/main/java/org/dynmap/hdmap/HDMapTile.java index 53293e81..5b3ba6fd 100644 --- a/src/main/java/org/dynmap/hdmap/HDMapTile.java +++ b/src/main/java/org/dynmap/hdmap/HDMapTile.java @@ -3,6 +3,7 @@ package org.dynmap.hdmap; import org.dynmap.DynmapChunk; import org.dynmap.DynmapWorld; import org.dynmap.MapManager; +import org.dynmap.MapType; import java.util.List; import org.dynmap.MapTile; @@ -21,20 +22,20 @@ public class HDMapTile extends MapTile { @Override public String getFilename() { - return getFilename("hdmap"); + return getFilename("hdmap", MapType.ImageFormat.FORMAT_PNG); } - public String getFilename(String prefix) { - return prefix + "/" + (tx >> 5) + '_' + (ty >> 5) + '/' + tx + "_" + ty + ".png"; + public String getFilename(String prefix, MapType.ImageFormat format) { + return prefix + "/" + (tx >> 5) + '_' + (ty >> 5) + '/' + tx + "_" + ty + "." + format.getFileExt(); } @Override public String getDayFilename() { - return getDayFilename("hdmap"); + return getDayFilename("hdmap", MapType.ImageFormat.FORMAT_PNG); } - public String getDayFilename(String prefix) { - return prefix + "_day/" + (tx >> 5) + '_' + (ty >> 5) + '/' + tx + "_" + ty + ".png"; + public String getDayFilename(String prefix, MapType.ImageFormat format) { + return prefix + "_day/" + (tx >> 5) + '_' + (ty >> 5) + '/' + tx + "_" + ty + "." + format.getFileExt(); } @Override diff --git a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index 0ba35fdb..5a5aa6dd 100644 --- a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -21,6 +21,7 @@ import org.dynmap.DynmapChunk; import org.dynmap.Log; import org.dynmap.MapManager; import org.dynmap.MapTile; +import org.dynmap.MapType; import org.dynmap.TileHashManager; import org.dynmap.debug.Debug; import org.dynmap.utils.MapIterator.BlockStep; @@ -967,7 +968,8 @@ public class IsoHDPerspective implements HDPerspective { String prefix = shaderstate[i].getMap().getPrefix(); if(rendered[i]) { renderone = true; - String fname = tile.getFilename(prefix); + MapType.ImageFormat fmt = shaderstate[i].getMap().getImageFormat(); + String fname = tile.getFilename(prefix, fmt); File f = new File(tile.getDynmapWorld().worldtilepath, fname); FileLockManager.getWriteLock(f); try { @@ -977,7 +979,7 @@ public class IsoHDPerspective implements HDPerspective { if(!f.getParentFile().exists()) f.getParentFile().mkdirs(); try { - FileLockManager.imageIOWrite(im[i].buf_img, "png", f); + FileLockManager.imageIOWrite(im[i].buf_img, fmt.getFileExt(), f); } catch (IOException e) { Debug.error("Failed to save image: " + f.getPath(), e); } catch (java.lang.NullPointerException e) { @@ -998,7 +1000,7 @@ public class IsoHDPerspective implements HDPerspective { MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[i]); /* Handle day image, if needed */ if(dayim[i] != null) { - fname = tile.getDayFilename(prefix); + fname = tile.getDayFilename(prefix, fmt); f = new File(tile.getDynmapWorld().worldtilepath, fname); FileLockManager.getWriteLock(f); prefix = prefix+"_day"; @@ -1010,7 +1012,7 @@ public class IsoHDPerspective implements HDPerspective { if(!f.getParentFile().exists()) f.getParentFile().mkdirs(); try { - FileLockManager.imageIOWrite(dayim[i].buf_img, "png", f); + FileLockManager.imageIOWrite(dayim[i].buf_img, fmt.getFileExt(), f); } catch (IOException e) { Debug.error("Failed to save image: " + f.getPath(), e); } catch (java.lang.NullPointerException e) { diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index b0050b29..70169c64 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -213,7 +213,6 @@ public class KzedMap extends MapType { } } - @Override public boolean render(MapChunkCache cache, MapTile tile, File outputFile) { if (tile instanceof KzedMapTile) { return ((KzedMapTile) tile).renderer.render(cache, (KzedMapTile) tile, outputFile); diff --git a/src/main/java/org/dynmap/utils/FileLockManager.java b/src/main/java/org/dynmap/utils/FileLockManager.java index 0e1b5b44..f257361c 100644 --- a/src/main/java/org/dynmap/utils/FileLockManager.java +++ b/src/main/java/org/dynmap/utils/FileLockManager.java @@ -6,6 +6,8 @@ import java.io.FileOutputStream; import java.io.RandomAccessFile; import java.util.HashMap; import java.awt.image.BufferedImage; +import java.awt.image.DirectColorModel; +import java.awt.image.WritableRaster; import java.io.IOException; import javax.imageio.ImageIO; @@ -145,7 +147,21 @@ public class FileLockManager { byte[] rslt; synchronized(baos_lock) { baos.reset(); - ImageIO.write(img, type, baos); /* Write to byte array stream - prevent bogus I/O errors */ + if(type.equals("jpg")) { + WritableRaster raster = img.getRaster(); + WritableRaster newRaster = raster.createWritableChild(0, 0, img.getWidth(), + img.getHeight(), 0, 0, new int[] {0, 1, 2}); + DirectColorModel cm = (DirectColorModel)img.getColorModel(); + DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), + cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask()); + // now create the new buffer that is used ot write the image: + BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); + ImageIO.write(rgbBuffer, type, baos); /* Write to byte array stream - prevent bogus I/O errors */ + rgbBuffer.flush(); + } + else { + ImageIO.write(img, type, baos); /* Write to byte array stream - prevent bogus I/O errors */ + } rslt = baos.toByteArray(); } while(!done) { diff --git a/src/main/java/org/dynmap/web/handlers/FileHandler.java b/src/main/java/org/dynmap/web/handlers/FileHandler.java index 36e7c3d1..e00ea5dc 100644 --- a/src/main/java/org/dynmap/web/handlers/FileHandler.java +++ b/src/main/java/org/dynmap/web/handlers/FileHandler.java @@ -27,6 +27,7 @@ public abstract class FileHandler implements HttpHandler { mimes.put(".htm", "text/html"); mimes.put(".js", "text/javascript"); mimes.put(".png", "image/png"); + mimes.put(".jpg", "image/jpeg"); mimes.put(".css", "text/css"); mimes.put(".txt", "text/plain"); } diff --git a/web/js/dynmaputils.js b/web/js/dynmaputils.js index 854334c0..9bbf5181 100644 --- a/web/js/dynmaputils.js +++ b/web/js/dynmaputils.js @@ -260,7 +260,8 @@ var DynmapTileLayer = L.TileLayer.extend({ zoom: this.zoomprefix(zoomoutlevel), zoomprefix: (zoomoutlevel==0)?"":(this.zoomprefix(zoomoutlevel)+"_"), x: x, - y: y + y: y, + fmt: this.options['image-format'] || 'png' }; } }); diff --git a/web/js/hdmap.js b/web/js/hdmap.js index 271803e5..3a33bafd 100644 --- a/web/js/hdmap.js +++ b/web/js/hdmap.js @@ -36,7 +36,7 @@ var HDMapType = DynmapTileLayer.extend({ // Y is inverted for HD-map. info.y = -info.y; info.scaledy = info.y >> 5; - return namedReplace('{prefix}{nightday}/{scaledx}_{scaledy}/{zoom}{x}_{y}.png', info); + return namedReplace('{prefix}{nightday}/{scaledx}_{scaledy}/{zoom}{x}_{y}.{fmt}', info); }, zoomprefix: function(amount) { // amount == 0 -> ''