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
/*
* 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
* <tt>premult</tt>
*/
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 <b>copy</b> of the data in
* <tt>ras</tt>. 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
* <tt>getData</tt> call).
* @param ras The Raster to copy.
* @return A writable copy of <tt>ras</tt>
*/
public static WritableRaster copyRaster(Raster ras) {
return copyRaster(ras, ras.getMinX(), ras.getMinY());
}
/**
* Creates a new raster that has a <b>copy</b> of the data in
* <tt>ras</tt>. 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
* <tt>getData</tt> 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 <tt>ras</tt>
*/
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 <tt>ras</tt> 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>
*
* 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 <tt>ras</tt>,
* use copyRaster (above).
* @param ras The raster to make writable.
* @return A Writable version of ras (shares DataBuffer with
* <tt>ras</tt>).
*/
public static WritableRaster makeRasterWritable(Raster ras) {
return makeRasterWritable(ras, ras.getMinX(), ras.getMinY());
}
/**
* Coerces <tt>ras</tt> 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 <tt>ras</tt>,
* 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 <tT>ras</tt> with it"s upper left
* hand coordinate set to minX, minY (shares it"s DataBuffer
* with <tt>ras</tt>).
*/
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());
}
}
*/
/**
* Extracts an alpha raster from a RenderedImage. The method tries to avoid copying data
* unnecessarily by checking if the RenderedImage is a BufferedImage which offers suitable
* direct methods.
* @param image the image
* @return the alpha raster
*/
public static Raster getAlphaRaster(RenderedImage image) {
ColorModel cm = image.getColorModel();
if (!cm.hasAlpha() || cm.getTransparency() != ColorModel.TRANSLUCENT) {
throw new IllegalStateException("Image doesn"t have an alpha channel");
}
Raster alpha;
if (image instanceof BufferedImage) {
//Optimization possible with BufferedImage (No copying)
alpha = ((BufferedImage)image).getAlphaRaster();
} else {
WritableRaster wraster = GraphicsUtil.makeRasterWritable(image.getData());
alpha = image.getColorModel().getAlphaRaster(wraster);
}
return alpha;
}
}
Dithering a 24-bit RGB image to a monochrome (1-bit or bilevel) image
//-----------------------------------------------------------------------//
// //
// 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);
}
}
Gray scale image operation
/*
* 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);
}
}
If the image has transparent pixels
/*
* 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 resized <code>BufferedImage</code>
*/
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 resized <code>BufferedImage</code>
* @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. Use <code>ImageUtils.IMAGE_JPEG</code> to save as JPEG images,
* or <code>ImageUtils.IMAGE_PNG</code> to save as PNG.
* @return <code>false</code> 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. Use <code>ImageUtils.IMAGE_JPEG</code> to save as JPEG images,
* or <code>ImageUtils.IMAGE_PNG</code> 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 a <code>BufferedImage</code> from an <code>Image</code>. 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 a <code>BufferedImage</code> from an <code>Image</code>. 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
* @return <code>true</code> of <code>false</code>, 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;
}
}
}
Image Color Operation: dark, bright, contrast, negative
/*
* 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);
}
}
ImageComparator compares a byte[] for equality by creating 2 hashes for the bytearray and comparing thoose hashes.
/**
*
* 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);
}
}
Image Filter
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);
}
}