Skip to content

Commit

Permalink
[GEOS-8962] Add support for image/vnd.jpeg-png8
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Oct 4, 2018
1 parent e7eebba commit bf70e51
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 23 deletions.
3 changes: 3 additions & 0 deletions doc/en/user/source/services/wms/outputformats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ where ``<format>`` is any of the options below.
if fully opaque and not paletted. In order to use this format in a meaningful way the GetMap must include a "&transparent=TRUE" parameter,
as without it GeoServer generates opaque images with the default/requested background color, making this format always return JPEG images (or always PNG, if they are paletted).
When using the layer preview to test this format, remember to add "&transparent=TRUE" to the preview URL, as normally the preview generates non transparent images.
* - JPEG-PNG8
- ``format=image/vnd.jpeg-png8``
- Same as JPEG-PNG, but generating a paletted output if the PNG format is chosen
* - GIF
- ``format=image/gif``
-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
# without introducing unneeded dependencies. Multiple resources names like this
# one can exist in other jar files and GWC will take care of loading them
#
formats.vector = image/png, image/png8, image/jpeg, image/gif, image/vnd.jpeg-png
formats.raster = image/png, image/png8, image/jpeg, image/gif, image/vnd.jpeg-png
formats.layergroup = image/png, image/png8, image/jpeg, image/gif, image/vnd.jpeg-png
formats.vector = image/png, image/png8, image/jpeg, image/gif, image/vnd.jpeg-png, image/vnd.jpeg-png8
formats.raster = image/png, image/png8, image/jpeg, image/gif, image/vnd.jpeg-png, image/vnd.jpeg-png8
formats.layergroup = image/png, image/png8, image/jpeg, image/gif, image/vnd.jpeg-png, image/vnd.jpeg-png8
36 changes: 32 additions & 4 deletions src/gwc/src/test/java/org/geoserver/gwc/GWCTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,17 @@
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -86,7 +96,15 @@
import org.geowebcache.GeoWebCacheEnvironment;
import org.geowebcache.GeoWebCacheException;
import org.geowebcache.GeoWebCacheExtensions;
import org.geowebcache.config.*;
import org.geowebcache.config.BlobStoreInfo;
import org.geowebcache.config.ConfigurationException;
import org.geowebcache.config.ConfigurationPersistenceException;
import org.geowebcache.config.DefaultGridsets;
import org.geowebcache.config.FileBlobStoreInfo;
import org.geowebcache.config.TileLayerConfiguration;
import org.geowebcache.config.XMLConfiguration;
import org.geowebcache.config.XMLGridSet;
import org.geowebcache.config.XMLGridSubset;
import org.geowebcache.conveyor.ConveyorTile;
import org.geowebcache.diskquota.DiskQuotaMonitor;
import org.geowebcache.diskquota.QuotaStore;
Expand Down Expand Up @@ -1367,7 +1385,12 @@ public void testGetDefaultAdvertisedCachedFormats() {
// from src/main/resources/org/geoserver/gwc/advertised_formats.properties
Set<String> defaultFormats =
ImmutableSet.of(
"image/png", "image/png8", "image/jpeg", "image/gif", "image/vnd.jpeg-png");
"image/png",
"image/png8",
"image/jpeg",
"image/gif",
"image/vnd.jpeg-png",
"image/vnd.jpeg-png8");

SetView<String> formatsWithUtfGrid =
union(defaultFormats, Collections.singleton("application/json;type=utfgrid"));
Expand Down Expand Up @@ -1398,7 +1421,12 @@ public void testGetPluggabledAdvertisedCachedFormats() throws IOException {
// from src/main/resources/org/geoserver/gwc/advertised_formats.properties
Set<String> defaultFormats =
ImmutableSet.of(
"image/png", "image/png8", "image/jpeg", "image/gif", "image/vnd.jpeg-png");
"image/png",
"image/png8",
"image/jpeg",
"image/gif",
"image/vnd.jpeg-png",
"image/vnd.jpeg-png8");

// see src/test/resources/org/geoserver/gwc/advertised_formats.properties
Set<String> expectedVector =
Expand Down
18 changes: 18 additions & 0 deletions src/wms/src/main/java/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,24 @@
<!-- The extension is actually dynamically determined -->
<property name="extension" value="jpg-png"/>
</bean>

<bean id="JpegPng8MapProducer" class="org.geoserver.wms.map.RenderedImageMapOutputFormat">
<constructor-arg>
<description>The prescribed output format MIME-Type, same as declared in the getcaps document</description>
<value>image/vnd.jpeg-png8</value>
</constructor-arg>
<constructor-arg ref="wms" />
<property name="transparencySupported">
<description>JPEG-PNG8 does support transparency</description>
<value>true</value>
</property>
<property name="paletteSupported">
<description>JPEG-PNG8 does support 8-bit indexed color maps</description>
<value>true</value>
</property>
<!-- The extension is actually dynamically determined -->
<property name="extension" value="jpg-png8"/>
</bean>

<bean id="JpegPngMapResponse" class="org.geoserver.wms.map.JpegPngMapResponse">
<constructor-arg index="0" ref="wms"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
import org.geoserver.wms.MapProducerCapabilities;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSMapContent;
import org.geoserver.wms.WebMap;

/** Allows encoding in JPEG or PNG depending on whether the image has transparency, or not */
public class JpegPngMapResponse extends RenderedImageMapResponse {

public static final String MIME = "image/vnd.jpeg-png";
public static final String MIME8 = "image/vnd.jpeg-png8";

private static final String[] OUTPUT_FORMATS = {MIME};
private static final String[] OUTPUT_FORMATS = {MIME, MIME8};

private static MapProducerCapabilities CAPABILITIES =
new MapProducerCapabilities(true, false, false, true, null);
Expand All @@ -43,13 +45,6 @@ public String getMimeType(Object value, Operation operation) throws ServiceExcep
return JpegOrPngChooser.getFromMap(map).getMime();
}

@Override
public String getPreferredDisposition(Object value, Operation operation) {
RenderedImageMap map = ((RenderedImageMap) value);
String extension = JpegOrPngChooser.getFromMap(map).getMime();
return map.getSimpleAttachmentFileName() + "." + extension;
}

/**
* Transforms the rendered image into the appropriate format, streaming to the output stream.
*
Expand Down Expand Up @@ -81,4 +76,19 @@ public String getExtension(RenderedImage image, WMSMapContent mapContent) {
return "png";
}
}

@Override
public String getAttachmentFileName(Object value, Operation operation) {
String fileName = ((WebMap) value).getAttachmentFileName();
int idx = fileName.lastIndexOf(".");
if (idx > 0) {
return fileName.substring(0, idx)
+ "."
+ getExtension(
((RenderedImageMap) value).getImage(),
((RenderedImageMap) value).getMapContext());
} else {
return fileName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void formatImageOutputStream(
}

// check to see if we have to see a translucent or bitmask quantizer
image = applyPalette(image, mapContent, "image/png8", true);
image = applyPalette(image, mapContent, f -> f != null && f.contains("png8"), true);
float quality = (100 - wms.getPngCompression()) / 100.0f;
JAIInfo.PngEncoderType encoder = wms.getPNGEncoderType();
if (encoder == JAIInfo.PngEncoderType.PNGJ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import it.geosolutions.jaiext.colorindexer.ColorIndexer;
import it.geosolutions.jaiext.colorindexer.LRUColorIndexer;
import it.geosolutions.jaiext.colorindexer.Quantizer;
import java.awt.Transparency;
import java.awt.*;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.function.Function;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.GetMapOutputFormat;
Expand Down Expand Up @@ -127,15 +128,16 @@ public final void write(
* the palette format has been requested, applying a bitmask or translucent palette inverter
* according to the user request and the image structure
*
* @param image
* @param mapContent
* @param palettedFormatName
* @param image The image to be eventually paletted
* @param mapContent The {@link org.geotools.map.MapContent}
* @param palettedFormatCheck The function checking if the format requested demands paletted
* output
* @param supportsTranslucency If false the code will always apply the bitmask transformer
*/
protected RenderedImage applyPalette(
RenderedImage image,
WMSMapContent mapContent,
String palettedFormatName,
Function<String, Boolean> palettedFormatCheck,
boolean supportsTranslucency) {
// check to see if we have to see a translucent or bitmask quantizer
GetMapRequest request = mapContent.getRequest();
Expand All @@ -156,7 +158,7 @@ protected RenderedImage applyPalette(
// user provided palette?
if (icm != null) {
image = forceIndexed8Bitmask(image, PaletteManager.getInverseColorMapOp(icm));
} else if (palettedFormatName.equalsIgnoreCase(format)) {
} else if (palettedFormatCheck.apply(format)) {
// or format that needs palette to be applied?
image = forceIndexed8Bitmask(image, null);
}
Expand All @@ -173,7 +175,7 @@ protected RenderedImage applyPalette(
// user provided palette?
if (mapContent.getPalette() != null) {
indexer = new CachingColorIndexer(new LRUColorIndexer(icm, 1024));
} else if (palettedFormatName.equalsIgnoreCase(format)) {
} else if (palettedFormatCheck.apply(format)) {
// build the palette and grab the optimized color indexer
indexer = new Quantizer(256).subsample().buildColorIndexer(image);
}
Expand All @@ -188,6 +190,28 @@ protected RenderedImage applyPalette(
return image;
}

/**
* Applies a transformation to 8 bits + palette in case the user requested a specific palette or
* the palette format has been requested, applying a bitmask or translucent palette inverter
* according to the user request and the image structure
*
* @param image The image to be eventually paletted
* @param mapContent The {@link org.geotools.map.MapContent}
* @param palettedFormatName The format name used to require paletted output
* @param supportsTranslucency If false the code will always apply the bitmask transformer
*/
protected RenderedImage applyPalette(
RenderedImage image,
WMSMapContent mapContent,
String palettedFormatName,
boolean supportsTranslucency) {
return applyPalette(
image,
mapContent,
format -> (palettedFormatName.equalsIgnoreCase(format)),
supportsTranslucency);
}

/** @param originalImage */
protected RenderedImage forceIndexed8Bitmask(
RenderedImage originalImage, InverseColorMapOp paletteInverter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@
<option value="image/gif">GIF</option>
<option id="jpeg" value="image/jpeg">JPEG</option>
<option id="jpeg-png" value="image/vnd.jpeg-png">JPEG-PNG</option>
<option id="jpeg-png8" value="image/vnd.jpeg-png8">JPEG-PNG8</option>
</select>
</li>
<li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
<option value="image/gif">GIF</option>
<option id="jpeg" value="image/jpeg">JPEG</option>
<option id="jpeg-png" value="image/vnd.jpeg-png">JPEG-PNG</option>
<option id="jpeg-png8" value="image/vnd.jpeg-png8">JPEG-PNG8</option>
</select>
</li>
<li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.geotools.image.ImageWorker;
import org.geotools.image.test.ImageAssert;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
Expand Down Expand Up @@ -1470,12 +1471,45 @@ public void testJpegPngTransparent() throws Exception {
assertNotBlank("testJpegPngTransparent", image);
}

@Test
public void testJpegPng8Transparent() throws Exception {
String request =
"wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fvnd.jpeg-png8&TRANSPARENT=true&STYLES"
+ "&LAYERS=cite%3ABasicPolygons&SRS=EPSG%3A4326&WIDTH=256&HEIGHT=256&BBOX=-2.4%2C1.4%2C0.4%2C4.2";
// checks it's a PNG
MockHttpServletResponse resp = getAsServletResponse(request);
assertEquals("image/png", resp.getContentType());
assertEquals(
"inline; filename=cite-BasicPolygons.png",
resp.getHeader(HttpHeaders.CONTENT_DISPOSITION));
BufferedImage image = ImageIO.read(getBinaryInputStream(resp));
assertNotBlank("testJpegPngTransparent", image);
// check it's paletted
assertThat(image.getColorModel(), instanceOf(IndexColorModel.class));
}

@Test
public void testJpegPngOpaque() throws Exception {
String request =
"wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fvnd.jpeg-png&TRANSPARENT=true&STYLES"
+ "&LAYERS=cite%3ABasicPolygons&SRS=EPSG%3A4326&WIDTH=256&HEIGHT=256&BBOX=-0.4%2C3.6%2C1%2C5";
// checks it's a JPEG, since it's opaque
MockHttpServletResponse resp = getAsServletResponse(request);
assertEquals("image/jpeg", resp.getContentType());
assertEquals(
"inline; filename=cite-BasicPolygons.jpg",
resp.getHeader(HttpHeaders.CONTENT_DISPOSITION));
InputStream is = getBinaryInputStream(resp);
BufferedImage image = ImageIO.read(is);
assertNotBlank("testJpegPngOpaque", image);
}

@Test
public void testJpegPng8Opaque() throws Exception {
String request =
"wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fvnd.jpeg-png8&TRANSPARENT=true&STYLES"
+ "&LAYERS=cite%3ABasicPolygons&SRS=EPSG%3A4326&WIDTH=256&HEIGHT=256&BBOX=-0.4%2C3.6%2C1%2C5";
// checks it's a JPEG, since it's opaque
BufferedImage image = getAsImage(request, "image/jpeg");
assertNotBlank("testJpegPngOpaque", image);
}
Expand Down

0 comments on commit bf70e51

Please sign in to comment.