From: <ls...@us...> - 2007-01-27 09:39:36
|
Revision: 3088 http://jnode.svn.sourceforge.net/jnode/?rev=3088&view=rev Author: lsantha Date: 2007-01-27 01:39:31 -0800 (Sat, 27 Jan 2007) Log Message: ----------- Classpath patches. Modified Paths: -------------- trunk/core/src/classpath/gnu/gnu/java/awt/peer/GLightweightPeer.java trunk/core/src/classpath/java/java/awt/CheckboxMenuItem.java trunk/core/src/classpath/java/java/awt/MenuItem.java trunk/core/src/classpath/java/java/awt/image/AffineTransformOp.java trunk/core/src/classpath/java/java/awt/image/BandCombineOp.java trunk/core/src/classpath/java/java/awt/image/ColorConvertOp.java trunk/core/src/classpath/java/java/awt/image/ColorModel.java trunk/core/src/classpath/java/java/awt/image/ConvolveOp.java trunk/core/src/classpath/java/java/awt/image/ImageConsumer.java trunk/core/src/classpath/java/java/awt/image/ImageFilter.java trunk/core/src/classpath/java/java/awt/image/LookupOp.java trunk/core/src/classpath/java/java/awt/image/PixelGrabber.java trunk/core/src/classpath/java/java/awt/image/RGBImageFilter.java trunk/core/src/classpath/java/java/awt/image/Raster.java trunk/core/src/classpath/java/java/awt/image/RenderedImage.java trunk/core/src/classpath/java/java/awt/image/ReplicateScaleFilter.java trunk/core/src/classpath/java/java/awt/image/RescaleOp.java trunk/core/src/classpath/java/java/awt/image/SampleModel.java trunk/core/src/classpath/java/java/awt/image/SinglePixelPackedSampleModel.java trunk/core/src/classpath/java/java/awt/image/WritableRaster.java trunk/core/src/classpath/java/java/text/DateFormat.java trunk/core/src/classpath/java/java/text/DateFormatSymbols.java trunk/core/src/classpath/java/java/text/DecimalFormatSymbols.java trunk/core/src/classpath/java/java/text/NumberFormat.java trunk/core/src/classpath/java/java/util/Arrays.java trunk/core/src/classpath/javax/javax/swing/ToolTipManager.java trunk/core/src/classpath/javax/javax/swing/TransferHandler.java trunk/core/src/classpath/tools/gnu/classpath/tools/jarsigner/JarVerifier.java trunk/core/src/classpath/tools/gnu/classpath/tools/jarsigner/Main.java trunk/core/src/classpath/tools/gnu/classpath/tools/jarsigner/Messages.java trunk/core/src/classpath/tools/gnu/classpath/tools/jarsigner/SFHelper.java trunk/core/src/classpath/tools/gnu/classpath/tools/native2ascii/Native2ASCII.java Added Paths: ----------- trunk/core/src/classpath/gnu/gnu/java/awt/ComponentReshapeEvent.java trunk/core/src/classpath/gnu/gnu/java/awt/dnd/ trunk/core/src/classpath/gnu/gnu/java/awt/dnd/GtkMouseDragGestureRecognizer.java trunk/core/src/classpath/java/java/text/spi/DateFormatProvider.java trunk/core/src/classpath/java/java/text/spi/DecimalFormatSymbolsProvider.java trunk/core/src/classpath/java/java/text/spi/NumberFormatProvider.java trunk/core/src/classpath/javax/javax/management/AttributeChangeNotification.java Added: trunk/core/src/classpath/gnu/gnu/java/awt/ComponentReshapeEvent.java =================================================================== --- trunk/core/src/classpath/gnu/gnu/java/awt/ComponentReshapeEvent.java (rev 0) +++ trunk/core/src/classpath/gnu/gnu/java/awt/ComponentReshapeEvent.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -0,0 +1,85 @@ +/* WindowResizeEvent.java -- Used to synchronize the AWT and peer sizes + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt; + +import java.awt.AWTEvent; +import java.awt.Component; + +/** + * This is used to update the AWT's knowledge about a Window's size when + * the user changes the window bounds. + * + * This event is _not_ posted to the eventqueue, but rather dispatched directly + * via Window.dispatchEvent(). It is the cleanest way we could find to update + * the AWT's knowledge of the window size. Small testprograms showed the + * following: + * - Component.reshape() and its derivatives are _not_ called. This makes sense + * as it could end up in loops,because this calls back into the peers. + * - Intercepting event dispatching for any events in + * EventQueue.dispatchEvent() showed that the size is still updated. So it + * is not done via an event dispatched over the eventqueue. + * + * Possible other candidates for implementation would have been: + * - Call a (private) callback method in Window/Component from the native + * side. + * - Call a (private) callback method in Window/Component via reflection. + * + * Both is uglier than sending this event directly. Note however that this + * is impossible to test, as Component.dispatchEvent() is final and can't be + * intercepted from outside code. But this impossibility to test the issue from + * outside code also means that this shouldn't raise any compatibility issues. + */ +public class ComponentReshapeEvent + extends AWTEvent +{ + + public int x; + public int y; + public int width; + public int height; + + public ComponentReshapeEvent(Component c, int x, int y, int width, int height) + { + super(c, 1999); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} Added: trunk/core/src/classpath/gnu/gnu/java/awt/dnd/GtkMouseDragGestureRecognizer.java =================================================================== --- trunk/core/src/classpath/gnu/gnu/java/awt/dnd/GtkMouseDragGestureRecognizer.java (rev 0) +++ trunk/core/src/classpath/gnu/gnu/java/awt/dnd/GtkMouseDragGestureRecognizer.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -0,0 +1,172 @@ +/* GtkMouseDragGestureRecognizer.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.dnd; + +import java.awt.Component; +import java.awt.Point; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.awt.event.MouseEvent; + +public class GtkMouseDragGestureRecognizer + extends MouseDragGestureRecognizer +{ + + public GtkMouseDragGestureRecognizer (DragSource ds) + { + this(ds, null, 0, null); + } + + public GtkMouseDragGestureRecognizer (DragSource ds, Component c) + { + this (ds, c, 0, null); + } + + public GtkMouseDragGestureRecognizer (DragSource ds, Component c, int act) + { + this(ds, c, act, null); + } + + public GtkMouseDragGestureRecognizer (DragSource ds, Component c, int act, + DragGestureListener dgl) + { + super(ds, c, act, dgl); + } + + public void registerListeners () + { + super.registerListeners(); + } + + public void unregisterListeners () + { + super.unregisterListeners(); + } + + public void mouseClicked (MouseEvent e) + { + // Nothing to do here. + } + + public void mousePressed (MouseEvent e) + { + events.clear(); + if (getDropActionFromEvent(e) != DnDConstants.ACTION_NONE) + appendEvent(e); + } + + public void mouseReleased (MouseEvent e) + { + events.clear(); + } + + public void mouseEntered (MouseEvent e) + { + events.clear(); + } + + public void mouseExited(MouseEvent e) + { + if (!events.isEmpty()) + if (getDropActionFromEvent(e) == DnDConstants.ACTION_NONE) + events.clear(); + } + + public void mouseDragged(MouseEvent e) + { + if (!events.isEmpty()) + { + int act = getDropActionFromEvent(e); + + if (act == DnDConstants.ACTION_NONE) + return; + + Point origin = ((MouseEvent) events.get(0)).getPoint(); + Point current = e.getPoint(); + int dx = Math.abs(origin.x - current.x); + int dy = Math.abs(origin.y - current.y); + int threshold = DragSource.getDragThreshold(); + + if (dx > threshold || dy > threshold) + fireDragGestureRecognized(act, origin); + else + appendEvent(e); + } + } + + public void mouseMoved (MouseEvent e) + { + // Nothing to do here. + } + + private int getDropActionFromEvent(MouseEvent e) + { + int modEx = e.getModifiersEx(); + int buttons = modEx & (MouseEvent.BUTTON1_DOWN_MASK + | MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK); + if (!(buttons == MouseEvent.BUTTON1_DOWN_MASK || + buttons == MouseEvent.BUTTON2_DOWN_MASK)) + return DnDConstants.ACTION_NONE; + + // Convert modifier to a drop action + int sourceActions = getSourceActions(); + int mod = modEx + & (MouseEvent.SHIFT_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK); + switch (mod) + { + case MouseEvent.SHIFT_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK: + return DnDConstants.ACTION_LINK & sourceActions; + case MouseEvent.CTRL_DOWN_MASK: + return DnDConstants.ACTION_COPY & sourceActions; + case MouseEvent.SHIFT_DOWN_MASK: + return DnDConstants.ACTION_MOVE & sourceActions; + default: + if ((sourceActions & DnDConstants.ACTION_MOVE) != 0) + return DnDConstants.ACTION_MOVE & sourceActions; + else if ((sourceActions & DnDConstants.ACTION_COPY) != 0) + return DnDConstants.ACTION_COPY & sourceActions; + else if ((sourceActions & DnDConstants.ACTION_LINK) != 0) + return DnDConstants.ACTION_LINK & sourceActions; + } + + return DnDConstants.ACTION_NONE & sourceActions; + } +} Modified: trunk/core/src/classpath/gnu/gnu/java/awt/peer/GLightweightPeer.java =================================================================== --- trunk/core/src/classpath/gnu/gnu/java/awt/peer/GLightweightPeer.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/gnu/gnu/java/awt/peer/GLightweightPeer.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -163,8 +163,10 @@ public FontMetrics getFontMetrics(Font f) { - // Nothing to do here for lightweights. - return null; + // We shouldn't end up here, but if we do we can still try do something + // reasonable. + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.getFontMetrics(f); } /* Returning null here tells the Component object that called us to @@ -201,7 +203,31 @@ public void handleEvent(AWTEvent e) { - // Nothing to do here for lightweights. + // This can only happen when an application posts a PaintEvent for + // a lightweight component directly. We still support painting for + // this case. + if (e instanceof PaintEvent) + { + PaintEvent pe = (PaintEvent) e; + Component target = (Component) e.getSource(); + if (target != null && target.isShowing()) + { + Graphics g = target.getGraphics(); + if (g != null) + { + try + { + Rectangle clip = pe.getUpdateRect(); + g.setClip(clip); + target.paint(g); + } + finally + { + g.dispose(); + } + } + } + } } public void hide() Modified: trunk/core/src/classpath/java/java/awt/CheckboxMenuItem.java =================================================================== --- trunk/core/src/classpath/java/java/awt/CheckboxMenuItem.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/CheckboxMenuItem.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -318,7 +318,7 @@ * @exception ClassCastException If listenerType doesn't specify a class or * interface that implements java.util.EventListener. */ - public EventListener[] getListeners (Class listenerType) + public <T extends EventListener> T[] getListeners (Class<T> listenerType) { if (listenerType == ItemListener.class) return AWTEventMulticaster.getListeners (item_listeners, listenerType); Modified: trunk/core/src/classpath/java/java/awt/MenuItem.java =================================================================== --- trunk/core/src/classpath/java/java/awt/MenuItem.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/MenuItem.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -523,11 +523,11 @@ * ClassClassException is thrown. * @since 1.3 */ - public EventListener[] getListeners(Class listenerType) + public <T extends EventListener> T[] getListeners(Class<T> listenerType) { if (listenerType == ActionListener.class) - return getActionListeners(); - return (EventListener[]) Array.newInstance(listenerType, 0); + return (T[]) getActionListeners(); + return (T[]) Array.newInstance(listenerType, 0); } /*************************************************************************/ Modified: trunk/core/src/classpath/java/java/awt/image/AffineTransformOp.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/AffineTransformOp.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/AffineTransformOp.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -39,6 +39,7 @@ package java.awt.image; import java.awt.Graphics2D; +import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; @@ -48,8 +49,11 @@ import java.util.Arrays; /** - * This class performs affine transformation between two images or - * rasters in 2 dimensions. + * AffineTransformOp performs matrix-based transformations (translations, + * scales, flips, rotations, and shears). + * + * If interpolation is required, nearest neighbour, bilinear, and bicubic + * methods are available. * * @author Olga Rodimina (rod...@re...) * @author Francis Kung (fk...@re...) @@ -115,14 +119,14 @@ } /** - * Creates empty BufferedImage with the size equal to that of the - * transformed image and correct number of bands. The newly created + * Creates a new BufferedImage with the size equal to that of the + * transformed image and the correct number of bands. The newly created * image is created with the specified ColorModel. - * If the ColorModel is equal to null, an appropriate ColorModel is used. + * If a ColorModel is not specified, an appropriate ColorModel is used. * - * @param src source image - * @param destCM color model for the destination image - * @return new compatible destination image + * @param src the source image. + * @param destCM color model for the destination image (can be null). + * @return a new compatible destination image. */ public BufferedImage createCompatibleDestImage (BufferedImage src, ColorModel destCM) @@ -145,21 +149,18 @@ } /** - * Creates empty WritableRaster with the size equal to the transformed - * source raster and correct number of bands + * Creates a new WritableRaster with the size equal to the transformed + * source raster and correct number of bands . * - * @param src source raster - * @throws RasterFormatException if resulting width or height of raster is 0 - * @return new compatible raster + * @param src the source raster. + * @throws RasterFormatException if resulting width or height of raster is 0. + * @return a new compatible raster. */ public WritableRaster createCompatibleDestRaster (Raster src) { Rectangle2D rect = getBounds2D(src); - // throw RasterFormatException if resulting width or height of the - // transformed raster is 0 - - if (rect.getWidth () == 0 || rect.getHeight () == 0) + if (rect.getWidth() == 0 || rect.getHeight() == 0) throw new RasterFormatException("width or height is 0"); return src.createCompatibleWritableRaster((int) rect.getWidth(), @@ -175,24 +176,22 @@ * @param dst destination image * @throws IllegalArgumentException if the source and destination image are * the same - * @return transformed source image + * @return transformed source image. */ public final BufferedImage filter (BufferedImage src, BufferedImage dst) { - if (dst == src) - throw new IllegalArgumentException ("src image cannot be the same as " + - "the dst image"); + throw new IllegalArgumentException("src image cannot be the same as " + + "the dst image"); // If the destination image is null, then use a compatible BufferedImage if (dst == null) dst = createCompatibleDestImage(src, null); - Graphics2D gr = (Graphics2D) dst.createGraphics (); - gr.setRenderingHints (hints); - gr.drawImage (src, transform, null); + Graphics2D gr = (Graphics2D) dst.createGraphics(); + gr.setRenderingHints(hints); + gr.drawImage(src, transform, null); return dst; - } /** @@ -204,10 +203,11 @@ * @param dst destination raster * @throws IllegalArgumentException if the source and destination are not * compatible - * @return transformed raster + * @return transformed raster. */ - public final WritableRaster filter (Raster src, WritableRaster dst) + public final WritableRaster filter(Raster src, WritableRaster dst) { + // Initial checks if (dst == src) throw new IllegalArgumentException("src image cannot be the same as" + " the dst image"); @@ -219,6 +219,24 @@ throw new IllegalArgumentException("src and dst must have same number" + " of bands"); + // Optimization for rasters that can be represented in the RGB colormodel: + // wrap the rasters in images, and let Cairo do the transformation + if (ColorModel.getRGBdefault().isCompatibleSampleModel(src.getSampleModel()) + && ColorModel.getRGBdefault().isCompatibleSampleModel(dst.getSampleModel())) + { + WritableRaster src2 = Raster.createWritableRaster(src.getSampleModel(), + src.getDataBuffer(), + new Point(src.getMinX(), + src.getMinY())); + BufferedImage iSrc = new BufferedImage(ColorModel.getRGBdefault(), + src2, false, null); + BufferedImage iDst = new BufferedImage(ColorModel.getRGBdefault(), dst, + false, null); + + return filter(iSrc, iDst).getRaster(); + } + + // Otherwise, we need to do the transformation in java code... // Create arrays to hold all the points double[] dstPts = new double[dst.getHeight() * dst.getWidth() * 2]; double[] srcPts = new double[dst.getHeight() * dst.getWidth() * 2]; @@ -287,7 +305,7 @@ } /** - * Returns interpolation type used during transformations + * Returns interpolation type used during transformations. * * @return interpolation type */ @@ -319,7 +337,7 @@ /** * Returns rendering hints that are used during transformation. * - * @return rendering hints + * @return the rendering hints used in this Op. */ public final RenderingHints getRenderingHints () { @@ -330,7 +348,7 @@ * Returns transform used in transformation between source and destination * image. * - * @return transform + * @return the transform used in this Op. */ public final AffineTransform getTransform () { @@ -377,6 +395,18 @@ { Rectangle srcbounds = src.getBounds(); + Object xyarr = null; + Object xp1arr = null; + Object yp1arr = null; + Object xyp1arr = null; + + double xy; + double xp1; + double yp1; + double xyp1; + + double[] result = new double[src.getNumBands()]; + // For all points in the destination raster, use bilinear interpolation // to find the value from the corrosponding source points for (int i = 0; i < dpts.length; i += 2) @@ -401,21 +431,64 @@ double xdiff = pts[i] + src.getMinX() - x; double ydiff = pts[i + 1] + src.getMinY() - y; - // Run the interpolation for each band + // Get surrounding pixels used in interpolation... optimized + // to use the smallest datatype possible. + if (src.getTransferType() == DataBuffer.TYPE_DOUBLE + || src.getTransferType() == DataBuffer.TYPE_FLOAT) + { + xyarr = src.getPixel(x, y, (double[])xyarr); + xp1arr = src.getPixel(x+1, y, (double[])xp1arr); + yp1arr = src.getPixel(x, y+1, (double[])yp1arr); + xyp1arr = src.getPixel(x+1, y+1, (double[])xyp1arr); + } + else + { + xyarr = src.getPixel(x, y, (int[])xyarr); + xp1arr = src.getPixel(x+1, y, (int[])xp1arr); + yp1arr = src.getPixel(x, y+1, (int[])yp1arr); + xyp1arr = src.getPixel(x+1, y+1, (int[])xyp1arr); + } + // using + // array[] pixels = src.getPixels(x, y, 2, 2, pixels); + // instead of doing four individual src.getPixel() calls + // should be faster, but benchmarking shows that it's not... + + // Run interpolation for each band for (int j = 0; j < src.getNumBands(); j++) { - double result = (src.getSampleDouble(x, y, j) * (1 - xdiff) - + src.getSampleDouble(x + 1, y, j) * xdiff) - * (1 - ydiff) - + (src.getSampleDouble(x, y + 1, j) - * (1 - xdiff) - + src.getSampleDouble(x + 1, y + 1, j) - * xdiff) + // Pull individual sample values out of array + if (src.getTransferType() == DataBuffer.TYPE_DOUBLE + || src.getTransferType() == DataBuffer.TYPE_FLOAT) + { + xy = ((double[])xyarr)[j]; + xp1 = ((double[])xp1arr)[j]; + yp1 = ((double[])yp1arr)[j]; + xyp1 = ((double[])xyp1arr)[j]; + } + else + { + xy = ((int[])xyarr)[j]; + xp1 = ((int[])xp1arr)[j]; + yp1 = ((int[])yp1arr)[j]; + xyp1 = ((int[])xyp1arr)[j]; + } + + // If all four samples are identical, there's no need to + // calculate anything + if (xy == xp1 && xy == yp1 && xy == xyp1) + result[j] = xy; + + // Run bilinear interpolation formula + else + result[j] = (xy * (1-xdiff) + xp1 * xdiff) + * (1-ydiff) + + (yp1 * (1-xdiff) + xyp1 * xdiff) * ydiff; - dst.setSample((int) dpts[i] + dst.getMinX(), - (int) dpts[i + 1] + dst.getMinY(), - j, result); } + + dst.setPixel((int)dpts[i] + dst.getMinX(), + (int)dpts[i+1] + dst.getMinY(), + result); } } } @@ -434,10 +507,11 @@ double[] pts) { Rectangle srcbounds = src.getBounds(); + double[] result = new double[src.getNumBands()]; + Object pixels = null; // For all points on the destination raster, perform bicubic interpolation // from corrosponding source points - double[] result = new double[src.getNumBands()]; for (int i = 0; i < dpts.length; i += 2) { if (srcbounds.contains((int) Math.round(pts[i]) + src.getMinX(), @@ -450,7 +524,6 @@ Arrays.fill(result, 0); for (int m = - 1; m < 3; m++) - { for (int n = - 1; n < 3; n++) { // R(x) = ( P(x+2)^3 - 4 P(x+1)^3 + 6 P(x)^3 - 4 P(x-1)^3 ) / 6 @@ -459,7 +532,6 @@ // Calculate R(m - dx) double rx = m - dx + 2; - if (rx > 0) r1 += rx * rx * rx; rx = m - dx + 1; @@ -509,17 +581,27 @@ else if (srcY < src.getMinY()) srcY = src.getMinY(); - // Calculate once for each band + // Calculate once for each band, using the smallest + // datatype possible + if (src.getTransferType() == DataBuffer.TYPE_DOUBLE + || src.getTransferType() == DataBuffer.TYPE_FLOAT) + { + pixels = src.getPixel(srcX, srcY, (double[])pixels); for (int j = 0; j < result.length; j++) - result[j] += src.getSample(srcX, srcY, j) * r1 * r2; + result[j] += ((double[])pixels)[j] * r1 * r2; + } + else + { + pixels = src.getPixel(srcX, srcY, (int[])pixels); + for (int j = 0; j < result.length; j++) + result[j] += ((int[])pixels)[j] * r1 * r2; } } // Put it all together - for (int j = 0; j < result.length; j++) - dst.setSample((int) dpts[i] + dst.getMinX(), - (int) dpts[i + 1] + dst.getMinY(), - j, result[j]); + dst.setPixel((int)dpts[i] + dst.getMinX(), + (int)dpts[i+1] + dst.getMinY(), + result); } } } Modified: trunk/core/src/classpath/java/java/awt/image/BandCombineOp.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/BandCombineOp.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/BandCombineOp.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -40,6 +40,7 @@ import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.util.Arrays; /** * Filter Raster pixels by applying a matrix. @@ -53,6 +54,9 @@ * for the destination. Therefore the destination Raster must contain the * same number of bands as the number of rows in the filter matrix. * + * This Op assumes that samples are integers; floating point sample types will + * be rounded to their nearest integer value during filtering. + * * @author Jerry Quinn (jl...@op...) */ public class BandCombineOp implements RasterOp @@ -109,19 +113,27 @@ throw new IllegalArgumentException("Destination raster is incompatible with source raster"); // Filter the pixels - float[] spix = new float[matrix[0].length - 1]; - float[] dpix = new float[matrix.length]; + int[] spix = new int[matrix[0].length - 1]; + int[] spix2 = new int[matrix[0].length - 1]; + int[] dpix = new int[matrix.length]; for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) { // In case matrix rows have implicit translation - spix[spix.length - 1] = 1.0f; + spix[spix.length - 1] = 1; src.getPixel(x, y, spix); + + // Do not re-calculate if pixel is identical to the last one + // (ie, blocks of the same colour) + if (!Arrays.equals(spix, spix2)) + { + System.arraycopy(spix, 0, spix2, 0, spix.length); for (int i = 0; i < matrix.length; i++) { dpix[i] = 0; for (int j = 0; j < matrix[0].length - 1; j++) - dpix[i] += spix[j] * matrix[i][j]; + dpix[i] += spix[j] * (int)matrix[i][j]; + } } dest.setPixel(x, y, dpix); } Modified: trunk/core/src/classpath/java/java/awt/image/ColorConvertOp.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/ColorConvertOp.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/ColorConvertOp.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -38,6 +38,8 @@ package java.awt.image; +import gnu.java.awt.Buffers; + import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; @@ -283,7 +285,8 @@ for (int i = 0; i < spaces.length - 2; i++) { WritableRaster tmp = createCompatibleDestRaster(src, spaces[i + 1], - false); + false, + src.getTransferType()); copyraster(src, spaces[i], tmp, spaces[i + 1]); src = tmp; } @@ -291,7 +294,8 @@ // The last conversion is done outside of the loop so that we can // use the dest raster supplied, instead of creating our own temp raster if (dest == null) - dest = createCompatibleDestRaster(src, spaces[spaces.length - 1], false); + dest = createCompatibleDestRaster(src, spaces[spaces.length - 1], false, + DataBuffer.TYPE_BYTE); copyraster(src, spaces[spaces.length - 2], dest, spaces[spaces.length - 1]); return dest; @@ -324,7 +328,8 @@ return new BufferedImage(dstCM, createCompatibleDestRaster(src.getRaster(), dstCM.getColorSpace(), - src.getColorModel().hasAlpha), + src.getColorModel().hasAlpha, + dstCM.getTransferType()), src.isPremultiplied, null); } @@ -349,7 +354,8 @@ // Create a new raster with the last ColorSpace in the conversion // chain, and with no alpha (implied) - return createCompatibleDestRaster(src, spaces[spaces.length-1], false); + return createCompatibleDestRaster(src, spaces[spaces.length-1], false, + DataBuffer.TYPE_BYTE); } /** @@ -417,11 +423,16 @@ return src.getBounds(); } - // Copy a source image to a destination image, respecting their colorspaces - // and performing colorspace conversions if necessary. This is done - // using Graphics2D in order to use the rendering hints. + /** + * Copy a source image to a destination image, respecting their colorspaces + * and performing colorspace conversions if necessary. + * + * @param src The source image. + * @param dst The destination image. + */ private void copyimage(BufferedImage src, BufferedImage dst) { + // This is done using Graphics2D in order to respect the rendering hints. Graphics2D gg = dst.createGraphics(); // If no hints are set there is no need to call @@ -433,8 +444,16 @@ gg.dispose(); } - // Copy a source raster to a destination raster, performing a colorspace - // conversion. + /** + * Copy a source raster to a destination raster, performing a colorspace + * conversion between the two. The conversion will respect the + * KEY_COLOR_RENDERING rendering hint if one is present. + * + * @param src The source raster. + * @param scs The colorspace of the source raster. + * @dst The destination raster. + * @dcs The colorspace of the destination raster. + */ private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, ColorSpace dcs) { float[] sbuf = new float[src.getNumBands()]; @@ -459,11 +478,19 @@ } } - // This method creates a compatible color model, given a source image and - // a colorspace. The choice of ComponentColorModel and DataBuffer.TYPE_BYTE - // is based on Mauve testing of the reference implementation. + /** + * This method creates a color model with the same colorspace and alpha + * settings as the source image. The created color model will always be a + * ComponentColorModel and have a BYTE transfer type. + * + * @param img The source image. + * @param cs The ColorSpace to use. + * @return A color model compatible with the source image. + */ private ColorModel createCompatibleColorModel(BufferedImage img, ColorSpace cs) { + // The choice of ComponentColorModel and DataBuffer.TYPE_BYTE is based on + // Mauve testing of the reference implementation. return new ComponentColorModel(cs, img.getColorModel().hasAlpha(), img.isAlphaPremultiplied(), @@ -471,13 +498,22 @@ DataBuffer.TYPE_BYTE); } - // This method creates a compatible Raster, given a source raster and - // colorspace. - private WritableRaster createCompatibleDestRaster(Raster src, ColorSpace cs, boolean hasAlpha) + /** + * This method creates a compatible Raster, given a source raster, colorspace, + * alpha value, and transfer type. + * + * @param src The source raster. + * @param cs The ColorSpace to use. + * @param hasAlpha Whether the raster should include a component for an alpha. + * @param transferType The size of a single data element. + * @return A compatible WritableRaster. + */ + private WritableRaster createCompatibleDestRaster(Raster src, ColorSpace cs, + boolean hasAlpha, + int transferType) { - // The use of a PixelInterleavedSampleModel (and it's parameters) and - // a DataBufferByte were determined using mauve tests, based on the - // reference implementation + // The use of a PixelInterleavedSampleModel weas determined using mauve + // tests, based on the reference implementation int numComponents = cs.getNumComponents(); if (hasAlpha) @@ -487,13 +523,15 @@ for (int i = 0; i < offsets.length; i++) offsets[i] = i; - return new WritableRaster(new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, + DataBuffer db = Buffers.createBuffer(transferType, + src.getWidth() * src.getHeight() * numComponents, + 1); + return new WritableRaster(new PixelInterleavedSampleModel(transferType, src.getWidth(), src.getHeight(), numComponents, numComponents * src.getWidth(), offsets), - new DataBufferByte(src.getWidth() * src.getHeight() * numComponents, 1), - new Point(src.getMinX(), src.getMinY())); + db, new Point(src.getMinX(), src.getMinY())); } } Modified: trunk/core/src/classpath/java/java/awt/image/ColorModel.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/ColorModel.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/ColorModel.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -119,12 +119,8 @@ */ public ColorModel(int bits) { - // @classpath-bugfix Fix difference between Sun's class and this class. - //this(bits * 4, // total bits, sRGB, four channels - this(bits, // total bits, sRGB, four channels - //nArray(bits, 4), // bits for each channel - nArray(bits / 4, 4), // bits for each channel - // @classpath-bugfix-end + this(bits * 4, // total bits, sRGB, four channels + nArray(bits, 4), // bits for each channel ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB true, // has alpha false, // not premultiplied @@ -628,40 +624,40 @@ return cspace; } - // Typically overridden public ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) { - if (this.isAlphaPremultiplied == isAlphaPremultiplied || ! hasAlpha) - return this; + // This method should always be overridden, but is not abstract. + throw new UnsupportedOperationException(); + } + protected void coerceDataWorker(WritableRaster raster, + boolean isAlphaPremultiplied) + { int w = raster.getWidth(); int h = raster.getHeight(); int x = raster.getMinX(); int y = raster.getMinY(); - int size = w*h; + int size = w * h; int numColors = getNumColorComponents(); int numComponents = getNumComponents(); - int alphaScale = (1<<getComponentSize(numColors)) - 1; + int alphaScale = (1 << getComponentSize(numColors)) - 1; double[] pixels = raster.getPixels(x, y, w, h, (double[]) null); - for (int i=0; i<size; i++) + for (int i = 0; i < size; i++) { - double alpha = pixels[i*numComponents+numColors]*alphaScale; - for (int c=0; c<numColors; c++) + double alpha = pixels[i * numComponents + numColors] / alphaScale; + for (int c = 0; c < numColors; c++) { - int offset = i*numComponents+c; + int offset = i * numComponents + c; if (isAlphaPremultiplied) - pixels[offset] = pixels[offset]/alpha; + pixels[offset] = Math.round(pixels[offset] * alpha); else - pixels[offset] = pixels[offset]*alpha; + pixels[offset] = Math.round(pixels[offset] / alpha); } } raster.setPixels(0, 0, w, h, pixels); - - // FIXME: what can we return? - return null; } /** Modified: trunk/core/src/classpath/java/java/awt/image/ConvolveOp.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/ConvolveOp.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/ConvolveOp.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -38,7 +38,6 @@ package java.awt.image; -import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -51,11 +50,13 @@ * with elements in the kernel to compute a new pixel. * * Each band in a Raster is convolved and copied to the destination Raster. + * For BufferedImages, convolution is applied to all components. Color + * conversion will be applied if needed. * - * For BufferedImages, convolution is applied to all components. If the - * source is not premultiplied, the data will be premultiplied before - * convolving. Premultiplication will be undone if the destination is not - * premultiplied. Color conversion will be applied if needed. + * Note that this filter ignores whether the source or destination is alpha + * premultiplied. The reference spec states that data will be premultiplied + * prior to convolving and divided back out afterwards (if needed), but testing + * has shown that this is not the case with their implementation. * * @author jl...@op... */ @@ -104,59 +105,83 @@ hints = null; } - - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, - * java.awt.image.BufferedImage) + /** + * Converts the source image using the kernel specified in the + * constructor. The resulting image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. + * + * The source and destination BufferedImage (if one is supplied) must have + * the same dimensions. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The convolved image. */ public final BufferedImage filter(BufferedImage src, BufferedImage dst) { if (src == dst) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Source and destination images " + + "cannot be the same."); if (dst == null) dst = createCompatibleDestImage(src, src.getColorModel()); // Make sure source image is premultiplied BufferedImage src1 = src; - if (!src.isPremultiplied) + // The spec says we should do this, but mauve testing shows that Sun's + // implementation does not check this. + /* + if (!src.isAlphaPremultiplied()) { src1 = createCompatibleDestImage(src, src.getColorModel()); src.copyData(src1.getRaster()); src1.coerceData(true); } + */ BufferedImage dst1 = dst; - if (!src.getColorModel().equals(dst.getColorModel())) + if (src1.getColorModel().getColorSpace().getType() != dst.getColorModel().getColorSpace().getType()) dst1 = createCompatibleDestImage(src, src.getColorModel()); filter(src1.getRaster(), dst1.getRaster()); + // Since we don't coerceData above, we don't need to divide it back out. + // This is wrong (one mauve test specifically tests converting a non- + // premultiplied image to a premultiplied image, and it shows that Sun + // simply ignores the premultipled flag, contrary to the spec), but we + // mimic it for compatibility. + /* + if (! dst.isAlphaPremultiplied()) + dst1.coerceData(false); + */ + + // Convert between color models if needed if (dst1 != dst) - { - // Convert between color models. - // TODO Check that premultiplied alpha is handled correctly here. - Graphics2D gg = dst.createGraphics(); - gg.setRenderingHints(hints); - gg.drawImage(dst1, 0, 0, null); - gg.dispose(); - } + new ColorConvertOp(hints).filter(dst1, dst); return dst; } - /* (non-Javadoc) - * @see - * java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, - * java.awt.image.ColorModel) + /** + * Creates an empty BufferedImage with the size equal to the source and the + * correct number of bands. The new image is created with the specified + * ColorModel, or if no ColorModel is supplied, an appropriate one is chosen. + * + * @param src The source image. + * @param dstCM A color model for the destination image (may be null). + * @return The new compatible destination image. */ public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { - // FIXME: set properties to those in src + if (dstCM != null) return new BufferedImage(dstCM, src.getRaster().createCompatibleWritableRaster(), - src.isPremultiplied, null); + src.isAlphaPremultiplied(), null); + + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); } /* (non-Javadoc) @@ -168,6 +193,8 @@ } /** + * Get the edge condition for this Op. + * * @return The edge condition. */ public int getEdgeCondition() @@ -185,9 +212,22 @@ return (Kernel) kernel.clone(); } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, - * java.awt.image.WritableRaster) + /** + * Converts the source raster using the kernel specified in the constructor. + * The resulting raster is stored in the destination raster if one is + * provided; otherwise a new WritableRaster is created and returned. + * + * If the convolved value for a sample is outside the range of [0-255], it + * will be clipped. + * + * The source and destination raster (if one is supplied) cannot be the same, + * and must also have the same dimensions. + * + * @param src The source raster. + * @param dest The destination raster. + * @throws IllegalArgumentException if the rasters identical. + * @throws ImagingOpException if the convolution is not possible. + * @return The transformed raster. */ public final WritableRaster filter(Raster src, WritableRaster dest) { @@ -209,6 +249,11 @@ int top = kernel.getYOrigin(); int bottom = Math.max(kHeight - top - 1, 0); + // Calculate max sample values for clipping + int[] maxValue = src.getSampleModel().getSampleSize(); + for (int i = 0; i < maxValue.length; i++) + maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1; + // process the region that is reachable... int regionW = src.width - left - right; int regionH = src.height - top - bottom; @@ -229,6 +274,13 @@ // FIXME: in the above line, I've had to reverse the order of // the samples array to make the tests pass. I haven't worked // out why this is necessary. + + // This clipping is is undocumented, but determined by testing. + if (v > maxValue[b]) + v = maxValue[b]; + else if (v < 0) + v = 0; + dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(), b, v); } @@ -310,13 +362,14 @@ return src.getBounds(); } - /** Return corresponding destination point for source point. + /** + * Returns the corresponding destination point for a source point. Because + * this is not a geometric operation, the destination and source points will + * be identical. * - * ConvolveOp will return the value of src unchanged. * @param src The source point. - * @param dst The destination point. - * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, - * java.awt.geom.Point2D) + * @param dst The transformed destination point. + * @return The transformed destination point. */ public final Point2D getPoint2D(Point2D src, Point2D dst) { Modified: trunk/core/src/classpath/java/java/awt/image/ImageConsumer.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/ImageConsumer.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/ImageConsumer.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -136,7 +136,7 @@ * * @param props the list of properties associated with this image */ - void setProperties(Hashtable props); + void setProperties(Hashtable<?,?> props); /** * This <code>ColorModel</code> should indicate the model used by Modified: trunk/core/src/classpath/java/java/awt/image/ImageFilter.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/ImageFilter.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/ImageFilter.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -104,12 +104,8 @@ */ public ImageFilter getFilterInstance(ImageConsumer ic) { - if ( ic == null ) - throw new IllegalArgumentException("null argument for ImageFilter.getFilterInstance(ImageConsumer)"); - - consumer = ic; ImageFilter f = (ImageFilter)clone(); - consumer = null; + f.consumer = ic; return f; } @@ -125,7 +121,6 @@ */ public void setDimensions(int width, int height) { - if (consumer != null) consumer.setDimensions(width, height); } @@ -135,11 +130,16 @@ * * @param props the list of properties associated with this image */ - public void setProperties(Hashtable props) + public void setProperties(Hashtable<?,?> props) { - props.put("filters", "ImageFilter"); - if (consumer != null) - consumer.setProperties(props); + Hashtable copy = (Hashtable) props.clone(); + Object o = copy.get("filters"); + if (o == null) + copy.put("filters", toString()); + else if (o instanceof String) + copy.put("filters", ((String) o) + toString()); + + consumer.setProperties(copy); } /** @@ -148,10 +148,11 @@ * method of the consumer is called with the specified <code>model</code>. * * @param model the color model to be used most often by setPixels - * @see ColorModel */ + * + * @see ColorModel + */ public void setColorModel(ColorModel model) { - if (consumer != null) consumer.setColorModel(model); } @@ -167,7 +168,6 @@ */ public void setHints(int flags) { - if (consumer != null) consumer.setHints(flags); } @@ -186,9 +186,9 @@ * @param scansize the width to use in extracting pixels from the <code>pixels</code> array */ public void setPixels(int x, int y, int w, int h, - ColorModel model, byte[] pixels, int offset, int scansize) + ColorModel model, byte[] pixels, int offset, + int scansize) { - if (consumer != null) consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); } @@ -207,9 +207,9 @@ * @param scansize the width to use in extracting pixels from the <code>pixels</code> array */ public void setPixels(int x, int y, int w, int h, - ColorModel model, int[] pixels, int offset, int scansize) + ColorModel model, int[] pixels, int offset, + int scansize) { - if (consumer != null) consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); } @@ -221,8 +221,6 @@ */ public void imageComplete(int status) { - if (consumer != null) consumer.imageComplete(status); } } - Modified: trunk/core/src/classpath/java/java/awt/image/LookupOp.java =================================================================== --- trunk/core/src/classpath/java/java/awt/image/LookupOp.java 2007-01-27 07:54:10 UTC (rev 3087) +++ trunk/core/src/classpath/java/java/awt/image/LookupOp.java 2007-01-27 09:39:31 UTC (rev 3088) @@ -38,7 +38,6 @@ package java.awt.image; -import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -67,7 +66,8 @@ private LookupTable lut; private RenderingHints hints; - /** Construct a new LookupOp. + /** + * Construct a new LookupOp using the given LookupTable. * * @param lookup LookupTable to use. * @param hints Rendering hints (can be null). @@ -78,16 +78,40 @@ this.hints = hints; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, java.awt.image.BufferedImage) + /** + * Converts the source image using the lookup table specified in the + * constructor. The resulting image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. + * + * The source image cannot use an IndexColorModel, and the destination image + * (if one is provided) must have the same size. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @throws ArrayIndexOutOfBoundsException if a pixel in the source is not + * contained in the LookupTable. + * @return The convolved image. */ public final BufferedImage filter(BufferedImage src, BufferedImage dst) { if (src.getColorModel() instanceof IndexColorModel) throw new IllegalArgumentException("LookupOp.filter: IndexColorModel " + "not allowed"); + + if (lut.getNumComponents() != 1 + && lut.getNumComponents() != src.getColorModel().getNumComponents() + && lut.getNumComponents() != src.getColorModel().getNumColorComponents()) + throw new IllegalArgumentException("LookupOp.filter: Incompatible " + + "lookup table and source image"); + if (dst == null) - dst = createCompatibleDestImage(src, src.getColorModel()); + dst = createCompatibleDestImage(src, null); + + else if (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth()) + throw new IllegalArgumentException("Source and destination images are " + + "different sizes."); // Set up for potential colormodel mismatch BufferedImage tgt; @@ -116,15 +140,23 @@ sr.getPixel(x, y, dbuf); System.arraycopy(dbuf, 0, tmp, 0, tmpBands); dr.setPixel(x, y, lut.lookupPixel(tmp, dbuf)); + + /* The reference implementation does not use LookupTable.lookupPixel, + * but rather it seems to copy the table into a native array. The + * effect of this (a probable bug in their implementation) is that + * an out-of-bounds lookup on a ByteLookupTable will *not* throw an + * out of bounds exception, but will instead return random garbage. + * A bad lookup on a ShortLookupTable, however, will throw an + * exception. + * + * Instead of mimicing this behaviour, we always throw an + * ArrayOutofBoundsException by virtue of using + * LookupTable.lookupPixle. + */ } } - else if (lut.getNumComponents() != 1 - && - lut.getNumComponents() != src.getColorModel().getNumComponents()) - throw new IllegalArgumentException("LookupOp.filter: " - + "Incompatible lookup " - + "table and source image"); - + else + { // No alpha to ignore int[] dbuf = new int[src.getColorModel().getNumComponents()]; @@ -132,16 +164,10 @@ for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) dr.setPixel(x, y, lut.lookupPixel(sr.getPixel(x, y, dbuf), dbuf)); + } if (tgt != dst) - { - // Convert between color models. - // TODO Check that premultiplied alpha is handled correctly here. - Graphics2D gg = dst.createGraphics(); - gg.setRenderingHints(hints); - gg.drawImage(tgt, 0, 0, null); - gg.dispose(); - } + new ColorConvertOp(hints).filter(tgt, dst); return dst; } @@ -160,18 +186,27 @@ public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { - // FIXME: set properties to those in src + if (dstCM != null) return new BufferedImage(dstCM, src.getRaster().createCompatibleWritableRaster(), - src.isPremultiplied, null); + src.isAlphaPremultiplied(), null); + + // This is a strange exception, done for compatibility with the reference + // (as demonstrated by a mauve testcase) + int imgType = src.getType(); + if (imgType == BufferedImage.TYPE_USHORT_GRAY) + imgType = BufferedImage.TYPE_BYTE_GRAY; + + return new BufferedImage(src.getWidth(), src.getHeight(), imgType); } - /** Return corresponding destination point for source point. + /** + * Returns the corresponding destination point for a given source point. * - * LookupOp will return the value of src unchanged. + * This Op will return the source point unchanged. + * * @param src The source point. * @param dst The destination point. - * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) */ public final Point2D getPoint2D(Point2D src, Point2D dst) { @@ -182,7 +217,11 @@ return dst; } - /** Return the LookupTable for this op. */ + /** + * Return the LookupTable for this op. + * + * @return The lookup table. + */ public final LookupTable getTable() { return lut; @@ -196,7 +235,8 @@ return hints; } - /** Filter a raster through a lookup table. + /** + * Filter a raster through a lookup table. * * Applies the lookup table for this Rasterop to each pixel of src and * puts the results in dest. If dest is null, a new Raster is created and @@ -207,7 +247,8 @@ * @return The WritableRaster with the filtered pixels. * @throws IllegalArgumentException if lookup ta... [truncated message content] |