diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java index e13c2039879287d04336b9638dc5d4be9b42e47c..0346aa55ac4bcc40d9f5702b7e4e7adcf1e5cf64 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java @@ -20,6 +20,8 @@ import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -41,7 +43,6 @@ import ar.com.hjg.pngj.ImageLine; import ar.com.hjg.pngj.ImageLineHelper; import ar.com.hjg.pngj.PngFilterType; import ar.com.hjg.pngj.PngWriter; - import ch.rinn.restrictions.Private; import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked; import ch.systemsx.cisd.base.io.IRandomAccessFile; @@ -699,25 +700,16 @@ public class ImageUtil public static void writeImageToPng(BufferedImage image, OutputStream out, PngFilterType filterType, int compressionLevel) { - final int cols = image.getWidth(); - final int rows = image.getHeight(); - int bitDepth = image.getColorModel().getComponentSize(0); - boolean hasAlpha = false; // NOTE: it would be nice to support alpha channel - boolean isGrayscale = isGrayscale(image); - ImageInfo imgInfo = new ImageInfo(cols, rows, bitDepth, hasAlpha, isGrayscale, false); + PngWritingHelper helper = PngWritingHelper.createHelper(image); + ImageInfo imgInfo = helper.getImageInfo(); + int rows = imgInfo.rows; PngWriter png = new PngWriter(out, imgInfo); png.setFilterType(filterType == null ? PngFilterType.FILTER_DEFAULT : filterType); png.setCompLevel(compressionLevel == -1 ? 6 : compressionLevel); ImageLine imageLine = new ImageLine(imgInfo); for (int row = 0; row < rows; ++row) { - if (isGrayscale) - { - fillGrayscaleLine(image, cols, isGrayscale, imageLine, row); - } else - { - fillRGBLine(image, cols, isGrayscale, imageLine, row); - } + helper.fillLine(imageLine, row); imageLine.setRown(row); png.writeRow(imageLine); } @@ -736,25 +728,6 @@ public class ImageUtil } } - private static void fillGrayscaleLine(BufferedImage image, final int cols, boolean isGrayscale, - ImageLine imageLine, int row) - { - for (int col = 0; col < cols; ++col) - { - imageLine.scanline[col] = image.getRaster().getSample(col, row, 0); - } - } - - private static void fillRGBLine(BufferedImage image, final int cols, boolean isGrayscale, - ImageLine imageLine, int row) - { - for (int col = 0; col < cols; ++col) - { - int pixel = image.getRGB(col, row); - ImageLineHelper.setPixelRGB8(imageLine, col, pixel); - } - } - /** * Parses specified string representation of an {@link ImageID}. If the argument is * <code>null</code> {@link ImageID#NULL} will be returned. diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/PngWritingHelper.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/PngWritingHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..42cb1100ad76122b426a9bfd03ea112faf43bdf7 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/PngWritingHelper.java @@ -0,0 +1,172 @@ +/* + * Copyright 2013 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.generic.shared.utils; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; + +import ar.com.hjg.pngj.ImageInfo; +import ar.com.hjg.pngj.ImageLine; +import ar.com.hjg.pngj.ImageLineHelper; + +/** + * An object that encapsulates some knowledge about how PNG files should be created. + * + * @author cramakri + */ +public class PngWritingHelper +{ + public static PngWritingHelper createHelper(BufferedImage image) + { + ColorModel colorModel = image.getColorModel(); + int[] componentSize = colorModel.getComponentSize(); + if (componentSize != null) + { + return new NormalPngWritingHelper(image, componentSize[0]); + } + if ("loci.formats.gui.Index16ColorModel".equals(colorModel.getClass().getName())) + { + return new Index16PngWritingHelper(image); + } else + { + throw new IllegalArgumentException( + "The image color model does not specify a bit depth for the color channels and is not one of the known special cases."); + } + } + + /** + * The png writing helper that handles most cases. + * + * @author cramakri + */ + private static class NormalPngWritingHelper extends PngWritingHelper + { + private NormalPngWritingHelper(BufferedImage image, int bitDepth) + { + super(image, bitDepth); + } + } + + /** + * The png writing helper that handles images that use the loci.formats.gui.Index16ColorModel + * color model from BioFormats and have no component information. Images that use this color + * model and have component information are treated normally. + * + * @author cramakri + */ + private static class Index16PngWritingHelper extends PngWritingHelper + { + private Index16PngWritingHelper(BufferedImage image) + { + super(image, 8); + } + + @Override + protected void fillRGBLine(ImageLine imageLine, int row) + { + for (int col = 0; col < cols; ++col) + { + WritableRaster raster = image.getRaster(); + short[] value = (short[]) raster.getDataElements(row, col, null); + // TODO The values converted by the color model seem to be byte swapped. We + // currently just put the value in the green channel until we figure out how to + // handle these images. + // Alpha is ignored by setPixelRGB8 + int pixel = (0 << 24) | (0 << 16) | ((value[0]) << 8) | (0 << 0); + ImageLineHelper.setPixelRGB8(imageLine, col, pixel); + + // Potentially useful code if we improve the implementation of this method. + // ColorModel colorModel = image.getColorModel(); + // int a = colorModel.getAlpha(value); + // int g = colorModel.getGreen(value); + } + } + + } + + private static boolean isGrayscale(BufferedImage image) + { + return image.getColorModel().getColorSpace().getNumComponents() == 1; + } + + protected final BufferedImage image; + + protected final int cols; + + protected final int rows; + + protected final boolean hasAlpha; + + protected final boolean isGrayscale; + + protected final int bitDepth; + + private PngWritingHelper(BufferedImage image, int bitDepth) + { + this.image = image; + cols = image.getWidth(); + rows = image.getHeight(); + this.bitDepth = bitDepth; + hasAlpha = false; // NOTE: it would be nice to support alpha channel + isGrayscale = isGrayscale(image); + } + + public int getRows() + { + return rows; + } + + public int getCols() + { + return cols; + } + + public ImageInfo getImageInfo() + { + ImageInfo imgInfo = new ImageInfo(cols, rows, bitDepth, hasAlpha, isGrayscale, false); + return imgInfo; + } + + public void fillLine(ImageLine imageLine, int row) + { + if (isGrayscale) + { + fillGrayscaleLine(imageLine, row); + } else + { + fillRGBLine(imageLine, row); + } + } + + protected void fillGrayscaleLine(ImageLine imageLine, int row) + { + for (int col = 0; col < cols; ++col) + { + imageLine.scanline[col] = image.getRaster().getSample(col, row, 0); + } + } + + protected void fillRGBLine(ImageLine imageLine, int row) + { + for (int col = 0; col < cols; ++col) + { + int pixel = image.getRGB(col, row); + ImageLineHelper.setPixelRGB8(imageLine, col, pixel); + } + } +}