/*
 * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * 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 boofcv.io.image;

import boofcv.core.image.GeneralizedImageOps;
import boofcv.struct.image.*;
import sun.awt.image.ByteInterleavedRaster;
import sun.awt.image.IntegerInterleavedRaster;
import sun.awt.image.ShortInterleavedRaster;
import sun.awt.image.SunWritableRaster;

import javax.swing.*;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.lang.reflect.Array;

/**
 * Functions for converting to and from {@link BufferedImage}.
 *
 * @author Peter Abeles
 */
public class ConvertBufferedImage {

	/**
	 * Returns an image which doesn't have an alpha channel.  If the input image doesn't have an alpha
	 * channel to start then its returned as is.  Otherwise a new image is created and the RGB channels are
	 * copied and the new image returned.
	 *
	 * @param image Input image
	 * @return Image without an alpha channel
	 */
	public static BufferedImage stripAlphaChannel( BufferedImage image ) {
		int numBands = image.getRaster().getNumBands();

		if( numBands == 4 ) {
			BufferedImage output = new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_INT_RGB);
			output.createGraphics().drawImage(image,0,0,null);
			return output;
		} else {
			return image;
		}
	}

	/**
	 * For BufferedImage stored as a byte array internally it extracts an
	 * interleaved image.  The input image and the returned image will both
	 * share the same internal data array.  Using this function allows unnecessary
	 * memory copying to be avoided.
	 *
	 * @param img Image whose internal data is extracted and wrapped.
	 * @return An image whose internal data is the same as the input image.
	 */
	public static InterleavedU8 extractInterleavedU8(BufferedImage img) {

		if (img.getRaster() instanceof ByteInterleavedRaster &&
				img.getType() != BufferedImage.TYPE_BYTE_INDEXED ) {
			ByteInterleavedRaster raster = (ByteInterleavedRaster) img.getRaster();

			InterleavedU8 ret = new InterleavedU8();

			ret.width = img.getWidth();
			ret.height = img.getHeight();
			ret.stride = raster.getScanlineStride();
			ret.startIndex = raster.getDataOffset(0)-raster.getPixelStride()+1;
			ret.setNumBands( raster.getNumBands() );
			ret.data = raster.getDataStorage();

			return ret;
		}
		throw new IllegalArgumentException("Buffered image does not have an interleaved byte raster");
	}

	/**
	 * For BufferedImage stored as a byte array internally it extracts an
	 * image.  The input image and the returned image will both
	 * share the same internal data array.  Using this function allows unnecessary
	 * memory copying to be avoided.
	 *
	 * @param img Image whose internal data is extracted and wrapped.
	 * @return An image whose internal data is the same as the input image.
	 */
	public static GrayU8 extractGrayU8(BufferedImage img) {
		if (img.getRaster() instanceof ByteInterleavedRaster &&
				img.getType() != BufferedImage.TYPE_BYTE_INDEXED ) {

			ByteInterleavedRaster raster = (ByteInterleavedRaster) img.getRaster();
			if (raster.getNumBands() != 1)
				throw new IllegalArgumentException("Input image has more than one channel");
			GrayU8 ret = new GrayU8();

			ret.width = img.getWidth();
			ret.height = img.getHeight();
			ret.startIndex = raster.getDataOffset(0);
			ret.stride = raster.getScanlineStride();
			ret.data = raster.getDataStorage();

			return ret;
		}
		throw new IllegalArgumentException("Buffered image does not have a gray scale byte raster");
	}

	/**
	 * Creates a new BufferedImage that internally uses the same data as the provided
	 * {@link InterleavedU8}.  If 3 bands then the image will be of type TYPE_3BYTE_BGR
	 * or if 1 band TYPE_BYTE_GRAY.
	 *
	 * @param img Input image who's data will be wrapped by the returned BufferedImage.
	 * @return BufferedImage which shared data with the input image.
	 */
	public static BufferedImage extractBuffered(InterleavedU8 img) {
		if (img.isSubimage())
			throw new IllegalArgumentException("Sub-images are not supported for this operation");

		final int width = img.width;
		final int height = img.height;
		final int numBands = img.numBands;

		// wrap the byte array
		DataBuffer bufferByte = new DataBufferByte(img.data, width * height * numBands, 0);

		ColorModel colorModel;
		int[] bOffs = null;

		if (numBands == 3) {
			ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
			int[] nBits = {8, 8, 8};
			bOffs = new int[]{2, 1, 0};
			colorModel = new ComponentColorModel(cs, nBits, false, false,
					Transparency.OPAQUE,
					DataBuffer.TYPE_BYTE);
		} else if (numBands == 1) {
			ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
			int[] nBits = {8};
			bOffs = new int[]{0};
			colorModel = new ComponentColorModel(cs, nBits, false, true,
					Transparency.OPAQUE,
					DataBuffer.TYPE_BYTE);
		} else {
			throw new IllegalArgumentException("Only 1 or 3 bands supported");
		}

		// Create a raster using the sample model and data buffer
		WritableRaster raster = Raster.createInterleavedRaster(
				bufferByte, width, height, img.stride, numBands, bOffs, new Point(0, 0));

		// Combine the color model and raster into a buffered image

		return new BufferedImage(colorModel, raster, false, null);
	}

	/**
	 * 

* Creates a new BufferedImage that internally uses the same data as the provided * GrayU8. The returned BufferedImage will be of type TYPE_BYTE_GRAY. *

*

*

* NOTE: This only works on images which are not subimages! *

* * @param img Input image who's data will be wrapped by the returned BufferedImage. * @return BufferedImage which shared data with the input image. */ public static BufferedImage extractBuffered(GrayU8 img) { if (img.isSubimage()) throw new IllegalArgumentException("Sub-images are not supported for this operation"); final int width = img.width; final int height = img.height; // wrap the byte array DataBuffer bufferByte = new DataBufferByte(img.data, width * height, 0); ColorModel colorModel; int[] bOffs = new int[]{0}; ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); int[] nBits = {8}; colorModel = new ComponentColorModel(cs, nBits, false, true, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); // Create a raster using the sample model and data buffer WritableRaster raster = Raster.createInterleavedRaster( bufferByte, width, height, img.stride, 1, bOffs, new Point(0, 0)); // Combine the color model and raster into a buffered image return new BufferedImage(colorModel, raster, false, null); } /** * Converts a buffered image into an image of the specified type. * * @param src Input BufferedImage which is to be converted * @param dst The image which it is being converted into * @param orderRgb If applicable, should it adjust the ordering of each color band to maintain color consistency */ public static void convertFrom(BufferedImage src, T dst , boolean orderRgb) { if( dst instanceof ImageGray) { ImageGray sb = (ImageGray)dst; convertFromSingle(src, sb, (Class) sb.getClass()); } else if( dst instanceof Planar) { Planar ms = (Planar)dst; convertFromMulti(src,ms,orderRgb,ms.getBandType()); } else if( dst instanceof ImageInterleaved ) { convertFromInterleaved(src, (ImageInterleaved) dst, orderRgb); } else { throw new IllegalArgumentException("Unknown type " + dst.getClass().getSimpleName()); } } /** * Converts a buffered image into an image of the specified type. * * @param src Input BufferedImage which is to be converted * @param orderRgb If applicable, should it adjust the ordering of each color band to maintain color consistency * @param imageType Type of image it is to be converted into * @return The image */ public static T convertFrom(BufferedImage src , boolean orderRgb , ImageType imageType) { T out = imageType.createImage(src.getWidth(),src.getHeight()); switch( imageType.getFamily() ) { case GRAY: convertFromSingle(src, (ImageGray)out, imageType.getImageClass()); break; case PLANAR: convertFromMulti(src, (Planar) out, orderRgb, imageType.getImageClass()); break; case INTERLEAVED: convertFromInterleaved(src, (ImageInterleaved) out, orderRgb); break; default: throw new RuntimeException("Not supported yet"); } return out; } public static T convertFrom(BufferedImage src , boolean orderRgb , T output ) { ImageType imageType = output.getImageType(); switch( imageType.getFamily() ) { case GRAY: convertFromSingle(src, (ImageGray)output, imageType.getImageClass()); break; case PLANAR: convertFromMulti(src, (Planar) output, orderRgb, imageType.getImageClass()); break; case INTERLEAVED: convertFromInterleaved(src, (ImageInterleaved) output, orderRgb); break; default: throw new RuntimeException("Not supported yet"); } return output; } /** * Converts a buffered image into an image of the specified type. In a 'dst' image is provided * it will be used for output, otherwise a new image will be created. */ public static T convertFromSingle(BufferedImage src, T dst, Class type) { if (type == GrayU8.class) { return (T) convertFrom(src, (GrayU8) dst); } else if( GrayI16.class.isAssignableFrom(type) ) { return (T) convertFrom(src, (GrayI16) dst,(Class)type); } else if (type == GrayF32.class) { return (T) convertFrom(src, (GrayF32) dst); } else { throw new IllegalArgumentException("Unknown type " + type); } } /** * Converts the buffered image into an {@link GrayU8}. If the buffered image * has multiple channels the intensities of each channel are averaged together. * * @param src Input image. * @param dst Where the converted image is written to. If null a new unsigned image is created. * @return Converted image. */ public static GrayU8 convertFrom(BufferedImage src, GrayU8 dst) { if (dst != null) { if (src.getWidth() != dst.getWidth() || src.getHeight() != dst.getHeight()) { throw new IllegalArgumentException("image dimension are different"); } } else { dst = new GrayU8(src.getWidth(), src.getHeight()); } try { if (src.getRaster() instanceof ByteInterleavedRaster ) { if( src.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.bufferedToGray((ByteInterleavedRaster) src.getRaster(), dst); } else { ConvertRaster.bufferedToGray(src, dst); } } else if (src.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.bufferedToGray((IntegerInterleavedRaster) src.getRaster(), dst); } else if( src.getRaster().getClass() == SunWritableRaster.class ) { ConvertRaster.bufferedToGray((SunWritableRaster) src.getRaster(), dst); } else { ConvertRaster.bufferedToGray(src, dst); } } catch( java.security.AccessControlException e) { // Applets don't allow access to the raster() ConvertRaster.bufferedToGray(src, dst); } return dst; } /** * Converts the buffered image into an {@link GrayI16}. If the buffered image * has multiple channels the intensities of each channel are averaged together. * * @param src Input image. * @param dst Where the converted image is written to. If null a new unsigned image is created. * @return Converted image. */ public static T convertFrom(BufferedImage src, T dst , Class type ) { if (dst != null) { if (src.getWidth() != dst.getWidth() || src.getHeight() != dst.getHeight()) { throw new IllegalArgumentException("image dimension are different"); } } else { dst = GeneralizedImageOps.createSingleBand(type, src.getWidth(), src.getHeight()); } try { if (src.getRaster() instanceof ShortInterleavedRaster ) { ConvertRaster.bufferedToGray((ShortInterleavedRaster) src.getRaster(), dst); return dst; } } catch( java.security.AccessControlException e) {} // Applets don't allow access to the raster() or the image type wasn't supported ConvertRaster.bufferedToGray(src, dst); return dst; } /** * Converts the buffered image into an {@link GrayF32}. If the buffered image * has multiple channels the intensities of each channel are averaged together. * * @param src Input image. * @param dst Where the converted image is written to. If null a new unsigned image is created. * @return Converted image. */ public static GrayF32 convertFrom(BufferedImage src, GrayF32 dst) { if (dst != null) { if (src.getWidth() != dst.getWidth() || src.getHeight() != dst.getHeight()) { String difference = "src = "+src.getWidth()+"x"+src.getHeight()+" dst = "+dst.getWidth()+"x"+dst.getHeight(); throw new IllegalArgumentException("image dimension are different. "+difference); } } else { dst = new GrayF32(src.getWidth(), src.getHeight()); } try { if (src.getRaster() instanceof ByteInterleavedRaster ) { if( src.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.bufferedToGray((ByteInterleavedRaster) src.getRaster(), dst); } else { ConvertRaster.bufferedToGray(src, dst); } } else if (src.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.bufferedToGray((IntegerInterleavedRaster) src.getRaster(), dst); } else if( src.getRaster().getClass() == SunWritableRaster.class ) { ConvertRaster.bufferedToGray((SunWritableRaster) src.getRaster(), dst); } else { ConvertRaster.bufferedToGray(src, dst); } } catch( java.security.AccessControlException e) { // Applets don't allow access to the raster() ConvertRaster.bufferedToGray(src, dst); } return dst; } /** * Converts the buffered image into an {@link Planar} image of the specified * type. * * @param src Input image. Not modified. * @param dst Output. The converted image is written to. If null a new unsigned image is created. * @param orderRgb If applicable, should it adjust the ordering of each color band to maintain color consistency. * Most of the time you want this to be true. * @param type Which type of data structure is each band. (GrayU8 or GrayF32) * @return Converted image. */ public static Planar convertFromMulti(BufferedImage src, Planar dst , boolean orderRgb , Class type ) { if( src == null ) throw new IllegalArgumentException("src is null!"); if (dst != null) { if (src.getWidth() != dst.getWidth() || src.getHeight() != dst.getHeight()) { throw new IllegalArgumentException("image dimension are different"); } } try { WritableRaster raster = src.getRaster(); int numBands; if( src.getType() == BufferedImage.TYPE_BYTE_INDEXED ) numBands = 3; else numBands = raster.getNumBands(); if( dst == null) dst = new Planar(type,src.getWidth(),src.getHeight(),numBands); else if( dst.getNumBands() != numBands ) throw new IllegalArgumentException("Expected "+numBands+" bands in dst not "+dst.getNumBands()); if( type == GrayU8.class ) { if (src.getRaster() instanceof ByteInterleavedRaster && src.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { if( src.getType() == BufferedImage.TYPE_BYTE_GRAY) { for( int i = 0; i < dst.getNumBands(); i++ ) ConvertRaster.bufferedToGray(src, ((Planar) dst).getBand(i)); } else { ConvertRaster.bufferedToMulti_U8((ByteInterleavedRaster) src.getRaster(), (Planar) dst); } } else if (src.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.bufferedToMulti_U8((IntegerInterleavedRaster) src.getRaster(), (Planar) dst); } else { ConvertRaster.bufferedToMulti_U8(src, (Planar) dst); } } else if( type == GrayF32.class ) { if (src.getRaster() instanceof ByteInterleavedRaster && src.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { if( src.getType() == BufferedImage.TYPE_BYTE_GRAY) { for( int i = 0; i < dst.getNumBands(); i++ ) ConvertRaster.bufferedToGray(src,((Planar)dst).getBand(i)); } else { ConvertRaster.bufferedToMulti_F32((ByteInterleavedRaster) src.getRaster(), (Planar) dst); } } else if (src.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.bufferedToMulti_F32((IntegerInterleavedRaster) src.getRaster(), (Planar) dst); } else { ConvertRaster.bufferedToMulti_F32(src, (Planar) dst); } } else { throw new IllegalArgumentException("Band type not supported yet"); } } catch( java.security.AccessControlException e) { // Applets don't allow access to the raster() if( dst == null ) dst = new Planar(type,src.getWidth(),src.getHeight(),3); if( type == GrayU8.class ) { ConvertRaster.bufferedToMulti_U8(src, (Planar) dst); } else if( type == GrayF32.class ) { ConvertRaster.bufferedToMulti_F32(src, (Planar)dst); } } // if requested, ensure the ordering of the bands if( orderRgb ) { orderBandsIntoRGB(dst,src); } return dst; } public static void convertFromInterleaved(BufferedImage src, ImageInterleaved dst, boolean orderRgb) { if (src == null) throw new IllegalArgumentException("src is null!"); if (src.getWidth() != dst.getWidth() || src.getHeight() != dst.getHeight()) { throw new IllegalArgumentException("image dimension are different"); } try { WritableRaster raster = src.getRaster(); int numBands; if( src.getType() == BufferedImage.TYPE_BYTE_INDEXED ) numBands = 3; else numBands = raster.getNumBands(); if( dst.getNumBands() != numBands ) throw new IllegalArgumentException("Expected "+numBands+" bands in dst not "+dst.getNumBands()); if( dst instanceof InterleavedU8 ) { if (src.getRaster() instanceof ByteInterleavedRaster ){ if(src.getType() != BufferedImage.TYPE_BYTE_INDEXED) { if (src.getType() == BufferedImage.TYPE_BYTE_GRAY) { ConvertRaster.bufferedToGray(src, (InterleavedU8) dst); } else { ConvertRaster.bufferedToInterleaved((ByteInterleavedRaster) src.getRaster(), (InterleavedU8) dst); } } else { ConvertRaster.bufferedToInterleaved(src, (InterleavedU8) dst); } } else if (src.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.bufferedToInterleaved((IntegerInterleavedRaster) src.getRaster(), (InterleavedU8) dst); } else if (src.getRaster() instanceof SunWritableRaster) { ConvertRaster.bufferedToInterleaved((SunWritableRaster) src.getRaster(), (InterleavedU8) dst); } else { ConvertRaster.bufferedToInterleaved(src, (InterleavedU8) dst); } } else if( dst instanceof InterleavedF32 ) { if (src.getRaster() instanceof ByteInterleavedRaster ) { if(src.getType() != BufferedImage.TYPE_BYTE_INDEXED) { if (src.getType() == BufferedImage.TYPE_BYTE_GRAY) { ConvertRaster.bufferedToGray(src, (InterleavedF32) dst); } else { ConvertRaster.bufferedToInterleaved((ByteInterleavedRaster) src.getRaster(), (InterleavedF32) dst); } } else { ConvertRaster.bufferedToInterleaved(src, (InterleavedF32) dst); } } else if (src.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.bufferedToInterleaved((IntegerInterleavedRaster) src.getRaster(), (InterleavedF32) dst); } else if (src.getRaster() instanceof SunWritableRaster) { ConvertRaster.bufferedToInterleaved((SunWritableRaster) src.getRaster(), (InterleavedF32) dst); } else { ConvertRaster.bufferedToInterleaved(src, (InterleavedF32) dst); } } else { throw new IllegalArgumentException("Data type not supported yet"); } } catch( java.security.AccessControlException e) { // Applets don't allow access to the raster() if( dst instanceof InterleavedU8 ) { ConvertRaster.bufferedToInterleaved(src, (InterleavedU8) dst); } else if( dst instanceof InterleavedF32 ) { ConvertRaster.bufferedToInterleaved(src, (InterleavedF32) dst); } else { throw new IllegalArgumentException("Unsupported dst image type"); } } // if requested, ensure the ordering of the bands if( orderRgb ) { orderBandsIntoRGB(dst,src); } } /** *

* Converts an image into a BufferedImage. The best way to think of this function is that it's a mindless * typecast. If you don't provide an output image then it will create one. However there isn't always a direct * equivalent between a BoofCV image and BufferedImage internal type. A "reasonable" choice will be made, but * for your application it might not be a good choice. *

* * @param src Input image. Pixels must have a value from 0 to 255. * @param dst Where the converted image is written to. If null a new image is created. See comment above about type. * @param orderRgb If applicable, should it change the order of the color bands (assumed RGB or ARGB) into the * order based on BufferedImage.TYPE. Most of the time you want this to be true. * @return Converted image. */ public static BufferedImage convertTo( ImageBase src, BufferedImage dst, boolean orderRgb ) { if( src instanceof ImageGray) { if( GrayU8.class == src.getClass() ) { return convertTo((GrayU8)src,dst); } else if( GrayI16.class.isInstance(src) ) { return convertTo((GrayI16)src,dst); } else if( GrayF32.class == src.getClass() ) { return convertTo((GrayF32)src,dst); } else { throw new IllegalArgumentException("ImageGray type is not yet supported: "+src.getClass().getSimpleName()); } } else if( src instanceof Planar) { Planar ms = (Planar)src; if( GrayU8.class == ms.getBandType() ) { return convertTo_U8((Planar) ms, dst, orderRgb); } else if( GrayF32.class == ms.getBandType() ) { return convertTo_F32((Planar) ms, dst, orderRgb); } else { throw new IllegalArgumentException("Planar type is not yet supported: "+ ms.getBandType().getSimpleName()); } } else if( src instanceof ImageInterleaved ) { if( InterleavedU8.class == src.getClass() ) { return convertTo((InterleavedU8)src,dst,orderRgb); } else if( InterleavedF32.class == src.getClass() ) { return convertTo((InterleavedF32)src,dst,orderRgb); } else { throw new IllegalArgumentException("ImageGray type is not yet supported: "+src.getClass().getSimpleName()); } } throw new IllegalArgumentException("Image type is not yet supported: "+src.getClass().getSimpleName()); } /** * Converts a {@link GrayU8} into a BufferedImage. If the buffered image * has multiple channels then the input image is copied into each channel. * * @param src Input image. * @param dst Where the converted image is written to. If null a new image is created. * @return Converted image. */ public static BufferedImage convertTo(GrayU8 src, BufferedImage dst) { dst = checkInputs(src, dst); try { if (dst.getRaster() instanceof ByteInterleavedRaster && dst.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.grayToBuffered(src, (ByteInterleavedRaster) dst.getRaster()); } else if (dst.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.grayToBuffered(src, (IntegerInterleavedRaster) dst.getRaster()); } else { ConvertRaster.grayToBuffered(src, dst); } // hack so that it knows the buffer has been modified dst.setRGB(0,0,dst.getRGB(0,0)); } catch( java.security.AccessControlException e) { ConvertRaster.grayToBuffered(src, dst); } return dst; } /** * Converts a {@link GrayI16} into a BufferedImage. If the buffered image * has multiple channels then the input image is copied into each channel. * * @param src Input image. * @param dst Where the converted image is written to. If null a new image is created. * @return Converted image. */ public static BufferedImage convertTo(GrayI16 src, BufferedImage dst) { dst = checkInputs(src, dst); try { if (dst.getRaster() instanceof ByteInterleavedRaster && dst.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.grayToBuffered(src, (ByteInterleavedRaster) dst.getRaster()); } else if (dst.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.grayToBuffered(src, (IntegerInterleavedRaster) dst.getRaster()); } else if( dst.getType() == BufferedImage.TYPE_USHORT_GRAY ) { ConvertRaster.grayToBuffered(src, (ShortInterleavedRaster) dst.getRaster()); } else { ConvertRaster.grayToBuffered(src, dst); } // hack so that it knows the buffer has been modified dst.setRGB(0,0,dst.getRGB(0,0)); } catch( java.security.AccessControlException e) { ConvertRaster.grayToBuffered(src, dst); } return dst; } /** * Converts the buffered image into an {@link GrayF32}. If the buffered image * has multiple channels then the input image is copied into each channel. The floating * point image is assumed to be between 0 and 255. * * @param src Input image. * @param dst Where the converted image is written to. If null a new image is created. * @return Converted image. */ public static BufferedImage convertTo(GrayF32 src, BufferedImage dst) { dst = checkInputs(src, dst); try { if (dst.getRaster() instanceof ByteInterleavedRaster && dst.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.grayToBuffered(src, (ByteInterleavedRaster) dst.getRaster()); } else if (dst.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.grayToBuffered(src, (IntegerInterleavedRaster) dst.getRaster()); } else { ConvertRaster.grayToBuffered(src, dst); } // hack so that it knows the buffer has been modified dst.setRGB(0,0,dst.getRGB(0,0)); } catch( java.security.AccessControlException e) { ConvertRaster.grayToBuffered(src, dst); } return dst; } /** * Converts a {@link Planar} {@link GrayU8} into a BufferedImage. * * @param src Input image. * @param dst Where the converted image is written to. If null a new image is created. * @param orderRgb If applicable, should it change the order of the color bands (assumed RGB or ARGB) into the * order based on BufferedImage.TYPE. Most of the time you want this to be true. * @return Converted image. */ public static BufferedImage convertTo_U8(Planar src, BufferedImage dst, boolean orderRgb ) { dst = checkInputs(src, dst); if( orderRgb ) { src = orderBandsIntoBuffered(src, dst); } try { if (dst.getRaster() instanceof ByteInterleavedRaster && dst.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.multToBuffered_U8(src, (ByteInterleavedRaster) dst.getRaster()); } else if (dst.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.multToBuffered_U8(src, (IntegerInterleavedRaster) dst.getRaster()); } else { ConvertRaster.multToBuffered_U8(src, dst); } // hack so that it knows the buffer has been modified dst.setRGB(0,0,dst.getRGB(0,0)); } catch( java.security.AccessControlException e) { ConvertRaster.multToBuffered_U8(src, dst); } return dst; } /** * Converts a {@link Planar} {@link GrayF32} into a BufferedImage. * * @param src Input image. * @param dst Where the converted image is written to. If null a new image is created. * @param orderRgb If applicable, should it change the order of the color bands (assumed RGB or ARGB) into the * order based on BufferedImage.TYPE. Most of the time you want this to be true. * @return Converted image. */ public static BufferedImage convertTo_F32(Planar src, BufferedImage dst, boolean orderRgb) { dst = checkInputs(src, dst); if( orderRgb ) { src = orderBandsIntoBuffered(src, dst); } try { if (dst.getRaster() instanceof ByteInterleavedRaster && dst.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.multToBuffered_F32(src, (ByteInterleavedRaster) dst.getRaster()); } else if (dst.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.multToBuffered_F32(src, (IntegerInterleavedRaster) dst.getRaster()); } else { ConvertRaster.multToBuffered_F32(src, dst); } // hack so that it knows the buffer has been modified dst.setRGB(0,0,dst.getRGB(0,0)); } catch( java.security.AccessControlException e) { ConvertRaster.multToBuffered_F32(src, dst); } return dst; } public static BufferedImage convertTo( InterleavedU8 src, BufferedImage dst, boolean orderRgb ) { dst = checkInputs(src, dst); try { if (dst.getRaster() instanceof ByteInterleavedRaster && dst.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.interleavedToBuffered(src, (ByteInterleavedRaster) dst.getRaster()); if( orderRgb ) orderBandsBufferedFromRGB((ByteInterleavedRaster)dst.getRaster(),dst.getType()); } else if (dst.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.interleavedToBuffered(src, (IntegerInterleavedRaster) dst.getRaster()); if( orderRgb ) orderBandsBufferedFromRGB((IntegerInterleavedRaster) dst.getRaster(), dst.getType()); } else { ConvertRaster.interleavedToBuffered(src, dst); } // hack so that it knows the buffer has been modified dst.setRGB(0,0,dst.getRGB(0,0)); } catch( java.security.AccessControlException e) { ConvertRaster.interleavedToBuffered(src, dst); } return dst; } public static BufferedImage convertTo( InterleavedF32 src, BufferedImage dst, boolean orderRgb ) { dst = checkInputs(src, dst); try { if (dst.getRaster() instanceof ByteInterleavedRaster && dst.getType() != BufferedImage.TYPE_BYTE_INDEXED ) { ConvertRaster.interleavedToBuffered(src, (ByteInterleavedRaster) dst.getRaster()); if( orderRgb ) orderBandsBufferedFromRGB((ByteInterleavedRaster)dst.getRaster(),dst.getType()); } else if (dst.getRaster() instanceof IntegerInterleavedRaster) { ConvertRaster.interleavedToBuffered(src, (IntegerInterleavedRaster) dst.getRaster()); if( orderRgb ) orderBandsBufferedFromRGB((IntegerInterleavedRaster) dst.getRaster(), dst.getType()); } else { ConvertRaster.interleavedToBuffered(src, dst); } // hack so that it knows the buffer has been modified dst.setRGB(0,0,dst.getRGB(0,0)); } catch( java.security.AccessControlException e) { ConvertRaster.interleavedToBuffered(src, dst); } return dst; } /** * If null the dst is declared, otherwise it checks to see if the 'dst' as the same shape as 'src'. */ public static BufferedImage checkInputs(ImageBase src, BufferedImage dst) { if (dst != null) { if (dst.getWidth() != src.getWidth() || dst.getHeight() != src.getHeight()) { throw new IllegalArgumentException("image dimension are different"); } } else { if( GrayI8.class.isInstance(src)) dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_GRAY); else if( GrayF.class.isInstance(src) ) // no good equivalent. Just assume the image is a regular gray scale image // with pixel values from 0 to 255 dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_GRAY); // throw new RuntimeException("Fail!"); else if( GrayI.class.isInstance(src)) // no good equivalent. I'm giving it the biggest pixel for the range dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_USHORT_GRAY); else dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB); } return dst; } /** * Draws the component into a BufferedImage. * * @param comp The component being drawn into an image. * @param storage if not null the component is drawn into it, if null a new BufferedImage is created. * @return image of the component */ public static BufferedImage convertTo(JComponent comp, BufferedImage storage) { if (storage == null) storage = new BufferedImage(comp.getWidth(), comp.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g2 = storage.createGraphics(); comp.paintComponents(g2); return storage; } /** * Returns a new image with the color bands in the appropriate ordering. The returned image will * reference the original image's image arrays. */ public static Planar orderBandsIntoBuffered(Planar src, BufferedImage dst) { // see if no change is required if( dst.getType() == BufferedImage.TYPE_INT_RGB ) return src; Planar tmp = new Planar(src.type, src.getNumBands()); tmp.width = src.width; tmp.height = src.height; tmp.stride = src.stride; tmp.startIndex = src.startIndex; for( int i = 0; i < src.getNumBands(); i++ ) { tmp.bands[i] = src.bands[i]; } orderBandsBufferedFromRgb(tmp, dst); return tmp; } /** * The image the BufferedImage was created from had RGB or RGBA color order. This swaps the bytes around * to put it into the expected local format */ public static void orderBandsBufferedFromRGB( ByteInterleavedRaster raster , int type ) { int height = raster.getHeight(); int width = raster.getWidth(); int stride = raster.getScanlineStride(); int offset = raster.getDataOffset(0)-raster.getPixelStride()+1; byte data[] = raster.getDataStorage(); if( BufferedImage.TYPE_3BYTE_BGR == type ) { for (int y = 0; y < height; y++) { int index = offset + y*stride; for (int x = 0; x < width; x++) { byte tmp = data[index]; data[index] = data[index+2]; data[index+2] = tmp; index += 3; } } } else if( BufferedImage.TYPE_4BYTE_ABGR == type ) { for (int y = 0; y < height; y++) { int index = offset + y*stride; for (int x = 0; x < width; x++) { byte tmp0 = data[index]; byte tmp1 = data[index+1]; data[index] = data[index+3]; data[index+1] = data[index+2]; data[index+2] = tmp1; data[index+3] = tmp0; index += 4; } } } else { throw new IllegalArgumentException("Unsupported buffered image type"); } } /** * The image the BufferedImage was created from had RGB or RGBA color order. This swaps the bytes around * to put it into the expected local format */ public static void orderBandsBufferedFromRGB( IntegerInterleavedRaster raster , int type ) { if( BufferedImage.TYPE_INT_RGB == type ) return; int height = raster.getHeight(); int width = raster.getWidth(); int stride = raster.getScanlineStride(); int offset = raster.getDataOffset(0)-raster.getPixelStride()+1; int data[] = raster.getDataStorage(); if( BufferedImage.TYPE_INT_BGR == type ) { for (int y = 0; y < height; y++) { int index = offset + y*stride; for (int x = 0; x < width; x++, index++) { int tmp = data[index]; int c1 = tmp & 0xFF; int c2 = (tmp >> 8) & 0xFF; int c3 = (tmp >> 16) & 0xFF; data[index] = c1 << 16 | c2 << 8 | c3; } } } else if( BufferedImage.TYPE_INT_ARGB == type ) { for (int y = 0; y < height; y++) { int index = offset + y*stride; for (int x = 0; x < width; x++, index++) { int tmp = data[index]; int c1 = tmp & 0xFF; int c2 = (tmp >> 8) & 0xFF; int c3 = (tmp >> 16) & 0xFF; int c4 = (tmp >> 24) & 0xFF; data[index] = c1 << 24 | c4 << 16 | c3 << 8 | c2; } } } else { throw new IllegalArgumentException("Unsupported buffered image type"); } } /** * If a Planar was created from a BufferedImage its colors might not be in the expected order. * Invoking this function ensures that the image will have the expected ordering. For images with * 3 bands it will be RGB and for 4 bands it will be ARGB. */ public static void orderBandsIntoRGB(Planar image , BufferedImage input ) { boolean swap = swapBandOrder(input); // Output formats are: RGB and RGBA if( swap ) { if( image.getNumBands() == 3 ) { int bufferedImageType = input.getType(); if( bufferedImageType == BufferedImage.TYPE_3BYTE_BGR || bufferedImageType == BufferedImage.TYPE_INT_BGR ) { T tmp = image.getBand(0); image.bands[0] = image.getBand(2); image.bands[2] = tmp; } } else if( image.getNumBands() == 4 ) { T[] temp = (T[])Array.newInstance(image.getBandType(),4); int bufferedImageType = input.getType(); if( bufferedImageType == BufferedImage.TYPE_INT_ARGB ) { temp[0] = image.getBand(1); temp[1] = image.getBand(2); temp[2] = image.getBand(3); temp[3] = image.getBand(0); } else if( bufferedImageType == BufferedImage.TYPE_4BYTE_ABGR ) { temp[0] = image.getBand(3); temp[1] = image.getBand(2); temp[2] = image.getBand(1); temp[3] = image.getBand(0); } image.bands[0] = temp[0]; image.bands[1] = temp[1]; image.bands[2] = temp[2]; image.bands[3] = temp[3]; } } } public static void orderBandsBufferedFromRgb(Planar image, BufferedImage input) { boolean swap = swapBandOrder(input); // Output formats are: RGB and RGBA if( swap ) { if( image.getNumBands() == 3 ) { int bufferedImageType = input.getType(); if( bufferedImageType == BufferedImage.TYPE_3BYTE_BGR || bufferedImageType == BufferedImage.TYPE_INT_BGR ) { T tmp = image.getBand(0); image.bands[0] = image.getBand(2); image.bands[2] = tmp; } } else if( image.getNumBands() == 4 ) { T[] temp = (T[])Array.newInstance(image.getBandType(),4); int bufferedImageType = input.getType(); if( bufferedImageType == BufferedImage.TYPE_INT_ARGB ) { temp[0] = image.getBand(3); temp[1] = image.getBand(0); temp[2] = image.getBand(1); temp[3] = image.getBand(2); } else if( bufferedImageType == BufferedImage.TYPE_4BYTE_ABGR ) { temp[0] = image.getBand(3); temp[1] = image.getBand(2); temp[2] = image.getBand(1); temp[3] = image.getBand(0); } image.bands[0] = temp[0]; image.bands[1] = temp[1]; image.bands[2] = temp[2]; image.bands[3] = temp[3]; } } } public static void orderBandsIntoRGB( ImageInterleaved image , BufferedImage input ) { if( image instanceof InterleavedU8 ) { orderBandsIntoRGB((InterleavedU8)image,input); } else if( image instanceof InterleavedF32 ) { orderBandsIntoRGB((InterleavedF32)image,input); } else { throw new IllegalArgumentException("Unsupported interleaved type"); } } public static void orderBandsIntoRGB( InterleavedU8 image , BufferedImage input ) { boolean swap = swapBandOrder(input); // Output formats are: RGB and RGBA if( swap ) { if( image.getNumBands() == 3 ) { for (int y = 0; y < image.height; y++) { int index = image.startIndex + y*image.stride; int indexEnd = index + image.width*3; while( index < indexEnd ) { byte tmp = image.data[index+2]; image.data[index+2] = image.data[index]; image.data[index] = tmp; index += 3; } } } else if( image.getNumBands() == 4 ) { int bufferedImageType = input.getType(); if( bufferedImageType == BufferedImage.TYPE_INT_ARGB ) { for (int y = 0; y < image.height; y++) { int index = image.startIndex + y*image.stride; int indexEnd = index + image.width*3; while( index < indexEnd ) { byte tmp = image.data[index]; image.data[index] = image.data[index+1]; image.data[index+1] = image.data[index+2]; image.data[index+2] = image.data[index+3]; image.data[index+3] = tmp; index += 4; } } } else if( bufferedImageType == BufferedImage.TYPE_4BYTE_ABGR ) { for (int y = 0; y < image.height; y++) { int index = image.startIndex + y*image.stride; int indexEnd = index + image.width*3; while( index < indexEnd ) { byte tmp1 = image.data[index+1]; byte tmp0 = image.data[index]; image.data[index] = image.data[index+3]; image.data[index+1] = image.data[index+2]; image.data[index+2] = tmp1; image.data[index+3] = tmp0; index += 4; } } } } } } public static void orderBandsIntoRGB( InterleavedF32 image , BufferedImage input ) { boolean swap = swapBandOrder(input); // Output formats are: RGB and RGBA if( swap ) { if( image.getNumBands() == 3 ) { for (int y = 0; y < image.height; y++) { int index = image.startIndex + y*image.stride; int indexEnd = index + image.width*3; while( index < indexEnd ) { float tmp = image.data[index+2]; image.data[index+2] = image.data[index]; image.data[index] = tmp; index += 3; } } } else if( image.getNumBands() == 4 ) { int bufferedImageType = input.getType(); if( bufferedImageType == BufferedImage.TYPE_INT_ARGB ) { for (int y = 0; y < image.height; y++) { int index = image.startIndex + y*image.stride; int indexEnd = index + image.width*3; while( index < indexEnd ) { float tmp = image.data[index]; image.data[index] = image.data[index+1]; image.data[index+1] = image.data[index+2]; image.data[index+2] = image.data[index+3]; image.data[index+3] = tmp; index += 4; } } } else if( bufferedImageType == BufferedImage.TYPE_4BYTE_ABGR ) { for (int y = 0; y < image.height; y++) { int index = image.startIndex + y*image.stride; int indexEnd = index + image.width*3; while( index < indexEnd ) { float tmp1 = image.data[index+1]; float tmp0 = image.data[index]; image.data[index] = image.data[index+3]; image.data[index+1] = image.data[index+2]; image.data[index+2] = tmp1; image.data[index+3] = tmp0; index += 4; } } } } } } private static boolean swapBandOrder(BufferedImage input) { boolean swap = false; // see if access to the raster is restricted or not try { WritableRaster raster = input.getRaster(); if( raster instanceof ByteInterleavedRaster) { ((ByteInterleavedRaster)raster).getDataStorage(); } else if( raster instanceof IntegerInterleavedRaster) { ((IntegerInterleavedRaster)raster).getDataStorage(); } int bufferedImageType = input.getType(); if( bufferedImageType == BufferedImage.TYPE_3BYTE_BGR || bufferedImageType == BufferedImage.TYPE_INT_BGR || bufferedImageType == BufferedImage.TYPE_INT_ARGB || bufferedImageType == BufferedImage.TYPE_4BYTE_ABGR ) { swap = true; } } catch( java.security.AccessControlException e) { // its in an applet or something and will need to use getRGB() to read/write from the image // so no need to re-order the bands } return swap; } /** * Checks to see if the input image is a subImage(). * @param img * @return */ public static boolean isSubImage( BufferedImage img ) { return img.getRaster().getParent() != null; } }

Ads help maintain this website.