Java/2D Graphics GUI/Image Filter
Содержание
- 1 Copy Raster
- 2 Dithering a 24-bit RGB image to a monochrome (1-bit or bilevel) image
- 3 Gray scale image operation
- 4 If the image has transparent pixels
- 5 Image Color Operation: dark, bright, contrast, negative
- 6 ImageComparator compares a byte[] for equality by creating 2 hashes for the bytearray and comparing thoose hashes.
- 7 Image Filter
Copy Raster
<source lang="java"> /*
* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */
/* $Id: GraphicsUtil.java 603243 2007-12-11 13:49:04Z jeremias $ */
import java.awt.Point; import java.awt.Rectangle; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ruponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.DirectColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; /**
* Set of utility methods for Graphics. * These generally bypass broken methods in Java2D or provide tweaked * implementations. * * @author * @version $Id: GraphicsUtil.java 603243 2007-12-11 13:49:04Z jeremias $ */
public class GraphicsUtil {
public static AffineTransform IDENTITY = new AffineTransform(); /** * Standard prebuilt Linear_sRGB color model with no alpha */ public static final ColorModel Linear_sRGB = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_LINEAR_RGB), 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0, false, DataBuffer.TYPE_INT); /** * Standard prebuilt Linear_sRGB color model with premultiplied alpha. */ public static final ColorModel Linear_sRGB_Pre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_LINEAR_RGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, true, DataBuffer.TYPE_INT); /** * Standard prebuilt Linear_sRGB color model with unpremultiplied alpha. */ public static final ColorModel Linear_sRGB_Unpre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_LINEAR_RGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, false, DataBuffer.TYPE_INT); /** * Standard prebuilt sRGB color model with no alpha. */ public static final ColorModel sRGB = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_sRGB), 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0, false, DataBuffer.TYPE_INT); /** * Standard prebuilt sRGB color model with premultiplied alpha. */ public static final ColorModel sRGB_Pre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_sRGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, true, DataBuffer.TYPE_INT); /** * Standard prebuilt sRGB color model with unpremultiplied alpha. */ public static final ColorModel sRGB_Unpre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_sRGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, false, DataBuffer.TYPE_INT); /** * Method that returns either Linear_sRGB_Pre or Linear_sRGB_UnPre * based on premult flag. * @param premult True if the ColorModel should have premultiplied alpha. * @return a ColorMdoel with Linear sRGB colorSpace and * the alpha channel set in accordance with * premult */ public static ColorModel makeLinear_sRGBCM(boolean premult) { return premult ? Linear_sRGB_Pre : Linear_sRGB_Unpre; } /** * Constructs a BufferedImage with a linear sRGB colorModel, and alpha. * @param width The desired width of the BufferedImage * @param height The desired height of the BufferedImage * @param premult The desired state of alpha premultiplied * @return The requested BufferedImage. */ public static BufferedImage makeLinearBufferedImage(int width, int height, boolean premult) { ColorModel cm = makeLinear_sRGBCM(premult); WritableRaster wr = cm.createCompatibleWritableRaster(width, height); return new BufferedImage(cm, wr, premult, null); } /** * Creates a new raster that has a copy of the data in * ras. This is highly optimized for speed. There is * no provision for changing any aspect of the SampleModel. * * This method should be used when you need to change the contents * of a Raster that you do not "own" (ie the result of a * getData call). * @param ras The Raster to copy. * @return A writable copy of ras */ public static WritableRaster copyRaster(Raster ras) { return copyRaster(ras, ras.getMinX(), ras.getMinY()); }
/** * Creates a new raster that has a copy of the data in * ras. This is highly optimized for speed. There is * no provision for changing any aspect of the SampleModel. * However you can specify a new location for the returned raster. * * This method should be used when you need to change the contents * of a Raster that you do not "own" (ie the result of a * getData call). * * @param ras The Raster to copy. * * @param minX The x location for the upper left corner of the * returned WritableRaster. * * @param minY The y location for the upper left corner of the * returned WritableRaster. * * @return A writable copy of ras */ public static WritableRaster copyRaster(Raster ras, int minX, int minY) { WritableRaster ret = Raster.createWritableRaster (ras.getSampleModel(), new Point(0,0)); ret = ret.createWritableChild (ras.getMinX()-ras.getSampleModelTranslateX(), ras.getMinY()-ras.getSampleModelTranslateY(), ras.getWidth(), ras.getHeight(), minX, minY, null); // Use System.arraycopy to copy the data between the two... DataBuffer srcDB = ras.getDataBuffer(); DataBuffer retDB = ret.getDataBuffer(); if (srcDB.getDataType() != retDB.getDataType()) { throw new IllegalArgumentException ("New DataBuffer doesn"t match original"); } int len = srcDB.getSize(); int banks = srcDB.getNumBanks(); int [] offsets = srcDB.getOffsets(); for (int b=0; b< banks; b++) { switch (srcDB.getDataType()) { case DataBuffer.TYPE_BYTE: { DataBufferByte srcDBT = (DataBufferByte)srcDB; DataBufferByte retDBT = (DataBufferByte)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } case DataBuffer.TYPE_INT: { DataBufferInt srcDBT = (DataBufferInt)srcDB; DataBufferInt retDBT = (DataBufferInt)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } case DataBuffer.TYPE_SHORT: { DataBufferShort srcDBT = (DataBufferShort)srcDB; DataBufferShort retDBT = (DataBufferShort)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } case DataBuffer.TYPE_USHORT: { DataBufferUShort srcDBT = (DataBufferUShort)srcDB; DataBufferUShort retDBT = (DataBufferUShort)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } } } return ret; } /** * Coerces ras to be writable. The returned Raster continues to * reference the DataBuffer from ras, so modifications to the returned* WritableRaster will be seen in ras.
* * This method should only be used if you need a WritableRaster due to * an interface (such as to construct a BufferedImage), but have no * intention of modifying the contents of the returned Raster. If * you have any doubt about other users of the data in ras, * use copyRaster (above). * @param ras The raster to make writable. * @return A Writable version of ras (shares DataBuffer with * ras). */ public static WritableRaster makeRasterWritable(Raster ras) { return makeRasterWritable(ras, ras.getMinX(), ras.getMinY()); } /** * Coerces ras to be writable. The returned Raster continues to * reference the DataBuffer from ras, so modifications to the returned * WritableRaster will be seen in ras.<p> * * You can specify a new location for the returned WritableRaster, this * is especially useful for constructing BufferedImages which require * the Raster to be at (0,0). * * This method should only be used if you need a WritableRaster due to * an interface (such as to construct a BufferedImage), but have no * intention of modifying the contents of the returned Raster. If * you have any doubt about other users of the data in ras, * use copyRaster (above). * * @param ras The raster to make writable. * * @param minX The x location for the upper left corner of the * returned WritableRaster. * * @param minY The y location for the upper left corner of the * returned WritableRaster. * * @return A Writable version of ras with it"s upper left * hand coordinate set to minX, minY (shares it"s DataBuffer * with ras). */ public static WritableRaster makeRasterWritable(Raster ras, int minX, int minY) { WritableRaster ret = Raster.createWritableRaster (ras.getSampleModel(), ras.getDataBuffer(), new Point(0,0)); ret = ret.createWritableChild (ras.getMinX()-ras.getSampleModelTranslateX(), ras.getMinY()-ras.getSampleModelTranslateY(), ras.getWidth(), ras.getHeight(), minX, minY, null); return ret; } /** * Create a new ColorModel with it"s alpha premultiplied state matching * newAlphaPreMult. * @param cm The ColorModel to change the alpha premult state of. * @param newAlphaPreMult The new state of alpha premult. * @return A new colorModel that has isAlphaPremultiplied() * equal to newAlphaPreMult. */ public static ColorModel coerceColorModel(ColorModel cm, boolean newAlphaPreMult) { if (cm.isAlphaPremultiplied() == newAlphaPreMult) return cm; // Easiest way to build proper colormodel for new Alpha state... // Eventually this should switch on known ColorModel types and // only fall back on this hack when the CM type is unknown. WritableRaster wr = cm.createCompatibleWritableRaster(1,1); return cm.coerceData(wr, newAlphaPreMult); } /** * Coerces data within a bufferedImage to match newAlphaPreMult, * Note that this can not change the colormodel of bi so you * * @param wr The raster to change the state of. * @param cm The colormodel currently associated with data in wr. * @param newAlphaPreMult The desired state of alpha Premult for raster. * @return A new colormodel that matches newAlphaPreMult. */ public static ColorModel coerceData(WritableRaster wr, ColorModel cm, boolean newAlphaPreMult) { // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() + // " Out: " + newAlphaPreMult); if (!cm.hasAlpha()) // Nothing to do no alpha channel return cm; if (cm.isAlphaPremultiplied() == newAlphaPreMult) // nothing to do alpha state matches... return cm; // System.out.println("CoerceData: " + wr.getSampleModel()); if (newAlphaPreMult) { multiplyAlpha(wr); } else { divideAlpha(wr); } return coerceColorModel(cm, newAlphaPreMult); } public static void multiplyAlpha(WritableRaster wr) { if (is_BYTE_COMP_Data(wr.getSampleModel())) mult_BYTE_COMP_Data(wr); else if (is_INT_PACK_Data(wr.getSampleModel(), true)) mult_INT_PACK_Data(wr); else { int [] pixel = null; int bands = wr.getNumBands(); float norm = 1f/255f; int x0, x1, y0, y1, a, b; float alpha; x0 = wr.getMinX(); x1 = x0+wr.getWidth(); y0 = wr.getMinY(); y1 = y0+wr.getHeight(); for (int y=y0; y<y1; y++) for (int x=x0; x<x1; x++) { pixel = wr.getPixel(x,y,pixel); a = pixel[bands-1]; if ((a >= 0) && (a < 255)) { alpha = a*norm; for (b=0; b<bands-1; b++) pixel[b] = (int)(pixel[b]*alpha+0.5f); wr.setPixel(x,y,pixel); } } } } public static void divideAlpha(WritableRaster wr) { if (is_BYTE_COMP_Data(wr.getSampleModel())) divide_BYTE_COMP_Data(wr); else if (is_INT_PACK_Data(wr.getSampleModel(), true)) divide_INT_PACK_Data(wr); else { int x0, x1, y0, y1, a, b; float ialpha; int bands = wr.getNumBands(); int [] pixel = null; x0 = wr.getMinX(); x1 = x0+wr.getWidth(); y0 = wr.getMinY(); y1 = y0+wr.getHeight(); for (int y=y0; y<y1; y++) for (int x=x0; x<x1; x++) { pixel = wr.getPixel(x,y,pixel); a = pixel[bands-1]; if ((a > 0) && (a < 255)) { ialpha = 255/(float)a; for (b=0; b<bands-1; b++) pixel[b] = (int)(pixel[b]*ialpha+0.5f); wr.setPixel(x,y,pixel); } } } } public static void copyBand(Raster src, int srcBand, WritableRaster dst, int dstBand) { Rectangle sR = src.getBounds(); Rectangle dR = dst.getBounds(); Rectangle cpR = sR.intersection(dR); copyBand(src, cpR, srcBand, dst, cpR, dstBand); } public static void copyBand(Raster src, Rectangle sR, int sBand, WritableRaster dst, Rectangle dR, int dBand) { int dy = dR.y -sR.y; int dx = dR.x -sR.x; sR = sR.intersection(src.getBounds()); dR = dR.intersection(dst.getBounds()); int width, height; if (dR.width < sR.width) width = dR.width; else width = sR.width; if (dR.height < sR.height) height = dR.height; else height = sR.height; int x = sR.x+dx; int [] samples = null; for (int y=sR.y; y< sR.y+height; y++) { samples = src.getSamples(sR.x, y, width, 1, sBand, samples); dst.setSamples(x, y+dy, width, 1, dBand, samples); } } public static boolean is_INT_PACK_Data(SampleModel sm, boolean requireAlpha) { // Check ColorModel is of type DirectColorModel if(!(sm instanceof SinglePixelPackedSampleModel)) return false; // Check transfer type if(sm.getDataType() != DataBuffer.TYPE_INT) return false; SinglePixelPackedSampleModel sppsm; sppsm = (SinglePixelPackedSampleModel)sm; int [] masks = sppsm.getBitMasks(); if (masks.length == 3) { if (requireAlpha) return false; } else if (masks.length != 4) return false; if(masks[0] != 0x00ff0000) return false; if(masks[1] != 0x0000ff00) return false; if(masks[2] != 0x000000ff) return false; if ((masks.length == 4) && (masks[3] != 0xff000000)) return false; return true; } public static boolean is_BYTE_COMP_Data(SampleModel sm) { // Check ColorModel is of type DirectColorModel if(!(sm instanceof ComponentSampleModel)) return false; // Check transfer type if(sm.getDataType() != DataBuffer.TYPE_BYTE) return false; return true; } protected static void divide_INT_PACK_Data(WritableRaster wr) { // System.out.println("Divide Int"); SinglePixelPackedSampleModel sppsm; sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = sppsm.getScanlineStride(); DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); // Access the pixel data array final int[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width; while (sp < end) { int pixel = pixels[sp]; int a = pixel>>>24; if (a<=0) { pixels[sp] = 0x00FFFFFF; } else if (a<255) { int aFP = (0x00FF0000/a); pixels[sp] = ((a << 24) | (((((pixel&0xFF0000)>>16)*aFP)&0xFF0000) ) | (((((pixel&0x00FF00)>>8) *aFP)&0xFF0000)>>8 ) | (((((pixel&0x0000FF)) *aFP)&0xFF0000)>>16)); } sp++; } } } protected static void mult_INT_PACK_Data(WritableRaster wr) { // System.out.println("Multiply Int: " + wr); SinglePixelPackedSampleModel sppsm; sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = sppsm.getScanlineStride(); DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); // Access the pixel data array final int[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width; while (sp < end) { int pixel = pixels[sp]; int a = pixel>>>24; if ((a>=0) && (a<255)) { // this does NOT include a == 255 (0xff) ! pixels[sp] = ((a << 24) | ((((pixel&0xFF0000)*a)>>8)&0xFF0000) | ((((pixel&0x00FF00)*a)>>8)&0x00FF00) | ((((pixel&0x0000FF)*a)>>8)&0x0000FF)); } sp++; } } } protected static void divide_BYTE_COMP_Data(WritableRaster wr) { // System.out.println("Multiply Int: " + wr); ComponentSampleModel csm; csm = (ComponentSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = csm.getScanlineStride(); final int pixStride = csm.getPixelStride(); final int [] bandOff = csm.getBandOffsets(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); final int base = (db.getOffset() + csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); int aOff = bandOff[bandOff.length-1]; int bands = bandOff.length-1; // Access the pixel data array final byte[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width*pixStride; while (sp < end) { int a = pixels[sp+aOff]&0xFF; if (a==0) { for (int b = 0; b < bands; b++) pixels[sp+bandOff[b]] = (byte)0xFF; } else if (a<255) { // this does NOT include a == 255 (0xff) ! int aFP = (0x00FF0000/a); for (int b = 0; b < bands; b++) { int i = sp+bandOff[b]; pixels[i] = (byte)(((pixels[i]&0xFF)*aFP)>>>16); } } sp+=pixStride; } } } protected static void mult_BYTE_COMP_Data(WritableRaster wr) { // System.out.println("Multiply Int: " + wr); ComponentSampleModel csm; csm = (ComponentSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = csm.getScanlineStride(); final int pixStride = csm.getPixelStride(); final int [] bandOff = csm.getBandOffsets(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); final int base = (db.getOffset() + csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); int aOff = bandOff[bandOff.length-1]; int bands = bandOff.length-1; // Access the pixel data array final byte[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width*pixStride; while (sp < end) { int a = pixels[sp+aOff]&0xFF; if (a!=0xFF) for (int b = 0; b < bands; b++) { int i = sp+bandOff[b]; pixels[i] = (byte)(((pixels[i]&0xFF)*a)>>8); } sp+=pixStride; } } } /* This is skanky debugging code that might be useful in the future: if (count == 33) { String label = "sub [" + x + ", " + y + "]: "; org.ImageDisplay.showImage (label, subBI); org.ImageDisplay.printImage (label, subBI, new Rectangle(75-iR.x, 90-iR.y, 32, 32)); } // if ((count++ % 50) == 10) // org.ImageDisplay.showImage("foo: ", subBI); Graphics2D realG2D = g2d; while (realG2D instanceof sun.java2d.ProxyGraphics2D) { realG2D = ((sun.java2d.ProxyGraphics2D)realG2D).getDelegate(); } if (realG2D instanceof sun.awt.image.BufferedImageGraphics2D) { count++; if (count == 34) { RenderedImage ri; ri = ((sun.awt.image.BufferedImageGraphics2D)realG2D).bufImg; // g2d.setComposite(SVGComposite.OVER); // org.ImageDisplay.showImage("Bar: " + count, cr); org.ImageDisplay.printImage("Bar: " + count, cr, new Rectangle(75, 90, 32, 32)); org.ImageDisplay.showImage ("Foo: " + count, ri); org.ImageDisplay.printImage("Foo: " + count, ri, new Rectangle(75, 90, 32, 32)); System.out.println("BI: " + ri); System.out.println("BISM: " + ri.getSampleModel()); System.out.println("BICM: " + ri.getColorModel()); System.out.println("BICM class: " + ri.getColorModel().getClass()); System.out.println("BICS: " + ri.getColorModel().getColorSpace()); System.out.println ("sRGB CS: " + ColorSpace.getInstance(ColorSpace.CS_sRGB)); System.out.println("G2D info"); System.out.println("\tComposite: " + g2d.getComposite()); System.out.println("\tTransform" + g2d.getTransform()); java.awt.RenderingHints rh = g2d.getRenderingHints(); java.util.Set keys = rh.keySet(); java.util.Iterator iter = keys.iterator(); while (iter.hasNext()) { Object o = iter.next(); System.out.println("\t" + o.toString() + " -> " + rh.get(o).toString()); } ri = cr; System.out.println("RI: " + ri); System.out.println("RISM: " + ri.getSampleModel()); System.out.println("RICM: " + ri.getColorModel()); System.out.println("RICM class: " + ri.getColorModel().getClass()); System.out.println("RICS: " + ri.getColorModel().getColorSpace()); } }
- /
Dithering a 24-bit RGB image to a monochrome (1-bit or bilevel) image
<source lang="java"> //-----------------------------------------------------------------------// // // // R G B T o B i l e v e l // // // //-----------------------------------------------------------------------// /*
* Copyright (c) 2002 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */
package omr.jai; import java.awt.*; import java.awt.color.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.image.*; import java.awt.image.renderable.*; import java.util.*; import javax.media.jai.*; import javax.media.jai.operator.*; import javax.media.jai.widget.*; /**
* Demo code for dithering a 24-bit RGB image to a monochrome (1-bit * or bilevel) image. The source image must be an 24-bit RGB image. * The result is displayed. * * Usage: java RGBToBilevel filename [true] * * If the second argument is present and equal to "true" then error diffusion * will be used; otherwise ordered dithering will be used. */
public class RGBToBilevel extends Frame {
public static void main(String[] args) { new RGBToBilevel(args[0], args.length > 1 ? args[1].equals("true") : false); } RGBToBilevel(final String fileName, boolean isErrorDiffusion) { // Load the file. PlanarImage src = JAI.create("fileload", fileName); // Load the ParameterBlock for the dithering operation // and set the operation name. ParameterBlock pb = new ParameterBlock(); pb.addSource(src); String opName = null; if(isErrorDiffusion) { opName = "errordiffusion"; LookupTableJAI lut = new LookupTableJAI(new byte[][] {{(byte)0x00, (byte)0xff}, {(byte)0x00, (byte)0xff}, {(byte)0x00, (byte)0xff}}); pb.add(lut); pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG); } else { opName = "ordereddither"; ColorCube cube = ColorCube.createColorCube(DataBuffer.TYPE_BYTE, 0, new int[] {2, 2, 2}); pb.add(cube); pb.add(KernelJAI.DITHER_MASK_443); } // Create a layout containing an IndexColorModel which maps // zero to zero and unity to 255; force SampleModel to be bilevel. ImageLayout layout = new ImageLayout(); byte[] map = new byte[] {(byte)0x00, (byte)0xff}; ColorModel cm = new IndexColorModel(1, 2, map, map, map); layout.setColorModel(cm); SampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, src.getWidth(), src.getHeight(), 1); layout.setSampleModel(sm); // Create a hint containing the layout. RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); // Dither the image. final PlanarImage dst = JAI.create(opName, pb, hints); // Exit on window closing. addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { JAI.create("filestore", dst, fileName + ".out", "PNG", null); System.exit(0); } });
// Display the result. //// ATTENTION A REMPLACER : add(new ScrollingImagePanel(dst, dst.getWidth(), dst.getHeight())); pack(); setVisible(true); }
}
</source>
Gray scale image operation
<source lang="java">
/*
* Copyright (c) 2000 David Flanagan. All rights reserved. * This code is from the book Java Examples in a Nutshell, 2nd Edition. * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied. * You may study, use, and modify it for any non-commercial purpose. * You may distribute it non-commercially as long as you retain this notice. * For a commercial use license, or to purchase the book (recommended), * visit http://www.davidflanagan.ru/javaexamples2. */
import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ByteLookupTable; import java.awt.image.ColorConvertOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.awt.image.LookupOp; import java.awt.image.RescaleOp; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.net.URL; import javax.swing.JFrame; import javax.swing.JPanel; /** A demonstration of various image processing filters */ public class ImageOps extends JPanel {
static final int WIDTH = 600, HEIGHT = 675; // Size of our example public String getName() { return "Image Processing"; } public int getWidth() { return WIDTH; } public int getHeight() { return HEIGHT; } Image image; /** This constructor loads the image we will manipulate */
public ImageOps() {
image = Toolkit.getDefaultToolkit().getImage("a.jpg"); } // These arrays of bytes are used by the LookupImageOp image filters below static byte[] brightenTable = new byte[256]; static byte[] thresholdTable = new byte[256]; static { // Initialize the arrays for (int i = 0; i < 256; i++) { brightenTable[i] = (byte) (Math.sqrt(i / 255.0) * 255); thresholdTable[i] = (byte) ((i < 225) ? 0 : i); } } // This AffineTransform is used by one of the image filters below static AffineTransform mirrorTransform; static { // Create and initialize the AffineTransform mirrorTransform = AffineTransform.getTranslateInstance(127, 0); mirrorTransform.scale(-1.0, 1.0); // flip horizontally } // These are the labels we"ll display for each of the filtered images static String[] filterNames = new String[] { "Original", "Gray Scale", "Negative", "Brighten (linear)", "Brighten (sqrt)", "Threshold", "Blur", "Sharpen", "Edge Detect", "Mirror", "Rotate (center)", "Rotate (lower left)" }; // The following BufferedImageOp image filter objects perform // different types of image processing operations. static BufferedImageOp[] filters = new BufferedImageOp[] { // 1) No filter here. We"ll display the original image null, // 2) Convert to Grayscale color space new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null), // 3) Image negative. Multiply each color value by -1.0 and add 255 new RescaleOp(-1.0f, 255f, null), // 4) Brighten using a linear formula that increases all color // values new RescaleOp(1.25f, 0, null), // 5) Brighten using the lookup table defined above new LookupOp(new ByteLookupTable(0, brightenTable), null), // 6) Threshold using the lookup table defined above new LookupOp(new ByteLookupTable(0, thresholdTable), null), // 7) Blur by "convolving" the image with a matrix new ConvolveOp(new Kernel(3, 3, new float[] { .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, })), // 8) Sharpen by using a different matrix new ConvolveOp(new Kernel(3, 3, new float[] { 0.0f, -0.75f, 0.0f, -0.75f, 4.0f, -0.75f, 0.0f, -0.75f, 0.0f })), // 9) Edge detect using yet another matrix new ConvolveOp(new Kernel(3, 3, new float[] { 0.0f, -0.75f, 0.0f, -0.75f, 3.0f, -0.75f, 0.0f, -0.75f, 0.0f })), // 10) Compute a mirror image using the transform defined above new AffineTransformOp(mirrorTransform, AffineTransformOp.TYPE_BILINEAR), // 11) Rotate the image 180 degrees about its center point new AffineTransformOp(AffineTransform.getRotateInstance(Math.PI, 64, 95), AffineTransformOp.TYPE_NEAREST_NEIGHBOR), // 12) Rotate the image 15 degrees about the bottom left new AffineTransformOp(AffineTransform.getRotateInstance(Math.PI / 12, 0, 190), AffineTransformOp.TYPE_NEAREST_NEIGHBOR), }; /** Draw the example */ public void paint(Graphics g1) { Graphics2D g = (Graphics2D) g1; // Create a BufferedImage big enough to hold the Image loaded // in the constructor. Then copy that image into the new // BufferedImage object so that we can process it. BufferedImage bimage = new BufferedImage(image.getWidth(this), image.getHeight(this), BufferedImage.TYPE_INT_RGB); Graphics2D ig = bimage.createGraphics(); ig.drawImage(image, 0, 0, this); // copy the image // Set some default graphics attributes g.setFont(new Font("SansSerif", Font.BOLD, 12)); // 12pt bold text g.setColor(Color.green); // Draw in green g.translate(10, 10); // Set some margins // Loop through the filters for (int i = 0; i < filters.length; i++) { // If the filter is null, draw the original image, otherwise, // draw the image as processed by the filter if (filters[i] == null) g.drawImage(bimage, 0, 0, this); else g.drawImage(filters[i].filter(bimage, null), 0, 0, this); g.drawString(filterNames[i], 0, 205); // Label the image g.translate(137, 0); // Move over if (i % 4 == 3) g.translate(-137 * 4, 215); // Move down after 4 } } public static void main(String[] a) { JFrame f = new JFrame(); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); f.setContentPane(new ImageOps()); f.pack(); f.setVisible(true); }
}
</source>
If the image has transparent pixels
<source lang="java"> /*
* Copyright (c) JForum Team * All rights reserved. * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * 1) Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * 2) Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or * other materials provided with the distribution. * 3) Neither the name of "Rafael Steil" nor * the names of its contributors may be used to endorse * or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE * * This file creation date: 21/04/2004 - 19:54:16 * The JForum Project * http://www.jforum.net */
import java.awt.Dimension; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.PixelGrabber; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.Locale; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import javax.imageio.stream.ImageOutputStream;
/**
* Utilities methods for image manipulation. It does not support writting of GIF images, but it can * read from. GIF images will be saved as PNG. * * @author Rafael Steil * @version $Id: ImageUtils.java,v 1.23 2007/09/09 01:05:22 rafaelsteil Exp $ */
public class ImageUtils {
public static final int IMAGE_UNKNOWN = -1; public static final int IMAGE_JPEG = 0; public static final int IMAGE_PNG = 1; public static final int IMAGE_GIF = 2; /** * Resizes an image * * @param imgName The image name to resize. Must be the complet path to the file * @param type int * @param maxWidth The image"s max width * @param maxHeight The image"s max height * @return A resizedBufferedImage
*/ public static BufferedImage resizeImage(String imgName, int type, int maxWidth, int maxHeight) { try { return resizeImage(ImageIO.read(new File(imgName)), type, maxWidth, maxHeight); } catch (IOException e) { e.printStackTrace(); } } /** * Resizes an image. * * @param image * The image to resize * @param maxWidth * The image"s max width * @param maxHeight * The image"s max height * @return A resizedBufferedImage
* @param type * int */ public static BufferedImage resizeImage(BufferedImage image, int type, int maxWidth, int maxHeight) { Dimension largestDimension = new Dimension(maxWidth, maxHeight); // Original size int imageWidth = image.getWidth(null); int imageHeight = image.getHeight(null); float aspectRatio = (float) imageWidth / imageHeight; if (imageWidth > maxWidth || imageHeight > maxHeight) { if ((float) largestDimension.width / largestDimension.height > aspectRatio) { largestDimension.width = (int) Math.ceil(largestDimension.height * aspectRatio); } else { largestDimension.height = (int) Math.ceil(largestDimension.width / aspectRatio); } imageWidth = largestDimension.width; imageHeight = largestDimension.height; } return createHeadlessSmoothBufferedImage(image, type, imageWidth, imageHeight); } /** * Saves an image to the disk. * * @param image The image to save * @param toFileName The filename to use * @param type The image type. UseImageUtils.IMAGE_JPEG
to save as JPEG images, * orImageUtils.IMAGE_PNG
to save as PNG. * @returnfalse
if no appropriate writer is found */ public static boolean saveImage(BufferedImage image, String toFileName, int type) { try { return ImageIO.write(image, type == IMAGE_JPEG ? "jpg" : "png", new File(toFileName)); } catch (IOException e) { e.printStackTrace(); } } /** * Compress and save an image to the disk. Currently this method only supports JPEG images. * * @param image The image to save * @param toFileName The filename to use * @param type The image type. UseImageUtils.IMAGE_JPEG
to save as JPEG images, * orImageUtils.IMAGE_PNG
to save as PNG. */ public static void saveCompressedImage(BufferedImage image, String toFileName, int type) { try { if (type == IMAGE_PNG) { throw new UnsupportedOperationException("PNG compression not implemented"); } Iterator iter = ImageIO.getImageWritersByFormatName("jpg"); ImageWriter writer; writer = (ImageWriter) iter.next(); ImageOutputStream ios = ImageIO.createImageOutputStream(new File(toFileName)); writer.setOutput(ios); ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault()); iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); iwparam.setCompressionQuality(0.7F); writer.write(null, new IIOImage(image, null, null), iwparam); ios.flush(); writer.dispose(); ios.close(); } catch (IOException e) { e.printStackTrace(); } } /** * Creates aBufferedImage
from anImage
. This method can * function on a completely headless system. This especially includes Linux and Unix systems * that do not have the X11 libraries installed, which are required for the AWT subsystem to * operate. This method uses nearest neighbor approximation, so it"s quite fast. Unfortunately, * the result is nowhere near as nice looking as the createHeadlessSmoothBufferedImage method. * * @param image The image to convert * @param w The desired image width * @param h The desired image height * @return The converted image * @param type int */ public static BufferedImage createHeadlessBufferedImage(BufferedImage image, int type, int width, int height) { if (type == ImageUtils.IMAGE_PNG && hasAlpha(image)) { type = BufferedImage.TYPE_INT_ARGB; } else { type = BufferedImage.TYPE_INT_RGB; } BufferedImage bi = new BufferedImage(width, height, type); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { bi.setRGB(x, y, image.getRGB(x * image.getWidth() / width, y * image.getHeight() / height)); } } return bi; } /** * Creates aBufferedImage
from anImage
. This method can * function on a completely headless system. This especially includes Linux and Unix systems * that do not have the X11 libraries installed, which are required for the AWT subsystem to * operate. The resulting image will be smoothly scaled using bilinear filtering. * * @param source The image to convert * @param w The desired image width * @param h The desired image height * @return The converted image * @param type int */ public static BufferedImage createHeadlessSmoothBufferedImage(BufferedImage source, int type, int width, int height) { if (type == ImageUtils.IMAGE_PNG && hasAlpha(source)) { type = BufferedImage.TYPE_INT_ARGB; } else { type = BufferedImage.TYPE_INT_RGB; } BufferedImage dest = new BufferedImage(width, height, type); int sourcex; int sourcey; double scalex = (double) width / source.getWidth(); double scaley = (double) height / source.getHeight(); int x1; int y1; double xdiff; double ydiff; int rgb; int rgb1; int rgb2; for (int y = 0; y < height; y++) { sourcey = y * source.getHeight() / dest.getHeight(); ydiff = scale(y, scaley) - sourcey; for (int x = 0; x < width; x++) { sourcex = x * source.getWidth() / dest.getWidth(); xdiff = scale(x, scalex) - sourcex; x1 = Math.min(source.getWidth() - 1, sourcex + 1); y1 = Math.min(source.getHeight() - 1, sourcey + 1); rgb1 = getRGBInterpolation(source.getRGB(sourcex, sourcey), source.getRGB(x1, sourcey), xdiff); rgb2 = getRGBInterpolation(source.getRGB(sourcex, y1), source.getRGB(x1, y1), xdiff); rgb = getRGBInterpolation(rgb1, rgb2, ydiff); dest.setRGB(x, y, rgb); } } return dest; } private static double scale(int point, double scale) { return point / scale; } private static int getRGBInterpolation(int value1, int value2, double distance) { int alpha1 = (value1 & 0xFF000000) >>> 24; int red1 = (value1 & 0x00FF0000) >> 16; int green1 = (value1 & 0x0000FF00) >> 8; int blue1 = (value1 & 0x000000FF); int alpha2 = (value2 & 0xFF000000) >>> 24; int red2 = (value2 & 0x00FF0000) >> 16; int green2 = (value2 & 0x0000FF00) >> 8; int blue2 = (value2 & 0x000000FF); int rgb = ((int) (alpha1 * (1.0 - distance) + alpha2 * distance) << 24) | ((int) (red1 * (1.0 - distance) + red2 * distance) << 16) | ((int) (green1 * (1.0 - distance) + green2 * distance) << 8) | (int) (blue1 * (1.0 - distance) + blue2 * distance); return rgb; } /** * Determines if the image has transparent pixels. * * @param image The image to check for transparent pixel.s * @returntrue
offalse
, according to the result */ public static boolean hasAlpha(Image image) { try { PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); pg.grabPixels(); return pg.getColorModel().hasAlpha(); } catch (InterruptedException e) { return false; } }
}
</source>
Image Color Operation: dark, bright, contrast, negative
<source lang="java"> /*
* Apollo - Motion capture and animation system * Copyright (c) 2005 Apollo * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * http://www.gnu.org/copyleft/gpl.html * * @author Giovane.Kuhn - brain@netuno.ru.br * */
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.awt.image.ByteLookupTable; import java.awt.image.LookupOp; import java.awt.image.LookupTable; import java.awt.image.ShortLookupTable; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.TitledBorder; public class ColorApp extends JFrame {
DisplayPanel displayPanel; JButton brightenButton, darkenButton, contrastIncButton, contrastDecButton, reverseButton, resetButton; public ColorApp() { super(); Container container = getContentPane(); displayPanel = new DisplayPanel(); container.add(displayPanel); JPanel panel = new JPanel(); panel.setLayout(new GridLayout(3, 2)); panel.setBorder(new TitledBorder("Click a Button to Perform the Associated Operation and Reset...")); brightenButton = new JButton("Brightness >>"); brightenButton.addActionListener(new ButtonListener()); darkenButton = new JButton("Darkness >>"); darkenButton.addActionListener(new ButtonListener()); contrastIncButton = new JButton("Contrast >>"); contrastIncButton.addActionListener(new ButtonListener()); contrastDecButton = new JButton("Contrast <<"); contrastDecButton.addActionListener(new ButtonListener()); reverseButton = new JButton("Negative"); reverseButton.addActionListener(new ButtonListener()); resetButton = new JButton("Reset"); resetButton.addActionListener(new ButtonListener()); panel.add(brightenButton); panel.add(darkenButton); panel.add(contrastIncButton); panel.add(contrastDecButton); panel.add(reverseButton); panel.add(resetButton); container.add(BorderLayout.SOUTH, panel); addWindowListener(new WindowEventHandler()); setSize(displayPanel.getWidth(), displayPanel.getHeight() + 25); show(); } class WindowEventHandler extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } public static void main(String arg[]) { new ColorApp(); } class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { JButton button = (JButton) e.getSource(); if (button.equals(brightenButton)) { displayPanel.brightenLUT(); displayPanel.applyFilter(); displayPanel.repaint(); } else if (button.equals(darkenButton)) { displayPanel.darkenLUT(); displayPanel.applyFilter(); displayPanel.repaint(); } else if (button.equals(contrastIncButton)) { displayPanel.contrastIncLUT(); displayPanel.applyFilter(); displayPanel.repaint(); } else if (button.equals(contrastDecButton)) { displayPanel.contrastDecLUT(); displayPanel.applyFilter(); displayPanel.repaint(); } else if (button.equals(reverseButton)) { displayPanel.reverseLUT(); displayPanel.applyFilter(); displayPanel.repaint(); } else if (button.equals(resetButton)) { displayPanel.reset(); displayPanel.repaint(); } } }
} class DisplayPanel extends JPanel {
Image displayImage; BufferedImage bi; Graphics2D big; LookupTable lookupTable; DisplayPanel() { setBackground(Color.black); // panel background color loadImage(); setSize(displayImage.getWidth(this), displayImage.getWidth(this)); // panel createBufferedImage(); } public void loadImage() { displayImage = Toolkit.getDefaultToolkit().getImage("a.jpg"); MediaTracker mt = new MediaTracker(this); mt.addImage(displayImage, 1); try { mt.waitForAll(); } catch (Exception e) { System.out.println("Exception while loading."); } if (displayImage.getWidth(this) == -1) { System.out.println("No jpg file"); System.exit(0); } } public void createBufferedImage() { bi = new BufferedImage(displayImage.getWidth(this), displayImage.getHeight(this), BufferedImage.TYPE_INT_ARGB); big = bi.createGraphics(); big.drawImage(displayImage, 0, 0, this); } public void brightenLUT() { short brighten[] = new short[256]; for (int i = 0; i < 256; i++) { short pixelValue = (short) (i + 10); if (pixelValue > 255) pixelValue = 255; else if (pixelValue < 0) pixelValue = 0; brighten[i] = pixelValue; } lookupTable = new ShortLookupTable(0, brighten); } public void darkenLUT() { short brighten[] = new short[256]; for (int i = 0; i < 256; i++) { short pixelValue = (short) (i - 10); if (pixelValue > 255) pixelValue = 255; else if (pixelValue < 0) pixelValue = 0; brighten[i] = pixelValue; } lookupTable = new ShortLookupTable(0, brighten); } public void contrastIncLUT() { short brighten[] = new short[256]; for (int i = 0; i < 256; i++) { short pixelValue = (short) (i * 1.2); if (pixelValue > 255) pixelValue = 255; else if (pixelValue < 0) pixelValue = 0; brighten[i] = pixelValue; } lookupTable = new ShortLookupTable(0, brighten); } public void contrastDecLUT() { short brighten[] = new short[256]; for (int i = 0; i < 256; i++) { short pixelValue = (short) (i / 1.2); if (pixelValue > 255) pixelValue = 255; else if (pixelValue < 0) pixelValue = 0; brighten[i] = pixelValue; } lookupTable = new ShortLookupTable(0, brighten); } public void reverseLUT() { byte reverse[] = new byte[256]; for (int i = 0; i < 256; i++) { reverse[i] = (byte) (255 - i); } lookupTable = new ByteLookupTable(0, reverse); } public void reset() { big.setColor(Color.black); big.clearRect(0, 0, bi.getWidth(this), bi.getHeight(this)); big.drawImage(displayImage, 0, 0, this); } public void applyFilter() { LookupOp lop = new LookupOp(lookupTable, null); lop.filter(bi, bi); } public void update(Graphics g) { g.clearRect(0, 0, getWidth(), getHeight()); paintComponent(g); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2D = (Graphics2D) g; g2D.drawImage(bi, 0, 0, this); }
}
</source>
ImageComparator compares a byte[] for equality by creating 2 hashes for the bytearray and comparing thoose hashes.
<source lang="java"> /**
* * JFreeReport : a free Java reporting library * * * Project Info: http://reporting.pentaho.org/ * * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors. * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * [Java is a trademark or registered trademark of Sun Microsystems, Inc. * in the United States and other countries.] * * ------------ * ImageComparator.java * ------------ * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors. */
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; /**
* The ImageComparator tries to compare a byte[] for equality by creating 2 hashes for the * bytearray and comparing thoose hashes. If no digest algorithms are available, then the * complete byte[] is used for comparison. * <p> * Using the digest method trades computing time for space. * * @author Thomas Morgner */
public class ImageComparator {
/** * A ImageCompareData that uses the complete image data for comparison. */ private static final class CompleteImageCompareData { /** * The image content. */ private final byte[] image; /** * Create a new CompleteImageCompareData instance. * * @param image the image data used for comparison. */ private CompleteImageCompareData (final byte[] image) { this.image = image; } /** * Checks whether the given Object equals this object. * * @param o the to be compared object * @return true, if both objects are equal */ public boolean equals (final Object o) { if (this == o) { return true; } if (!(o instanceof CompleteImageCompareData)) { return false; } final CompleteImageCompareData data = (CompleteImageCompareData) o; if (!Arrays.equals(image, data.image)) { return false; } return true; } /** * returns a hashcode for this class. * * @return always 0. */ public int hashCode () { return image.length; } } /** * An ImageComparator which uses precomputed Message-Digests to compare the image. */ private static final class DigestImageCompareData { /** * An MD5 digest. */ private byte[] digestMD5Data; /** * An SHA digest. */ private byte[] digestSHAData; /** * Creates a new DigestImageCompareData instance. * * @param digestMD5Data the MD5 digest data * @param digestSHAData the SHA1 digest data */ private DigestImageCompareData (final byte[] digestMD5Data, final byte[] digestSHAData) { if (digestMD5Data == null || digestSHAData == null) { throw new NullPointerException(); } this.digestMD5Data = digestMD5Data; this.digestSHAData = digestSHAData; } /** * Checks whether the given Object equals this object. * * @param o the to be compared object * @return true, if both objects are equal */ public boolean equals (final Object o) { if (this == o) { return true; } if (!(o instanceof DigestImageCompareData)) { return false; } final DigestImageCompareData data = (DigestImageCompareData) o; if (!Arrays.equals(digestMD5Data, data.digestMD5Data)) { return false; } if (!Arrays.equals(digestSHAData, data.digestSHAData)) { return false; } return true; } /** * returns a hashcode for this class. * * @return always 0. */ public int hashCode () { return 0; } } /** * An MD5 message digest. */ private MessageDigest digestMD5; /** * An SHA message digest. */ private MessageDigest digestSHA; /** * Creates a new ImageComparator. The comparator checks whether the MD5 and the SHA * message digest implementations are available. If they are not available, an * alternative comparison method is used, which is more memory consuming. */ public ImageComparator () { try { digestMD5 = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException nse) { System.out.println("No MD5 algorithm available"); } try { digestSHA = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException nse) { System.out.println("No SHA algorithm available"); } } /** * Creates 2 comparable objects. These objects can be compared for equality. * * @param image the image data which should be prepared for comparison * @param fast whether to prefer the memory intensive faster compare method to the * digest based comparation. This may result in outofmemory errors on huge * reports or images. * @return the prepared image data. */ public Object createCompareData (final byte[] image, final boolean fast) { if (fast == false && (digestMD5 != null && digestSHA != null)) { final byte[] dataMD5 = digestMD5.digest(image); final byte[] dataSHA = digestSHA.digest(image); if (dataSHA != null && dataMD5 != null) { return new DigestImageCompareData(dataMD5, dataSHA); } } return new CompleteImageCompareData(image); }
}
</source>
Image Filter
<source lang="java"> import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BandCombineOp; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ByteLookupTable; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.awt.image.LookupOp; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.net.URL; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; public class Java2DExample extends JFrame {
private JMenu filterMenu = new JMenu("Image Filters"); private ImagePanel imagePanel; private MyFilter invertFilter = new InvertFilter(); private MyFilter sharpenFilter = new SharpenFilter(); private MyFilter blurFilter = new BlurFilter(); private MyFilter colorFilter = new ColorFilter(); public Java2DExample() { super("Java 2D Image Processing Demo"); imagePanel = new ImagePanel(Java2DExample.class.getResource("yourImage.png")); JMenuBar menuBar = new JMenuBar(); setJMenuBar(menuBar); filterMenu.setMnemonic("I"); JMenuItem originalMenuItem = new JMenuItem("Display Original"); originalMenuItem.setMnemonic("O"); originalMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent action) { imagePanel.displayOriginalImage(); } }); JMenuItem invertMenuItem = createMenuItem("Invert", "I", invertFilter); JMenuItem sharpenMenuItem = createMenuItem("Sharpen", "S", sharpenFilter); JMenuItem blurMenuItem = createMenuItem("Blur", "B", blurFilter); JMenuItem changeColorsMenuItem = createMenuItem("Change Colors", "C", colorFilter); filterMenu.add(originalMenuItem); filterMenu.add(invertMenuItem); filterMenu.add(sharpenMenuItem); filterMenu.add(blurMenuItem); filterMenu.add(changeColorsMenuItem); menuBar.add(filterMenu); getContentPane().add(imagePanel, BorderLayout.CENTER); } public JMenuItem createMenuItem(String menuItemName, char mnemonic, final MyFilter filter) { JMenuItem menuItem = new JMenuItem(menuItemName); menuItem.setMnemonic(mnemonic); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent action) { imagePanel.applyFilter(filter); } }); return menuItem; } public static void main(String args[]) { Java2DExample application = new Java2DExample(); application.setDefaultCloseOperation(EXIT_ON_CLOSE); application.pack(); application.setVisible(true); }
} interface MyFilter {
public abstract BufferedImage processImage(BufferedImage image);
} class BlurFilter implements MyFilter {
public BufferedImage processImage(BufferedImage image) { float[] blurMatrix = { 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f }; BufferedImageOp blurFilter = new ConvolveOp(new Kernel(3, 3, blurMatrix), ConvolveOp.EDGE_NO_OP, null); return blurFilter.filter(image, null); }
} class ImagePanel extends JPanel {
private BufferedImage displayImage; private BufferedImage originalImage; private Image image; public ImagePanel(URL imageURL) { image = Toolkit.getDefaultToolkit().createImage(imageURL); MediaTracker mediaTracker = new MediaTracker(this); mediaTracker.addImage(image, 0); try { mediaTracker.waitForAll(); } catch (InterruptedException e) { e.printStackTrace(); } originalImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB); displayImage = originalImage; Graphics2D graphics = displayImage.createGraphics(); graphics.drawImage(image, null, null); } public void applyFilter(MyFilter filter) { displayImage = filter.processImage(displayImage); repaint(); } public void displayOriginalImage() { displayImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB); Graphics2D graphics = displayImage.createGraphics(); graphics.drawImage(originalImage, null, null); repaint(); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D graphics = (Graphics2D) g; graphics.drawImage(displayImage, 0, 0, null); } public Dimension getPreferredSize() { return new Dimension(displayImage.getWidth(), displayImage.getHeight()); } public Dimension getMinimumSize() { return getPreferredSize(); }
} class SharpenFilter implements MyFilter {
public BufferedImage processImage(BufferedImage image) { float[] sharpenMatrix = { 0.0f, -1.0f, 0.0f, -1.0f, 5.0f, -1.0f, 0.0f, -1.0f, 0.0f }; BufferedImageOp sharpenFilter = new ConvolveOp(new Kernel(3, 3, sharpenMatrix), ConvolveOp.EDGE_NO_OP, null); return sharpenFilter.filter(image, null); }
} class InvertFilter implements MyFilter {
public BufferedImage processImage(BufferedImage image) { byte[] invertArray = new byte[256]; for (int counter = 0; counter < 256; counter++) invertArray[counter] = (byte) (255 - counter); BufferedImageOp invertFilter = new LookupOp(new ByteLookupTable(0, invertArray), null); return invertFilter.filter(image, null); }
} class ColorFilter implements MyFilter {
public BufferedImage processImage(BufferedImage image) { float[][] colorMatrix = { { 1f, 0f, 0f }, { 0.5f, 1.0f, 0.5f }, { 0.2f, 0.4f, 0.6f } }; BandCombineOp changeColors = new BandCombineOp(colorMatrix, null); Raster sourceRaster = image.getRaster(); WritableRaster displayRaster = sourceRaster.createCompatibleWritableRaster(); changeColors.filter(sourceRaster, displayRaster); return new BufferedImage(image.getColorModel(), displayRaster, true, null); }
}