All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.image.ImageSupport Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2012 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.day.image;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.io.IOException;
import java.util.Iterator;

import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ImageWriterSpi;

import ch.randelshofer.media.jpeg.CMYKJPEGImageReaderSpi;

import com.day.imageio.plugins.GIFImageMetadata;
import com.day.imageio.plugins.GIFStreamMetadata;
import com.day.imageio.plugins.GifImageWriterSpi;

/**
 * The ImageSupport class provides methods, which are implemented
 * differently for Java 1.3 and for Java 1.4. These methods are implemented as
 * public static methods, which delegate to protected instance methods of
 * implementations of this abstract class.
 *
 * @version $Revision$
 * @author fmeschbe
 * @since coati
 * @audience core, renamed from GifImageSupport to ImageSupport in gumbaer
 */
public class ImageSupport {

    /** The JRE dependent ImageSupport instance */
    private static final ImageSupport instance;

    /** Flag indicating whether GIF Image Writer has been registered
     * with the ImageIO registry through this class.
     * @see ImageSupport#registerImageIOSpi()
     * @see ImageSupport#deregisterImageIOSpi()
     */
    private static boolean registered = false;

    /** The GifImageWriterSpi instance used for (de-)registration */
    private ImageWriterSpi gifImageWriterSpi;

    /** The CMYKJPEGImageReaderSpi instance used for (de-)registration */
    private ImageReaderSpi cmykJpegImageReaderSpi;

    /** Private constructor prevents instantiation */
    protected ImageSupport() {}

    static {
        // get the instance of the ImageSupport implementation
        instance = new ImageSupport();

        // prevent ImageIO from using files for caching
        ImageIO.setUseCache(false);
    }

    /**
     * Initializes the GIF image writing support. The first task is to define
     * the classes to use for the GIF image writing and the to register the
     * image writer SPI and to get the instance of the support class.
     * 

* This does nothing if the writing support has already been initialized. */ static void initialize() { registerImageIOSpi(); } /** * Interpret the GIF metadata of a GIF image stream. The access to this * metadata is not very portable, as we directly access Sun's GIF Metadata * information. * * @param reader The reader, which read the image data * * @throws IOException if reading the meta data from the image fails. * @throws IllegalStateException if the input source of the reader has * not been set. */ static void getGIFMetaData(Layer layer, ImageReader reader) throws IOException { instance.doGetGIFMetaData(layer, reader); } /** * Returns an image writer suitable to write the given format. * This special implementation makes sure, that we use our own GIF writer * implementation even if the platform provides another GIF writer. The * reason for this is, that we depend on the image and stream metadata * format. * * @param format The format to write, which must be something link "gif", * "jpeg", or the likes. * * @return An ImageWriter suitable to write the * layer or null if no such writer is * available. */ static ImageWriter getImageWriter(String format) throws IOException { if ("gif".equalsIgnoreCase(format)) { // should create a writer from the SPI return instance.getGIFImageWriterSpi().createWriterInstance(); } // check both the mimetype and the format Iterator writers = ImageIO.getImageWritersByFormatName(format); if (!writers.hasNext()) { writers = ImageIO.getImageWritersByMIMEType(format); } if (writers.hasNext()) { return (ImageWriter) writers.next(); } // nothing found, return nothing return null; } /** * Do GIF preprocessing : Reduce colors according to the quality desired * and define metadata structures. *

* As a side effect the base image will be replaced by a reduced color * one ! * * @param numCol The number of colors for the GIF Image * * @return The stream and image metadata as the first and second element * in the returned array of IIOMetadata. */ static IIOMetadata[] createGIFMetadata(Layer layer, ImageWriter writer, int numCol) { return instance.doCreateGIFMetadata(layer, writer, numCol); } /** * Forces the data to match the state specified in the * isAlphaPremultiplied variable. It may multiply or * divide the color raster data by alpha, or do nothing if the data is * in the correct state. *

* NOTE: Due to a bug in the DirectColorModel.coerceData * implementation of Java 1.3 we have to hack this in using the * implementation of Java 1.4 for the 1.3 version and delegating to the * library implementation for the 1.4 version. * * @param image The image to apply the alpha channel for. * @param isAlphaPremultiplied true if the alpha has been * premultiplied; false otherwise. * * @return The image with correct state. Depending on the implementation and * the real need for coercion, the image may or may not be the same as * the input image. Callers should not assume to get a * different image or even different raster data object. */ public static BufferedImage coerceData(BufferedImage image, boolean isAlphaPremultiplied) { return instance.doCoerceData(image, isAlphaPremultiplied); } /** * Registers the GIF Image writer to be used depending on the Java * runtime to the ImageIO registry. If the image writer has already been * registered, this method has no effect. */ public static void registerImageIOSpi() { if (!registered) { IIORegistry reg = IIORegistry.getDefaultInstance(); reg.registerServiceProvider(instance.getGIFImageWriterSpi()); reg.registerServiceProvider(instance.getCmykJpegImageReaderSpi()); registered = true; } } /** * Deregisters the GIF Image writer to be used depending on the Java * runtime from the ImageIO registry. If the image writer is not registered, * this method has no effect. */ public static void deregisterImageIOSpi() { if (registered) { IIORegistry reg = IIORegistry.getDefaultInstance(); reg.deregisterServiceProvider(instance.getGIFImageWriterSpi()); reg.deregisterServiceProvider(instance.getCmykJpegImageReaderSpi()); registered = false; } } //---------- to be implemented --------------------------------------------- /** * Interpret the GIF metadata of a GIF image stream. The access to this * metadata is not very portable, as we directly access Sun's GIF Metadata * information. * * @param reader The reader, which read the image data * @throws IOException if reading the meta data from the image fails. * @throws IllegalStateException if the input source of the reader has not * been set. */ protected void doGetGIFMetaData(Layer layer, ImageReader reader) throws IOException { // Get background and transparency color, if available.... IIOMetadata smd; IIOMetadata imd; try { smd = reader.getStreamMetadata(); imd = reader.getImageMetadata(layer.getImageIndex()); } catch (IIOException iioe) { // log or throw return; } byte[] globalColorTable = (byte[]) get(smd, "globalColorTable"); if (globalColorTable != null) { int bgidx = 3 * getInt(smd, "backgroundColorIndex", Integer.MAX_VALUE); if (bgidx < globalColorTable.length) { int r = (globalColorTable[bgidx++] + 0x100) & 0xff; int g = (globalColorTable[bgidx++] + 0x100) & 0xff; int b = (globalColorTable[bgidx] + 0x100) & 0xff; // Set the background color layer.setBackground(new Color(r, g, b)); } } // Set the transparency color if (getBoolean(imd, "transparentColorFlag", false)) { byte[] colTab = (byte[]) get(imd, "localColorTable"); if (colTab == null && globalColorTable != null) { colTab = globalColorTable; } if (colTab != null) { int transidx = 3 * getInt(imd, "transparentColorIndex", 0); int r = (colTab[transidx++] + 0x100) & 0xff; int g = (colTab[transidx++] + 0x100) & 0xff; int b = (colTab[transidx++] + 0x100) & 0xff; // Set the transparency color layer.setTransparency(new Color(r, g, b, 0)); } } } /** * Do GIF preprocessing : Reduce colors according to the quality desired and * define metadata structures. *

* As a side effect the base image will be replaced by a reduced color one ! * * @param numCol The number of colors for the GIF Image * @return The stream and image metadata as the first and second element in * the returned array of IIOMetadata. */ protected IIOMetadata[] doCreateGIFMetadata(Layer layer, ImageWriter writer, int numCol) { // Assert correctness of the numCol value if (numCol <= 0) { numCol = 2; } else if (numCol > 256) { numCol = 256; } // Reduce colors and convert to IndexColorModel if needed BufferedImage reduced = DitherOp.convertToIndexed(layer.getImage(), numCol, layer.getTransparency(), layer.getBackgroundColor(), null); if (reduced != layer.getImage()) { /** * This is not optimal, the reduced image should only be used for * writing but not to replace the layer's image ... */ layer.setImage(reduced); } // we use the IndexColorModel later IndexColorModel icm = (IndexColorModel) reduced.getColorModel(); // Set some properties like background color and transparency color GIFImageMetadata imd = (GIFImageMetadata) writer.getDefaultImageMetadata( null, null); imd.imageLeftPosition = layer.getX() > 0 ? layer.getX() : 0; // only // if // positive imd.imageTopPosition = layer.getY() > 0 ? layer.getY() : 0; // only if // positive imd.imageWidth = layer.getWidth(); imd.imageHeight = layer.getHeight(); GIFStreamMetadata smd = (GIFStreamMetadata) writer.getDefaultStreamMetadata(null); smd.logicalScreenHeight = layer.getHeight(); smd.logicalScreenWidth = layer.getWidth(); if (layer.getTransparency() != null) { imd.transparentColorFlag = true; imd.transparentColorIndex = icm.getTransparentPixel(); } if (layer.getBackgroundColor().equals(layer.getTransparency())) { smd.backgroundColorIndex = icm.getTransparentPixel(); } else { smd.backgroundColorIndex = toIndex(icm, layer.getBackgroundColor().getRGB()); } return new IIOMetadata[] { smd, imd }; } /** * Forces the data to match the state specified in the * isAlphaPremultiplied variable. It may multiply or * divide the color raster data by alpha, or do nothing if the data is * in the correct state. * * @param image The image to apply the alpha channel for. * @param isAlphaPremultiplied true if the alpha has been * premultiplied; false otherwise. * * @return The image with correct state. For this implementation this is * always the same as the input image. */ protected BufferedImage doCoerceData(BufferedImage image, boolean isAlphaPremultiplied) { image.coerceData(isAlphaPremultiplied); return image; } /** * Returns the {@link com.day.imageio.plugins.GifImageWriterSpi} instance * associated with this support implementation. The object returned may * be used for registration to the ImageIO registry of Writer SPIs. * * @return The GIF image writer SPI instance. */ protected ImageWriterSpi getGIFImageWriterSpi() { if (gifImageWriterSpi == null) { gifImageWriterSpi = new GifImageWriterSpi(); } return gifImageWriterSpi; } /** * Returns the {@link CMYKJPEGImageReaderSpi} instance * associated with this support implementation. The object returned may * be used for registration to the ImageIO registry of Reader SPIs. * * @return The GIF image writer SPI instance. */ protected ImageReaderSpi getCmykJpegImageReaderSpi() { if (cmykJpegImageReaderSpi == null) { cmykJpegImageReaderSpi = new CMYKJPEGImageReaderSpi(); } return cmykJpegImageReaderSpi; } //----------- helpers ------------------------------------------------------ /** * Gets the nearest matching color index for the given rgba color value. If * the alpha channel value of rgb is zero, the index for the * transparent color is returned. *

* This method's implementation can only handle color maps in the int, byte, * or short transfer type format. For any other format, 0 is returned. * * @param icm The IndexColorModel containing the color map * @param rgb The ARGB color value to find in the color map * * @return The index of the color entry most closely matching the ARGB color * value or zero if the color model is not based on one of the * int, short, or byte transfer types. */ protected static int toIndex(IndexColorModel icm, int rgb) { Object pixel = icm.getDataElements(rgb, null); switch (icm.getTransferType()) { case DataBuffer.TYPE_INT: return ((int[]) pixel)[0]; case DataBuffer.TYPE_BYTE: return ((byte[]) pixel)[0]; case DataBuffer.TYPE_USHORT: return ((short[]) pixel)[0]; default: return 0; } } private static int getInt(Object object, String name, int defValue) { try { return object.getClass().getField(name).getInt(object); } catch (Throwable t) { // don't care } return defValue; } private static boolean getBoolean(Object object, String name, boolean defValue) { try { return object.getClass().getField(name).getBoolean(object); } catch (Throwable t) { // don't care } return defValue; } private static Object get(Object object, String name) { try { return object.getClass().getField(name).get(object); } catch (Throwable t) { // don't care } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy