summaryrefslogtreecommitdiffstats
path: root/awt/java/awt/image/RescaleOp.java
diff options
context:
space:
mode:
Diffstat (limited to 'awt/java/awt/image/RescaleOp.java')
-rw-r--r--awt/java/awt/image/RescaleOp.java659
1 files changed, 659 insertions, 0 deletions
diff --git a/awt/java/awt/image/RescaleOp.java b/awt/java/awt/image/RescaleOp.java
new file mode 100644
index 0000000..0e96031
--- /dev/null
+++ b/awt/java/awt/image/RescaleOp.java
@@ -0,0 +1,659 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ * @date: Oct 6, 2005
+ */
+
+package java.awt.image;
+
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.*;
+import java.util.Arrays;
+
+import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * The Class RescaleOp performs rescaling of the source image data
+ * by multiplying the pixel values with a scale factor
+ * and then adding an offset.
+ */
+public class RescaleOp implements BufferedImageOp, RasterOp {
+
+ /** The scale factors. */
+ private float scaleFactors[];
+
+ /** The offsets. */
+ private float offsets[];
+
+ /** The hints. */
+ private RenderingHints hints;
+
+ static {
+ // TODO
+ //System.loadLibrary("imageops");
+ }
+
+ /**
+ * Instantiates a new RescaleOp object with the specified
+ * scale factors and offsets.
+ *
+ * @param scaleFactors the array of scale factor values.
+ * @param offsets the array of offset values.
+ * @param hints the RenderingHints or null.
+ */
+ public RescaleOp(float[] scaleFactors, float[] offsets, RenderingHints hints) {
+ int numFactors = Math.min(scaleFactors.length, offsets.length);
+
+ this.scaleFactors = new float[numFactors];
+ this.offsets = new float[numFactors];
+
+ System.arraycopy(scaleFactors, 0, this.scaleFactors, 0, numFactors);
+ System.arraycopy(offsets, 0, this.offsets, 0, numFactors);
+
+ this.hints = hints;
+ }
+
+ /**
+ * Instantiates a new RescaleOp object with the specified
+ * scale factor and offset.
+ *
+ * @param scaleFactor the scale factor.
+ * @param offset the offset.
+ * @param hints the RenderingHints or null.
+ */
+ public RescaleOp(float scaleFactor, float offset, RenderingHints hints) {
+ scaleFactors = new float[1];
+ offsets = new float[1];
+
+ scaleFactors[0] = scaleFactor;
+ offsets[0] = offset;
+
+ this.hints = hints;
+ }
+
+ /**
+ * Gets the number of scaling factors.
+ *
+ * @return the number of scaling factors.
+ */
+ public final int getNumFactors() {
+ return scaleFactors.length;
+ }
+
+ public final RenderingHints getRenderingHints() {
+ return hints;
+ }
+
+ /**
+ * Gets the scale factors of this RescaleOp.
+ *
+ * @param scaleFactors the desired scale factors array will be copied
+ * to this array.
+ *
+ * @return the scale factors array.
+ */
+ public final float[] getScaleFactors(float[] scaleFactors) {
+ if (scaleFactors == null) {
+ scaleFactors = new float[this.scaleFactors.length];
+ }
+
+ int minLength = Math.min(scaleFactors.length, this.scaleFactors.length);
+ System.arraycopy(this.scaleFactors, 0, scaleFactors, 0, minLength);
+ return scaleFactors;
+ }
+
+ /**
+ * Gets the offsets array of this RescaleOp.
+ *
+ * @param offsets the desired offsets array will be copied to this array.
+ *
+ * @return the offsets array of this RescaleOp.
+ */
+ public final float[] getOffsets(float[] offsets) {
+ if (offsets == null) {
+ offsets = new float[this.offsets.length];
+ }
+
+ int minLength = Math.min(offsets.length, this.offsets.length);
+ System.arraycopy(this.offsets, 0, offsets, 0, minLength);
+ return offsets;
+ }
+
+ public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
+ if (dstPt == null) {
+ dstPt = new Point2D.Float();
+ }
+
+ dstPt.setLocation(srcPt);
+ return dstPt;
+ }
+
+ public final Rectangle2D getBounds2D(Raster src) {
+ return src.getBounds();
+ }
+
+ public final Rectangle2D getBounds2D(BufferedImage src) {
+ return getBounds2D(src.getRaster());
+ }
+
+ public WritableRaster createCompatibleDestRaster(Raster src) {
+ return src.createCompatibleWritableRaster();
+ }
+
+ public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
+ if (dstCM == null) {
+ dstCM = src.getColorModel();
+ }
+
+ if (dstCM instanceof IndexColorModel) {
+ dstCM = ColorModel.getRGBdefault();
+ }
+
+ WritableRaster r =
+ dstCM.isCompatibleSampleModel(src.getSampleModel()) ?
+ src.getRaster().createCompatibleWritableRaster(src.getWidth(), src.getHeight()) :
+ dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight());
+
+ return new BufferedImage(
+ dstCM,
+ r,
+ dstCM.isAlphaPremultiplied(),
+ null
+ );
+ }
+
+ public final WritableRaster filter(Raster src, WritableRaster dst) {
+ if (dst == null) {
+ dst = createCompatibleDestRaster(src);
+ } else {
+ if (src.getNumBands() != dst.getNumBands()) {
+ // awt.21D=Number of src bands ({0}) does not match number of dst bands ({1})
+ throw new IllegalArgumentException(Messages.getString("awt.21D", //$NON-NLS-1$
+ src.getNumBands(), dst.getNumBands()));
+ }
+ }
+
+ if (
+ this.scaleFactors.length != 1 &&
+ this.scaleFactors.length != src.getNumBands()
+ ) {
+ // awt.21E=Number of scaling constants is not equal to the number of bands
+ throw new IllegalArgumentException(Messages.getString("awt.21E")); //$NON-NLS-1$
+ }
+
+ // TODO
+ //if (ippFilter(src, dst, BufferedImage.TYPE_CUSTOM, false) != 0)
+ if (slowFilter(src, dst, false) != 0) {
+ // awt.21F=Unable to transform source
+ throw new ImagingOpException (Messages.getString("awt.21F")); //$NON-NLS-1$
+ }
+
+ return dst;
+ }
+
+ /**
+ * Slow filter.
+ *
+ * @param src the src
+ * @param dst the dst
+ * @param skipAlpha the skip alpha
+ *
+ * @return the int
+ */
+ private final int slowFilter(Raster src, WritableRaster dst, boolean skipAlpha) {
+ SampleModel sm = src.getSampleModel();
+
+ int numBands = src.getNumBands();
+ int srcHeight = src.getHeight();
+ int srcWidth = src.getWidth();
+
+ int srcMinX = src.getMinX();
+ int srcMinY = src.getMinY();
+ int dstMinX = dst.getMinX();
+ int dstMinY = dst.getMinY();
+
+ int[] maxValues = new int[numBands];
+ int[] masks = new int[numBands];
+ int[] sampleSizes = sm.getSampleSize();
+
+ for (int i=0; i < numBands; i++){
+ maxValues[i] = (1 << sampleSizes[i]) - 1;
+ masks[i] = ~(maxValues[i]);
+ }
+
+ // Processing bounds
+ float[] pixels = null;
+ pixels = src.getPixels(srcMinX, srcMinY, srcWidth, srcHeight, pixels);
+
+ // Cycle over pixels to be calculated
+ if (skipAlpha) { // Always suppose that alpha channel is the last band
+ if (scaleFactors.length > 1) {
+ for (int i = 0; i < pixels.length; ){
+ for (int bandIdx = 0; bandIdx < numBands-1; bandIdx++, i++){
+ pixels[i] = pixels[i] * scaleFactors[bandIdx] + offsets[bandIdx];
+ // Check for overflow now
+ if (((int)pixels[i] & masks[bandIdx]) != 0) {
+ if (pixels[i] < 0) {
+ pixels[i] = 0;
+ } else {
+ pixels[i] = maxValues[bandIdx];
+ }
+ }
+ }
+
+ i++;
+ }
+ } else {
+ for (int i = 0; i < pixels.length; ){
+ for (int bandIdx = 0; bandIdx < numBands-1; bandIdx++, i++){
+ pixels[i] = pixels[i] * scaleFactors[0] + offsets[0];
+ // Check for overflow now
+ if (((int)pixels[i] & masks[bandIdx]) != 0) {
+ if (pixels[i] < 0) {
+ pixels[i] = 0;
+ } else {
+ pixels[i] = maxValues[bandIdx];
+ }
+ }
+ }
+
+ i++;
+ }
+ }
+ } else {
+ if (scaleFactors.length > 1) {
+ for (int i = 0; i < pixels.length; ){
+ for (int bandIdx = 0; bandIdx < numBands; bandIdx++, i++){
+ pixels[i] = pixels[i] * scaleFactors[bandIdx] + offsets[bandIdx];
+ // Check for overflow now
+ if (((int)pixels[i] & masks[bandIdx]) != 0) {
+ if (pixels[i] < 0) {
+ pixels[i] = 0;
+ } else {
+ pixels[i] = maxValues[bandIdx];
+ }
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < pixels.length; ){
+ for (int bandIdx = 0; bandIdx < numBands; bandIdx++, i++){
+ pixels[i] = pixels[i] * scaleFactors[0] + offsets[0];
+ // Check for overflow now
+ if (((int)pixels[i] & masks[bandIdx]) != 0) {
+ if (pixels[i] < 0) {
+ pixels[i] = 0;
+ } else {
+ pixels[i] = maxValues[bandIdx];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ dst.setPixels(dstMinX, dstMinY, srcWidth, srcHeight, pixels);
+
+ return 0;
+ }
+
+ public final BufferedImage filter(BufferedImage src, BufferedImage dst) {
+ ColorModel srcCM = src.getColorModel();
+
+ if (srcCM instanceof IndexColorModel) {
+ // awt.220=Source should not have IndexColorModel
+ throw new IllegalArgumentException(Messages.getString("awt.220")); //$NON-NLS-1$
+ }
+
+ // Check if the number of scaling factors matches the number of bands
+ int nComponents = srcCM.getNumComponents();
+ boolean skipAlpha;
+ if (srcCM.hasAlpha()) {
+ if (scaleFactors.length == 1 || scaleFactors.length == nComponents-1) {
+ skipAlpha = true;
+ } else if (scaleFactors.length == nComponents) {
+ skipAlpha = false;
+ } else {
+ // awt.21E=Number of scaling constants is not equal to the number of bands
+ throw new IllegalArgumentException(Messages.getString("awt.21E")); //$NON-NLS-1$
+ }
+ } else if (scaleFactors.length == 1 || scaleFactors.length == nComponents) {
+ skipAlpha = false;
+ } else {
+ // awt.21E=Number of scaling constants is not equal to the number of bands
+ throw new IllegalArgumentException(Messages.getString("awt.21E")); //$NON-NLS-1$
+ }
+
+ BufferedImage finalDst = null;
+ if (dst == null) {
+ finalDst = dst;
+ dst = createCompatibleDestImage(src, srcCM);
+ } else if (!srcCM.equals(dst.getColorModel())) {
+ // Treat BufferedImage.TYPE_INT_RGB and BufferedImage.TYPE_INT_ARGB as same
+ if (
+ !((src.getType() == BufferedImage.TYPE_INT_RGB ||
+ src.getType() == BufferedImage.TYPE_INT_ARGB) &&
+ (dst.getType() == BufferedImage.TYPE_INT_RGB ||
+ dst.getType() == BufferedImage.TYPE_INT_ARGB))
+ ) {
+ finalDst = dst;
+ dst = createCompatibleDestImage(src, srcCM);
+ }
+ }
+
+ // TODO
+ //if (ippFilter(src.getRaster(), dst.getRaster(), src.getType(), skipAlpha) != 0)
+ if (slowFilter(src.getRaster(), dst.getRaster(), skipAlpha) != 0) {
+ // awt.21F=Unable to transform source
+ throw new ImagingOpException (Messages.getString("awt.21F")); //$NON-NLS-1$
+ }
+
+ if (finalDst != null) {
+ Graphics2D g = finalDst.createGraphics();
+ g.setComposite(AlphaComposite.Src);
+ g.drawImage(dst, 0, 0, null);
+ } else {
+ finalDst = dst;
+ }
+
+ return finalDst;
+ }
+
+ // Don't forget to pass allocated arrays for levels and values, size should be numBands*4
+ /**
+ * Creates the levels.
+ *
+ * @param sm the sm
+ * @param numBands the num bands
+ * @param skipAlpha the skip alpha
+ * @param levels the levels
+ * @param values the values
+ * @param channelsOrder the channels order
+ */
+ private final void createLevels(
+ SampleModel sm, int numBands, boolean skipAlpha,
+ int levels[], int values[], int channelsOrder[]
+ ) {
+ // Suppose same sample size for all channels, otherwise use slow filter
+ int maxValue = (1 << sm.getSampleSize(0)) - 1;
+
+ // For simplicity introduce these arrays
+ float extScaleFactors[] = new float[numBands];
+ float extOffsets[] = new float[numBands];
+
+ if (scaleFactors.length != 1) {
+ System.arraycopy(scaleFactors, 0, extScaleFactors, 0, scaleFactors.length);
+ System.arraycopy(offsets, 0, extOffsets, 0, scaleFactors.length);
+ } else {
+ for (int i = 0; i < numBands; i++) {
+ extScaleFactors[i] = scaleFactors[0];
+ extOffsets[i] = offsets[0];
+ }
+ }
+
+ if (skipAlpha) {
+ extScaleFactors[numBands-1] = 1;
+ extOffsets[numBands-1] = 0;
+ }
+
+ // Create a levels
+ for (int i=0; i<numBands; i++) {
+ if (extScaleFactors[i] == 0) {
+ levels[i*4] = 0;
+ levels[i*4+1] = 0;
+ levels[i*4+2] = maxValue+1;
+ levels[i*4+3] = maxValue+1;
+ }
+
+ float minLevel = -extOffsets[i] / extScaleFactors[i];
+ float maxLevel = (maxValue - extOffsets[i]) / extScaleFactors[i];
+
+ if (minLevel < 0) {
+ minLevel = 0;
+ } else if (minLevel > maxValue){
+ minLevel = maxValue;
+ }
+
+ if (maxLevel < 0) {
+ maxLevel = 0;
+ } else if (maxLevel > maxValue){
+ maxLevel = maxValue;
+ }
+
+ levels[i*4] = 0;
+ if (minLevel > maxLevel) {
+ levels[i*4+1] = (int) maxLevel;
+ levels[i*4+2] = (int) minLevel;
+ } else {
+ levels[i*4+1] = (int) minLevel;
+ levels[i*4+2] = (int) maxLevel;
+ }
+ levels[i*4+3] = maxValue+1;
+
+ // Fill values
+ for (int k=0; k<4; k++) {
+ int idx = i*4+k;
+ values[idx] = (int) (extScaleFactors[i] * levels[idx] + extOffsets[i]);
+ if (values[idx] < 0) {
+ values[idx] = 0;
+ } else if (values[idx] > maxValue){
+ values[idx] = maxValue;
+ }
+ }
+ }
+
+ // Reorder data if channels are stored in different order
+ if (channelsOrder != null) {
+ int len = numBands*4;
+ int savedLevels[] = new int[len];
+ int savedValues[] = new int[len];
+ System.arraycopy(levels, 0, savedLevels, 0, len);
+ System.arraycopy(values, 0, savedValues, 0, len);
+ for (int i = 0; i < channelsOrder.length; i++) {
+ System.arraycopy(savedLevels, i*4, levels, channelsOrder[i]*4, 4);
+ System.arraycopy(savedValues, i*4, values, channelsOrder[i]*4, 4);
+ }
+ }
+ }
+
+ // TODO remove when this method is used
+ /**
+ * Ipp filter.
+ *
+ * @param src the src
+ * @param dst the dst
+ * @param imageType the image type
+ * @param skipAlpha the skip alpha
+ *
+ * @return the int
+ */
+ @SuppressWarnings("unused")
+ private final int ippFilter(
+ Raster src, WritableRaster dst,
+ int imageType, boolean skipAlpha
+ ) {
+ int res;
+
+ int srcStride, dstStride;
+ int channels;
+ int offsets[] = null;
+ int channelsOrder[] = null;
+
+ switch (imageType) {
+ case BufferedImage.TYPE_INT_ARGB:
+ case BufferedImage.TYPE_INT_ARGB_PRE:
+ case BufferedImage.TYPE_INT_RGB: {
+ channels = 4;
+ srcStride = src.getWidth()*4;
+ dstStride = dst.getWidth()*4;
+ channelsOrder = new int[] {2, 1, 0, 3};
+ break;
+ }
+
+ case BufferedImage.TYPE_4BYTE_ABGR:
+ case BufferedImage.TYPE_4BYTE_ABGR_PRE:
+ case BufferedImage.TYPE_INT_BGR: {
+ channels = 4;
+ srcStride = src.getWidth()*4;
+ dstStride = dst.getWidth()*4;
+ break;
+ }
+
+ case BufferedImage.TYPE_BYTE_GRAY: {
+ channels = 1;
+ srcStride = src.getWidth();
+ dstStride = dst.getWidth();
+ break;
+ }
+
+ case BufferedImage.TYPE_3BYTE_BGR: {
+ channels = 3;
+ srcStride = src.getWidth()*3;
+ dstStride = dst.getWidth()*3;
+ channelsOrder = new int[] {2, 1, 0};
+ break;
+ }
+
+ case BufferedImage.TYPE_USHORT_GRAY:
+ case BufferedImage.TYPE_USHORT_565_RGB:
+ case BufferedImage.TYPE_USHORT_555_RGB:
+ case BufferedImage.TYPE_BYTE_BINARY: {
+ return slowFilter(src, dst, skipAlpha);
+ }
+
+ default: {
+ SampleModel srcSM = src.getSampleModel();
+ SampleModel dstSM = dst.getSampleModel();
+
+ if (
+ srcSM instanceof PixelInterleavedSampleModel &&
+ dstSM instanceof PixelInterleavedSampleModel
+ ) {
+ // Check PixelInterleavedSampleModel
+ if (
+ srcSM.getDataType() != DataBuffer.TYPE_BYTE ||
+ dstSM.getDataType() != DataBuffer.TYPE_BYTE
+ ) {
+ return slowFilter(src, dst, skipAlpha);
+ }
+
+ channels = srcSM.getNumBands(); // Have IPP functions for 1, 3 and 4 channels
+ if (!(channels == 1 || channels == 3 || channels == 4)) {
+ return slowFilter(src, dst, skipAlpha);
+ }
+
+ srcStride = ((ComponentSampleModel) srcSM).getScanlineStride();
+ dstStride = ((ComponentSampleModel) dstSM).getScanlineStride();
+
+ channelsOrder = ((ComponentSampleModel) srcSM).getBandOffsets();
+ } else if (
+ srcSM instanceof SinglePixelPackedSampleModel &&
+ dstSM instanceof SinglePixelPackedSampleModel
+ ) {
+ // Check SinglePixelPackedSampleModel
+ SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel) srcSM;
+ SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel) dstSM;
+
+ channels = sppsm1.getNumBands();
+
+ // TYPE_INT_RGB, TYPE_INT_ARGB...
+ if (
+ sppsm1.getDataType() != DataBuffer.TYPE_INT ||
+ sppsm2.getDataType() != DataBuffer.TYPE_INT ||
+ !(channels == 3 || channels == 4)
+ ) {
+ return slowFilter(src, dst, skipAlpha);
+ }
+
+ // Check compatibility of sample models
+ if (
+ !Arrays.equals(sppsm1.getBitOffsets(), sppsm2.getBitOffsets()) ||
+ !Arrays.equals(sppsm1.getBitMasks(), sppsm2.getBitMasks())
+ ) {
+ return slowFilter(src, dst, skipAlpha);
+ }
+
+ for (int i=0; i<channels; i++) {
+ if (sppsm1.getSampleSize(i) != 8) {
+ return slowFilter(src, dst, skipAlpha);
+ }
+ }
+
+ channelsOrder = new int[channels];
+ int bitOffsets[] = sppsm1.getBitOffsets();
+ for (int i=0; i<channels; i++) {
+ channelsOrder[i] = bitOffsets[i] / 8;
+ }
+
+ if (channels == 3) { // Don't skip channel now, could be optimized
+ channels = 4;
+ }
+
+ srcStride = sppsm1.getScanlineStride() * 4;
+ dstStride = sppsm2.getScanlineStride() * 4;
+ } else {
+ return slowFilter(src, dst, skipAlpha);
+ }
+
+ // Fill offsets if there's a child raster
+ if (src.getParent() != null || dst.getParent() != null) {
+ if (
+ src.getSampleModelTranslateX() != 0 ||
+ src.getSampleModelTranslateY() != 0 ||
+ dst.getSampleModelTranslateX() != 0 ||
+ dst.getSampleModelTranslateY() != 0
+ ) {
+ offsets = new int[4];
+ offsets[0] = -src.getSampleModelTranslateX() + src.getMinX();
+ offsets[1] = -src.getSampleModelTranslateY() + src.getMinY();
+ offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX();
+ offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY();
+ }
+ }
+ }
+ }
+
+ int levels[] = new int[4*channels];
+ int values[] = new int[4*channels];
+
+ createLevels(src.getSampleModel(), channels, skipAlpha, levels, values, channelsOrder);
+
+ Object srcData, dstData;
+ AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance();
+ try {
+ srcData = dbAccess.getData(src.getDataBuffer());
+ dstData = dbAccess.getData(dst.getDataBuffer());
+ } catch (IllegalArgumentException e) {
+ return -1; // Unknown data buffer type
+ }
+
+ res = LookupOp.ippLUT(
+ srcData, src.getWidth(), src.getHeight(), srcStride,
+ dstData, dst.getWidth(), dst.getHeight(), dstStride,
+ levels, values,
+ channels, offsets,
+ true
+ );
+
+ return res;
+ }
+}