mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-01-24 00:00:05 -05:00
Fixed JavaDoc errors to enable Java 8 build.
This commit is contained in:
@@ -1,110 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ImageConsumer;
|
||||
import java.awt.image.ImageProducer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AbstractImageSource
|
||||
* <p/>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/AbstractImageSource.java#1 $
|
||||
*/
|
||||
public abstract class AbstractImageSource implements ImageProducer {
|
||||
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>();
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int xOff;
|
||||
protected int yOff;
|
||||
|
||||
// ImageProducer interface
|
||||
public void addConsumer(final ImageConsumer pConsumer) {
|
||||
if (consumers.contains(pConsumer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumers.add(pConsumer);
|
||||
|
||||
try {
|
||||
initConsumer(pConsumer);
|
||||
sendPixels(pConsumer);
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
||||
|
||||
// Get rid of "sticky" consumers...
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
removeConsumer(pConsumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConsumer(final ImageConsumer pConsumer) {
|
||||
consumers.remove(pConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation silently ignores this instruction. If pixel data is
|
||||
* not in TDLR order by default, subclasses must override this method.
|
||||
*
|
||||
* @param pConsumer the consumer that requested the resend
|
||||
*
|
||||
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
||||
*/
|
||||
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void startProduction(final ImageConsumer pConsumer) {
|
||||
addConsumer(pConsumer);
|
||||
}
|
||||
|
||||
public boolean isConsumer(final ImageConsumer pConsumer) {
|
||||
return consumers.contains(pConsumer);
|
||||
}
|
||||
|
||||
protected abstract void initConsumer(ImageConsumer pConsumer);
|
||||
|
||||
protected abstract void sendPixels(ImageConsumer pConsumer);
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ImageConsumer;
|
||||
import java.awt.image.ImageProducer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AbstractImageSource
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/AbstractImageSource.java#1 $
|
||||
*/
|
||||
public abstract class AbstractImageSource implements ImageProducer {
|
||||
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>();
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int xOff;
|
||||
protected int yOff;
|
||||
|
||||
// ImageProducer interface
|
||||
public void addConsumer(final ImageConsumer pConsumer) {
|
||||
if (consumers.contains(pConsumer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumers.add(pConsumer);
|
||||
|
||||
try {
|
||||
initConsumer(pConsumer);
|
||||
sendPixels(pConsumer);
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
||||
|
||||
// Get rid of "sticky" consumers...
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
removeConsumer(pConsumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (isConsumer(pConsumer)) {
|
||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConsumer(final ImageConsumer pConsumer) {
|
||||
consumers.remove(pConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation silently ignores this instruction. If pixel data is
|
||||
* not in TDLR order by default, subclasses must override this method.
|
||||
*
|
||||
* @param pConsumer the consumer that requested the resend
|
||||
*
|
||||
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
||||
*/
|
||||
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void startProduction(final ImageConsumer pConsumer) {
|
||||
addConsumer(pConsumer);
|
||||
}
|
||||
|
||||
public boolean isConsumer(final ImageConsumer pConsumer) {
|
||||
return consumers.contains(pConsumer);
|
||||
}
|
||||
|
||||
protected abstract void initConsumer(ImageConsumer pConsumer);
|
||||
|
||||
protected abstract void sendPixels(ImageConsumer pConsumer);
|
||||
}
|
||||
|
||||
@@ -1,170 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts the contrast and brightness of an image.
|
||||
* <p/>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||
* <p/>
|
||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BrightnessContrastFilter.java#1 $
|
||||
*
|
||||
* @todo consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947
|
||||
*/
|
||||
|
||||
public class BrightnessContrastFilter extends RGBImageFilter {
|
||||
|
||||
// TODO: Replace with RescaleOp?
|
||||
|
||||
// This filter can filter IndexColorModel, as it is does not depend on
|
||||
// the pixels' location
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
// Use a pre-calculated lookup table for performance
|
||||
private final int[] LUT;
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with default values
|
||||
* ({@code brightness=0.3, contrast=0.3}).
|
||||
* <p/>
|
||||
* This will slightly increase both brightness and contrast.
|
||||
*/
|
||||
public BrightnessContrastFilter() {
|
||||
this(0.3f, 0.3f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with the given values for brightness
|
||||
* and contrast.
|
||||
* <p/>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||
* <p/>
|
||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||
*
|
||||
* @param pBrightness adjust the brightness of the image, in the range
|
||||
* {@code -2.0,..,0.0,..,2.0}.
|
||||
* @param pContrast adjust the contrast of the image, in the range
|
||||
* {@code -1.0,..,0.0,..,1.0}.
|
||||
*/
|
||||
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
||||
LUT = createLUT(pBrightness, pContrast);
|
||||
}
|
||||
|
||||
private static int[] createLUT(float pBrightness, float pContrast) {
|
||||
int[] lut = new int[256];
|
||||
|
||||
// Hmmm.. This approximates Photoshop values.. Not good though..
|
||||
double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast;
|
||||
|
||||
// Convert range [-1,..,0,..,1] -> [0,..,1,..,2]
|
||||
double brightness = pBrightness + 1.0;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0)));
|
||||
}
|
||||
|
||||
// Special case, to ensure only primary colors for max contrast
|
||||
if (pContrast == 1f) {
|
||||
lut[127] = lut[126];
|
||||
}
|
||||
|
||||
return lut;
|
||||
}
|
||||
|
||||
private static int clamp(int i) {
|
||||
if (i < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (i > 255) {
|
||||
return 255;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel, adjusting brightness and contrast according to this
|
||||
* filter.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in default color space
|
||||
*
|
||||
* @return the filtered pixel value in the default color space
|
||||
*/
|
||||
public int filterRGB(int pX, int pY, int pARGB) {
|
||||
// Get color components
|
||||
int r = pARGB >> 16 & 0xFF;
|
||||
int g = pARGB >> 8 & 0xFF;
|
||||
int b = pARGB & 0xFF;
|
||||
|
||||
// Scale to new contrast
|
||||
r = LUT[r];
|
||||
g = LUT[g];
|
||||
b = LUT[b];
|
||||
|
||||
// Return ARGB pixel, leave transparency as is
|
||||
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts the contrast and brightness of an image.
|
||||
* <p>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||
* </p>
|
||||
* <p>
|
||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BrightnessContrastFilter.java#1 $
|
||||
*/
|
||||
// TODO: consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947
|
||||
public class BrightnessContrastFilter extends RGBImageFilter {
|
||||
|
||||
// TODO: Replace with RescaleOp?
|
||||
|
||||
// This filter can filter IndexColorModel, as it is does not depend on
|
||||
// the pixels' location
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
// Use a pre-calculated lookup table for performance
|
||||
private final int[] LUT;
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with default values
|
||||
* ({@code brightness=0.3, contrast=0.3}).
|
||||
* <p>
|
||||
* This will slightly increase both brightness and contrast.
|
||||
* </p>
|
||||
*/
|
||||
public BrightnessContrastFilter() {
|
||||
this(0.3f, 0.3f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BrightnessContrastFilter with the given values for brightness
|
||||
* and contrast.
|
||||
* <p>
|
||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will make the pixels darker.
|
||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||
* Positive values will make the pixels brighter.
|
||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||
* </p>
|
||||
* <p>
|
||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||
* A value of {@code 0.0} means no change.
|
||||
* Negative values will reduce contrast.
|
||||
* Maximum negative value ({@code -1}) will make all filtered pixels grey
|
||||
* (no contrast).
|
||||
* Positive values will increase contrast.
|
||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||
* </p>
|
||||
*
|
||||
* @param pBrightness adjust the brightness of the image, in the range
|
||||
* {@code -2.0,..,0.0,..,2.0}.
|
||||
* @param pContrast adjust the contrast of the image, in the range
|
||||
* {@code -1.0,..,0.0,..,1.0}.
|
||||
*/
|
||||
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
||||
LUT = createLUT(pBrightness, pContrast);
|
||||
}
|
||||
|
||||
private static int[] createLUT(float pBrightness, float pContrast) {
|
||||
int[] lut = new int[256];
|
||||
|
||||
// Hmmm.. This approximates Photoshop values.. Not good though..
|
||||
double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast;
|
||||
|
||||
// Convert range [-1,..,0,..,1] -> [0,..,1,..,2]
|
||||
double brightness = pBrightness + 1.0;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0)));
|
||||
}
|
||||
|
||||
// Special case, to ensure only primary colors for max contrast
|
||||
if (pContrast == 1f) {
|
||||
lut[127] = lut[126];
|
||||
}
|
||||
|
||||
return lut;
|
||||
}
|
||||
|
||||
private static int clamp(int i) {
|
||||
if (i < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (i > 255) {
|
||||
return 255;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel, adjusting brightness and contrast according to this
|
||||
* filter.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in default color space
|
||||
*
|
||||
* @return the filtered pixel value in the default color space
|
||||
*/
|
||||
public int filterRGB(int pX, int pY, int pARGB) {
|
||||
// Get color components
|
||||
int r = pARGB >> 16 & 0xFF;
|
||||
int g = pARGB >> 8 & 0xFF;
|
||||
int b = pARGB & 0xFF;
|
||||
|
||||
// Scale to new contrast
|
||||
r = LUT[r];
|
||||
g = LUT[g];
|
||||
b = LUT[b];
|
||||
|
||||
// Return ARGB pixel, leave transparency as is
|
||||
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,14 +44,16 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
* A faster, lighter and easier way to convert an {@code Image} to a
|
||||
* {@code BufferedImage} than using a {@code PixelGrabber}.
|
||||
* Clients may provide progress listeners to monitor conversion progress.
|
||||
* <p/>
|
||||
* <p>
|
||||
* Supports source image subsampling and source region extraction.
|
||||
* Supports source images with 16 bit {@link ColorModel} and
|
||||
* {@link DataBuffer#TYPE_USHORT} transfer type, without converting to
|
||||
* 32 bit/TYPE_INT.
|
||||
* <p/>
|
||||
* </p>
|
||||
* <p>
|
||||
* NOTE: Does not support images with more than one {@code ColorModel} or
|
||||
* different types of pixel data. This is not very common.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageFactory.java#1 $
|
||||
|
||||
@@ -1,92 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
||||
* <p/>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageIcon.java#2 $
|
||||
*/
|
||||
public class BufferedImageIcon implements Icon {
|
||||
private final BufferedImage image;
|
||||
private int width;
|
||||
private int height;
|
||||
private final boolean fast;
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage) {
|
||||
this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
||||
this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) {
|
||||
image = Validate.notNull(pImage, "image");
|
||||
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
|
||||
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
|
||||
|
||||
fast = useFastRendering;
|
||||
}
|
||||
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
if (fast || !(g instanceof Graphics2D)) {
|
||||
//System.out.println("Scaling fast");
|
||||
g.drawImage(image, x, y, width, height, null);
|
||||
}
|
||||
else {
|
||||
//System.out.println("Scaling using interpolation");
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, xform, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageIcon.java#2 $
|
||||
*/
|
||||
public class BufferedImageIcon implements Icon {
|
||||
private final BufferedImage image;
|
||||
private int width;
|
||||
private int height;
|
||||
private final boolean fast;
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage) {
|
||||
this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
||||
this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight);
|
||||
}
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) {
|
||||
image = Validate.notNull(pImage, "image");
|
||||
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
|
||||
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
|
||||
|
||||
fast = useFastRendering;
|
||||
}
|
||||
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
if (fast || !(g instanceof Graphics2D)) {
|
||||
//System.out.println("Scaling fast");
|
||||
g.drawImage(image, x, y, width, height, null);
|
||||
}
|
||||
else {
|
||||
//System.out.println("Scaling using interpolation");
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, xform, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,483 +1,485 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This {@code BufferedImageOp/RasterOp} implements basic
|
||||
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
||||
* <P/>
|
||||
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
||||
* <!-- - -
|
||||
* | |x|7|
|
||||
* - - - -
|
||||
* |3|5|1|
|
||||
* - - -->
|
||||
* <P/>
|
||||
* <TABLE border="1" cellpadding="4" cellspacing="0">
|
||||
* <TR><TD bgcolor="#000000"> </TD><TD class="TableHeadingColor"
|
||||
* align="center">X</TD><TD>7/16</TD></TR>
|
||||
* <TR><TD>3/16</TD><TD>5/16</TD><TD>1/16</TD></TR>
|
||||
* </TABLE>
|
||||
* <P/>
|
||||
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
|
||||
* for more information.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: DiffusionDither.java#1 $
|
||||
*/
|
||||
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
||||
|
||||
private static final int FS_SCALE = 1 << 8;
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
protected final IndexColorModel indexColorModel;
|
||||
private boolean alternateScans = true;
|
||||
|
||||
/**
|
||||
* Creates a {@code DiffusionDither}, using the given
|
||||
* {@code IndexColorModel} for dithering into.
|
||||
*
|
||||
* @param pICM an IndexColorModel.
|
||||
*/
|
||||
public DiffusionDither(final IndexColorModel pICM) {
|
||||
// Store color model
|
||||
indexColorModel = pICM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DiffusionDither}, with no fixed
|
||||
* {@code IndexColorModel}. The color model will be generated for each
|
||||
* filtering, unless the destination image already has an
|
||||
* {@code IndexColorModel}.
|
||||
*/
|
||||
public DiffusionDither() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scan mode. If the parameter is true, error distribution for
|
||||
* every even line will be left-to-right, while odd lines will be
|
||||
* right-to-left.
|
||||
* The default is {@code true}.
|
||||
*
|
||||
* @param pUse {@code true} if scan mode should be alternating left/right
|
||||
*/
|
||||
public void setAlternateScans(boolean pUse) {
|
||||
alternateScans = pUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code BufferedImage} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @return a compatible {@code BufferedImage}
|
||||
*
|
||||
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
||||
* an instance of {@code IndexColorModel}.
|
||||
*/
|
||||
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
||||
if (pDestCM == null) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
getICM(pSource));
|
||||
}
|
||||
else if (pDestCM instanceof IndexColorModel) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
(IndexColorModel) pDestCM);
|
||||
}
|
||||
else {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @param pSrc the source raster
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc) {
|
||||
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
*
|
||||
* @param pSrc the source raster.
|
||||
* @param pIndexColorModel the index color model used to create a {@code Raster}.
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
||||
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bounding box of the filtered destination image. Since
|
||||
* this is not a geometric operation, the bounding box does not
|
||||
* change.
|
||||
* @param pSrc the {@code BufferedImage} to be filtered
|
||||
* @return the bounds of the filtered definition image.
|
||||
*/
|
||||
public final Rectangle2D getBounds2D(BufferedImage pSrc) {
|
||||
return getBounds2D(pSrc.getRaster());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding box of the filtered destination Raster. Since
|
||||
* this is not a geometric operation, the bounding box does not
|
||||
* change.
|
||||
* @param pSrc the {@code Raster} to be filtered
|
||||
* @return the bounds of the filtered definition {@code Raster}.
|
||||
*/
|
||||
public final Rectangle2D getBounds2D(Raster pSrc) {
|
||||
return pSrc.getBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location of the destination point given a
|
||||
* point in the source. If {@code dstPt} is not
|
||||
* {@code null}, it will be used to hold the return value.
|
||||
* Since this is not a geometric operation, the {@code srcPt}
|
||||
* will equal the {@code dstPt}.
|
||||
* @param pSrcPt a {@code Point2D} that represents a point
|
||||
* in the source image
|
||||
* @param pDstPt a {@code Point2D}that represents the location
|
||||
* in the destination
|
||||
* @return the {@code Point2D} in the destination that
|
||||
* corresponds to the specified point in the source.
|
||||
*/
|
||||
public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) {
|
||||
// Create new Point, if needed
|
||||
if (pDstPt == null) {
|
||||
pDstPt = new Point2D.Float();
|
||||
}
|
||||
|
||||
// Copy location
|
||||
pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
|
||||
|
||||
// Return dest
|
||||
return pDstPt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rendering mHints for this op.
|
||||
* @return the {@code RenderingHints} object associated
|
||||
* with this op.
|
||||
*/
|
||||
public final RenderingHints getRenderingHints() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int ARGB to int triplet.
|
||||
*/
|
||||
private static int[] toRGBArray(int pARGB, int[] pBuffer) {
|
||||
pBuffer[0] = ((pARGB & 0x00ff0000) >> 16);
|
||||
pBuffer[1] = ((pARGB & 0x0000ff00) >> 8);
|
||||
pBuffer[2] = ((pARGB & 0x000000ff));
|
||||
//pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a int triplet to int ARGB.
|
||||
*/
|
||||
private static int toIntARGB(int[] pRGB) {
|
||||
return 0xff000000 // All opaque
|
||||
| (pRGB[0] << 16)
|
||||
| (pRGB[1] << 8)
|
||||
| (pRGB[2]);
|
||||
/*
|
||||
| ((int) (pRGB[0] << 16) & 0x00ff0000)
|
||||
| ((int) (pRGB[1] << 8) & 0x0000ff00)
|
||||
| ((int) (pRGB[2] ) & 0x000000ff);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs a single-input/single-output dither operation, applying basic
|
||||
* Floyd-Steinberg error-diffusion to the image.
|
||||
*
|
||||
* @param pSource the source image
|
||||
* @param pDest the destination image
|
||||
*
|
||||
* @return the destination image, or a new image, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
||||
// Create destination image, if none provided
|
||||
if (pDest == null) {
|
||||
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
||||
}
|
||||
else if (!(pDest.getColorModel() instanceof IndexColorModel)) {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
|
||||
// Filter rasters
|
||||
filter(pSource.getRaster(), pDest.getRaster(), (IndexColorModel) pDest.getColorModel());
|
||||
|
||||
return pDest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a single-input/single-output dither operation, applying basic
|
||||
* Floyd-Steinberg error-diffusion to the image.
|
||||
*
|
||||
* @param pSource the source raster, assumed to be in sRGB
|
||||
* @param pDest the destination raster, may be {@code null}
|
||||
*
|
||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest) {
|
||||
return filter(pSource, pDest, getICM(pSource));
|
||||
}
|
||||
|
||||
private IndexColorModel getICM(BufferedImage pSource) {
|
||||
return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
|
||||
}
|
||||
private IndexColorModel getICM(Raster pSource) {
|
||||
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource));
|
||||
}
|
||||
|
||||
private IndexColorModel createIndexColorModel(Raster pSource) {
|
||||
BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
image.setData(pSource);
|
||||
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a single-input/single-output dither operation, applying basic
|
||||
* Floyd-Steinberg error-diffusion to the image.
|
||||
*
|
||||
* @param pSource the source raster, assumed to be in sRGB
|
||||
* @param pDest the destination raster, may be {@code null}
|
||||
* @param pColorModel the indexed color model to use
|
||||
*
|
||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
||||
int width = pSource.getWidth();
|
||||
int height = pSource.getHeight();
|
||||
|
||||
// Create destination raster if needed
|
||||
if (pDest == null) {
|
||||
pDest = createCompatibleDestRaster(pSource, pColorModel);
|
||||
}
|
||||
|
||||
// Initialize Floyd-Steinberg error vectors.
|
||||
// +2 to handle the previous pixel and next pixel case minimally
|
||||
// When reference for column, add 1 to reference as this buffer is
|
||||
// offset from actual column position by one to allow FS to not check
|
||||
// left/right edge conditions
|
||||
int[][] currErr = new int[width + 2][3];
|
||||
int[][] nextErr = new int[width + 2][3];
|
||||
|
||||
// Random errors in [-1 .. 1] - for first row
|
||||
for (int i = 0; i < width + 2; i++) {
|
||||
// Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE
|
||||
currErr[i][0] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||
currErr[i][1] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||
currErr[i][2] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||
}
|
||||
|
||||
// Temp buffers
|
||||
final int[] diff = new int[3]; // No alpha
|
||||
final int[] inRGB = new int[4];
|
||||
final int[] outRGB = new int[4];
|
||||
Object pixel = null;
|
||||
boolean forward = true;
|
||||
|
||||
// Loop through image data
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Clear out next error rows for colour errors
|
||||
for (int i = nextErr.length; --i >= 0;) {
|
||||
nextErr[i][0] = 0;
|
||||
nextErr[i][1] = 0;
|
||||
nextErr[i][2] = 0;
|
||||
}
|
||||
|
||||
// Set up start column and limit
|
||||
int x;
|
||||
int limit;
|
||||
if (forward) {
|
||||
x = 0;
|
||||
limit = width;
|
||||
}
|
||||
else {
|
||||
x = width - 1;
|
||||
limit = -1;
|
||||
}
|
||||
|
||||
// TODO: Use getPixels instead of getPixel for better performance?
|
||||
|
||||
// Loop over row
|
||||
while (true) {
|
||||
// Get RGB from original raster
|
||||
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES.
|
||||
pSource.getPixel(x, y, inRGB);
|
||||
|
||||
// Get error for this pixel & add error to rgb
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Make a 28.4 FP number, add Error (with fraction),
|
||||
// rounding and truncate to int
|
||||
inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4;
|
||||
|
||||
// Clamp
|
||||
if (inRGB[i] > 255) {
|
||||
inRGB[i] = 255;
|
||||
}
|
||||
else if (inRGB[i] < 0) {
|
||||
inRGB[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get pixel value...
|
||||
// It is VERY important that we are using a IndexColorModel that
|
||||
// support reverse color lookup for speed.
|
||||
pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel);
|
||||
|
||||
// ...set it...
|
||||
pDest.setDataElements(x, y, pixel);
|
||||
|
||||
// ..and get back the closet match
|
||||
pDest.getPixel(x, y, outRGB);
|
||||
|
||||
// Convert the value to default sRGB
|
||||
// Should work for all transfertypes supported by IndexColorModel
|
||||
toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB);
|
||||
|
||||
// Find diff
|
||||
diff[0] = inRGB[0] - outRGB[0];
|
||||
diff[1] = inRGB[1] - outRGB[1];
|
||||
diff[2] = inRGB[2] - outRGB[2];
|
||||
|
||||
// Apply F-S error diffusion
|
||||
// Serpentine scan: left-right
|
||||
if (forward) {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x + 1)
|
||||
currErr[x + 2][0] += diff[0] * 7;
|
||||
currErr[x + 2][1] += diff[1] * 7;
|
||||
currErr[x + 2][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x - 1)
|
||||
nextErr[x][0] += diff[0] * 3;
|
||||
nextErr[x][1] += diff[1] * 3;
|
||||
nextErr[x][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x + 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x + 2][0] += diff[0]; // * 1;
|
||||
nextErr[x + 2][1] += diff[1]; // * 1;
|
||||
nextErr[x + 2][2] += diff[2]; // * 1;
|
||||
|
||||
// Next
|
||||
x++;
|
||||
|
||||
// Done?
|
||||
if (x >= limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x - 1)
|
||||
currErr[x][0] += diff[0] * 7;
|
||||
currErr[x][1] += diff[1] * 7;
|
||||
currErr[x][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x + 1)
|
||||
nextErr[x + 2][0] += diff[0] * 3;
|
||||
nextErr[x + 2][1] += diff[1] * 3;
|
||||
nextErr[x + 2][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x - 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x][0] += diff[0]; // * 1;
|
||||
nextErr[x][1] += diff[1]; // * 1;
|
||||
nextErr[x][2] += diff[2]; // * 1;
|
||||
|
||||
// Previous
|
||||
x--;
|
||||
|
||||
// Done?
|
||||
if (x <= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make next error info current for next iteration
|
||||
int[][] temperr;
|
||||
temperr = currErr;
|
||||
currErr = nextErr;
|
||||
nextErr = temperr;
|
||||
|
||||
// Toggle direction
|
||||
if (alternateScans) {
|
||||
forward = !forward;
|
||||
}
|
||||
}
|
||||
|
||||
return pDest;
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This {@code BufferedImageOp/RasterOp} implements basic
|
||||
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
||||
* <p>
|
||||
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
||||
* <!-- - -
|
||||
* | |x|7|
|
||||
* - - - -
|
||||
* |3|5|1|
|
||||
* - - -->
|
||||
* </p>
|
||||
* <table border="1" cellpadding="4" cellspacing="0">
|
||||
* <caption>Floyd-Steinberg error-diffusion weights</caption>
|
||||
* <tr><td bgcolor="#000000"> </td><td class="TableHeadingColor"
|
||||
* align="center">x</td><td>7/16</td></tr>
|
||||
* <tr><td>3/16</td><td>5/16</td><td>1/16</td></tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
|
||||
* for more information.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: DiffusionDither.java#1 $
|
||||
*/
|
||||
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
||||
|
||||
private static final int FS_SCALE = 1 << 8;
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
protected final IndexColorModel indexColorModel;
|
||||
private boolean alternateScans = true;
|
||||
|
||||
/**
|
||||
* Creates a {@code DiffusionDither}, using the given
|
||||
* {@code IndexColorModel} for dithering into.
|
||||
*
|
||||
* @param pICM an IndexColorModel.
|
||||
*/
|
||||
public DiffusionDither(final IndexColorModel pICM) {
|
||||
// Store color model
|
||||
indexColorModel = pICM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DiffusionDither}, with no fixed
|
||||
* {@code IndexColorModel}. The color model will be generated for each
|
||||
* filtering, unless the destination image already has an
|
||||
* {@code IndexColorModel}.
|
||||
*/
|
||||
public DiffusionDither() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scan mode. If the parameter is true, error distribution for
|
||||
* every even line will be left-to-right, while odd lines will be
|
||||
* right-to-left.
|
||||
* The default is {@code true}.
|
||||
*
|
||||
* @param pUse {@code true} if scan mode should be alternating left/right
|
||||
*/
|
||||
public void setAlternateScans(boolean pUse) {
|
||||
alternateScans = pUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code BufferedImage} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @return a compatible {@code BufferedImage}
|
||||
*
|
||||
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
||||
* an instance of {@code IndexColorModel}.
|
||||
*/
|
||||
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
||||
if (pDestCM == null) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
getICM(pSource));
|
||||
}
|
||||
else if (pDestCM instanceof IndexColorModel) {
|
||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_BYTE_INDEXED,
|
||||
(IndexColorModel) pDestCM);
|
||||
}
|
||||
else {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
* Only {@code IndexColorModel} allowed.
|
||||
*
|
||||
* @param pSrc the source raster
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc) {
|
||||
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a compatible {@code Raster} to dither into.
|
||||
*
|
||||
* @param pSrc the source raster.
|
||||
* @param pIndexColorModel the index color model used to create a {@code Raster}.
|
||||
*
|
||||
* @return a {@code WritableRaster}
|
||||
*/
|
||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
||||
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bounding box of the filtered destination image. Since
|
||||
* this is not a geometric operation, the bounding box does not
|
||||
* change.
|
||||
* @param pSrc the {@code BufferedImage} to be filtered
|
||||
* @return the bounds of the filtered definition image.
|
||||
*/
|
||||
public final Rectangle2D getBounds2D(BufferedImage pSrc) {
|
||||
return getBounds2D(pSrc.getRaster());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding box of the filtered destination Raster. Since
|
||||
* this is not a geometric operation, the bounding box does not
|
||||
* change.
|
||||
* @param pSrc the {@code Raster} to be filtered
|
||||
* @return the bounds of the filtered definition {@code Raster}.
|
||||
*/
|
||||
public final Rectangle2D getBounds2D(Raster pSrc) {
|
||||
return pSrc.getBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location of the destination point given a
|
||||
* point in the source. If {@code dstPt} is not
|
||||
* {@code null}, it will be used to hold the return value.
|
||||
* Since this is not a geometric operation, the {@code srcPt}
|
||||
* will equal the {@code dstPt}.
|
||||
* @param pSrcPt a {@code Point2D} that represents a point
|
||||
* in the source image
|
||||
* @param pDstPt a {@code Point2D}that represents the location
|
||||
* in the destination
|
||||
* @return the {@code Point2D} in the destination that
|
||||
* corresponds to the specified point in the source.
|
||||
*/
|
||||
public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) {
|
||||
// Create new Point, if needed
|
||||
if (pDstPt == null) {
|
||||
pDstPt = new Point2D.Float();
|
||||
}
|
||||
|
||||
// Copy location
|
||||
pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
|
||||
|
||||
// Return dest
|
||||
return pDstPt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rendering mHints for this op.
|
||||
* @return the {@code RenderingHints} object associated
|
||||
* with this op.
|
||||
*/
|
||||
public final RenderingHints getRenderingHints() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int ARGB to int triplet.
|
||||
*/
|
||||
private static int[] toRGBArray(int pARGB, int[] pBuffer) {
|
||||
pBuffer[0] = ((pARGB & 0x00ff0000) >> 16);
|
||||
pBuffer[1] = ((pARGB & 0x0000ff00) >> 8);
|
||||
pBuffer[2] = ((pARGB & 0x000000ff));
|
||||
//pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a int triplet to int ARGB.
|
||||
*/
|
||||
private static int toIntARGB(int[] pRGB) {
|
||||
return 0xff000000 // All opaque
|
||||
| (pRGB[0] << 16)
|
||||
| (pRGB[1] << 8)
|
||||
| (pRGB[2]);
|
||||
/*
|
||||
| ((int) (pRGB[0] << 16) & 0x00ff0000)
|
||||
| ((int) (pRGB[1] << 8) & 0x0000ff00)
|
||||
| ((int) (pRGB[2] ) & 0x000000ff);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs a single-input/single-output dither operation, applying basic
|
||||
* Floyd-Steinberg error-diffusion to the image.
|
||||
*
|
||||
* @param pSource the source image
|
||||
* @param pDest the destination image
|
||||
*
|
||||
* @return the destination image, or a new image, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
||||
// Create destination image, if none provided
|
||||
if (pDest == null) {
|
||||
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
||||
}
|
||||
else if (!(pDest.getColorModel() instanceof IndexColorModel)) {
|
||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||
}
|
||||
|
||||
// Filter rasters
|
||||
filter(pSource.getRaster(), pDest.getRaster(), (IndexColorModel) pDest.getColorModel());
|
||||
|
||||
return pDest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a single-input/single-output dither operation, applying basic
|
||||
* Floyd-Steinberg error-diffusion to the image.
|
||||
*
|
||||
* @param pSource the source raster, assumed to be in sRGB
|
||||
* @param pDest the destination raster, may be {@code null}
|
||||
*
|
||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest) {
|
||||
return filter(pSource, pDest, getICM(pSource));
|
||||
}
|
||||
|
||||
private IndexColorModel getICM(BufferedImage pSource) {
|
||||
return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
|
||||
}
|
||||
private IndexColorModel getICM(Raster pSource) {
|
||||
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource));
|
||||
}
|
||||
|
||||
private IndexColorModel createIndexColorModel(Raster pSource) {
|
||||
BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
image.setData(pSource);
|
||||
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a single-input/single-output dither operation, applying basic
|
||||
* Floyd-Steinberg error-diffusion to the image.
|
||||
*
|
||||
* @param pSource the source raster, assumed to be in sRGB
|
||||
* @param pDest the destination raster, may be {@code null}
|
||||
* @param pColorModel the indexed color model to use
|
||||
*
|
||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||
* {@code null}.
|
||||
*/
|
||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
||||
int width = pSource.getWidth();
|
||||
int height = pSource.getHeight();
|
||||
|
||||
// Create destination raster if needed
|
||||
if (pDest == null) {
|
||||
pDest = createCompatibleDestRaster(pSource, pColorModel);
|
||||
}
|
||||
|
||||
// Initialize Floyd-Steinberg error vectors.
|
||||
// +2 to handle the previous pixel and next pixel case minimally
|
||||
// When reference for column, add 1 to reference as this buffer is
|
||||
// offset from actual column position by one to allow FS to not check
|
||||
// left/right edge conditions
|
||||
int[][] currErr = new int[width + 2][3];
|
||||
int[][] nextErr = new int[width + 2][3];
|
||||
|
||||
// Random errors in [-1 .. 1] - for first row
|
||||
for (int i = 0; i < width + 2; i++) {
|
||||
// Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE
|
||||
currErr[i][0] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||
currErr[i][1] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||
currErr[i][2] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
|
||||
}
|
||||
|
||||
// Temp buffers
|
||||
final int[] diff = new int[3]; // No alpha
|
||||
final int[] inRGB = new int[4];
|
||||
final int[] outRGB = new int[4];
|
||||
Object pixel = null;
|
||||
boolean forward = true;
|
||||
|
||||
// Loop through image data
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Clear out next error rows for colour errors
|
||||
for (int i = nextErr.length; --i >= 0;) {
|
||||
nextErr[i][0] = 0;
|
||||
nextErr[i][1] = 0;
|
||||
nextErr[i][2] = 0;
|
||||
}
|
||||
|
||||
// Set up start column and limit
|
||||
int x;
|
||||
int limit;
|
||||
if (forward) {
|
||||
x = 0;
|
||||
limit = width;
|
||||
}
|
||||
else {
|
||||
x = width - 1;
|
||||
limit = -1;
|
||||
}
|
||||
|
||||
// TODO: Use getPixels instead of getPixel for better performance?
|
||||
|
||||
// Loop over row
|
||||
while (true) {
|
||||
// Get RGB from original raster
|
||||
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES.
|
||||
pSource.getPixel(x, y, inRGB);
|
||||
|
||||
// Get error for this pixel & add error to rgb
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Make a 28.4 FP number, add Error (with fraction),
|
||||
// rounding and truncate to int
|
||||
inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4;
|
||||
|
||||
// Clamp
|
||||
if (inRGB[i] > 255) {
|
||||
inRGB[i] = 255;
|
||||
}
|
||||
else if (inRGB[i] < 0) {
|
||||
inRGB[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get pixel value...
|
||||
// It is VERY important that we are using a IndexColorModel that
|
||||
// support reverse color lookup for speed.
|
||||
pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel);
|
||||
|
||||
// ...set it...
|
||||
pDest.setDataElements(x, y, pixel);
|
||||
|
||||
// ..and get back the closet match
|
||||
pDest.getPixel(x, y, outRGB);
|
||||
|
||||
// Convert the value to default sRGB
|
||||
// Should work for all transfertypes supported by IndexColorModel
|
||||
toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB);
|
||||
|
||||
// Find diff
|
||||
diff[0] = inRGB[0] - outRGB[0];
|
||||
diff[1] = inRGB[1] - outRGB[1];
|
||||
diff[2] = inRGB[2] - outRGB[2];
|
||||
|
||||
// Apply F-S error diffusion
|
||||
// Serpentine scan: left-right
|
||||
if (forward) {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x + 1)
|
||||
currErr[x + 2][0] += diff[0] * 7;
|
||||
currErr[x + 2][1] += diff[1] * 7;
|
||||
currErr[x + 2][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x - 1)
|
||||
nextErr[x][0] += diff[0] * 3;
|
||||
nextErr[x][1] += diff[1] * 3;
|
||||
nextErr[x][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x + 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x + 2][0] += diff[0]; // * 1;
|
||||
nextErr[x + 2][1] += diff[1]; // * 1;
|
||||
nextErr[x + 2][2] += diff[2]; // * 1;
|
||||
|
||||
// Next
|
||||
x++;
|
||||
|
||||
// Done?
|
||||
if (x >= limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// Row 1 (y)
|
||||
// Update error in this pixel (x - 1)
|
||||
currErr[x][0] += diff[0] * 7;
|
||||
currErr[x][1] += diff[1] * 7;
|
||||
currErr[x][2] += diff[2] * 7;
|
||||
|
||||
// Row 2 (y + 1)
|
||||
// Update error in this pixel (x + 1)
|
||||
nextErr[x + 2][0] += diff[0] * 3;
|
||||
nextErr[x + 2][1] += diff[1] * 3;
|
||||
nextErr[x + 2][2] += diff[2] * 3;
|
||||
// Update error in this pixel (x)
|
||||
nextErr[x + 1][0] += diff[0] * 5;
|
||||
nextErr[x + 1][1] += diff[1] * 5;
|
||||
nextErr[x + 1][2] += diff[2] * 5;
|
||||
// Update error in this pixel (x - 1)
|
||||
// TODO: Consider calculating this using
|
||||
// error term = error - sum(error terms 1, 2 and 3)
|
||||
// See Computer Graphics (Foley et al.), p. 573
|
||||
nextErr[x][0] += diff[0]; // * 1;
|
||||
nextErr[x][1] += diff[1]; // * 1;
|
||||
nextErr[x][2] += diff[2]; // * 1;
|
||||
|
||||
// Previous
|
||||
x--;
|
||||
|
||||
// Done?
|
||||
if (x <= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make next error info current for next iteration
|
||||
int[][] temperr;
|
||||
temperr = currErr;
|
||||
currErr = nextErr;
|
||||
nextErr = temperr;
|
||||
|
||||
// Toggle direction
|
||||
if (alternateScans) {
|
||||
forward = !forward;
|
||||
}
|
||||
}
|
||||
|
||||
return pDest;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* GraphicsUtil
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GraphicsUtil.java#1 $
|
||||
*/
|
||||
public final class GraphicsUtil {
|
||||
|
||||
/**
|
||||
* Enables anti-aliasing in the {@code Graphics} object.
|
||||
* <p/>
|
||||
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
||||
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
||||
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.RenderingHints#KEY_ANTIALIASING
|
||||
*/
|
||||
public static void enableAA(final Graphics pGraphics) {
|
||||
((Graphics2D) pGraphics).setRenderingHint(
|
||||
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alpha in the {@code Graphics} object.
|
||||
* <p/>
|
||||
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
||||
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
||||
* alpha.
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
||||
* number in the inclusive range [0.0, 1.0].
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.AlphaComposite#SRC_OVER
|
||||
* @see java.awt.AlphaComposite#getInstance(int, float)
|
||||
*/
|
||||
public static void setAlpha(final Graphics pGraphics, final float pAlpha) {
|
||||
((Graphics2D) pGraphics).setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha)
|
||||
);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* GraphicsUtil
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GraphicsUtil.java#1 $
|
||||
*/
|
||||
public final class GraphicsUtil {
|
||||
|
||||
/**
|
||||
* Enables anti-aliasing in the {@code Graphics} object.
|
||||
* <p>
|
||||
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
||||
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
||||
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
||||
* </p>
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.RenderingHints#KEY_ANTIALIASING
|
||||
*/
|
||||
public static void enableAA(final Graphics pGraphics) {
|
||||
((Graphics2D) pGraphics).setRenderingHint(
|
||||
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alpha in the {@code Graphics} object.
|
||||
* <p>
|
||||
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
||||
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
||||
* alpha.
|
||||
* </p>
|
||||
*
|
||||
* @param pGraphics the graphics object
|
||||
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
||||
* number in the inclusive range [0.0, 1.0].
|
||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||
* {@code Graphics2D}.
|
||||
*
|
||||
* @see java.awt.AlphaComposite#SRC_OVER
|
||||
* @see java.awt.AlphaComposite#getInstance(int, float)
|
||||
*/
|
||||
public static void setAlpha(final Graphics pGraphics, final float pAlpha) {
|
||||
((Graphics2D) pGraphics).setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,131 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
/**
|
||||
* This class can convert a color image to grayscale.
|
||||
* <P/>
|
||||
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GrayFilter.java#1 $
|
||||
*
|
||||
*/
|
||||
public class GrayFilter extends RGBImageFilter {
|
||||
|
||||
// This filter can filter IndexColorModel
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
private int low = 0;
|
||||
private float range = 1.0f;
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion.
|
||||
*/
|
||||
public GrayFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion, and a dynamic range between
|
||||
* pLow and pHigh.
|
||||
*
|
||||
* @param pLow float in the range 0..1
|
||||
* @param pHigh float in the range 0..1 and >= pLow
|
||||
*/
|
||||
public GrayFilter(float pLow, float pHigh) {
|
||||
if (pLow > pHigh) {
|
||||
pLow = 0f;
|
||||
}
|
||||
// Make sure high and low are inside range
|
||||
if (pLow < 0f) {
|
||||
pLow = 0f;
|
||||
}
|
||||
else if (pLow > 1f) {
|
||||
pLow = 1f;
|
||||
}
|
||||
if (pHigh < 0f) {
|
||||
pHigh = 0f;
|
||||
}
|
||||
else if (pHigh > 1f) {
|
||||
pHigh = 1f;
|
||||
}
|
||||
|
||||
low = (int) (pLow * 255f);
|
||||
range = pHigh - pLow;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion, and a dynamic
|
||||
* range between pLow and pHigh.
|
||||
*
|
||||
* @param pLow integer in the range 0..255
|
||||
* @param pHigh inteeger in the range 0..255 and >= pLow
|
||||
*/
|
||||
public GrayFilter(int pLow, int pHigh) {
|
||||
this(pLow / 255f, pHigh / 255f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel using ITU color-conversion.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in default color space
|
||||
*
|
||||
* @return the filtered pixel value in the default color space
|
||||
*/
|
||||
public int filterRGB(int pX, int pY, int pARGB) {
|
||||
// Get color components
|
||||
int r = pARGB >> 16 & 0xFF;
|
||||
int g = pARGB >> 8 & 0xFF;
|
||||
int b = pARGB & 0xFF;
|
||||
|
||||
// ITU standard: Gray scale=(222*Red+707*Green+71*Blue)/1000
|
||||
int gray = (222 * r + 707 * g + 71 * b) / 1000;
|
||||
|
||||
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
||||
|
||||
if (range != 1.0f) {
|
||||
// Apply range
|
||||
gray = low + (int) (gray * range);
|
||||
}
|
||||
|
||||
// Return ARGB pixel
|
||||
return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
/**
|
||||
* This class can convert a color image to grayscale.
|
||||
* <p>
|
||||
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
*
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GrayFilter.java#1 $
|
||||
*
|
||||
*/
|
||||
public class GrayFilter extends RGBImageFilter {
|
||||
|
||||
// This filter can filter IndexColorModel
|
||||
{
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
private int low = 0;
|
||||
private float range = 1.0f;
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion.
|
||||
*/
|
||||
public GrayFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion, and a dynamic range between
|
||||
* pLow and pHigh.
|
||||
*
|
||||
* @param pLow float in the range 0..1
|
||||
* @param pHigh float in the range 0..1 and >= pLow
|
||||
*/
|
||||
public GrayFilter(float pLow, float pHigh) {
|
||||
if (pLow > pHigh) {
|
||||
pLow = 0f;
|
||||
}
|
||||
// Make sure high and low are inside range
|
||||
if (pLow < 0f) {
|
||||
pLow = 0f;
|
||||
}
|
||||
else if (pLow > 1f) {
|
||||
pLow = 1f;
|
||||
}
|
||||
if (pHigh < 0f) {
|
||||
pHigh = 0f;
|
||||
}
|
||||
else if (pHigh > 1f) {
|
||||
pHigh = 1f;
|
||||
}
|
||||
|
||||
low = (int) (pLow * 255f);
|
||||
range = pHigh - pLow;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GrayFilter using ITU color-conversion, and a dynamic
|
||||
* range between pLow and pHigh.
|
||||
*
|
||||
* @param pLow integer in the range 0..255
|
||||
* @param pHigh integer in the range 0..255 and >= pLow
|
||||
*/
|
||||
public GrayFilter(int pLow, int pHigh) {
|
||||
this(pLow / 255f, pHigh / 255f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters one pixel using ITU color-conversion.
|
||||
*
|
||||
* @param pX x
|
||||
* @param pY y
|
||||
* @param pARGB pixel value in default color space
|
||||
*
|
||||
* @return the filtered pixel value in the default color space
|
||||
*/
|
||||
public int filterRGB(int pX, int pY, int pARGB) {
|
||||
// Get color components
|
||||
int r = pARGB >> 16 & 0xFF;
|
||||
int g = pARGB >> 8 & 0xFF;
|
||||
int b = pARGB & 0xFF;
|
||||
|
||||
// ITU standard: Gray scale=(222*Red+707*Green+71*Blue)/1000
|
||||
int gray = (222 * r + 707 * g + 71 * b) / 1000;
|
||||
|
||||
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
||||
|
||||
if (range != 1.0f) {
|
||||
// Apply range
|
||||
gray = low + (int) (gray * range);
|
||||
}
|
||||
|
||||
// Return ARGB pixel
|
||||
return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,213 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
/**
|
||||
* Inverse Colormap to provide efficient lookup of any given input color
|
||||
* to the closest match to the given color map.
|
||||
* <p/>
|
||||
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
||||
* in "Graphics Gems Volume II"
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author Robin Luiten (Java port)
|
||||
* @author Spencer W. Thomas (original c version).
|
||||
*
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/InverseColorMap.java#1 $
|
||||
*/
|
||||
class InverseColorMap {
|
||||
/**
|
||||
* Number of high bits of each color channel to use to lookup near match
|
||||
*/
|
||||
final static int QUANTBITS = 5;
|
||||
|
||||
/**
|
||||
* Truncated bits of each color channel
|
||||
*/
|
||||
final static int TRUNCBITS = 8 - QUANTBITS;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for blue in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_BLUE = (1 << 5) - 1;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for green in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS);
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for red in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS);
|
||||
|
||||
/**
|
||||
* Maximum value a quantised color channel can have
|
||||
*/
|
||||
final static int MAXQUANTVAL = 1 << 5;
|
||||
|
||||
byte[] rgbMapByte;
|
||||
int[] rgbMapInt;
|
||||
int numColors;
|
||||
int maxColor;
|
||||
byte[] inverseRGB; // inverse rgb color map
|
||||
int transparentIndex = -1;
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap) {
|
||||
this(pRGBColorMap, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
InverseColorMap(int[] pRGBColorMap) {
|
||||
this(pRGBColorMap, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
* @param pTransparent the index of the transparent pixel in the map
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapByte = pRGBColorMap;
|
||||
numColors = rgbMapByte.length / 4;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
* @param pTransparent the index of the transparent pixel in the map
|
||||
*/
|
||||
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapInt = pRGBColorMap;
|
||||
numColors = rgbMapInt.length;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple inverse color table creation method.
|
||||
* @param pTemp temp array
|
||||
*/
|
||||
void initIRGB(int[] pTemp) {
|
||||
final int x = (1 << TRUNCBITS); // 8 the size of 1 Dimension of each quantized cell
|
||||
final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors
|
||||
final int xsqr2 = xsqr + xsqr;
|
||||
|
||||
for (int i = 0; i < numColors; ++i) {
|
||||
if (i == transparentIndex) {
|
||||
// Skip the transparent pixel
|
||||
continue;
|
||||
}
|
||||
|
||||
int red, r, rdist, rinc, rxx;
|
||||
int green, g, gdist, ginc, gxx;
|
||||
int blue, b, bdist, binc, bxx;
|
||||
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
if (rgbMapByte != null) {
|
||||
red = rgbMapByte[i * 4] & 0xFF;
|
||||
green = rgbMapByte[i * 4 + 1] & 0xFF;
|
||||
blue = rgbMapByte[i * 4 + 2] & 0xFF;
|
||||
}
|
||||
else if (rgbMapInt != null) {
|
||||
red = (rgbMapInt[i] >> 16) & 0xFF;
|
||||
green = (rgbMapInt[i] >> 8) & 0xFF;
|
||||
blue = rgbMapInt[i] & 0xFF;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("colormap == null");
|
||||
}
|
||||
|
||||
rdist = red - x / 2; // distance of red to center of current cell
|
||||
gdist = green - x / 2; // green
|
||||
bdist = blue - x / 2; // blue
|
||||
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
|
||||
|
||||
rinc = 2 * (xsqr - (red << TRUNCBITS));
|
||||
ginc = 2 * (xsqr - (green << TRUNCBITS));
|
||||
binc = 2 * (xsqr - (blue << TRUNCBITS));
|
||||
|
||||
int rgbI = 0;
|
||||
for (r = 0, rxx = rinc; r < MAXQUANTVAL; rdist += rxx, ++r, rxx += xsqr2) {
|
||||
for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) {
|
||||
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
||||
if (i == 0 || pTemp[rgbI] > bdist) {
|
||||
pTemp[rgbI] = bdist;
|
||||
inverseRGB[rgbI] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the nearest color to from the color map.
|
||||
*
|
||||
* @param pColor the color to get the nearest color to from color map
|
||||
* color must be of format {@code 0x00RRGGBB} - standard default RGB
|
||||
* @return index of color which closest matches input color by using the
|
||||
* created inverse color map.
|
||||
*/
|
||||
public final int getIndexNearest(int pColor) {
|
||||
return inverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) +
|
||||
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the nearest color to from the color map.
|
||||
*
|
||||
* @param pRed red component of the color to get the nearest color to from color map
|
||||
* @param pGreen green component of the color to get the nearest color to from color map
|
||||
* @param pBlue blue component of the color to get the nearest color to from color map
|
||||
* @return index of color which closest matches input color by using the
|
||||
* created inverse color map.
|
||||
*/
|
||||
public final int getIndexNearest(int pRed, int pGreen, int pBlue) {
|
||||
// NOTE: the third line in expression for blue is shifting DOWN not UP.
|
||||
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
||||
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
/**
|
||||
* Inverse Colormap to provide efficient lookup of any given input color
|
||||
* to the closest match to the given color map.
|
||||
* <p>
|
||||
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
||||
* in "Graphics Gems Volume II".
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author Robin Luiten (Java port)
|
||||
* @author Spencer W. Thomas (original c version).
|
||||
*
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/InverseColorMap.java#1 $
|
||||
*/
|
||||
class InverseColorMap {
|
||||
/**
|
||||
* Number of high bits of each color channel to use to lookup near match
|
||||
*/
|
||||
final static int QUANTBITS = 5;
|
||||
|
||||
/**
|
||||
* Truncated bits of each color channel
|
||||
*/
|
||||
final static int TRUNCBITS = 8 - QUANTBITS;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for blue in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_BLUE = (1 << 5) - 1;
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for green in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS);
|
||||
|
||||
/**
|
||||
* BITMASK representing the bits for red in the color lookup
|
||||
*/
|
||||
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS);
|
||||
|
||||
/**
|
||||
* Maximum value a quantised color channel can have
|
||||
*/
|
||||
final static int MAXQUANTVAL = 1 << 5;
|
||||
|
||||
byte[] rgbMapByte;
|
||||
int[] rgbMapInt;
|
||||
int numColors;
|
||||
int maxColor;
|
||||
byte[] inverseRGB; // inverse rgb color map
|
||||
int transparentIndex = -1;
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap) {
|
||||
this(pRGBColorMap, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
*/
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
InverseColorMap(int[] pRGBColorMap) {
|
||||
this(pRGBColorMap, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
* @param pTransparent the index of the transparent pixel in the map
|
||||
*/
|
||||
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapByte = pRGBColorMap;
|
||||
numColors = rgbMapByte.length / 4;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||
* @param pTransparent the index of the transparent pixel in the map
|
||||
*/
|
||||
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
||||
rgbMapInt = pRGBColorMap;
|
||||
numColors = rgbMapInt.length;
|
||||
transparentIndex = pTransparent;
|
||||
|
||||
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple inverse color table creation method.
|
||||
* @param pTemp temp array
|
||||
*/
|
||||
void initIRGB(int[] pTemp) {
|
||||
final int x = (1 << TRUNCBITS); // 8 the size of 1 Dimension of each quantized cell
|
||||
final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors
|
||||
final int xsqr2 = xsqr + xsqr;
|
||||
|
||||
for (int i = 0; i < numColors; ++i) {
|
||||
if (i == transparentIndex) {
|
||||
// Skip the transparent pixel
|
||||
continue;
|
||||
}
|
||||
|
||||
int red, r, rdist, rinc, rxx;
|
||||
int green, g, gdist, ginc, gxx;
|
||||
int blue, b, bdist, binc, bxx;
|
||||
|
||||
// HaraldK 20040801: Added support for int[]
|
||||
if (rgbMapByte != null) {
|
||||
red = rgbMapByte[i * 4] & 0xFF;
|
||||
green = rgbMapByte[i * 4 + 1] & 0xFF;
|
||||
blue = rgbMapByte[i * 4 + 2] & 0xFF;
|
||||
}
|
||||
else if (rgbMapInt != null) {
|
||||
red = (rgbMapInt[i] >> 16) & 0xFF;
|
||||
green = (rgbMapInt[i] >> 8) & 0xFF;
|
||||
blue = rgbMapInt[i] & 0xFF;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("colormap == null");
|
||||
}
|
||||
|
||||
rdist = red - x / 2; // distance of red to center of current cell
|
||||
gdist = green - x / 2; // green
|
||||
bdist = blue - x / 2; // blue
|
||||
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
|
||||
|
||||
rinc = 2 * (xsqr - (red << TRUNCBITS));
|
||||
ginc = 2 * (xsqr - (green << TRUNCBITS));
|
||||
binc = 2 * (xsqr - (blue << TRUNCBITS));
|
||||
|
||||
int rgbI = 0;
|
||||
for (r = 0, rxx = rinc; r < MAXQUANTVAL; rdist += rxx, ++r, rxx += xsqr2) {
|
||||
for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) {
|
||||
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
||||
if (i == 0 || pTemp[rgbI] > bdist) {
|
||||
pTemp[rgbI] = bdist;
|
||||
inverseRGB[rgbI] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the nearest color to from the color map.
|
||||
*
|
||||
* @param pColor the color to get the nearest color to from color map
|
||||
* color must be of format {@code 0x00RRGGBB} - standard default RGB
|
||||
* @return index of color which closest matches input color by using the
|
||||
* created inverse color map.
|
||||
*/
|
||||
public final int getIndexNearest(int pColor) {
|
||||
return inverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) +
|
||||
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the nearest color to from the color map.
|
||||
*
|
||||
* @param pRed red component of the color to get the nearest color to from color map
|
||||
* @param pGreen green component of the color to get the nearest color to from color map
|
||||
* @param pBlue blue component of the color to get the nearest color to from color map
|
||||
* @return index of color which closest matches input color by using the
|
||||
* created inverse color map.
|
||||
*/
|
||||
public final int getIndexNearest(int pRed, int pGreen, int pBlue) {
|
||||
// NOTE: the third line in expression for blue is shifting DOWN not UP.
|
||||
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
||||
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,187 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import magick.MagickImage;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
|
||||
/**
|
||||
* This class accelerates certain graphics operations, using
|
||||
* JMagick and ImageMagick, if available.
|
||||
* If those libraries are not installed, this class silently does nothing.
|
||||
* <p/>
|
||||
* Set the system property {@code "com.twelvemonkeys.image.accel"} to
|
||||
* {@code false}, to disable, even if JMagick is installed.
|
||||
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
|
||||
* <p/>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickAccelerator.java#3 $
|
||||
*/
|
||||
final class MagickAccelerator {
|
||||
|
||||
private static final boolean DEBUG = Magick.DEBUG;
|
||||
private static final boolean USE_MAGICK = useMagick();
|
||||
|
||||
private static final int RESAMPLE_OP = 0;
|
||||
|
||||
private static Class[] nativeOp = new Class[1];
|
||||
|
||||
static {
|
||||
try {
|
||||
nativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
System.err.println("Could not find class: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean useMagick() {
|
||||
try {
|
||||
boolean available = SystemUtil.isClassAvailable("magick.MagickImage");
|
||||
|
||||
if (DEBUG && !available) {
|
||||
System.err.print("ImageMagick bindings not available.");
|
||||
}
|
||||
|
||||
boolean useMagick =
|
||||
available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel"));
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println(
|
||||
useMagick
|
||||
? "Will use ImageMagick bindings to accelerate image resampling operations."
|
||||
: "Will not use ImageMagick to accelerate image resampling operations."
|
||||
);
|
||||
}
|
||||
|
||||
return useMagick;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Most probably in case of a SecurityManager
|
||||
System.err.println("Could not enable ImageMagick bindings: " + t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNativeOpIndex(Class pOpClass) {
|
||||
for (int i = 0; i < nativeOp.length; i++) {
|
||||
if (pOpClass == nativeOp[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) {
|
||||
if (!USE_MAGICK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage result = null;
|
||||
switch (getNativeOpIndex(pOperation.getClass())) {
|
||||
case RESAMPLE_OP:
|
||||
ResampleOp resample = (ResampleOp) pOperation;
|
||||
result = resampleMagick(pInput, resample.width, resample.height, resample.filterType);
|
||||
|
||||
// NOTE: If output parameter is non-null, we have to return that
|
||||
// image, instead of result
|
||||
if (pOutput != null) {
|
||||
//pOutput.setData(result.getRaster()); // Fast, but less compatible
|
||||
// NOTE: For some reason, this is sometimes super-slow...?
|
||||
ImageUtil.drawOnto(pOutput, result);
|
||||
result = pOutput;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Simply fall through, allowing acceleration to be added later
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) {
|
||||
// Convert to Magick, scale and convert back
|
||||
MagickImage image = null;
|
||||
MagickImage scaled = null;
|
||||
try {
|
||||
image = MagickUtil.toMagick(pSrc);
|
||||
|
||||
long start = 0;
|
||||
if (DEBUG) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// NOTE: setFilter affects zoomImage, NOT scaleImage
|
||||
image.setFilter(pFilterType);
|
||||
scaled = image.zoomImage(pWidth, pHeight);
|
||||
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
|
||||
|
||||
if (DEBUG) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
System.out.println("Filtered: " + time + " ms");
|
||||
}
|
||||
|
||||
return MagickUtil.toBuffered(scaled);
|
||||
}
|
||||
//catch (MagickException e) {
|
||||
catch (Exception e) {
|
||||
// NOTE: Stupid workaround: If MagickException is caught, a
|
||||
// NoClassDefFoundError is thrown, when MagickException class is
|
||||
// unavailable...
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
|
||||
throw new ImageConversionException(e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
// NOTE: ImageMagick might be unstable after a while, if image data
|
||||
// is not deallocated. The GC/finalize method handles this, but in
|
||||
// special circumstances, it's not triggered often enough.
|
||||
if (image != null) {
|
||||
image.destroyImages();
|
||||
}
|
||||
if (scaled != null) {
|
||||
scaled.destroyImages();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import magick.MagickImage;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
|
||||
/**
|
||||
* This class accelerates certain graphics operations, using
|
||||
* JMagick and ImageMagick, if available.
|
||||
* If those libraries are not installed, this class silently does nothing.
|
||||
* <p>
|
||||
* Set the system property {@code "com.twelvemonkeys.image.accel"} to
|
||||
* {@code false}, to disable, even if JMagick is installed.
|
||||
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickAccelerator.java#3 $
|
||||
*/
|
||||
final class MagickAccelerator {
|
||||
|
||||
private static final boolean DEBUG = Magick.DEBUG;
|
||||
private static final boolean USE_MAGICK = useMagick();
|
||||
|
||||
private static final int RESAMPLE_OP = 0;
|
||||
|
||||
private static Class[] nativeOp = new Class[1];
|
||||
|
||||
static {
|
||||
try {
|
||||
nativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
System.err.println("Could not find class: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean useMagick() {
|
||||
try {
|
||||
boolean available = SystemUtil.isClassAvailable("magick.MagickImage");
|
||||
|
||||
if (DEBUG && !available) {
|
||||
System.err.print("ImageMagick bindings not available.");
|
||||
}
|
||||
|
||||
boolean useMagick =
|
||||
available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel"));
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println(
|
||||
useMagick
|
||||
? "Will use ImageMagick bindings to accelerate image resampling operations."
|
||||
: "Will not use ImageMagick to accelerate image resampling operations."
|
||||
);
|
||||
}
|
||||
|
||||
return useMagick;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// Most probably in case of a SecurityManager
|
||||
System.err.println("Could not enable ImageMagick bindings: " + t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNativeOpIndex(Class pOpClass) {
|
||||
for (int i = 0; i < nativeOp.length; i++) {
|
||||
if (pOpClass == nativeOp[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) {
|
||||
if (!USE_MAGICK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage result = null;
|
||||
switch (getNativeOpIndex(pOperation.getClass())) {
|
||||
case RESAMPLE_OP:
|
||||
ResampleOp resample = (ResampleOp) pOperation;
|
||||
result = resampleMagick(pInput, resample.width, resample.height, resample.filterType);
|
||||
|
||||
// NOTE: If output parameter is non-null, we have to return that
|
||||
// image, instead of result
|
||||
if (pOutput != null) {
|
||||
//pOutput.setData(result.getRaster()); // Fast, but less compatible
|
||||
// NOTE: For some reason, this is sometimes super-slow...?
|
||||
ImageUtil.drawOnto(pOutput, result);
|
||||
result = pOutput;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Simply fall through, allowing acceleration to be added later
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) {
|
||||
// Convert to Magick, scale and convert back
|
||||
MagickImage image = null;
|
||||
MagickImage scaled = null;
|
||||
try {
|
||||
image = MagickUtil.toMagick(pSrc);
|
||||
|
||||
long start = 0;
|
||||
if (DEBUG) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// NOTE: setFilter affects zoomImage, NOT scaleImage
|
||||
image.setFilter(pFilterType);
|
||||
scaled = image.zoomImage(pWidth, pHeight);
|
||||
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
|
||||
|
||||
if (DEBUG) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
System.out.println("Filtered: " + time + " ms");
|
||||
}
|
||||
|
||||
return MagickUtil.toBuffered(scaled);
|
||||
}
|
||||
//catch (MagickException e) {
|
||||
catch (Exception e) {
|
||||
// NOTE: Stupid workaround: If MagickException is caught, a
|
||||
// NoClassDefFoundError is thrown, when MagickException class is
|
||||
// unavailable...
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
|
||||
throw new ImageConversionException(e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
// NOTE: ImageMagick might be unstable after a while, if image data
|
||||
// is not deallocated. The GC/finalize method handles this, but in
|
||||
// special circumstances, it's not triggered often enough.
|
||||
if (image != null) {
|
||||
image.destroyImages();
|
||||
}
|
||||
if (scaled != null) {
|
||||
scaled.destroyImages();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,79 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ReplicateScaleFilter;
|
||||
|
||||
/**
|
||||
* An {@code ImageFilter} class for subsampling images.
|
||||
* <p/>
|
||||
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
||||
* object to produce subsampled versions of existing images.
|
||||
*
|
||||
* @see java.awt.image.FilteredImageSource
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/SubsamplingFilter.java#1 $
|
||||
*/
|
||||
public class SubsamplingFilter extends ReplicateScaleFilter {
|
||||
private int xSub;
|
||||
private int ySub;
|
||||
|
||||
/**
|
||||
* Creates a {@code SubsamplingFilter}.
|
||||
*
|
||||
* @param pXSub
|
||||
* @param pYSub
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pXSub} or {@code pYSub} is
|
||||
* less than 1.
|
||||
*/
|
||||
public SubsamplingFilter(int pXSub, int pYSub) {
|
||||
super(1, 1); // These are NOT REAL values, but we have to defer setting
|
||||
// until w/h is available, in setDimensions below
|
||||
|
||||
if (pXSub < 1 || pYSub < 1) {
|
||||
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
||||
}
|
||||
|
||||
xSub = pXSub;
|
||||
ySub = pYSub;
|
||||
}
|
||||
|
||||
/** {@code ImageFilter} implementation, do not invoke. */
|
||||
public void setDimensions(int pWidth, int pHeight) {
|
||||
destWidth = (pWidth + xSub - 1) / xSub;
|
||||
destHeight = (pHeight + ySub - 1) / ySub;
|
||||
|
||||
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
|
||||
super.setDimensions(pWidth, pHeight);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* 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.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import java.awt.image.ReplicateScaleFilter;
|
||||
|
||||
/**
|
||||
* An {@code ImageFilter} class for subsampling images.
|
||||
* <p>
|
||||
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
||||
* object to produce subsampled versions of existing images.
|
||||
* </p>
|
||||
*
|
||||
* @see java.awt.image.FilteredImageSource
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/SubsamplingFilter.java#1 $
|
||||
*/
|
||||
public class SubsamplingFilter extends ReplicateScaleFilter {
|
||||
private int xSub;
|
||||
private int ySub;
|
||||
|
||||
/**
|
||||
* Creates a {@code SubsamplingFilter}.
|
||||
*
|
||||
* @param pXSub
|
||||
* @param pYSub
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code pXSub} or {@code pYSub} is
|
||||
* less than 1.
|
||||
*/
|
||||
public SubsamplingFilter(int pXSub, int pYSub) {
|
||||
super(1, 1); // These are NOT REAL values, but we have to defer setting
|
||||
// until w/h is available, in setDimensions below
|
||||
|
||||
if (pXSub < 1 || pYSub < 1) {
|
||||
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
||||
}
|
||||
|
||||
xSub = pXSub;
|
||||
ySub = pYSub;
|
||||
}
|
||||
|
||||
/** {@code ImageFilter} implementation, do not invoke. */
|
||||
public void setDimensions(int pWidth, int pHeight) {
|
||||
destWidth = (pWidth + xSub - 1) / xSub;
|
||||
destHeight = (pHeight + ySub - 1) / ySub;
|
||||
|
||||
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
|
||||
super.setDimensions(pWidth, pHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
|
||||
/**
|
||||
* Classes for image manipulation.
|
||||
* <p/>
|
||||
* <p>
|
||||
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
||||
* </p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
|
||||
Reference in New Issue
Block a user