diff options
Diffstat (limited to 'gnu')
20 files changed, 1020 insertions, 191 deletions
diff --git a/gnu/java/awt/ComponentReshapeEvent.java b/gnu/java/awt/ComponentReshapeEvent.java new file mode 100644 index 000000000..8f15c8519 --- /dev/null +++ b/gnu/java/awt/ComponentReshapeEvent.java @@ -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; + } +} diff --git a/gnu/java/awt/peer/NativeEventLoopRunningEvent.java b/gnu/java/awt/peer/NativeEventLoopRunningEvent.java new file mode 100644 index 000000000..962ecd990 --- /dev/null +++ b/gnu/java/awt/peer/NativeEventLoopRunningEvent.java @@ -0,0 +1,58 @@ +/* NativeEventLoopRunningEvent.java -- communicates to EventQueue the + state of the native event loop + 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.peer; + +import java.awt.AWTEvent; + +public class NativeEventLoopRunningEvent + extends AWTEvent +{ + private boolean running; + + public NativeEventLoopRunningEvent(Object source) + { + super(source, 2999); + running = ((Boolean) source).booleanValue(); + } + + public boolean isRunning() + { + return running; + } +} diff --git a/gnu/java/awt/peer/gtk/BufferedImageGraphics.java b/gnu/java/awt/peer/gtk/BufferedImageGraphics.java index e188d3fd0..341fa2a4e 100644 --- a/gnu/java/awt/peer/gtk/BufferedImageGraphics.java +++ b/gnu/java/awt/peer/gtk/BufferedImageGraphics.java @@ -38,23 +38,27 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Toolkit; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.awt.image.Raster; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferInt; import java.awt.image.ColorModel; +import java.awt.image.DataBufferInt; import java.awt.image.DirectColorModel; -import java.awt.image.RenderedImage; import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; import java.util.WeakHashMap; /** @@ -68,7 +72,13 @@ public class BufferedImageGraphics extends CairoGraphics2D /** * the buffered Image. */ - private BufferedImage image; + private BufferedImage image, buffer; + + /** + * Allows us to lock the image from updates (if we want to perform a few + * intermediary operations on the cairo surface, then update it all at once) + */ + private boolean locked; /** * Image size. @@ -93,7 +103,7 @@ public class BufferedImageGraphics extends CairoGraphics2D /** * Colormodels we recognize for fast copying. */ - static ColorModel rgb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF); + static ColorModel rgb32 = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF); static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000); private boolean hasFastCM; @@ -105,6 +115,8 @@ public class BufferedImageGraphics extends CairoGraphics2D this.image = bi; imageWidth = bi.getWidth(); imageHeight = bi.getHeight(); + locked = false; + if(bi.getColorModel().equals(rgb32)) { hasFastCM = true; @@ -113,7 +125,7 @@ public class BufferedImageGraphics extends CairoGraphics2D else if(bi.getColorModel().equals(argb32)) { hasFastCM = true; - hasAlpha = false; + hasAlpha = true; } else hasFastCM = false; @@ -163,6 +175,7 @@ public class BufferedImageGraphics extends CairoGraphics2D cairo_t = surface.newCairoContext(); imageWidth = copyFrom.imageWidth; imageHeight = copyFrom.imageHeight; + locked = false; copy( copyFrom, cairo_t ); setClip(0, 0, surface.width, surface.height); } @@ -172,6 +185,9 @@ public class BufferedImageGraphics extends CairoGraphics2D */ private void updateBufferedImage(int x, int y, int width, int height) { + if (locked) + return; + int[] pixels = surface.getPixels(imageWidth * imageHeight); if( x > imageWidth || y > imageHeight ) @@ -184,18 +200,18 @@ public class BufferedImageGraphics extends CairoGraphics2D if( y + height > imageHeight ) height = imageHeight - y; - boolean wasPremultiplied = image.isAlphaPremultiplied(); - image.coerceData(true); - - if( !hasFastCM ) + // The setRGB method assumes (or should assume) that pixels are NOT + // alpha-premultiplied, but Cairo stores data with premultiplication + // (thus the pixels returned in getPixels are premultiplied). + // This is ignored for consistency, however, since in + // CairoGrahpics2D.drawImage we also use non-premultiplied data + if(!hasFastCM) image.setRGB(x, y, width, height, pixels, x + y * imageWidth, imageWidth); else System.arraycopy(pixels, y * imageWidth, ((DataBufferInt)image.getRaster().getDataBuffer()). getData(), y * imageWidth, height * imageWidth); - - image.coerceData(wasPremultiplied); } /** @@ -228,36 +244,214 @@ public class BufferedImageGraphics extends CairoGraphics2D */ public void draw(Shape s) { - super.draw(s); - Rectangle r = s.getBounds(); - updateBufferedImage(r.x, r.y, r.width, r.height); + if (comp == null || comp instanceof AlphaComposite) + { + super.draw(s); + Rectangle r = s.getBounds(); + + if (shiftDrawCalls) + updateBufferedImage(r.x, r.y, r.width+1, r.height+1); + else + updateBufferedImage(r.x, r.y, r.width, r.height); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.draw(s); + + drawComposite(s.getBounds2D(), null); + } } public void fill(Shape s) { - super.fill(s); - Rectangle r = s.getBounds(); - updateBufferedImage(r.x, r.y, r.width, r.height); + if (comp == null || comp instanceof AlphaComposite) + { + super.fill(s); + Rectangle r = s.getBounds(); + updateBufferedImage(r.x, r.y, r.width, r.height); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } } public void drawRenderedImage(RenderedImage image, AffineTransform xform) { - super.drawRenderedImage(image, xform); - updateBufferedImage(0, 0, imageWidth, imageHeight); + if (comp == null || comp instanceof AlphaComposite) + { + super.drawRenderedImage(image, xform); + updateBufferedImage(0, 0, imageWidth, imageHeight); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } + } protected boolean drawImage(Image img, AffineTransform xform, Color bgcolor, ImageObserver obs) { - boolean rv = super.drawImage(img, xform, bgcolor, obs); - updateBufferedImage(0, 0, imageWidth, imageHeight); - return rv; + if (comp == null || comp instanceof AlphaComposite) + { + boolean rv = super.drawImage(img, xform, bgcolor, obs); + updateBufferedImage(0, 0, imageWidth, imageHeight); + return rv; + } + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(), + bImg.getHeight() + bImg.getMinY()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + return drawComposite(new Rectangle2D.Double(origin.getX(), + origin.getY(), + pt.getX(), pt.getY()), + obs); + } } public void drawGlyphVector(GlyphVector gv, float x, float y) { - super.drawGlyphVector(gv, x, y); - updateBufferedImage(0, 0, imageWidth, imageHeight); + if (comp == null || comp instanceof AlphaComposite) + { + super.drawGlyphVector(gv, x, y); + updateBufferedImage(0, 0, imageWidth, imageHeight); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } + } + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Clip source to visible areas that need updating + Rectangle2D clip = this.getClipBounds(); + Rectangle2D.intersect(bounds, clip, bounds); + clip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, clip, bounds); + + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Get destination clip to bounds + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + + Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], + points[2] - points[0], + points[3] - points[1]); + + Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); + + BufferedImage current = image; + current = current.getSubimage((int)deviceBounds.getX(), + (int)deviceBounds.getY(), + (int)deviceBounds.getWidth(), + (int)deviceBounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + current.getRaster()); + + // Prevent the clearRect in CairoGraphics2D.drawImage from clearing + // our composited image + locked = true; + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(current, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + new Color(0,0,0,0), null); + locked = false; + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + buffer = new BufferedImage(image.getWidth(), image.getHeight(), + BufferedImage.TYPE_INT_ARGB); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + return image.getColorModel(); + } + + protected ColorModel getBufferCM() + { + return ColorModel.getRGBdefault(); } } diff --git a/gnu/java/awt/peer/gtk/CairoGraphics2D.java b/gnu/java/awt/peer/gtk/CairoGraphics2D.java index fd69c9b48..4c60336a3 100644 --- a/gnu/java/awt/peer/gtk/CairoGraphics2D.java +++ b/gnu/java/awt/peer/gtk/CairoGraphics2D.java @@ -176,7 +176,7 @@ public abstract class CairoGraphics2D extends Graphics2D * coords be shifted to land on 0.5-pixel boundaries, in order to land on * "middle of pixel" coordinates and light up complete pixels. */ - private boolean shiftDrawCalls = false; + protected boolean shiftDrawCalls = false; /** * Keep track if the first clip to be set, which is restored on setClip(null); @@ -220,7 +220,7 @@ public abstract class CairoGraphics2D extends Graphics2D { nativePointer = init(cairo_t_pointer); setRenderingHints(new RenderingHints(getDefaultHints())); - font = new Font("SansSerif", Font.PLAIN, 12); + setFont(new Font("SansSerif", Font.PLAIN, 12)); setColor(Color.black); setBackground(Color.white); setPaint(Color.black); @@ -236,7 +236,6 @@ public abstract class CairoGraphics2D extends Graphics2D nativePointer = init(cairo_t_pointer); paint = g.paint; stroke = g.stroke; - comp = g.comp; setRenderingHints(g.hints); Color foreground; @@ -263,8 +262,7 @@ public abstract class CairoGraphics2D extends Graphics2D else transform = new AffineTransform(g.transform); - font = g.font; - + setFont(g.font); setColor(foreground); setBackground(bg); setPaint(paint); @@ -386,6 +384,10 @@ public abstract class CairoGraphics2D extends Graphics2D float x, float y, int n, int[] codes, float[] positions); + /** + * Set the font in cairo. + */ + private native void cairoSetFont(long pointer, GdkFontPeer font); private native void cairoRelCurveTo(long pointer, double dx1, double dy1, double dx2, double dy2, double dx3, @@ -965,27 +967,30 @@ public abstract class CairoGraphics2D extends Graphics2D compCtx.dispose(); compCtx = null; - if (comp == null) - comp = AlphaComposite.SrcOver; - if (comp instanceof AlphaComposite) { AlphaComposite a = (AlphaComposite) comp; - cairoSetOperator(nativePointer, a.getRule()); + cairoSetOperator(nativePointer, a.getRule()); } else { - // FIXME: this check is only required "if this Graphics2D - // context is drawing to a Component on the display screen". - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkPermission(new AWTPermission("readDisplayPixels")); - - // FIXME: implement general Composite support - //throw new java.lang.UnsupportedOperationException(); - // this is in progress! yay! - compCtx = comp.createContext(getNativeCM(), getNativeCM(), hints); + cairoSetOperator(nativePointer, AlphaComposite.SRC_OVER); + + if (comp != null) + { + // FIXME: this check is only required "if this Graphics2D + // context is drawing to a Component on the display screen". + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new AWTPermission("readDisplayPixels")); + + // FIXME: implement general Composite support + //throw new java.lang.UnsupportedOperationException(); + // this is in progress! yay! + //compCtx = comp.createContext(getNativeCM(), getNativeCM(), hints); + compCtx = comp.createContext(getBufferCM(), getNativeCM(), hints); + } } } @@ -1003,6 +1008,12 @@ public abstract class CairoGraphics2D extends Graphics2D // for now, so that the build doesn't break. return null; } + + // This may be overridden by some subclasses + protected ColorModel getBufferCM() + { + return getNativeCM(); + } ///////////////////////// DRAWING PRIMITIVES /////////////////////////////////// @@ -1017,13 +1028,13 @@ public abstract class CairoGraphics2D extends Graphics2D return; } - createPath(s); + createPath(s, true); cairoStroke(nativePointer); } public void fill(Shape s) { - createPath(s); + createPath(s, false); double alpha = 1.0; if (comp instanceof AlphaComposite) @@ -1031,7 +1042,7 @@ public abstract class CairoGraphics2D extends Graphics2D cairoFill(nativePointer, alpha); } - private void createPath(Shape s) + private void createPath(Shape s, boolean isDraw) { cairoNewPath(nativePointer); @@ -1039,8 +1050,8 @@ public abstract class CairoGraphics2D extends Graphics2D if (s instanceof Rectangle2D) { Rectangle2D r = (Rectangle2D) s; - cairoRectangle(nativePointer, shifted(r.getX(), shiftDrawCalls), - shifted(r.getY(), shiftDrawCalls), r.getWidth(), + cairoRectangle(nativePointer, shifted(r.getX(),shiftDrawCalls && isDraw), + shifted(r.getY(), shiftDrawCalls && isDraw), r.getWidth(), r.getHeight()); } @@ -1070,9 +1081,9 @@ public abstract class CairoGraphics2D extends Graphics2D } cairoArc(nativePointer, - shifted(e.getCenterX() / xscale, shiftDrawCalls), - shifted(e.getCenterY() / yscale, shiftDrawCalls), radius, 0, - Math.PI * 2); + shifted(e.getCenterX() / xscale, shiftDrawCalls && isDraw), + shifted(e.getCenterY() / yscale, shiftDrawCalls && isDraw), + radius, 0, Math.PI * 2); if (xscale != 1 || yscale != 1) cairoRestore(nativePointer); @@ -1081,7 +1092,7 @@ public abstract class CairoGraphics2D extends Graphics2D // All other shapes are broken down and drawn in steps using the // PathIterator else - walkPath(s.getPathIterator(null), shiftDrawCalls); + walkPath(s.getPathIterator(null), shiftDrawCalls && isDraw); } /** @@ -1214,7 +1225,7 @@ public abstract class CairoGraphics2D extends Graphics2D Rectangle2D r = getRealBounds(); - if( width < 0 || height < 0 ) + if( width <= 0 || height <= 0 ) return; // Return if outside the surface if( x + dx > r.getWidth() || y + dy > r.getHeight() ) @@ -1358,9 +1369,6 @@ public abstract class CairoGraphics2D extends Graphics2D int width = b.getWidth(); int height = b.getHeight(); - boolean wasPremultplied = b.isAlphaPremultiplied(); - b.coerceData(true); - // If this BufferedImage has a BufferedImageGraphics object, // use the cached CairoSurface that BIG is drawing onto @@ -1380,31 +1388,32 @@ public abstract class CairoGraphics2D extends Graphics2D ((CairoSurface)raster).drawSurface(nativePointer, i2u, alpha, getInterpolation()); updateColor(); - b.coerceData(wasPremultplied); return true; } if( bgcolor != null ) { - // Fill a rectangle with the background color - // to composite the image onto. - Paint oldPaint = paint; - AffineTransform oldTransform = transform; - setPaint( bgcolor ); - setTransform( invertedXform ); - fillRect(0, 0, width, height); - setTransform( oldTransform ); - setPaint( oldPaint ); + Color oldColor = bg; + setBackground(bgcolor); + + double[] origin = new double[] {0,0}; + xform.transform(origin, 0, origin, 0, 1); + clearRect((int)origin[0], (int)origin[1], width, height); + + setBackground(oldColor); } int[] pixels = b.getRGB(0, 0, width, height, null, 0, width); + + // FIXME: The above method returns data in the standard ARGB colorspace, + // meaning data should NOT be alpha pre-multiplied; however Cairo expects + // data to be premultiplied. drawPixels(nativePointer, pixels, width, height, width, i2u, alpha, getInterpolation()); // Cairo seems to lose the current color which must be restored. updateColor(); - b.coerceData(wasPremultplied); return true; } @@ -1607,6 +1616,8 @@ public abstract class CairoGraphics2D extends Graphics2D font = ((ClasspathToolkit)(Toolkit.getDefaultToolkit())) .getFont(f.getName(), f.getAttributes()); + + cairoSetFont(nativePointer, (GdkFontPeer)getFont().getPeer()); } public Font getFont() diff --git a/gnu/java/awt/peer/gtk/CairoSurface.java b/gnu/java/awt/peer/gtk/CairoSurface.java index 91b10369d..d3b3d4504 100644 --- a/gnu/java/awt/peer/gtk/CairoSurface.java +++ b/gnu/java/awt/peer/gtk/CairoSurface.java @@ -72,16 +72,22 @@ public class CairoSurface extends WritableRaster */ long bufferPointer; - // nativeGetPixels will return [0]=red, [1]=green, [2]=blue, [3]=alpha - static ColorModel nativeColorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), - 32, - 0x000000FF, + // FIXME: use only the cairoCM_pre colormodel + // since that's what Cairo really uses (is there a way to do this cheaply? + // we use a non-multiplied model most of the time to avoid costly coercion + // operations...) + static ColorModel cairoColorModel = new DirectColorModel(32, 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + + static ColorModel cairoCM_pre = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + 32, 0x00FF0000, 0x0000FF00, - 0x00FF0000, + 0x000000FF, 0xFF000000, true, Buffers.smallestAppropriateTransferType(32)); - /** * Allocates and clears the buffer and creates the cairo surface. * @param width, height - the image size @@ -147,7 +153,7 @@ public class CairoSurface extends WritableRaster */ public CairoSurface(int width, int height) { - super(createNativeSampleModel(width, height), + super(createCairoSampleModel(width, height), null, new Point(0, 0)); if(width <= 0 || height <= 0) @@ -260,7 +266,9 @@ public class CairoSurface extends WritableRaster */ public static BufferedImage getBufferedImage(CairoSurface surface) { - return new BufferedImage(nativeColorModel, surface, true, new Hashtable()); + return new BufferedImage(cairoColorModel, surface, + cairoColorModel.isAlphaPremultiplied(), + new Hashtable()); } private class CairoDataBuffer extends DataBuffer @@ -326,10 +334,10 @@ public class CairoSurface extends WritableRaster /** * Creates a SampleModel that matches Cairo's native format */ - protected static SampleModel createNativeSampleModel(int w, int h) + protected static SampleModel createCairoSampleModel(int w, int h) { return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, - new int[]{0x000000FF, 0x0000FF00, - 0x00FF0000, 0xFF000000}); + new int[]{0x00FF0000, 0x0000FF00, + 0x000000FF, 0xFF000000}); } } diff --git a/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java b/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java index 7bd136c38..36743b9c2 100644 --- a/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java +++ b/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java @@ -38,10 +38,26 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import java.awt.AlphaComposite; +import java.awt.Color; import java.awt.Graphics; -import java.awt.GraphicsEnvironment; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.RenderedImage; +import java.util.Hashtable; /** * Implementation of Graphics2D on a Cairo surface. @@ -49,6 +65,7 @@ import java.awt.geom.Rectangle2D; public class CairoSurfaceGraphics extends CairoGraphics2D { protected CairoSurface surface; + private BufferedImage buffer; private long cairo_t; /** @@ -59,6 +76,7 @@ public class CairoSurfaceGraphics extends CairoGraphics2D this.surface = surface; cairo_t = surface.newCairoContext(); setup( cairo_t ); + setClip(0, 0, surface.width, surface.height); } /** @@ -91,4 +109,200 @@ public class CairoSurfaceGraphics extends CairoGraphics2D { surface.copyAreaNative(x, y, width, height, dx, dy, surface.width); } + + /** + * Overloaded methods that do actual drawing need to account for custom + * composites + */ + public void draw(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.draw(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.draw(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void fill(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.fill(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawRenderedImage(image, xform); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } + + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (comp == null || comp instanceof AlphaComposite) + return super.drawImage(img, xform, bgcolor, obs); + + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(), + bImg.getHeight() + bImg.getMinY()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + return drawComposite(new Rectangle2D.Double(origin.getX(), + origin.getY(), + pt.getX(), pt.getY()), + obs); + } + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawGlyphVector(gv, x, y); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } + } + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Clip source to visible areas that need updating + Rectangle2D clip = this.getClipBounds(); + Rectangle2D.intersect(bounds, clip, bounds); + clip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, clip, bounds); + + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Get destination clip to bounds + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + + Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], + points[2] - points[0], + points[3] - points[1]); + + Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); + + BufferedImage current = CairoSurface.getBufferedImage(surface); + current = current.getSubimage((int)deviceBounds.getX(), + (int)deviceBounds.getY(), + (int)deviceBounds.getWidth(), + (int)deviceBounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + buffer2.getRaster()); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(buffer2, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + new Color(0,0,0,0), null); + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + buffer = new BufferedImage(getBufferCM(), + surface.createCompatibleWritableRaster(), + getBufferCM().isAlphaPremultiplied(), + new Hashtable()); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + return CairoSurface.cairoCM_pre; + } + + protected ColorModel getBufferCM() + { + return CairoSurface.cairoColorModel; + } } diff --git a/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java b/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java index 44c1ad926..1449cdf61 100644 --- a/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java +++ b/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java @@ -424,10 +424,18 @@ public class FreetypeGlyphVector extends GlyphVector return logicalBounds; Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 ); + AffineTransform tx = new AffineTransform(); for( int i = 1; i < nGlyphs; i++ ) { - Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i ); - rect = rect.createUnion( r2 ); + Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i ); + + // Translate to the glyph's position + tx.setToTranslation(glyphPositions[i*2], glyphPositions[i*2+1]); + Point2D pt = new Point2D.Double(r2.getMinX(), r2.getMinY()); + tx.transform(pt, pt); + r2.setRect(pt.getX(), pt.getY(), r2.getWidth(), r2.getHeight()); + + rect = rect.createUnion( r2 ); } logicalBounds = rect; @@ -448,8 +456,14 @@ public class FreetypeGlyphVector extends GlyphVector public Shape getOutline() { GeneralPath path = new GeneralPath(); + AffineTransform tx = new AffineTransform(); for( int i = 0; i < getNumGlyphs(); i++ ) - path.append( getGlyphOutline( i ), false ); + { + Shape outline = getGlyphOutline(i); + tx.setToTranslation(glyphPositions[i*2], glyphPositions[i*2 +1]); + outline = tx.createTransformedShape(outline); + path.append(outline, false); + } return path; } diff --git a/gnu/java/awt/peer/gtk/GtkFramePeer.java b/gnu/java/awt/peer/gtk/GtkFramePeer.java index bb6f8b3bb..d113e92f5 100644 --- a/gnu/java/awt/peer/gtk/GtkFramePeer.java +++ b/gnu/java/awt/peer/gtk/GtkFramePeer.java @@ -57,6 +57,11 @@ public class GtkFramePeer extends GtkWindowPeer native void removeMenuBarPeer (); native void gtkFixedSetVisible (boolean visible); + private native void maximize(); + private native void unmaximize(); + private native void iconify(); + private native void deiconify(); + int getMenuBarHeight () { return menuBar == null ? 0 : getMenuBarHeight (menuBar); @@ -199,12 +204,25 @@ public class GtkFramePeer extends GtkWindowPeer public int getState () { - return 0; + return windowState; } public void setState (int state) { - + switch (state) + { + case Frame.NORMAL: + if ((windowState & Frame.ICONIFIED) != 0) + deiconify(); + if ((windowState & Frame.MAXIMIZED_BOTH) != 0) + unmaximize(); + break; + case Frame.ICONIFIED: + iconify(); + break; + case Frame.MAXIMIZED_BOTH: + maximize(); + } } public void setMaximizedBounds (Rectangle r) diff --git a/gnu/java/awt/peer/gtk/GtkMainThread.java b/gnu/java/awt/peer/gtk/GtkMainThread.java index 19d3f1d0b..a4e280fe4 100644 --- a/gnu/java/awt/peer/gtk/GtkMainThread.java +++ b/gnu/java/awt/peer/gtk/GtkMainThread.java @@ -38,6 +38,44 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import gnu.java.awt.peer.NativeEventLoopRunningEvent; + +import java.awt.AWTEvent; + +/** + * The Java thread representing the native GTK main loop, that is, + * GtkMainThread.mainThread, terminates when GtkToolkit.gtkMain() + * returns. That happens in response to the last window peer being + * disposed (see GtkWindowPeer.dispose). + * + * When GtkMainThread.destroyWindow is called for the last window, it + * in turn calls GtkMainThread.endMainThread, which calls gtk_quit. + * gtk_quit signals gtk_main to return, which causes GtkMainThread.run + * to return. + * + * There should only be one native GTK main loop running at any given + * time. In order to safely start and stop the GTK main loop, we use + * a running flag and corresponding runningLock. startMainThread will + * not return until the native GTK main loop has started, as confirmed + * by the native set_running_flag callback setting the running flag to + * true. Without this protection, gtk_quit could be called before the + * main loop has actually started, which causes GTK assertion + * failures. Likewise endMainThread will not return until the native + * GTK main loop has ended. + * + * post_running_flag_callback is called during gtk_main initialization + * and no window can be created before startMainThread returns. This + * ensures that calling post_running_flag_callback is the first action + * taken by the native GTK main loop. + * + * GtkMainThread.mainThread is started when the window count goes from + * zero to one. + * + * GtkMainThread keeps the AWT event queue informed of its status by + * posting NativeEventLoopRunningEvents. The AWT event queue uses + * this status to determine whether or not the AWT exit conditions + * have been met (see EventQueue.isShutdown). + */ public class GtkMainThread extends Thread { /** Count of the number of open windows */ @@ -46,6 +84,12 @@ public class GtkMainThread extends Thread /** Lock for the above */ private static Object nWindowsLock = new Object(); + /** Indicates whether or not the GTK main loop is running. */ + private static boolean running = false; + + /** Lock for the above. */ + private static Object runningLock = new Object(); + /** The main thread instance (singleton) */ public static GtkMainThread mainThread; @@ -60,26 +104,75 @@ public class GtkMainThread extends Thread GtkToolkit.gtkMain (); } + private static void setRunning(boolean running) + { + synchronized (runningLock) + { + GtkMainThread.running = running; + runningLock.notifyAll(); + } + } + private static void startMainThread() { - if( mainThread == null ) + synchronized (runningLock) { - mainThread = new GtkMainThread(); - mainThread.start(); + if (!running) + { + mainThread = new GtkMainThread(); + mainThread.start(); + + while (!running) + { + try + { + runningLock.wait(); + } + catch (InterruptedException e) + { + System.err.println ("GtkMainThread.startMainThread:" + + " interrupted while waiting " + + " for GTK main loop to start"); + } + } + GtkGenericPeer.q() + .postEvent(new NativeEventLoopRunningEvent(new Boolean(true))); + } } } private static void endMainThread() { - if( mainThread != null ) - GtkToolkit.gtkQuit(); + synchronized (runningLock) + { + if (running) + { + GtkToolkit.gtkQuit(); + + while (running) + { + try + { + runningLock.wait(); + } + catch (InterruptedException e) + { + System.err.println ("GtkMainThread.endMainThread:" + + " interrupted while waiting " + + " for GTK main loop to stop"); + } + } + GtkGenericPeer.q() + .postEvent(new NativeEventLoopRunningEvent(new Boolean(false))); + } + } } public static void createWindow() { - synchronized( nWindowsLock ) + synchronized (nWindowsLock) { - if( numberOfWindows == 0 ) + if (numberOfWindows == 0) startMainThread(); numberOfWindows++; } @@ -87,11 +180,11 @@ public class GtkMainThread extends Thread public static void destroyWindow() { - synchronized( nWindowsLock ) + synchronized (nWindowsLock) { numberOfWindows--; - if( numberOfWindows == 0 ) + if (numberOfWindows == 0) endMainThread(); } } -}
\ No newline at end of file +} diff --git a/gnu/java/awt/peer/gtk/GtkToolkit.java b/gnu/java/awt/peer/gtk/GtkToolkit.java index 3f87ca6e6..d2721b43f 100644 --- a/gnu/java/awt/peer/gtk/GtkToolkit.java +++ b/gnu/java/awt/peer/gtk/GtkToolkit.java @@ -664,6 +664,14 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit return new GtkMouseInfoPeer(); } + public boolean isFrameStateSupported(int state) + { + // GTK supports ICONFIED, NORMAL and MAXIMIZE_BOTH, but + // not (yet?) MAXIMIZE_VERT and MAXIMIZE_HORIZ. + return state == Frame.NORMAL || state == Frame.ICONIFIED + || state == Frame.MAXIMIZED_BOTH; + } + public native int getMouseNumberOfButtons(); } // class GtkToolkit diff --git a/gnu/java/awt/peer/gtk/GtkVolatileImage.java b/gnu/java/awt/peer/gtk/GtkVolatileImage.java index 44e7b027b..8660ced8e 100644 --- a/gnu/java/awt/peer/gtk/GtkVolatileImage.java +++ b/gnu/java/awt/peer/gtk/GtkVolatileImage.java @@ -37,13 +37,21 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; -import java.awt.ImageCapabilities; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; +import java.awt.ImageCapabilities; +import java.awt.Point; import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; import java.awt.image.ImageObserver; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.VolatileImage; +import java.awt.image.WritableRaster; public class GtkVolatileImage extends VolatileImage { @@ -52,6 +60,12 @@ public class GtkVolatileImage extends VolatileImage final GtkComponentPeer component; + static ColorModel gdkColorModel = new DirectColorModel(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + /** * Don't touch, accessed from native code. */ @@ -62,6 +76,17 @@ public class GtkVolatileImage extends VolatileImage native void destroy(long pointer); native int[] nativeGetPixels(long pointer); + + /** + * Gets the pixels in the current image from GDK. + * + * Note that pixels are in 32-bit RGBA, non-premultiplied, which is different + * from Cairo's premultiplied ARGB, which is different from Java's standard + * non-premultiplied ARGB. Caution is advised when using this method, to + * ensure that the data format remains consistent with what you expect. + * + * @return the current pixels, as reported by GDK. + */ public int[] getPixels() { return nativeGetPixels(nativePointer); @@ -113,9 +138,11 @@ public class GtkVolatileImage extends VolatileImage public BufferedImage getSnapshot() { - CairoSurface cs = new CairoSurface( width, height ); - cs.setPixels( getPixels() ); - return CairoSurface.getBufferedImage( cs ); + WritableRaster raster = Raster.createWritableRaster(createGdkSampleModel(width, height), + new Point(0, 0)); + raster.setDataElements(0, 0, getPixels()); + return new BufferedImage(gdkColorModel, raster, + gdkColorModel.isAlphaPremultiplied(), null); } public Graphics getGraphics() @@ -167,4 +194,14 @@ public class GtkVolatileImage extends VolatileImage { return null; } + + /** + * Creates a SampleModel that matches GDK's native format + */ + protected static SampleModel createGdkSampleModel(int w, int h) + { + return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, + new int[]{0x000000FF, 0x0000FF00, + 0x00FF0000, 0xFF000000}); + } } diff --git a/gnu/java/awt/peer/gtk/GtkWindowPeer.java b/gnu/java/awt/peer/gtk/GtkWindowPeer.java index 1f340611e..1abc5ca84 100644 --- a/gnu/java/awt/peer/gtk/GtkWindowPeer.java +++ b/gnu/java/awt/peer/gtk/GtkWindowPeer.java @@ -38,6 +38,8 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; +import gnu.java.awt.ComponentReshapeEvent; + import java.awt.Component; import java.awt.Frame; import java.awt.Graphics; @@ -62,8 +64,7 @@ public class GtkWindowPeer extends GtkContainerPeer protected static final int GDK_WINDOW_TYPE_HINT_DOCK = 6; protected static final int GDK_WINDOW_TYPE_HINT_DESKTOP = 7; - private boolean hasBeenShown = false; - private int oldState = Frame.NORMAL; + protected int windowState = Frame.NORMAL; // Cached awt window component location, width and height. private int x, y, width, height; @@ -224,9 +225,31 @@ public class GtkWindowPeer extends GtkContainerPeer // only called from GTK thread protected void postConfigureEvent (int x, int y, int width, int height) { + int frame_x = x - insets.left; + int frame_y = y - insets.top; int frame_width = width + insets.left + insets.right; int frame_height = height + insets.top + insets.bottom; + // Update the component's knowledge about the size. + // Important: Please look at the big comment in ComponentReshapeEvent + // to learn why we did it this way. If you change this code, make + // sure that the peer->AWT bounds update still works. + // (for instance: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29448 ) + + // We do this befor we post the ComponentEvent, because (in Window) + // we invalidate() / revalidate() when a ComponentEvent is seen, + // and the AWT must already know about the new size then. + if (frame_x != this.x || frame_y != this.y || frame_width != this.width + || frame_height != this.height) + { + ComponentReshapeEvent ev = new ComponentReshapeEvent(awtComponent, + frame_x, + frame_y, + frame_width, + frame_height); + awtComponent.dispatchEvent(ev); + } + if (frame_width != getWidth() || frame_height != getHeight()) { @@ -236,9 +259,6 @@ public class GtkWindowPeer extends GtkContainerPeer ComponentEvent.COMPONENT_RESIZED)); } - int frame_x = x - insets.left; - int frame_y = y - insets.top; - if (frame_x != getX() || frame_y != getY()) { @@ -247,6 +267,7 @@ public class GtkWindowPeer extends GtkContainerPeer q().postEvent(new ComponentEvent(awtComponent, ComponentEvent.COMPONENT_MOVED)); } + } public void show () @@ -261,23 +282,26 @@ public class GtkWindowPeer extends GtkContainerPeer void postWindowEvent (int id, Window opposite, int newState) { - if (id == WindowEvent.WINDOW_OPENED) + if (id == WindowEvent.WINDOW_STATE_CHANGED) { - // Post a WINDOW_OPENED event the first time this window is shown. - if (!hasBeenShown) + if (windowState != newState) { + // Post old styleWindowEvent with WINDOW_ICONIFIED or + // WINDOW_DEICONIFIED if appropriate. + if ((windowState & Frame.ICONIFIED) != 0 + && (newState & Frame.ICONIFIED) == 0) + q().postEvent(new WindowEvent((Window) awtComponent, + WindowEvent.WINDOW_DEICONIFIED, + opposite, 0, 0)); + else if ((windowState & Frame.ICONIFIED) == 0 + && (newState & Frame.ICONIFIED) != 0) + q().postEvent(new WindowEvent((Window) awtComponent, + WindowEvent.WINDOW_ICONIFIED, + opposite, 0, 0)); + // Post new-style WindowStateEvent. q().postEvent (new WindowEvent ((Window) awtComponent, id, - opposite)); - hasBeenShown = true; - } - } - else if (id == WindowEvent.WINDOW_STATE_CHANGED) - { - if (oldState != newState) - { - q().postEvent (new WindowEvent ((Window) awtComponent, id, opposite, - oldState, newState)); - oldState = newState; + opposite, windowState, newState)); + windowState = newState; } } else @@ -356,13 +380,6 @@ public class GtkWindowPeer extends GtkContainerPeer return g; } - protected void updateComponent (PaintEvent event) - { - // Do not clear anything before painting. Sun never calls - // Window.update, only Window.paint. - paintComponent(event); - } - protected void postMouseEvent(int id, long when, int mods, int x, int y, int clickCount, boolean popupTrigger) { diff --git a/gnu/java/awt/peer/gtk/VolatileImageGraphics.java b/gnu/java/awt/peer/gtk/VolatileImageGraphics.java index 560d31750..3a9f9d693 100644 --- a/gnu/java/awt/peer/gtk/VolatileImageGraphics.java +++ b/gnu/java/awt/peer/gtk/VolatileImageGraphics.java @@ -282,12 +282,12 @@ public class VolatileImageGraphics extends ComponentGraphics if (buffer == null) { WritableRaster rst; - rst = Raster.createWritableRaster(CairoSurface. - createNativeSampleModel(owner.width, + rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(owner.width, owner.height), new Point(0,0)); - buffer = new BufferedImage(CairoSurface.nativeColorModel, rst, true, + buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst, + GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(), new Hashtable()); } else @@ -301,7 +301,12 @@ public class VolatileImageGraphics extends ComponentGraphics protected ColorModel getNativeCM() { - return CairoSurface.nativeColorModel; + // We should really return GtkVolatileImage.gdkColorModel , + // but CairoGraphics2D doesn't handle alpha premultiplication properly (see + // the fixme in drawImage) so we use the naive Cairo model instead to trick + // the compositing context. + // Because getNativeCM() == getBufferCM() for this peer, it doesn't break. + return CairoSurface.cairoCM_pre; } } diff --git a/gnu/java/lang/InstrumentationImpl.java b/gnu/java/lang/InstrumentationImpl.java index 3192683ef..2425b35c8 100644 --- a/gnu/java/lang/InstrumentationImpl.java +++ b/gnu/java/lang/InstrumentationImpl.java @@ -1,6 +1,6 @@ /* InstrumentationImpl.java -- GNU implementation of java.lang.instrument.Instrumentation - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -67,7 +67,7 @@ public final class InstrumentationImpl implements Instrumentation new ArrayList<ClassFileTransformer>(); - private InstrumentationImpl() + InstrumentationImpl() { } diff --git a/gnu/java/net/PlainDatagramSocketImpl.java b/gnu/java/net/PlainDatagramSocketImpl.java index be2fc796b..a84525e18 100644 --- a/gnu/java/net/PlainDatagramSocketImpl.java +++ b/gnu/java/net/PlainDatagramSocketImpl.java @@ -1,5 +1,5 @@ /* PlainDatagramSocketImpl.java -- Default DatagramSocket implementation - Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,7 +42,6 @@ import gnu.java.nio.VMChannel; import java.io.IOException; import java.io.InterruptedIOException; -import java.lang.reflect.Field; import java.net.DatagramPacket; import java.net.DatagramSocketImpl; import java.net.InetAddress; @@ -69,7 +68,6 @@ import java.nio.ByteBuffer; */ public final class PlainDatagramSocketImpl extends DatagramSocketImpl { - private final VMChannel channel; /** @@ -171,7 +169,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected void connect(InetAddress addr, int port) throws SocketException { - VMPlainDatagramSocketImpl.connect(this, addr, port); + channel.connect(new InetSocketAddress(addr, port), 0); } /** @@ -203,7 +201,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized void setTimeToLive(int ttl) throws IOException { - setOption(VMPlainDatagramSocketImpl.IP_TTL, new Integer(ttl)); + impl.setTimeToLive(ttl); } /** @@ -215,12 +213,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized int getTimeToLive() throws IOException { - Object obj = getOption(VMPlainDatagramSocketImpl.IP_TTL); - - if (! (obj instanceof Integer)) - throw new IOException("Internal Error"); - - return ((Integer) obj).intValue(); + return impl.getTimeToLive(); } protected int getLocalPort() @@ -318,30 +311,53 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl /** * Sets the value of an option on the socket * - * @param option_id The identifier of the option to set - * @param val The value of the option to set + * @param optionId The identifier of the option to set + * @param value The value of the option to set * * @exception SocketException If an error occurs */ - public synchronized void setOption(int option_id, Object val) + public synchronized void setOption(int optionId, Object value) throws SocketException { - impl.setOption(option_id, val); + switch (optionId) + { + case IP_MULTICAST_IF: + case IP_MULTICAST_IF2: + impl.setMulticastInterface(optionId, (InetAddress) value); + break; + + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_LINGER: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + impl.setOption(optionId, value); + return; + + default: + throw new SocketException("cannot set option " + optionId); + } } /** * Retrieves the value of an option on the socket * - * @param option_id The identifier of the option to retrieve + * @param optionId The identifier of the option to retrieve * * @return The value of the option * * @exception SocketException If an error occurs */ - public synchronized Object getOption(int option_id) + public synchronized Object getOption(int optionId) throws SocketException { - if (option_id == SO_BINDADDR) + if (optionId == SO_BINDADDR) { try { @@ -361,7 +377,10 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl throw se; } } - return impl.getOption(option_id); + if (optionId == IP_MULTICAST_IF || optionId == IP_MULTICAST_IF2) + return impl.getMulticastInterface(optionId); + + return impl.getOption(optionId); } /** diff --git a/gnu/java/net/PlainSocketImpl.java b/gnu/java/net/PlainSocketImpl.java index 750243d5d..64e498746 100644 --- a/gnu/java/net/PlainSocketImpl.java +++ b/gnu/java/net/PlainSocketImpl.java @@ -39,12 +39,9 @@ exception statement from your version. */ package gnu.java.net; -import gnu.java.nio.SelectorProviderImpl; import gnu.java.nio.SocketChannelImpl; import gnu.java.nio.VMChannel; -import gnu.java.security.action.GetSecurityPropertyAction; -import java.io.EOFException; import java.io.InputStream; import java.io.IOException; import java.io.InterruptedIOException; @@ -56,7 +53,6 @@ import java.net.SocketException; import java.net.SocketImpl; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; /** * Written using on-line Java Platform 1.2 API Specification, as well @@ -148,26 +144,21 @@ public class PlainSocketImpl extends SocketImpl { switch (optionId) { - case IP_MULTICAST_IF: - case IP_MULTICAST_IF2: - throw new UnsupportedOperationException("FIXME"); - + case SO_LINGER: case IP_MULTICAST_LOOP: case SO_BROADCAST: case SO_KEEPALIVE: case SO_OOBINLINE: case TCP_NODELAY: case IP_TOS: - case SO_LINGER: case SO_RCVBUF: case SO_SNDBUF: case SO_TIMEOUT: case SO_REUSEADDR: impl.setOption(optionId, value); return; - - default: - throw new SocketException("cannot set option " + optionId); + default: + throw new SocketException("Unrecognized TCP option: " + optionId); } } @@ -197,10 +188,26 @@ public class PlainSocketImpl extends SocketImpl throw se; } } - if (optionId == IP_MULTICAST_IF || optionId == IP_MULTICAST_IF2) - throw new UnsupportedOperationException ("can't get option " + - optionId + " yet"); - return impl.getOption(optionId); + + // This filters options which are invalid for TCP. + switch (optionId) + { + case SO_LINGER: + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + return impl.getOption(optionId); + default: + throw new SocketException("Unrecognized TCP option: " + optionId); + } + } public void shutdownInput() throws IOException @@ -274,7 +281,10 @@ public class PlainSocketImpl extends SocketImpl boolean connected = channel.connect(address, timeout); if (!connected) throw new SocketTimeoutException("connect timed out"); - InetSocketAddress addr = channel.getVMChannel().getPeerAddress(); + + // Using the given SocketAddress is important to preserve + // hostnames given by the caller. + InetSocketAddress addr = (InetSocketAddress) address; this.address = addr.getAddress(); this.port = addr.getPort(); } @@ -364,6 +374,9 @@ public class PlainSocketImpl extends SocketImpl { if (impl.getState().isValid()) impl.close(); + + address = null; + port = -1; } public void sendUrgentData(int data) throws IOException @@ -417,11 +430,18 @@ public class PlainSocketImpl extends SocketImpl { if (channel == null) return null; + try { InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); if (remote == null) return null; + // To mimic behavior of the RI the InetAddress instance which was + // used to establish the connection is returned instead of one that + // was created by the native layer (this preserves exact hostnames). + if (address != null) + return address; + return remote.getAddress(); } catch (IOException ioe) @@ -471,6 +491,7 @@ public class PlainSocketImpl extends SocketImpl { if (channel == null) return -1; + try { InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); diff --git a/gnu/java/nio/KqueueSelectorImpl.java b/gnu/java/nio/KqueueSelectorImpl.java index eed86119c..34ca1dc59 100644 --- a/gnu/java/nio/KqueueSelectorImpl.java +++ b/gnu/java/nio/KqueueSelectorImpl.java @@ -63,7 +63,10 @@ import java.util.Set; */ public class KqueueSelectorImpl extends AbstractSelector { - private static final int sizeof_struct_kevent; + // Prepended underscore to field name to make it distinct + // from the method with the similar name. + private static final int _sizeof_struct_kevent; + private static final int MAX_DOUBLING_CAPACITY = 16384; private static final int CAP_INCREMENT = 1024; private static final int INITIAL_CAPACITY; @@ -80,10 +83,10 @@ public class KqueueSelectorImpl extends AbstractSelector } if (kqueue_supported ()) - sizeof_struct_kevent = sizeof_struct_kevent(); + _sizeof_struct_kevent = sizeof_struct_kevent(); else - sizeof_struct_kevent = -1; - INITIAL_CAPACITY = 16 * sizeof_struct_kevent; + _sizeof_struct_kevent = -1; + INITIAL_CAPACITY = 16 * _sizeof_struct_kevent; } /** @@ -205,7 +208,7 @@ public class KqueueSelectorImpl extends AbstractSelector key.interestOps = 0; } - int events_size = (2 * sizeof_struct_kevent) * keys.size(); + int events_size = (2 * _sizeof_struct_kevent) * keys.size(); int num_events = 0; for (Iterator it = keys.entrySet().iterator(); it.hasNext(); ) @@ -256,7 +259,7 @@ public class KqueueSelectorImpl extends AbstractSelector if (blockedThread.isInterrupted()) timeout = 0; n = kevent(kq, events, num_events, - events.capacity() / sizeof_struct_kevent, timeout); + events.capacity() / _sizeof_struct_kevent, timeout); } finally { @@ -267,7 +270,7 @@ public class KqueueSelectorImpl extends AbstractSelector } //System.out.println("dump of keys selected:"); - //dump_selection_keys((ByteBuffer) events.duplicate().limit(n * sizeof_struct_kevent)); + //dump_selection_keys((ByteBuffer) events.duplicate().limit(n * _sizeof_struct_kevent)); // Commit the operations we've just added in the call to kevent. for (Iterator it = keys.values().iterator(); it.hasNext(); ) @@ -280,8 +283,8 @@ public class KqueueSelectorImpl extends AbstractSelector int x = 0; for (int i = 0; i < n; i++) { - events.position(x).limit(x + sizeof_struct_kevent); - x += sizeof_struct_kevent; + events.position(x).limit(x + _sizeof_struct_kevent); + x += _sizeof_struct_kevent; int y = fetch_key(events.slice()); KqueueSelectionKeyImpl key = (KqueueSelectionKeyImpl) keys.get(new Integer(y)); @@ -370,7 +373,7 @@ public class KqueueSelectorImpl extends AbstractSelector { synchronized (keys) { - if (events.capacity() < (2 * sizeof_struct_kevent) * keys.size()) + if (events.capacity() < (2 * _sizeof_struct_kevent) * keys.size()) { int cap = events.capacity(); if (cap >= MAX_DOUBLING_CAPACITY) @@ -380,7 +383,7 @@ public class KqueueSelectorImpl extends AbstractSelector events = ByteBuffer.allocateDirect(cap); } - else if (events.capacity() > 4 * (sizeof_struct_kevent) * keys.size() + 1 + else if (events.capacity() > 4 * (_sizeof_struct_kevent) * keys.size() + 1 && events.capacity() > INITIAL_CAPACITY) { int cap = events.capacity(); @@ -437,7 +440,7 @@ public class KqueueSelectorImpl extends AbstractSelector /** * Return the size of a <code>struct kevent</code> on this system. - * + * * @return The size of <code>struct kevent</code>. */ private static native int sizeof_struct_kevent(); diff --git a/gnu/javax/crypto/jce/cipher/CipherAdapter.java b/gnu/javax/crypto/jce/cipher/CipherAdapter.java index b7cd755f5..0863b1f1c 100644 --- a/gnu/javax/crypto/jce/cipher/CipherAdapter.java +++ b/gnu/javax/crypto/jce/cipher/CipherAdapter.java @@ -373,14 +373,24 @@ class CipherAdapter engineInit(opmode, key, spec, random); } - protected byte[] engineUpdate(byte[] input, int off, int len) + protected byte[] engineUpdate(byte[] input, int inOff, int inLen) { + if (inLen == 0) // nothing to process + return new byte[0]; final int blockSize = mode.currentBlockSize(); - final int count = (partLen + len) / blockSize; - final byte[] out = new byte[count * blockSize]; + int blockCount = (partLen + inLen) / blockSize; + + // always keep data for unpadding in padded decryption mode; + // might even be a complete block + if (pad != null + && ((Integer) attributes.get(IMode.STATE)).intValue() == IMode.DECRYPTION + && (partLen + inLen) % blockSize == 0) + blockCount--; + + final byte[] out = new byte[blockCount * blockSize]; try { - engineUpdate(input, off, len, out, 0); + engineUpdate(input, inOff, inLen, out, 0); } catch (ShortBufferException x) // should not happen { @@ -395,7 +405,15 @@ class CipherAdapter if (inLen == 0) // nothing to process return 0; final int blockSize = mode.currentBlockSize(); - final int blockCount = (partLen + inLen) / blockSize; + int blockCount = (partLen + inLen) / blockSize; + + // always keep data for unpadding in padded decryption mode; + // might even be a complete block + if (pad != null + && ((Integer) attributes.get(IMode.STATE)).intValue() == IMode.DECRYPTION + && (partLen + inLen) % blockSize == 0) + blockCount--; + final int result = blockCount * blockSize; if (result > out.length - outOff) throw new ShortBufferException(); @@ -447,16 +465,21 @@ class CipherAdapter break; case IMode.DECRYPTION: int padLen; + byte[] buf3 = new byte[buf.length + partLen]; try { - padLen = pad.unpad(buf, 0, buf.length); + if (partLen != mode.currentBlockSize()) + throw new WrongPaddingException(); + System.arraycopy(buf, 0, buf3, 0, buf.length); + mode.update(partBlock, 0, buf3, buf.length); + padLen = pad.unpad(buf3, 0, buf3.length); } catch (WrongPaddingException wpe) { throw new BadPaddingException(wpe.getMessage()); } - result = new byte[buf.length - padLen]; - System.arraycopy(buf, 0, result, 0, result.length); + result = new byte[buf3.length - padLen]; + System.arraycopy(buf3, 0, result, 0, result.length); break; default: throw new IllegalStateException(); diff --git a/gnu/javax/crypto/pad/PKCS7.java b/gnu/javax/crypto/pad/PKCS7.java index bef1784ee..8fce5b8b0 100644 --- a/gnu/javax/crypto/pad/PKCS7.java +++ b/gnu/javax/crypto/pad/PKCS7.java @@ -100,8 +100,8 @@ public final class PKCS7 throws WrongPaddingException { int limit = offset + length; - int result = in[limit - 1] & 0xFF; - for (int i = 0; i < result; i++) + int result = in[--limit] & 0xFF; + for (int i = 0; i < result - 1; i++) if (result != (in[--limit] & 0xFF)) throw new WrongPaddingException(); if (Configuration.DEBUG) diff --git a/gnu/xml/transform/TransformerImpl.java b/gnu/xml/transform/TransformerImpl.java index 732a911bd..f43b6602f 100644 --- a/gnu/xml/transform/TransformerImpl.java +++ b/gnu/xml/transform/TransformerImpl.java @@ -327,7 +327,8 @@ class TransformerImpl domDoc.setCheckWellformedness(false); } parent.normalize(); - strip(stylesheet, parent); + if (stylesheet != null) + strip(stylesheet, parent); Document resultDoc = (parent instanceof Document) ? (Document) parent : parent.getOwnerDocument(); |