diff options
author | Thomas Fitzsimmons <fitzsim@redhat.com> | 2006-06-09 16:07:07 +0000 |
---|---|---|
committer | Thomas Fitzsimmons <fitzsim@gcc.gnu.org> | 2006-06-09 16:07:07 +0000 |
commit | 02440ca432f1aa4de7aeda563d2493a20148554b (patch) | |
tree | 0802013820f309170e8ae5584b09e9672bab2cb6 /libjava/classpath/gnu/java | |
parent | 6c65d7577ca4e36675571fd8bad43ef6d34e2831 (diff) | |
download | gcc-02440ca432f1aa4de7aeda563d2493a20148554b.zip gcc-02440ca432f1aa4de7aeda563d2493a20148554b.tar.gz gcc-02440ca432f1aa4de7aeda563d2493a20148554b.tar.bz2 |
String.java, [...]: Merge from GNU Classpath HEAD.
2006-06-09 Thomas Fitzsimmons <fitzsim@redhat.com>
* java/lang/String.java, classpath/native/jni/classpath/jcl.h,
classpath/native/jni/qt-peer/eventmethods.h,
classpath/native/jni/qt-peer/qtmenupeer.cpp,
classpath/native/jni/qt-peer/.cvsignore,
classpath/native/jni/gtk-peer/gdkdisplay.h,
classpath/native/jni/gtk-peer/cairographics2d.h,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkFontPeer.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_FreetypeGlyphVector.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_ComponentGraphicsCopy.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_ComponentGraphics.c,
classpath/native/jni/gtk-peer/.cvsignore,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkImage.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkCanvasPeer.c,
classpath/native/jni/gtk-peer/gtkpeer.h,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkClipboard.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoSurface.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkScreenGraphicsDevice.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkTextLayout.c,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkFramePeer.c,
classpath/native/jni/gtk-peer/Makefile.am,
classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkComponentPeer.c,
classpath/native/jawt/Makefile.am,
classpath/native/jawt/.cvsignore,
classpath/native/target/Linux/Makefile.in,
classpath/native/plugin/gcjwebplugin.cc,
classpath/native/plugin/Makefile.am,
classpath/native/plugin/.cvsignore,
classpath/resource/Makefile.in,
classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java,
classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,
classpath/gnu/java/awt/peer/gtk/CairoSurface.java,
classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java,
classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java,
classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java,
classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java,
classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java,
classpath/gnu/java/awt/peer/gtk/ComponentGraphicsCopy.java,
classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java,
classpath/gnu/java/awt/peer/gtk/GdkGraphics.java,
classpath/gnu/java/awt/peer/gtk/GtkToolkit.java,
classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java,
classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java,
classpath/gnu/java/awt/peer/gtk/GdkTextLayout.java,
classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java,
classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java,
classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java,
classpath/gnu/java/awt/peer/gtk/GtkImage.java,
classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java,
classpath/gnu/java/awt/peer/gtk/GdkGlyphVector.java,
classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java,
classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java,
classpath/gnu/java/awt/peer/swing/SwingComponent.java,
classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java,
classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java,
classpath/gnu/java/awt/peer/swing/SwingFramePeer.java,
classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java,
classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java,
classpath/gnu/java/awt/print/JavaPrinterJob.java,
classpath/gnu/java/awt/print/PostScriptGraphics2D.java,
classpath/gnu/java/awt/print/SpooledDocument.java,
classpath/gnu/java/awt/print/JavaPrinterGraphics.java,
classpath/gnu/java/awt/BitwiseXORComposite.java,
classpath/gnu/java/awt/font/GNUGlyphVector.java,
classpath/gnu/java/awt/font/opentype/NameDecoder.java,
classpath/gnu/java/awt/java2d/RasterGraphics.java,
classpath/gnu/java/awt/java2d/TexturePaintContext.java,
classpath/gnu/java/awt/java2d/PolyEdge.java,
classpath/gnu/java/awt/java2d/AbstractGraphics2D.java,
classpath/gnu/java/awt/java2d/AlphaCompositeContext.java,
classpath/gnu/java/awt/java2d/ImagePaint.java,
classpath/gnu/java/awt/Buffers.java,
classpath/gnu/classpath/Configuration.java.in,
classpath/gnu/javax/swing/text/html/CombinedAttributes.java,
classpath/gnu/javax/swing/text/html/CharacterAttributeTranslator.java,
classpath/gnu/javax/swing/text/html/parser/htmlAttributeSet.java,
classpath/gnu/javax/swing/text/html/parser/SmallHtmlAttributeSet.java,
classpath/gnu/javax/swing/text/html/ImageViewIconFactory.java,
classpath/tools/toolwrapper.c,
classpath/tools/gnu/classpath/tools/native2ascii/Native2ASCII.java,
classpath/tools/gnu/classpath/tools/native2ascii/Messages.java,
classpath/tools/gnu/classpath/tools/getopt/FileArgumentCallback.java,
classpath/tools/gnu/classpath/tools/getopt/OptionGroup.java,
classpath/tools/gnu/classpath/tools/getopt/OptionException.java,
classpath/tools/gnu/classpath/tools/getopt/Messages.java,
classpath/tools/gnu/classpath/tools/getopt/Option.java,
classpath/tools/gnu/classpath/tools/getopt/Parser.java,
classpath/tools/gnu/classpath/tools/getopt/ClasspathToolParser.java,
classpath/tools/gnu/classpath/tools/jarsigner/JarSigner.java,
classpath/tools/gnu/classpath/tools/jarsigner/Main.java,
classpath/tools/gnu/classpath/tools/jarsigner/Messages.java,
classpath/tools/gnu/classpath/tools/jarsigner/package.html,
classpath/tools/gnu/classpath/tools/keytool/ListCmd.java,
classpath/tools/gnu/classpath/tools/keytool/StorePasswdCmd.java,
classpath/tools/gnu/classpath/tools/keytool/ExportCmd.java,
classpath/tools/gnu/classpath/tools/keytool/GenKeyCmd.java,
classpath/tools/gnu/classpath/tools/keytool/Messages.java,
classpath/tools/gnu/classpath/tools/keytool/package.html,
classpath/tools/gnu/classpath/tools/keytool/Command.java,
classpath/tools/gnu/classpath/tools/keytool/IdentityDBCmd.java,
classpath/tools/gnu/classpath/tools/keytool/Main.java,
classpath/tools/gnu/classpath/tools/keytool/DeleteCmd.java,
classpath/tools/gnu/classpath/tools/keytool/CertReqCmd.java,
classpath/tools/gnu/classpath/tools/keytool/SelfCertCmd.java,
classpath/tools/gnu/classpath/tools/keytool/KeyCloneCmd.java,
classpath/tools/gnu/classpath/tools/keytool/KeyPasswdCmd.java,
classpath/tools/gnu/classpath/tools/keytool/ImportCmd.java,
classpath/tools/gnu/classpath/tools/keytool/PrintCertCmd.java,
classpath/tools/gnu/classpath/tools/rmi/registry/package.html,
classpath/tools/gnu/classpath/tools/rmi/RMIC.txt,
classpath/tools/gnu/classpath/tools/rmi/RMIC.java,
classpath/tools/gnu/classpath/tools/appletviewer/ErrorApplet.java,
classpath/tools/gnu/classpath/tools/appletviewer/AppletClassLoader.java,
classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletContext.java,
classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletContext.java,
classpath/tools/gnu/classpath/tools/appletviewer/AppletSecurityManager.java,
classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletContext.java,
classpath/tools/gnu/classpath/tools/appletviewer/AppletWarning.java,
classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletViewer.java,
classpath/tools/gnu/classpath/tools/appletviewer/AppletTag.java,
classpath/tools/gnu/classpath/tools/appletviewer/ConsoleDialog.java,
classpath/tools/gnu/classpath/tools/appletviewer/Main.java,
classpath/tools/gnu/classpath/tools/appletviewer/StandaloneAppletWindow.java,
classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletViewer.java,
classpath/tools/gnu/classpath/tools/appletviewer/TagParser.java,
classpath/tools/gnu/classpath/tools/appletviewer/PluginAppletWindow.java,
classpath/tools/gnu/classpath/tools/appletviewer/CommonAppletStub.java,
classpath/tools/gnu/classpath/tools/serialver/Messages.java,
classpath/tools/gnu/classpath/tools/serialver/SerialVer.java,
classpath/tools/gnu/classpath/tools/jar/Creator.java,
classpath/tools/gnu/classpath/tools/jar/Entry.java,
classpath/tools/gnu/classpath/tools/jar/Lister.java,
classpath/tools/gnu/classpath/tools/jar/Main.java,
classpath/tools/gnu/classpath/tools/jar/Updater.java,
classpath/tools/gnu/classpath/tools/jar/Messages.java,
classpath/tools/gnu/classpath/tools/jar/Extractor.java,
classpath/tools/gnu/classpath/tools/jar/Action.java,
classpath/tools/gnu/classpath/tools/jar/Indexer.java,
classpath/tools/gnu/classpath/tools/jar/WorkSet.java,
classpath/tools/gnu/classpath/tools/giop/GRMIC.txt,
classpath/tools/gnu/classpath/tools/giop/grmic/GiopRmicCompiler.java,
classpath/tools/gnu/classpath/tools/giop/GRMIC.java,
classpath/tools/Makefile.am, classpath/tools/jarsigner.in,
classpath/tools/keytool.in, classpath/tools/appletviewer.in,
classpath/tools/.cvsignore, classpath/configure.ac,
classpath/javax/swing/JTabbedPane.java,
classpath/javax/swing/AbstractButton.java,
classpath/javax/swing/JViewport.java,
classpath/javax/swing/KeyboardManager.java,
classpath/javax/swing/JMenuItem.java,
classpath/javax/swing/JMenuBar.java,
classpath/javax/swing/MenuSelectionManager.java,
classpath/javax/swing/JOptionPane.java,
classpath/javax/swing/JSpinner.java,
classpath/javax/swing/JCheckBoxMenuItem.java,
classpath/javax/swing/JEditorPane.java,
classpath/javax/swing/JFormattedTextField.java,
classpath/javax/swing/JTree.java,
classpath/javax/swing/CellRendererPane.java,
classpath/javax/swing/JScrollPane.java,
classpath/javax/swing/tree/VariableHeightLayoutCache.java,
classpath/javax/swing/tree/TreeNode.java,
classpath/javax/swing/tree/FixedHeightLayoutCache.java,
classpath/javax/swing/tree/DefaultTreeCellEditor.java,
classpath/javax/swing/tree/TreePath.java,
classpath/javax/swing/tree/RowMapper.java,
classpath/javax/swing/tree/DefaultMutableTreeNode.java,
classpath/javax/swing/tree/DefaultTreeModel.java,
classpath/javax/swing/tree/AbstractLayoutCache.java,
classpath/javax/swing/tree/TreeSelectionModel.java,
classpath/javax/swing/tree/DefaultTreeSelectionModel.java,
classpath/javax/swing/tree/DefaultTreeCellRenderer.java,
classpath/javax/swing/tree/ExpandVetoException.java,
classpath/javax/swing/JList.java,
classpath/javax/swing/table/JTableHeader.java,
classpath/javax/swing/table/AbstractTableModel.java,
classpath/javax/swing/table/DefaultTableModel.java,
classpath/javax/swing/table/TableCellEditor.java,
classpath/javax/swing/table/TableCellRenderer.java,
classpath/javax/swing/ProgressMonitor.java,
classpath/javax/swing/JToolBar.java,
classpath/javax/swing/TransferHandler.java,
classpath/javax/swing/DefaultCellEditor.java,
classpath/javax/swing/DefaultButtonModel.java,
classpath/javax/swing/JLayeredPane.java,
classpath/javax/swing/text/DefaultEditorKit.java,
classpath/javax/swing/text/DefaultCaret.java,
classpath/javax/swing/text/FieldView.java,
classpath/javax/swing/text/JTextComponent.java,
classpath/javax/swing/text/TextAction.java,
classpath/javax/swing/text/StyleContext.java,
classpath/javax/swing/text/html/HTMLDocument.java,
classpath/javax/swing/text/html/MinimalHTMLWriter.java,
classpath/javax/swing/text/html/ImageView.java,
classpath/javax/swing/text/html/HTMLEditorKit.java,
classpath/javax/swing/text/AbstractWriter.java,
classpath/javax/swing/text/GapContent.java,
classpath/javax/swing/text/Utilities.java,
classpath/javax/swing/text/PlainView.java,
classpath/javax/swing/UIManager.java,
classpath/javax/swing/JSplitPane.java,
classpath/javax/swing/JComponent.java,
classpath/javax/swing/SwingUtilities.java,
classpath/javax/swing/border/AbstractBorder.java,
classpath/javax/swing/border/CompoundBorder.java,
classpath/javax/swing/border/TitledBorder.java,
classpath/javax/swing/border/MatteBorder.java,
classpath/javax/swing/border/BevelBorder.java,
classpath/javax/swing/RepaintManager.java,
classpath/javax/swing/JTable.java,
classpath/javax/swing/UIDefaults.java,
classpath/javax/swing/DefaultDesktopManager.java,
classpath/javax/swing/JMenu.java,
classpath/javax/swing/JLabel.java,
classpath/javax/swing/JSlider.java,
classpath/javax/swing/plaf/basic/BasicToolBarUI.java,
classpath/javax/swing/plaf/basic/BasicButtonUI.java,
classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java,
classpath/javax/swing/plaf/basic/BasicTextAreaUI.java,
classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java,
classpath/javax/swing/plaf/basic/BasicSpinnerUI.java,
classpath/javax/swing/plaf/basic/BasicSliderUI.java,
classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java,
classpath/javax/swing/plaf/basic/BasicComboPopup.java,
classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java,
classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java,
classpath/javax/swing/plaf/basic/BasicProgressBarUI.java,
classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java,
classpath/javax/swing/plaf/basic/BasicPanelUI.java,
classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java,
classpath/javax/swing/plaf/basic/BasicTreeUI.java,
classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java,
classpath/javax/swing/plaf/basic/BasicFileChooserUI.java,
classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java,
classpath/javax/swing/plaf/basic/BasicComboBoxUI.java,
classpath/javax/swing/plaf/basic/BasicListUI.java,
classpath/javax/swing/plaf/basic/BasicIconFactory.java,
classpath/javax/swing/plaf/basic/BasicTextUI.java,
classpath/javax/swing/plaf/basic/BasicLookAndFeel.java,
classpath/javax/swing/plaf/basic/BasicDirectoryModel.java,
classpath/javax/swing/plaf/basic/BasicRootPaneUI.java,
classpath/javax/swing/plaf/basic/BasicTableUI.java,
classpath/javax/swing/plaf/basic/SharedUIDefaults.java,
classpath/javax/swing/plaf/multi/MultiComboBoxUI.java,
classpath/javax/swing/plaf/multi/MultiListUI.java,
classpath/javax/swing/plaf/multi/MultiSplitPaneUI.java,
classpath/javax/swing/plaf/multi/MultiFileChooserUI.java,
classpath/javax/swing/plaf/multi/MultiOptionPaneUI.java,
classpath/javax/swing/plaf/multi/MultiTabbedPaneUI.java,
classpath/javax/swing/plaf/multi/MultiLookAndFeel.java,
classpath/javax/swing/plaf/metal/MetalSliderUI.java,
classpath/javax/swing/plaf/metal/MetalIconFactory.java,
classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java,
classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java,
classpath/javax/swing/plaf/metal/MetalLookAndFeel.java,
classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java,
classpath/javax/swing/plaf/metal/MetalSeparatorUI.java,
classpath/javax/swing/plaf/metal/MetalBorders.java,
classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java,
classpath/javax/swing/plaf/metal/MetalScrollBarUI.java,
classpath/javax/swing/plaf/metal/MetalRootPaneUI.java,
classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java,
classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java,
classpath/javax/swing/plaf/metal/MetalToolTipUI.java,
classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java,
classpath/javax/swing/plaf/metal/MetalFileChooserUI.java,
classpath/javax/swing/plaf/metal/MetalUtils.java,
classpath/javax/swing/plaf/metal/MetalComboBoxButton.java,
classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java,
classpath/javax/swing/plaf/metal/MetalButtonUI.java,
classpath/javax/swing/JPopupMenu.java,
classpath/javax/swing/JProgressBar.java,
classpath/javax/swing/WindowConstants.java,
classpath/javax/swing/JFrame.java,
classpath/javax/swing/JFileChooser.java,
classpath/javax/swing/JComboBox.java,
classpath/javax/swing/event/EventListenerList.java,
classpath/javax/swing/ListSelectionModel.java,
classpath/javax/swing/JScrollBar.java,
classpath/java/text/SimpleDateFormat.java,
classpath/java/text/NumberFormat.java,
classpath/java/text/class-dependencies.conf,
classpath/java/awt/image/ColorModel.java,
classpath/java/awt/image/BufferedImage.java,
classpath/java/awt/Window.java,
classpath/java/awt/ContainerOrderFocusTraversalPolicy.java,
classpath/java/awt/LightweightDispatcher.java,
classpath/java/awt/EventDispatchThread.java,
classpath/java/awt/BasicStroke.java,
classpath/java/awt/ColorPaintContext.java,
classpath/java/awt/Container.java,
classpath/java/awt/TexturePaint.java,
classpath/java/awt/Component.java, classpath/java/awt/Font.java,
classpath/java/awt/GraphicsConfiguration.java,
classpath/java/awt/DefaultKeyboardFocusManager.java,
classpath/java/awt/print/PrinterJob.java,
classpath/java/awt/im/InputContext.java,
classpath/java/awt/dnd/DragGestureRecognizer.java,
classpath/java/awt/Toolkit.java,
classpath/java/awt/font/GraphicAttribute.java,
classpath/java/awt/font/ImageGraphicAttribute.java,
classpath/java/awt/font/GlyphVector.java,
classpath/java/awt/font/GlyphMetrics.java,
classpath/java/awt/font/ShapeGraphicAttribute.java,
classpath/java/awt/Graphics2D.java,
classpath/include/gnu_java_awt_peer_gtk_GdkGraphicsEnvironment.h,
classpath/include/gnu_java_awt_peer_gtk_ComponentGraphics.h,
classpath/include/gnu_java_awt_peer_gtk_CairoGraphics2D.h,
classpath/include/gnu_java_awt_peer_gtk_FreetypeGlyphVector.h,
classpath/include/gnu_java_awt_peer_gtk_GtkCanvasPeer.h,
classpath/include/config.h.in,
classpath/include/gnu_java_awt_peer_gtk_GdkTextLayout.h,
classpath/include/gnu_java_awt_peer_gtk_GtkComponentPeer.h,
classpath/include/gnu_java_awt_peer_gtk_GdkFontPeer.h,
classpath/include/gnu_java_awt_peer_gtk_ComponentGraphicsCopy.h,
classpath/include/gnu_java_awt_peer_gtk_GtkVolatileImage.h,
classpath/include/gnu_java_awt_peer_gtk_GtkImage.h,
classpath/include/gnu_java_awt_peer_gtk_CairoSurface.h,
classpath/include/gnu_java_awt_peer_gtk_GdkScreenGraphicsDevice.h:
Merge from GNU Classpath HEAD.
From-SVN: r114510
Diffstat (limited to 'libjava/classpath/gnu/java')
42 files changed, 6612 insertions, 2792 deletions
diff --git a/libjava/classpath/gnu/java/awt/BitwiseXORComposite.java b/libjava/classpath/gnu/java/awt/BitwiseXORComposite.java index b568e11..9205df1 100644 --- a/libjava/classpath/gnu/java/awt/BitwiseXORComposite.java +++ b/libjava/classpath/gnu/java/awt/BitwiseXORComposite.java @@ -59,7 +59,7 @@ import java.awt.image.WritableRaster; * /> * * <p>The above screen shot shows the result of applying six different - * BitwiseXORComposites. They were constructed with the colors colors + * BitwiseXORComposites. They were constructed with the colors * white, blue, black, orange, green, and brown, respectively. Each * composite was used to paint a fully white rectangle on top of the * blue bar in the background. diff --git a/libjava/classpath/gnu/java/awt/Buffers.java b/libjava/classpath/gnu/java/awt/Buffers.java index c6ccf91..2015634 100644 --- a/libjava/classpath/gnu/java/awt/Buffers.java +++ b/libjava/classpath/gnu/java/awt/Buffers.java @@ -144,25 +144,7 @@ public final class Buffers */ public static Object getData(DataBuffer buffer) { - if (buffer instanceof DataBufferByte) - return ((DataBufferByte) buffer).getData(); - - if (buffer instanceof DataBufferShort) - return ((DataBufferShort) buffer).getData(); - - if (buffer instanceof DataBufferUShort) - return ((DataBufferUShort) buffer).getData(); - - if (buffer instanceof DataBufferInt) - return ((DataBufferInt) buffer).getData(); - - if (buffer instanceof DataBufferFloat) - return ((DataBufferFloat) buffer).getData(); - - if (buffer instanceof DataBufferDouble) - return ((DataBufferDouble) buffer).getData(); - - throw new ClassCastException("Unknown data buffer type"); + return getData(buffer, 0, null, 0, buffer.getSize()); } @@ -172,46 +154,46 @@ public final class Buffers * given destination array is null. */ public static Object getData(DataBuffer src, int srcOffset, - Object dest, int destOffset, + Object dest, int dstOffset, int length) { Object from; - if (src instanceof DataBufferByte) - { - from = ((DataBufferByte) src).getData(); - if (dest == null) dest = new byte[length+destOffset]; - } - else if (src instanceof DataBufferShort) - { - from = ((DataBufferShort) src).getData(); - if (dest == null) dest = new short[length+destOffset]; - } - else if (src instanceof DataBufferUShort) - { - from = ((DataBufferUShort) src).getData(); - if (dest == null) dest = new short[length+destOffset]; - } - else if (src instanceof DataBufferInt) - { - from = ((DataBufferInt) src).getData(); - if (dest == null) dest = new int[length+destOffset]; - } - else if (src instanceof DataBufferFloat) - { - from = ((DataBufferFloat) src).getData(); - if (dest == null) dest = new float[length+destOffset]; - } - else if (src instanceof DataBufferDouble) - { - from = ((DataBufferDouble) src).getData(); - if (dest == null) dest = new double[length+destOffset]; - } - else + switch(src.getDataType()) { + case DataBuffer.TYPE_BYTE: + if (dest == null) dest = new byte[length+dstOffset]; + for(int i = 0; i < length; i++) + ((byte[])dest)[i + dstOffset] = (byte)src.getElem(i + srcOffset); + break; + + case DataBuffer.TYPE_DOUBLE: + if (dest == null) dest = new double[length+dstOffset]; + for(int i = 0; i < length; i++) + ((double[])dest)[i + dstOffset] = src.getElemDouble(i + srcOffset); + break; + + case DataBuffer.TYPE_FLOAT: + if (dest == null) dest = new float[length+dstOffset]; + for(int i = 0; i < length; i++) + ((float[])dest)[i + dstOffset] = src.getElemFloat(i + srcOffset); + break; + + case DataBuffer.TYPE_INT: + if (dest == null) dest = new int[length+dstOffset]; + for(int i = 0; i < length; i++) + ((int[])dest)[i + dstOffset] = src.getElem(i + srcOffset); + break; + + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + if (dest == null) dest = new short[length+dstOffset]; + for(int i = 0; i < length; i++) + ((short[])dest)[i + dstOffset] = (short)src.getElem(i + srcOffset); + break; + + case DataBuffer.TYPE_UNDEFINED: throw new ClassCastException("Unknown data buffer type"); } - - System.arraycopy(from, srcOffset, dest, destOffset, length); return dest; } diff --git a/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java index 9688698..f17a451 100644 --- a/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java +++ b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java @@ -110,7 +110,7 @@ public class GNUGlyphVector fontSize = font.getSize2D(); transform = font.getTransform(); // returns a modifiable copy - transform.concatenate(renderContext.getTransform()); + //transform.concatenate(renderContext.getTransform()); } diff --git a/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java index bc0c0df..e4ea202 100644 --- a/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java +++ b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java @@ -48,7 +48,7 @@ import java.util.Locale; * * @author Sascha Brawer (brawer@dandelis.ch) */ -class NameDecoder +public class NameDecoder { public static final int NAME_COPYRIGHT = 0; @@ -122,27 +122,38 @@ class NameDecoder nameTable.position(0); /* We understand only format 0 of the name table. */ - if (nameTable.getChar() != 0) + if (nameTable.getShort() != 0) return null; macLanguage = getMacLanguageCode(locale); msLanguage = getMicrosoftLanguageCode(locale); - numRecords = nameTable.getChar(); - offset = nameTable.getChar(); + numRecords = nameTable.getShort(); + offset = nameTable.getShort(); for (int i = 0; i < numRecords; i++) { - namePlatform = nameTable.getChar(); - nameEncoding = nameTable.getChar(); - nameLanguage = nameTable.getChar(); - nameID = nameTable.getChar(); - nameLen = nameTable.getChar(); - nameStart = offset + nameTable.getChar(); + namePlatform = nameTable.getShort(); + nameEncoding = nameTable.getShort(); + nameLanguage = nameTable.getShort(); + nameID = nameTable.getShort(); + nameLen = nameTable.getShort(); + nameStart = offset + nameTable.getShort(); if (nameID != name) continue; + // Handle PS seperately as it can be only ASCII, although + // possibly encoded as UTF-16BE + if ( name == NAME_POSTSCRIPT ) + { + if( nameTable.get(nameStart) == 0 ) // Peek at top byte + result = decodeName("UTF-16BE", nameTable, nameStart, nameLen); + else + result = decodeName("ASCII", nameTable, nameStart, nameLen); + return result; + } + match = false; switch (namePlatform) { @@ -393,14 +404,19 @@ class NameDecoder private static String decodeName(int platform, int encoding, int language, ByteBuffer buffer, int offset, int len) { - byte[] byteBuf; - String charsetName; - int oldPosition; - - charsetName = getCharsetName(platform, language, encoding); + String charsetName = getCharsetName(platform, language, encoding); if (charsetName == null) return null; + return decodeName(charsetName, buffer, offset, len); + } + + private static String decodeName(String charsetName, + ByteBuffer buffer, int offset, int len) + { + byte[] byteBuf; + int oldPosition; + byteBuf = new byte[len]; oldPosition = buffer.position(); try diff --git a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java index e93c43e..7df9949 100644 --- a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java +++ b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java @@ -84,7 +84,48 @@ import java.util.Iterator; import java.util.Map; /** - * Implements general and shared behaviour for Graphics2D implementation. + * This is a 100% Java implementation of the Java2D rendering pipeline. It is + * meant as a base class for Graphics2D implementations. + * + * <h2>Backend interface</h2> + * <p> + * The backend must at the very least provide a Raster which the the rendering + * pipeline can paint into. This must be implemented in + * {@link #getDestinationRaster()}. For some backends that might be enough, like + * when the target surface can be directly access via the raster (like in + * BufferedImages). Other targets need some way to synchronize the raster with + * the surface, which can be achieved by implementing the + * {@link #updateRaster(Raster, int, int, int, int)} method, which always gets + * called after a chunk of data got painted into the raster. + * </p> + * <p>The backend is free to provide implementations for the various raw* + * methods for optimized AWT 1.1 style painting of some primitives. This should + * accelerate painting of Swing greatly. When doing so, the backend must also + * keep track of the clip and translation, probably by overriding + * some clip and translate methods. Don't forget to message super in such a + * case.</p> + * + * <h2>Acceleration options</h2> + * <p> + * The fact that it is + * pure Java makes it a little slow. However, there are several ways of + * accelerating the rendering pipeline: + * <ol> + * <li><em>Optimization hooks for AWT 1.1 - like graphics operations.</em> + * The most important methods from the {@link java.awt.Graphics} class + * have a corresponding <code>raw*</code> method, which get called when + * several optimization conditions are fullfilled. These conditions are + * described below. Subclasses can override these methods and delegate + * it directly to a native backend.</li> + * <li><em>Native PaintContexts and CompositeContext.</em> The implementations + * for the 3 PaintContexts and AlphaCompositeContext can be accelerated + * using native code. These have proved to two of the most performance + * critical points in the rendering pipeline and cannot really be done quickly + * in plain Java because they involve lots of shuffling around with large + * arrays. In fact, you really would want to let the graphics card to the + * work, they are made for this.</li> + * </ol> + * </p> * * @author Roman Kennke (kennke@aicas.com) */ @@ -146,11 +187,6 @@ public abstract class AbstractGraphics2D private Raster paintRaster; /** - * A cached pixel array. - */ - private int[] pixel; - - /** * The raster of the destination surface. This is where the painting is * performed. */ @@ -168,7 +204,7 @@ public abstract class AbstractGraphics2D private transient ArrayList[] edgeTable; /** - * Indicates if cerain graphics primitives can be rendered in an optimized + * Indicates if certain graphics primitives can be rendered in an optimized * fashion. This will be the case if the following conditions are met: * - The transform may only be a translation, no rotation, shearing or * scaling. @@ -198,8 +234,6 @@ public abstract class AbstractGraphics2D hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_DEFAULT); renderingHints = new RenderingHints(hints); - - pixel = new int[4]; } /** @@ -212,40 +246,211 @@ public abstract class AbstractGraphics2D { // Stroke the shape. Shape strokedShape = stroke.createStrokedShape(shape); - - // Clip the stroked shape. -// Shape clipped = clipShape(strokedShape); -// if (clipped != null) -// { -// // Fill the shape. -// fillShape(clipped, false); -// } - // FIXME: Clipping doesn't seem to work. + // Fill the stroked shape. fillShape(strokedShape, false); } - public boolean drawImage(Image image, AffineTransform xform, ImageObserver obs) + + /** + * Draws the specified image and apply the transform for image space -> + * user space conversion. + * + * This method is implemented to special case RenderableImages and + * RenderedImages and delegate to + * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and + * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly. + * Other image types are not yet handled. + * + * @param image the image to be rendered + * @param xform the transform from image space to user space + * @param obs the image observer to be notified + */ + public boolean drawImage(Image image, AffineTransform xform, + ImageObserver obs) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + boolean ret = false; + Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs), + image.getHeight(obs)); + return drawImageImpl(image, xform, obs, areaOfInterest); + } + + /** + * Draws the specified image and apply the transform for image space -> + * user space conversion. This method only draw the part of the image + * specified by <code>areaOfInterest</code>. + * + * This method is implemented to special case RenderableImages and + * RenderedImages and delegate to + * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and + * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly. + * Other image types are not yet handled. + * + * @param image the image to be rendered + * @param xform the transform from image space to user space + * @param obs the image observer to be notified + * @param areaOfInterest the area in image space that is rendered + */ + private boolean drawImageImpl(Image image, AffineTransform xform, + ImageObserver obs, Rectangle areaOfInterest) + { + boolean ret; + if (image == null) + { + ret = true; + } + else if (image instanceof RenderedImage) + { + // FIXME: Handle the ImageObserver. + drawRenderedImageImpl((RenderedImage) image, xform, areaOfInterest); + ret = true; + } + else if (image instanceof RenderableImage) + { + // FIXME: Handle the ImageObserver. + drawRenderableImageImpl((RenderableImage) image, xform, areaOfInterest); + ret = true; + } + else + { + // FIXME: Implement rendering of other Image types. + ret = false; + } + return ret; } + /** + * Renders a BufferedImage and applies the specified BufferedImageOp before + * to filter the BufferedImage somehow. The resulting BufferedImage is then + * passed on to {@link #drawRenderedImage(RenderedImage, AffineTransform)} + * to perform the final rendering. + * + * @param image the source buffered image + * @param op the filter to apply to the buffered image before rendering + * @param x the x coordinate to render the image to + * @param y the y coordinate to render the image to + */ public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + BufferedImage filtered = + op.createCompatibleDestImage(image, image.getColorModel()); + AffineTransform t = new AffineTransform(); + t.translate(x, y); + drawRenderedImage(filtered, t); } + /** + * Renders the specified image to the destination raster. The specified + * transform is used to convert the image into user space. The transform + * of this AbstractGraphics2D object is used to transform from user space + * to device space. + * + * The rendering is performed using the scanline algorithm that performs the + * rendering of other shapes and a custom Paint implementation, that supplies + * the pixel values of the rendered image. + * + * @param image the image to render to the destination raster + * @param xform the transform from image space to user space + */ public void drawRenderedImage(RenderedImage image, AffineTransform xform) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + Rectangle areaOfInterest = new Rectangle(image.getMinX(), + image.getHeight(), + image.getWidth(), + image.getHeight()); + drawRenderedImageImpl(image, xform, areaOfInterest); + } + + /** + * Renders the specified image to the destination raster. The specified + * transform is used to convert the image into user space. The transform + * of this AbstractGraphics2D object is used to transform from user space + * to device space. Only the area specified by <code>areaOfInterest</code> + * is finally rendered to the target. + * + * The rendering is performed using the scanline algorithm that performs the + * rendering of other shapes and a custom Paint implementation, that supplies + * the pixel values of the rendered image. + * + * @param image the image to render to the destination raster + * @param xform the transform from image space to user space + */ + private void drawRenderedImageImpl(RenderedImage image, + AffineTransform xform, + Rectangle areaOfInterest) + { + // First we compute the transformation. This is made up of 3 parts: + // 1. The areaOfInterest -> image space transform. + // 2. The image space -> user space transform. + // 3. The user space -> device space transform. + AffineTransform t = new AffineTransform(); + t.translate(- areaOfInterest.x - image.getMinX(), + - areaOfInterest.y - image.getMinY()); + t.concatenate(xform); + t.concatenate(transform); + AffineTransform it = null; + try + { + it = t.createInverse(); + } + catch (NoninvertibleTransformException ex) + { + // Ignore -- we return if the transform is not invertible. + } + if (it != null) + { + // Transform the area of interest into user space. + GeneralPath aoi = new GeneralPath(areaOfInterest); + aoi.transform(xform); + // Render the shape using the standard renderer, but with a temporary + // ImagePaint. + ImagePaint p = new ImagePaint(image, it); + Paint savedPaint = paint; + try + { + paint = p; + fillShape(aoi, false); + } + finally + { + paint = savedPaint; + } + } } + /** + * Renders a renderable image. This produces a RenderedImage, which is + * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)} + * to perform the final rendering. + * + * @param image the renderable image to be rendered + * @param xform the transform from image space to user space + */ public void drawRenderableImage(RenderableImage image, AffineTransform xform) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + Rectangle areaOfInterest = new Rectangle((int) image.getMinX(), + (int) image.getHeight(), + (int) image.getWidth(), + (int) image.getHeight()); + drawRenderableImageImpl(image, xform, areaOfInterest); + + } + + /** + * Renders a renderable image. This produces a RenderedImage, which is + * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)} + * to perform the final rendering. Only the area of the image specified + * by <code>areaOfInterest</code> is rendered. + * + * @param image the renderable image to be rendered + * @param xform the transform from image space to user space + */ + private void drawRenderableImageImpl(RenderableImage image, + AffineTransform xform, + Rectangle areaOfInterest) + { + // TODO: Maybe make more clever usage of a RenderContext here. + RenderedImage rendered = image.createDefaultRendering(); + drawRenderedImageImpl(rendered, xform, areaOfInterest); } /** @@ -257,9 +462,14 @@ public abstract class AbstractGraphics2D */ public void drawString(String text, int x, int y) { - FontRenderContext ctx = getFontRenderContext(); - GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray()); - drawGlyphVector(gv, x, y); + if (isOptimized) + rawDrawString(text, x, y); + else + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray()); + drawGlyphVector(gv, x, y); + } } /** @@ -313,9 +523,6 @@ public abstract class AbstractGraphics2D */ public void fill(Shape shape) { -// Shape clipped = clipShape(shape); -// if (clipped != null) -// fillShape(clipped, false); fillShape(shape, false); } @@ -355,7 +562,6 @@ public abstract class AbstractGraphics2D else { updateOptimization(); - rawSetForeground((Color) paint); } } } @@ -537,7 +743,7 @@ public abstract class AbstractGraphics2D if (clip != null) { AffineTransform clipTransform = new AffineTransform(); - clipTransform.scale(-scaleX, -scaleY); + clipTransform.scale(1 / scaleX, 1 / scaleY); updateClip(clipTransform); } updateOptimization(); @@ -712,8 +918,7 @@ public abstract class AbstractGraphics2D public FontRenderContext getFontRenderContext() { - //return new FontRenderContext(transform, false, false); - return new FontRenderContext(new AffineTransform(), false, false); + return new FontRenderContext(transform, false, true); } /** @@ -726,22 +931,15 @@ public abstract class AbstractGraphics2D public void drawGlyphVector(GlyphVector gv, float x, float y) { int numGlyphs = gv.getNumGlyphs(); - AffineTransform t = new AffineTransform(); - t.translate(x, y); - -// // TODO: We could use fill(gv.getOutline()), but that seems to be - // slightly more inefficient. + translate(x, y); + // TODO: We could use fill(gv.getOutline()), but that seems to be + // slightly more inefficient. for (int i = 0; i < numGlyphs; i++) { - //fill(gv.getGlyphVisualBounds(i)); - GeneralPath p = new GeneralPath(gv.getGlyphOutline(i)); - p.transform(t); - //Shape clipped = clipShape(p); - //if (clipped != null) - // fillShape(clipped, true); - // FIXME: Clipping doesn't seem to work correctly. - fillShape(p, true); + Shape o = gv.getGlyphOutline(i); + fillShape(o, true); } + translate(-x, -y); } /** @@ -918,8 +1116,10 @@ public abstract class AbstractGraphics2D public void copyArea(int x, int y, int width, int height, int dx, int dy) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + if (isOptimized) + rawCopyArea(x, y, width, height, dx, dy); + else + copyAreaImpl(x, y, width, height, dx, dy); } /** @@ -978,11 +1178,15 @@ public abstract class AbstractGraphics2D */ public void clearRect(int x, int y, int width, int height) { - Paint savedForeground = getPaint(); - setPaint(getBackground()); - //System.err.println("clearRect transform type: " + transform.getType()); - fillRect(x, y, width, height); - setPaint(savedForeground); + if (isOptimized) + rawClearRect(x, y, width, height); + else + { + Paint savedForeground = getPaint(); + setPaint(getBackground()); + fillRect(x, y, width, height); + setPaint(savedForeground); + } } /** @@ -1087,47 +1291,153 @@ public abstract class AbstractGraphics2D fill(new Polygon(xPoints, yPoints, npoints)); } + /** + * Draws the specified image at the specified location. This forwards + * to {@link #drawImage(Image, AffineTransform, ImageObserver)}. + * + * @param image the image to render + * @param x the x location to render to + * @param y the y location to render to + * @param observer the image observer to receive notification + */ public boolean drawImage(Image image, int x, int y, ImageObserver observer) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + boolean ret; + if (isOptimized) + ret = rawDrawImage(image, x, y, observer); + else + { + AffineTransform t = new AffineTransform(); + t.translate(x, y); + ret = drawImage(image, t, observer); + } + return ret; } + /** + * Draws the specified image at the specified location. The image + * is scaled to the specified width and height. This forwards + * to {@link #drawImage(Image, AffineTransform, ImageObserver)}. + * + * @param image the image to render + * @param x the x location to render to + * @param y the y location to render to + * @param width the target width of the image + * @param height the target height of the image + * @param observer the image observer to receive notification + */ public boolean drawImage(Image image, int x, int y, int width, int height, ImageObserver observer) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + AffineTransform t = new AffineTransform(); + t.translate(x, y); + double scaleX = (double) image.getWidth(observer) / (double) width; + double scaleY = (double) image.getHeight(observer) / (double) height; + t.scale(scaleX, scaleY); + return drawImage(image, t, observer); } + /** + * Draws the specified image at the specified location. This forwards + * to {@link #drawImage(Image, AffineTransform, ImageObserver)}. + * + * @param image the image to render + * @param x the x location to render to + * @param y the y location to render to + * @param bgcolor the background color to use for transparent pixels + * @param observer the image observer to receive notification + */ public boolean drawImage(Image image, int x, int y, Color bgcolor, ImageObserver observer) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + AffineTransform t = new AffineTransform(); + t.translate(x, y); + // TODO: Somehow implement the background option. + return drawImage(image, t, observer); } + /** + * Draws the specified image at the specified location. The image + * is scaled to the specified width and height. This forwards + * to {@link #drawImage(Image, AffineTransform, ImageObserver)}. + * + * @param image the image to render + * @param x the x location to render to + * @param y the y location to render to + * @param width the target width of the image + * @param height the target height of the image + * @param bgcolor the background color to use for transparent pixels + * @param observer the image observer to receive notification + */ public boolean drawImage(Image image, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + AffineTransform t = new AffineTransform(); + t.translate(x, y); + double scaleX = (double) image.getWidth(observer) / (double) width; + double scaleY = (double) image.getHeight(observer) / (double) height; + t.scale(scaleX, scaleY); + // TODO: Somehow implement the background option. + return drawImage(image, t, observer); } + /** + * Draws an image fragment to a rectangular area of the target. + * + * @param image the image to render + * @param dx1 the first corner of the destination rectangle + * @param dy1 the first corner of the destination rectangle + * @param dx2 the second corner of the destination rectangle + * @param dy2 the second corner of the destination rectangle + * @param sx1 the first corner of the source rectangle + * @param sy1 the first corner of the source rectangle + * @param sx2 the second corner of the source rectangle + * @param sy2 the second corner of the source rectangle + * @param observer the image observer to be notified + */ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + int sx = Math.min(sx1, sx1); + int sy = Math.min(sy1, sy2); + int sw = Math.abs(sx1 - sx2); + int sh = Math.abs(sy1 - sy2); + int dx = Math.min(dx1, dx1); + int dy = Math.min(dy1, dy2); + int dw = Math.abs(dx1 - dx2); + int dh = Math.abs(dy1 - dy2); + + AffineTransform t = new AffineTransform(); + t.translate(sx - dx, sy - dy); + double scaleX = (double) sw / (double) dw; + double scaleY = (double) sh / (double) dh; + t.scale(scaleX, scaleY); + Rectangle areaOfInterest = new Rectangle(sx, sy, sw, sh); + return drawImageImpl(image, t, observer, areaOfInterest); } + /** + * Draws an image fragment to a rectangular area of the target. + * + * @param image the image to render + * @param dx1 the first corner of the destination rectangle + * @param dy1 the first corner of the destination rectangle + * @param dx2 the second corner of the destination rectangle + * @param dy2 the second corner of the destination rectangle + * @param sx1 the first corner of the source rectangle + * @param sy1 the first corner of the source rectangle + * @param sx2 the second corner of the source rectangle + * @param sy2 the second corner of the source rectangle + * @param bgcolor the background color to use for transparent pixels + * @param observer the image observer to be notified + */ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); + // FIXME: Do something with bgcolor. + return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } /** @@ -1154,8 +1464,8 @@ public abstract class AbstractGraphics2D Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING); // We default to antialiasing on for text as long as we have no // good hinting implemented. - antialias = (v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON - || v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + antialias = (v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + //|| v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); } else { @@ -1163,237 +1473,150 @@ public abstract class AbstractGraphics2D antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON); } - Rectangle2D userBounds = s.getBounds2D(); - - // Flatten the path. TODO: Determine the best flattening factor - // wrt to speed and quality. - PathIterator path = s.getPathIterator(getTransform(), 1.0); - - // Build up polygons and let the native backend render this using - // rawFillShape() which would provide a default implementation for - // drawPixel using a PolyScan algorithm. - double[] seg = new double[6]; - - // TODO: Use ArrayList<PolyEdge> here when availble. - ArrayList segs = new ArrayList(); - double segX = 0.; // The start point of the current edge. - double segY = 0.; - double polyX = 0.; // The start point of the current polygon. - double polyY = 0.; - - double minX = Integer.MAX_VALUE; - double maxX = Integer.MIN_VALUE; - double minY = Integer.MAX_VALUE; - double maxY = Integer.MIN_VALUE; + double offs = 0.5; + if (antialias) + offs = offs / AA_SAMPLING; - //System.err.println("fill polygon"); - while (! path.isDone()) - { - int segType = path.currentSegment(seg); - minX = Math.min(minX, seg[0]); - maxX = Math.max(maxX, seg[0]); - minY = Math.min(minY, seg[1]); - maxY = Math.max(maxY, seg[1]); - - //System.err.println("segment: " + segType + ", " + seg[0] + ", " + seg[1]); - if (segType == PathIterator.SEG_MOVETO) - { - segX = seg[0]; - segY = seg[1]; - polyX = seg[0]; - polyY = seg[1]; - } - else if (segType == PathIterator.SEG_CLOSE) - { - // Close the polyline. - PolyEdge edge = new PolyEdge(segX, segY, polyX, polyY); - segs.add(edge); - } - else if (segType == PathIterator.SEG_LINETO) - { - PolyEdge edge = new PolyEdge(segX, segY, seg[0], seg[1]); - segs.add(edge); - segX = seg[0]; - segY = seg[1]; - } - path.next(); - } + Rectangle2D userBounds = s.getBounds2D(); + Rectangle2D deviceBounds = new Rectangle2D.Double(); + ArrayList segs = getSegments(s, transform, deviceBounds, false, offs); + Rectangle2D clipBounds = new Rectangle2D.Double(); + ArrayList clipSegs = getSegments(clip, transform, clipBounds, true, offs); + segs.addAll(clipSegs); + Rectangle2D inclClipBounds = new Rectangle2D.Double(); + Rectangle2D.union(clipBounds, deviceBounds, inclClipBounds); if (segs.size() > 0) { if (antialias) - fillShapeAntialias(segs, minX, minY, maxX, maxY, userBounds); + fillShapeAntialias(segs, deviceBounds, userBounds, inclClipBounds); else - rawFillShape(segs, minX, minY, maxX, maxY, userBounds); + fillShapeImpl(segs, deviceBounds, userBounds, inclClipBounds); } } /** - * Draws one pixel in the target coordinate space. This method draws the - * specified pixel by getting the painting pixel for that coordinate - * from the paintContext and compositing the pixel with the compositeContext. - * The resulting pixel is then set by calling {@link #rawSetPixel}. + * Returns the color model of this Graphics object. * - * @param x the x coordinate - * @param y the y coordinate + * @return the color model of this Graphics object */ - protected void drawPixel(int x, int y) - { - // FIXME: Implement efficient compositing. - if (! (paint instanceof Color)) - { - int[] paintPixel = paintRaster.getPixel(x, y, pixel); - Color c = new Color(paintPixel[0], paintPixel[1], paintPixel[2]); - rawSetForeground(c); - } - rawSetPixel(x, y); - } + protected abstract ColorModel getColorModel(); /** - * Draws a pixel in the target coordinate space using the specified color. - * - * @param x the x coordinate - * @param y the y coordinate + * Returns the bounds of the target. + * + * @return the bounds of the target */ - protected void rawSetPixel(int x, int y) + protected Rectangle getDeviceBounds() { - // FIXME: Provide default implementation or remove method. + return destinationRaster.getBounds(); } /** - * Sets the foreground color for drawing. + * Draws a line in optimization mode. The implementation should respect the + * clip and translation. It can assume that the clip is a rectangle and that + * the transform is only a translating transform. * - * @param c the color to set + * @param x0 the starting point, X coordinate + * @param y0 the starting point, Y coordinate + * @param x1 the end point, X coordinate + * @param y1 the end point, Y coordinate */ - protected void rawSetForeground(Color c) - { - // Probably remove method. - } - - protected void rawSetForeground(int r, int g, int b) + protected void rawDrawLine(int x0, int y0, int x1, int y1) { - rawSetForeground(new Color(r, g, b)); + draw(new Line2D.Float(x0, y0, x1, y1)); } /** - * Returns the color model of this Graphics object. + * Draws a string in optimization mode. The implementation should respect the + * clip and translation. It can assume that the clip is a rectangle and that + * the transform is only a translating transform. * - * @return the color model of this Graphics object + * @param text the string to be drawn + * @param x the start of the baseline, X coordinate + * @param y the start of the baseline, Y coordinate */ - protected abstract ColorModel getColorModel(); + protected void rawDrawString(String text, int x, int y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray()); + drawGlyphVector(gv, x, y); + } /** - * Returns the bounds of the target. + * Clears a rectangle in optimization mode. The implementation should respect the + * clip and translation. It can assume that the clip is a rectangle and that + * the transform is only a translating transform. * - * @return the bounds of the target + * @param x the upper left corner, X coordinate + * @param y the upper left corner, Y coordinate + * @param w the width + * @param h the height */ - protected Rectangle getDeviceBounds() + protected void rawClearRect(int x, int y, int w, int h) { - return destinationRaster.getBounds(); + Paint savedForeground = getPaint(); + setPaint(getBackground()); + rawFillRect(x, y, w, h); + setPaint(savedForeground); } /** - * Returns the bounds of the drawing area in user space. + * Fills a rectangle in optimization mode. The implementation should respect + * the clip but can assume that it is a rectangle. * - * @return the bounds of the drawing area in user space + * @param x the upper left corner, X coordinate + * @param y the upper left corner, Y coordinate + * @param w the width + * @param h the height */ - protected Rectangle2D getUserBounds() + protected void rawFillRect(int x, int y, int w, int h) { - PathIterator pathIter = getDeviceBounds().getPathIterator(getTransform()); - GeneralPath path = new GeneralPath(); - path.append(pathIter, true); - return path.getBounds(); - + fill(new Rectangle(x, y, w, h)); } + /** - * Draws a line in optimization mode. The implementation should respect the - * clip but can assume that it is a rectangle. + * Draws an image in optimization mode. The implementation should respect + * the clip but can assume that it is a rectangle. * - * @param x0 the starting point, X coordinate - * @param y0 the starting point, Y coordinate - * @param x1 the end point, X coordinate - * @param y1 the end point, Y coordinate + * @param image the image to be painted + * @param x the location, X coordinate + * @param y the location, Y coordinate + * @param obs the image observer to be notified + * + * @return <code>true</code> when the image is painted completely, + * <code>false</code> if it is still rendered */ - protected void rawDrawLine(int x0, int y0, int x1, int y1) + protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs) { - // This is an implementation of Bresenham's line drawing algorithm. - int dy = y1 - y0; - int dx = x1 - x0; - int stepx, stepy; - - if (dy < 0) - { - dy = -dy; - stepy = -1; - } - else - { - stepy = 1; - } - if (dx < 0) - { - dx = -dx; - stepx = -1; - } - else - { - stepx = 1; - } - dy <<= 1; - dx <<= 1; - - drawPixel(x0, y0); - if (dx > dy) - { - int fraction = dy - (dx >> 1); // same as 2*dy - dx - while (x0 != x1) - { - if (fraction >= 0) - { - y0 += stepy; - fraction -= dx; - } - x0 += stepx; - fraction += dy; - drawPixel(x0, y0); - } - } - else - { - int fraction = dx - (dy >> 1); - while (y0 != y1) - { - if (fraction >= 0) - { - x0 += stepx; - fraction -= dy; - } - y0 += stepy; - fraction += dx; - drawPixel(x0, y0); - } - } + AffineTransform t = new AffineTransform(); + t.translate(x, y); + return drawImage(image, t, obs); } /** - * Fills a rectangle in optimization mode. The implementation should respect - * the clip but can assume that it is a rectangle. + * Copies a rectangular region to another location. * * @param x the upper left corner, X coordinate * @param y the upper left corner, Y coordinate * @param w the width * @param h the height + * @param dx + * @param dy */ - protected void rawFillRect(int x, int y, int w, int h) + protected void rawCopyArea(int x, int y, int w, int h, int dx, int dy) { - int x2 = x + w; - int y2 = y + h; - for (int xc = x; xc < x2; xc++) - { - for (int yc = y; yc < y2; yc++) - { - drawPixel(xc, yc); - } - } + copyAreaImpl(x, y, w, h, dx, dy); + } + + // Private implementation methods. + + /** + * Copies a rectangular area of the target raster to a different location. + */ + private void copyAreaImpl(int x, int y, int w, int h, int dx, int dy) + { + // FIXME: Implement this properly. + throw new UnsupportedOperationException("Not implemented yet."); } /** @@ -1403,8 +1626,9 @@ public abstract class AbstractGraphics2D * * The polygon is already clipped when this method is called. */ - protected void rawFillShape(ArrayList segs, double minX, double minY, - double maxX, double maxY, Rectangle2D userBounds) + private void fillShapeImpl(ArrayList segs, Rectangle2D deviceBounds2D, + Rectangle2D userBounds, + Rectangle2D inclClipBounds) { // This is an implementation of a polygon scanline conversion algorithm // described here: @@ -1413,19 +1637,25 @@ public abstract class AbstractGraphics2D // Create table of all edges. // The edge buckets, sorted and indexed by their Y values. + double minX = deviceBounds2D.getMinX(); + double minY = deviceBounds2D.getMinY(); + double maxX = deviceBounds2D.getMaxX(); + double maxY = deviceBounds2D.getMaxY(); + double icMinY = inclClipBounds.getMinY(); + double icMaxY = inclClipBounds.getMaxY(); Rectangle deviceBounds = new Rectangle((int) minX, (int) minY, (int) Math.ceil(maxX) - (int) minX, (int) Math.ceil(maxY) - (int) minY); PaintContext pCtx = paint.createContext(getColorModel(), deviceBounds, userBounds, transform, renderingHints); - ArrayList[] edgeTable = new ArrayList[(int) Math.ceil(maxY) - - (int) Math.ceil(minY) + 1]; + ArrayList[] edgeTable = new ArrayList[(int) Math.ceil(icMaxY) + - (int) Math.ceil(icMinY) + 1]; for (Iterator i = segs.iterator(); i.hasNext();) { PolyEdge edge = (PolyEdge) i.next(); - int yindex = (int) ((int) Math.ceil(edge.y0) - (int) Math.ceil(minY)); + int yindex = (int) ((int) Math.ceil(edge.y0) - (int) Math.ceil(icMinY)); if (edgeTable[yindex] == null) // Create bucket when needed. edgeTable[yindex] = new ArrayList(); edgeTable[yindex].add(edge); // Add edge to the bucket of its line. @@ -1445,7 +1675,7 @@ public abstract class AbstractGraphics2D PolyEdgeComparator comparator = new PolyEdgeComparator(); // Scan all relevant lines. - int minYInt = (int) Math.ceil(minY); + int minYInt = (int) Math.ceil(icMinY); for (int y = minYInt; y <= maxY; y++) { ArrayList bucket = edgeTable[y - minYInt]; @@ -1496,35 +1726,30 @@ public abstract class AbstractGraphics2D // Now draw all pixels inside the polygon. // This is the last edge that intersected the scanline. PolyEdge previous = null; // Gets initialized below. - boolean active = false; + boolean insideShape = false; + boolean insideClip = false; //System.err.println("scanline: " + y); for (Iterator i = activeEdges.iterator(); i.hasNext();) { PolyEdge edge = (PolyEdge) i.next(); - // Only fill scanline, if the current edge actually intersects - // the scanline. There may be edges that lie completely - // within the current scanline. - //System.err.println("previous: " + previous); - //System.err.println("edge: " + edge); - if (active) + if (edge.y1 <= y) + continue; + + // Draw scanline when we are inside the shape AND inside the + // clip. + if (insideClip && insideShape) { - if (edge.y1 > y) - { - int x0 = (int) previous.xIntersection; - int x1 = (int) edge.xIntersection; - fillScanline(pCtx, x0, x1, y); - previous = edge; - active = false; - } + int x0 = (int) previous.xIntersection; + int x1 = (int) edge.xIntersection; + if (x0 < x1) + fillScanline(pCtx, x0, x1, y); } + // Update state. + previous = edge; + if (edge.isClip) + insideClip = ! insideClip; else - { - if (edge.y1 > y) - { - previous = edge; - active = true; - } - } + insideShape = ! insideShape; } } pCtx.dispose(); @@ -1544,7 +1769,8 @@ public abstract class AbstractGraphics2D CompositeContext cCtx = composite.createContext(paintColorModel, getColorModel(), renderingHints); - cCtx.compose(paintRaster, destinationRaster, destinationRaster); + WritableRaster targetChild = destinationRaster.createWritableTranslatedChild(-x0,- y); + cCtx.compose(paintRaster, targetChild, targetChild); updateRaster(destinationRaster, x0, y, x1 - x0, 1); cCtx.dispose(); } @@ -1553,14 +1779,10 @@ public abstract class AbstractGraphics2D * Fills arbitrary shapes in an anti-aliased fashion. * * @param segs the line segments which define the shape which is to be filled - * @param minX the bounding box, left X - * @param minY the bounding box, upper Y - * @param maxX the bounding box, right X - * @param maxY the bounding box, lower Y */ - private void fillShapeAntialias(ArrayList segs, double minX, double minY, - double maxX, double maxY, - Rectangle2D userBounds) + private void fillShapeAntialias(ArrayList segs, Rectangle2D deviceBounds2D, + Rectangle2D userBounds, + Rectangle2D inclClipBounds) { // This is an implementation of a polygon scanline conversion algorithm // described here: @@ -1568,23 +1790,32 @@ public abstract class AbstractGraphics2D // The antialiasing is implemented using a sampling technique, we do // not scan whole lines but fractions of the line. + double minX = deviceBounds2D.getMinX(); + double minY = deviceBounds2D.getMinY(); + double maxX = deviceBounds2D.getMaxX(); + double maxY = deviceBounds2D.getMaxY(); + double icMinY = inclClipBounds.getMinY(); + double icMaxY = inclClipBounds.getMaxY(); + double icMinX = inclClipBounds.getMinX(); + double icMaxX = inclClipBounds.getMaxX(); Rectangle deviceBounds = new Rectangle((int) minX, (int) minY, (int) Math.ceil(maxX) - (int) minX, (int) Math.ceil(maxY) - (int) minY); - PaintContext pCtx = paint.createContext(getColorModel(), deviceBounds, + PaintContext pCtx = paint.createContext(ColorModel.getRGBdefault(), + deviceBounds, userBounds, transform, renderingHints); // This array will contain the oversampled transparency values for // each pixel in the scanline. - int numScanlines = (int) Math.ceil(maxY) - (int) minY; - int numScanlinePixels = (int) Math.ceil(maxX) - (int) minX + 1; + int numScanlines = (int) Math.ceil(icMaxY) - (int) icMinY; + int numScanlinePixels = (int) Math.ceil(icMaxX) - (int) icMinX + 1; if (alpha == null || alpha.length < (numScanlinePixels + 1)) alpha = new int[numScanlinePixels + 1]; - int firstLine = (int) minY; + int firstLine = (int) icMinY; //System.err.println("minY: " + minY); - int firstSubline = (int) (Math.ceil((minY - Math.floor(minY)) * AA_SAMPLING)); + int firstSubline = (int) (Math.ceil((icMinY - Math.floor(icMinY)) * AA_SAMPLING)); double firstLineDouble = firstLine + firstSubline / (double) AA_SAMPLING; //System.err.println("firstSubline: " + firstSubline); @@ -1629,8 +1860,11 @@ public abstract class AbstractGraphics2D // Scan all lines. int yindex = 0; //System.err.println("firstLine: " + firstLine + ", maxY: " + maxY + ", firstSubline: " + firstSubline); - for (int y = firstLine; y <= maxY; y++) + for (int y = firstLine; y <= icMaxY; y++) { + int leftX = (int) icMaxX; + int rightX = (int) icMinX; + boolean emptyScanline = true; for (int subY = firstSubline; subY < AA_SAMPLING; subY++) { //System.err.println("scanline: " + y + ", subScanline: " + subY); @@ -1687,17 +1921,16 @@ public abstract class AbstractGraphics2D // Now draw all pixels inside the polygon. // This is the last edge that intersected the scanline. PolyEdge previous = null; // Gets initialized below. - boolean active = false; + boolean insideClip = false; + boolean insideShape = false; //System.err.println("scanline: " + y + ", subscanline: " + subY); for (Iterator i = activeEdges.iterator(); i.hasNext();) { PolyEdge edge = (PolyEdge) i.next(); - // Only fill scanline, if the current edge actually intersects - // the scanline. There may be edges that lie completely - // within the current scanline. - //System.err.println("previous: " + previous); - //System.err.println("edge: " + edge); - if (active) + if (edge.y1 <= (y + (subY / (double) AA_SAMPLING))) + continue; + + if (insideClip && insideShape) { // TODO: Use integer arithmetics here. if (edge.y1 > (y + (subY / (double) AA_SAMPLING))) @@ -1708,32 +1941,30 @@ public abstract class AbstractGraphics2D int x1 = (int) Math.min(Math.max(edge.xIntersection, minX), maxX); //System.err.println("minX: " + minX + ", x0: " + x0 + ", x1: " + x1 + ", maxX: " + maxX); // TODO: Pull out cast. - alpha[x0 - (int) minX]++; - alpha[x1 - (int) minX + 1]--; - previous = edge; - active = false; + int left = x0 - (int) minX; + int right = x1 - (int) minX + 1; + alpha[left]++; + alpha[right]--; + leftX = Math.min(x0, leftX); + rightX = Math.max(x1+2, rightX); + emptyScanline = false; } } + previous = edge; + if (edge.isClip) + insideClip = ! insideClip; else - { - // TODO: Use integer arithmetics here. - if (edge.y1 > (y + (subY / (double) AA_SAMPLING))) - { - //System.err.println(edge); - previous = edge; - active = true; - } - } + insideShape = ! insideShape; } yindex++; } firstSubline = 0; // Render full scanline. //System.err.println("scanline: " + y); - fillScanlineAA(alpha, (int) minX, (int) y, numScanlinePixels, pCtx); + if (! emptyScanline) + fillScanlineAA(alpha, leftX, (int) y, rightX - leftX, pCtx, + (int) minX); } - if (paint instanceof Color && composite == AlphaComposite.SrcOver) - rawSetForeground((Color) paint); pCtx.dispose(); } @@ -1747,40 +1978,54 @@ public abstract class AbstractGraphics2D * @param x0 the beginning of the scanline * @param y the y coordinate of the line */ - private void fillScanlineAA(int[] alpha, int x0, int y, int numScanlinePixels, - PaintContext pCtx) + private void fillScanlineAA(int[] alpha, int x0, int yy, int numPixels, + PaintContext pCtx, int offs) { - // FIXME: This doesn't work. Fixit. CompositeContext cCtx = composite.createContext(pCtx.getColorModel(), getColorModel(), renderingHints); - Raster paintRaster = pCtx.getRaster(x0, y, numScanlinePixels, 1); - System.err.println("paintColorModel: " + pCtx.getColorModel()); + Raster paintRaster = pCtx.getRaster(x0, yy, numPixels, 1); + //System.err.println("paintColorModel: " + pCtx.getColorModel()); WritableRaster aaRaster = paintRaster.createCompatibleWritableRaster(); int numBands = paintRaster.getNumBands(); - int[] pixels = new int[numScanlinePixels + paintRaster.getNumBands()]; - pixels = paintRaster.getPixels(x0, y, numScanlinePixels, 1, pixels); ColorModel cm = pCtx.getColorModel(); - double lastAlpha = 0.; int lastAlphaInt = 0; - int[] components = new int[4]; - - for (int i = 0; i < pixels.length; i++) + + Object pixel = null; + int[] comps = null; + int x1 = x0 + numPixels; + for (int x = x0; x < x1; x++) { + int i = x - offs; if (alpha[i] != 0) { lastAlphaInt += alpha[i]; - lastAlpha = lastAlphaInt / AA_SAMPLING; + lastAlpha = (double) lastAlphaInt / (double) AA_SAMPLING; + alpha[i] = 0; } - components = cm.getComponents(pixel[i], components, 0); - components[0] = (int) (components[0] * lastAlpha); - pixel[i] = cm.getDataElement(components, 0); + pixel = paintRaster.getDataElements(x - x0, 0, pixel); + comps = cm.getComponents(pixel, comps, 0); + if (cm.hasAlpha() && ! cm.isAlphaPremultiplied()) + comps[comps.length - 1] *= lastAlpha; + else + { + int max; + if (cm.hasAlpha()) + max = comps.length - 2; + else + max = comps.length - 1; + for (int j = 0; j < max; j++) + comps[j] *= lastAlpha; + } + pixel = cm.getDataElements(comps, 0, pixel); + aaRaster.setDataElements(x - x0, 0, pixel); } - aaRaster.setPixels(0, 0, numScanlinePixels, 1, pixels); - cCtx.compose(aaRaster, destinationRaster, destinationRaster); - updateRaster(destinationRaster, x0, y, numScanlinePixels, 1); + WritableRaster targetChild = + destinationRaster.createWritableTranslatedChild(-x0, -yy); + cCtx.compose(aaRaster, targetChild, targetChild); + updateRaster(destinationRaster, x0, yy, numPixels, 1); cCtx.dispose(); } @@ -1798,8 +2043,8 @@ public abstract class AbstractGraphics2D // FIXME: Should not be necessary. A clip of null should mean // 'clip against device bounds. - clip = getDeviceBounds(); destinationRaster = getDestinationRaster(); + clip = getDeviceBounds(); } /** @@ -1912,40 +2157,77 @@ public abstract class AbstractGraphics2D } /** - * Clips the specified shape using the current clip. If the resulting shape - * is empty, this will return <code>null</code>. + * Converts the specified shape into a list of segments. * - * @param s the shape to clip + * @param s the shape to convert + * @param t the transformation to apply before converting + * @param deviceBounds an output parameter; holds the bounding rectangle of + * s in device space after return + * @param isClip true when the shape is a clip, false for normal shapes; + * this influences the settings in the created PolyEdge instances. * - * @return the clipped shape or <code>null</code> if the result is empty + * @return a list of PolyEdge that form the shape in device space */ - private Shape clipShape(Shape s) + private ArrayList getSegments(Shape s, AffineTransform t, + Rectangle2D deviceBounds, boolean isClip, + double offs) { - Shape clipped = null; + // Flatten the path. TODO: Determine the best flattening factor + // wrt to speed and quality. + PathIterator path = s.getPathIterator(getTransform(), 1.0); - // Clip the shape if necessary. - if (clip != null) - { - Area a; - if (! (s instanceof Area)) - a = new Area(s); - else - a = (Area) s; + // Build up polygons and let the native backend render this using + // rawFillShape() which would provide a default implementation for + // drawPixel using a PolyScan algorithm. + double[] seg = new double[6]; - Area clipArea; - if (! (clip instanceof Area)) - clipArea = new Area(clip); - else - clipArea = (Area) clip; + // TODO: Use ArrayList<PolyEdge> here when availble. + ArrayList segs = new ArrayList(); + double segX = 0.; // The start point of the current edge. + double segY = 0.; + double polyX = 0.; // The start point of the current polygon. + double polyY = 0.; - a.intersect(clipArea); - if (! a.isEmpty()) - clipped = a; - } - else + double minX = Integer.MAX_VALUE; + double maxX = Integer.MIN_VALUE; + double minY = Integer.MAX_VALUE; + double maxY = Integer.MIN_VALUE; + + //System.err.println("fill polygon"); + while (! path.isDone()) { - clipped = s; + int segType = path.currentSegment(seg); + minX = Math.min(minX, seg[0]); + maxX = Math.max(maxX, seg[0]); + minY = Math.min(minY, seg[1]); + maxY = Math.max(maxY, seg[1]); + + //System.err.println("segment: " + segType + ", " + seg[0] + ", " + seg[1]); + if (segType == PathIterator.SEG_MOVETO) + { + segX = seg[0]; + segY = seg[1]; + polyX = seg[0]; + polyY = seg[1]; + } + else if (segType == PathIterator.SEG_CLOSE) + { + // Close the polyline. + PolyEdge edge = new PolyEdge(segX, segY - offs, + polyX, polyY - offs, isClip); + segs.add(edge); + } + else if (segType == PathIterator.SEG_LINETO) + { + PolyEdge edge = new PolyEdge(segX, segY - offs, + seg[0], seg[1] - offs, isClip); + segs.add(edge); + segX = seg[0]; + segY = seg[1]; + } + path.next(); } - return clipped; + deviceBounds.setRect(minX, minY, maxX - minX, maxY - minY); + return segs; } } diff --git a/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java b/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java index e67c921..2e3690d 100644 --- a/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java +++ b/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java @@ -236,7 +236,7 @@ public class AlphaCompositeContext } else { - for (int i = srcComponentsLength - 1; i >= 0; i--) + for (int i = srcComponentsLength - 2; i >= 0; i--) srcComponents[i] *= srcComponents[srcComponentsLength - 1]; } if (! dstColorModel.isAlphaPremultiplied()) diff --git a/libjava/classpath/gnu/java/awt/java2d/ImagePaint.java b/libjava/classpath/gnu/java/awt/java2d/ImagePaint.java new file mode 100644 index 0000000..7e5fb56 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/ImagePaint.java @@ -0,0 +1,192 @@ +/* ImagePaint.java -- Supplies the pixels for image rendering + 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.java2d; + +import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Transparency; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; + +/** + * This class is used as a temporary Paint object to supply the pixel values + * for image rendering using the normal scanline conversion implementation. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class ImagePaint + implements Paint +{ + + /** + * The PaintContext implementation for the ImagePaint. + */ + private class ImagePaintContext + implements PaintContext + { + + /** + * The target raster. + */ + private WritableRaster target; + + /** + * Nothing to do here. + */ + public void dispose() + { + // Nothing to do here. + } + + /** + * Returns the color model. + * + * @return the color model + */ + public ColorModel getColorModel() + { + return image.getColorModel(); + } + + /** + * Supplies the pixel to be rendered. + * + * @see PaintContext#getRaster(int, int, int, int) + */ + public Raster getRaster(int x1, int y1, int w, int h) + { + ensureRasterSize(w, h); + int x2 = x1 + w; + int y2 = y1 + h; + float[] src = new float[2]; + float[] dest = new float[2]; + Raster source = image.getData(); + int minX = source.getMinX(); + int maxX = source.getWidth() + minX; + int minY = source.getMinY(); + int maxY = source.getHeight() + minY; + Object pixel = null; + for (int y = y1; y < y2; y++) + { + for (int x = x1; x < x2; x++) + { + src[0] = x; + src[1] = y; + transform.transform(src, 0, dest, 0, 1); + int dx = (int) dest[0]; + int dy = (int) dest[1]; + // Pixels outside the source image are not of interest, skip + // them. + if (dx >= minX && dx < maxX && dy >= minY && dy < maxY) + { + pixel = source.getDataElements(dx, dy, pixel); + target.setDataElements(x - x1, y - y1, pixel); + } + } + } + return target; + } + + /** + * Ensures that the target raster exists and has at least the specified + * size. + * + * @param w the requested target width + * @param h the requested target height + */ + private void ensureRasterSize(int w, int h) + { + if (target == null || target.getWidth() < w || target.getHeight() < h) + { + Raster s = image.getData(); + target = s.createCompatibleWritableRaster(w, h); + } + } + } + + /** + * The image to render. + */ + RenderedImage image; + + /** + * The transform from image space to device space. This is the inversed + * transform of the concatenated + * transform image space -> user space -> device space transform. + */ + AffineTransform transform; + + /** + * Creates a new ImagePaint for rendering the specified image using the + * specified device space -> image space transform. This transform + * is the inversed transform of the usual image space -> user space -> device + * space transform. + * + * The ImagePaint will only render the image in the specified area of + * interest (which is specified in image space). + * + * @param i the image to render + * @param t the device space to user space transform + */ + ImagePaint(RenderedImage i, AffineTransform t) + { + image = i; + transform = t; + } + + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + return new ImagePaintContext(); + } + + public int getTransparency() + { + return Transparency.OPAQUE; + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java index 621bd3a..8dbdbab 100644 --- a/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java +++ b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java @@ -65,6 +65,11 @@ public class PolyEdge double xIntersection; /** + * Indicates whether this edge is from the clip or from the target shape. + */ + boolean isClip; + + /** * Creates a new PolyEdge with the specified coordinates. * * @param x0 the starting point, x coordinate @@ -72,8 +77,9 @@ public class PolyEdge * @param x1 the end point, x coordinate * @param y1 the end point, y coordinate */ - PolyEdge(double x0, double y0, double x1, double y1) + PolyEdge(double x0, double y0, double x1, double y1, boolean clip) { + isClip = clip; if (y0 < y1) { this.x0 = x0; diff --git a/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java b/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java index 4de8035..98d47b4 100644 --- a/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java +++ b/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java @@ -65,8 +65,10 @@ public class RasterGraphics public RasterGraphics(WritableRaster r, ColorModel cm) { + super(); raster = r; colorModel = cm; + init(); } /** diff --git a/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java b/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java new file mode 100644 index 0000000..1a782ce --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java @@ -0,0 +1,205 @@ +/* TexturePaintContext.java -- PaintContext impl for TexturePaint + 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.java2d; + +import java.awt.AWTError; +import java.awt.PaintContext; +import java.awt.Rectangle; +import java.awt.TexturePaint; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * A {@link PaintContext} implementation for {@link TexturePaint}, done in + * pure Java. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class TexturePaintContext + implements PaintContext +{ + + /** + * The TexturePaint object. + */ + private BufferedImage image; + + /** + * The Raster that holds the texture. + */ + private WritableRaster paintRaster; + + /** + * The transform from userspace into device space. + */ + private AffineTransform transform; + + /** + * Creates a new TexturePaintContext for the specified TexturePaint object. + * This initializes the Raster which is returned by + * {@link #getRaster(int, int, int, int)}. + * + * @param t the texture paint object + * @param db the bounds of the target raster in device space + * @param ub the bounds of the target raster in user space + * @param xform the transformation from user space to device space + */ + public TexturePaintContext(TexturePaint t, Rectangle db, + Rectangle2D ub, AffineTransform xform) + { + image = t.getImage(); + + try + { + // Prepare transform for mapping from device space into image space. + // In order to achieve this we take the transform for userspace-> + // devicespace, append the correct scale and translation according + // to the anchor (which gives us the image->userspace->devicespace + // transform), and invert that (which gives use the device->user->image + // transform). + Rectangle2D anchor = t.getAnchorRect(); + BufferedImage image = t.getImage(); + double scaleX = anchor.getWidth() / image.getWidth(); + double scaleY = anchor.getHeight() / image.getHeight(); + transform = (AffineTransform) xform.clone(); + transform.scale(scaleX, scaleY); + transform.translate(-anchor.getMinX(), -anchor.getMaxX()); + transform = transform.createInverse(); + } + catch (NoninvertibleTransformException ex) + { + AWTError err = + new AWTError("Unexpected NoninvertibleTransformException"); + err.initCause(ex); + throw err; + } + } + + /** + * Disposes the PaintContext. Nothing is to do here, since we don't use + * any native resources in that implementation. + */ + public void dispose() + { + // Nothing to do here. + } + + /** + * Returns the color model of this PaintContext. This implementation returnes + * the color model used by the BufferedImage in the TexturePaint object, + * this avoids costly color model transformations (at least at this point). + * + * @return the color model of this PaintContext + */ + public ColorModel getColorModel() + { + return image.getColorModel(); + } + + /** + * Returns the Raster that is used for painting. + * + * @param x1 the x location of the raster inside the user bounds of this paint + * context + * @param y1 the y location of the raster inside the user bounds of this paint + * context + * @param w the width + * @param h the height + * + * @return the Raster that is used for painting + * + */ + public Raster getRaster(int x1, int y1, int w, int h) + { + ensureRasterSize(w, h); + int x2 = x1 + w; + int y2 = y1 + h; + float[] src = new float[2]; + float[] dest = new float[2]; + Raster source = image.getData(); + int minX = source.getMinX(); + int width = source.getWidth(); + int minY = source.getMinY(); + int height = source.getHeight(); + Object pixel = null; + for (int y = y1; y < y2; y++) + { + for (int x = x1; x < x2; x++) + { + // Transform the coordinates from device space into image space. + src[0] = x; + src[1] = y; + transform.transform(src, 0, dest, 0, 1); + int dx = (int) dest[0]; + int dy = (int) dest[1]; + + // The modulo operation gives us the replication effect. + dx = ((dx - minX) % width) + minX; + dy = ((dy - minY) % height) + minY; + + // Copy the pixel. + pixel = source.getDataElements(dx, dy, pixel); + paintRaster.setDataElements(x - x1, y - y1, pixel); + } + } + return paintRaster; + } + + /** + * Ensures that the target raster exists and has at least the specified + * size. + * + * @param w the requested target width + * @param h the requested target height + */ + private void ensureRasterSize(int w, int h) + { + if (paintRaster == null || paintRaster.getWidth() < w + || paintRaster.getHeight() < h) + { + Raster s = image.getData(); + paintRaster = s.createCompatibleWritableRaster(w, h); + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java new file mode 100644 index 0000000..d9d300d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java @@ -0,0 +1,258 @@ +/* BufferedImageGraphics.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.peer.gtk; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.RenderedImage; +import java.awt.image.ImageObserver; +import java.util.WeakHashMap; + +/** + * Implementation of Graphics2D on a Cairo surface. + * + * Simutanously maintains a CairoSurface and updates the + * BufferedImage from that after each drawing operation. + */ +public class BufferedImageGraphics extends CairoGraphics2D +{ + /** + * the buffered Image. + */ + private BufferedImage image; + + /** + * Image size. + */ + private int imageWidth, imageHeight; + + /** + * The cairo surface that we actually draw on. + */ + CairoSurface surface; + + /** + * Cache BufferedImageGraphics surfaces. + */ + static WeakHashMap bufferedImages = new WeakHashMap(); + + /** + * Its corresponding cairo_t. + */ + private long cairo_t; + + /** + * Colormodels we recognize for fast copying. + */ + static ColorModel rgb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF); + static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, + 0xFF000000); + private boolean hasFastCM; + private boolean hasAlpha; + + + public BufferedImageGraphics(BufferedImage bi) + { + this.image = bi; + imageWidth = bi.getWidth(); + imageHeight = bi.getHeight(); + if(bi.getColorModel().equals(rgb32)) + { + hasFastCM = true; + hasAlpha = false; + } + else if(bi.getColorModel().equals(argb32)) + { + hasFastCM = true; + hasAlpha = false; + } + else + hasFastCM = false; + + // Cache surfaces. + if( bufferedImages.get( bi ) != null ) + surface = (CairoSurface)bufferedImages.get( bi ); + else + { + surface = new CairoSurface( imageWidth, imageHeight ); + bufferedImages.put(bi, surface); + } + + cairo_t = surface.newCairoContext(); + + DataBuffer db = bi.getRaster().getDataBuffer(); + int[] pixels; + // get pixels + + if(db instanceof CairoSurface) + pixels = ((CairoSurface)db).getPixels(imageWidth * imageHeight); + else + { + if( hasFastCM ) + { + pixels = ((DataBufferInt)db).getData(); + if( !hasAlpha ) + for(int i = 0; i < pixels.length; i++) + pixels[i] &= 0xFFFFFFFF; + } + else + { + pixels = CairoGraphics2D.findSimpleIntegerArray + (image.getColorModel(),image.getData()); + } + } + surface.setPixels( pixels ); + + setup( cairo_t ); + setClip(0, 0, imageWidth, imageHeight); + } + + BufferedImageGraphics(BufferedImageGraphics copyFrom) + { + surface = copyFrom.surface; + cairo_t = surface.newCairoContext(); + imageWidth = copyFrom.imageWidth; + imageHeight = copyFrom.imageHeight; + copy( copyFrom, cairo_t ); + setClip(0, 0, surface.width, surface.height); + } + + /** + * Update a rectangle of the bufferedImage. This can be improved upon a lot. + */ + private void updateBufferedImage(int x, int y, int width, int height) + { + int[] pixels = surface.getPixels(imageWidth * imageHeight); + + if( x > imageWidth || y > imageHeight ) + return; + // Clip edges. + if( x < 0 ){ width = width + x; x = 0; } + if( y < 0 ){ height = height + y; y = 0; } + if( x + width > imageWidth ) + width = imageWidth - x; + if( y + height > imageHeight ) + height = imageHeight - y; + + 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); + } + + /** + * Abstract methods. + */ + public Graphics create() + { + return new BufferedImageGraphics(this); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + return null; + } + + protected Rectangle2D getRealBounds() + { + return new Rectangle2D.Double(0.0, 0.0, imageWidth, imageHeight); + } + + public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy) + { + surface.copyAreaNative(x, y, width, height, dx, dy, surface.width); + updateBufferedImage(x + dx, y + dy, width, height); + } + + /** + * Overloaded methods that do actual drawing need to enter the gdk threads + * and also do certain things before and after. + */ + public void draw(Shape s) + { + super.draw(s); + Rectangle r = s.getBounds(); + updateBufferedImage(r.x, r.y, r.width, r.height); + } + + public void fill(Shape s) + { + super.fill(s); + Rectangle r = s.getBounds(); + updateBufferedImage(r.x, r.y, r.width, r.height); + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + super.drawRenderedImage(image, xform); + updateBufferedImage(0, 0, imageWidth, imageHeight); + } + + 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; + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + super.drawGlyphVector(gv, x, y); + updateBufferedImage(0, 0, imageWidth, imageHeight); + } +} + diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java b/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java index dcebc2f..3179d33 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java @@ -1,5 +1,5 @@ -/* GdkGraphics2D.java -- - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. +/* CairoGraphics2D.java -- + Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -58,17 +58,21 @@ import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; +import java.awt.Polygon; import java.awt.TexturePaint; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; +import java.awt.geom.Area; +import java.awt.geom.Line2D; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; @@ -92,92 +96,141 @@ import java.util.HashMap; import java.util.Map; import java.util.Stack; -public class GdkGraphics2D extends Graphics2D +/** + * This is an abstract implementation of Graphics2D on Cairo. + * + * It should be subclassed for different Cairo contexts. + * + * Note for subclassers: Apart from the constructor (see comments below), + * The following abstract methods must be implemented: + * + * Graphics create() + * GraphicsConfiguration getDeviceConfiguration() + * copyArea(int x, int y, int width, int height, int dx, int dy) + * + * Also, dispose() must be overloaded to free any native datastructures + * used by subclass and in addition call super.dispose() to free the + * native cairographics2d structure and cairo_t. + * + * @author Sven de Marothy + */ +public abstract class CairoGraphics2D extends Graphics2D { - ////////////////////////////////////// - ////// State Management Methods ////// - ////////////////////////////////////// - static { - if (! Configuration.GTK_CAIRO_ENABLED) - throw new Error("Graphics2D not implemented. " - + "Cairo was not found or disabled at configure time"); - System.loadLibrary("gtkpeer"); - - initStaticState(); } - - static native void initStaticState(); - - private final int native_state = GtkGenericPeer.getUniqueInteger(); - // These are package-private to avoid accessor methods. + /** + * Important: This is a pointer to the native cairographics2d structure + * + * DO NOT CHANGE WITHOUT CHANGING NATIVE CODE. + */ + long nativePointer; + + // Drawing state variables + /** + * The current paint + */ Paint paint; + + /** + * The current stroke + */ Stroke stroke; - Color fg; - Color bg; + + /* + * Current foreground and background color. + */ + Color fg, bg; + + /** + * Current clip shape. + */ Shape clip; + + /** + * Current transform. + */ AffineTransform transform; - private GtkComponentPeer component; - // This is package-private to avoid an accessor method. + + /** + * Current font. + */ Font font; - private RenderingHints hints; - private BufferedImage bimage; - private boolean pixelConversionRequired; - private int[] pixelBuffer; - // This is package-private to avoid an accessor method. + + /** + * The current compositing context, if any. + */ Composite comp; - private Stack stateStack; - - private native void initStateUnlocked(GtkComponentPeer component); - private native void initState(GtkComponentPeer component); - private native void initState(int width, int height); - private native void initState(int[] pixes, int width, int height); - private native void copyState(GdkGraphics2D g); - public native void dispose(); - private native void cairoSurfaceSetFilter(int filter); - private native void cairoSurfaceSetFilterUnlocked(int filter); - native void connectSignals(GtkComponentPeer component); - public void finalize() - { - dispose(); - } + /** + * Rendering hint map. + */ + private RenderingHints hints; - public Graphics create() - { - return new GdkGraphics2D(this); - } + /** + * Some operations (drawing rather than filling) require that their + * 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; + + /** + * Keep track if the first clip to be set, which is restored on setClip(null); + */ + private boolean firstClip = true; + private Shape originalClip; + + /** + * Stroke used for 3DRects + */ + private static BasicStroke draw3DRectStroke = new BasicStroke(); - public Graphics create(int x, int y, int width, int height) + static ColorModel rgb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF); + static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, + 0xFF000000); + + /** + * Constructor does nothing. + */ + public CairoGraphics2D() { - return new GdkGraphics2D(this, x, y, width, height); } - private void fail_g2d () - { - System.err.println ("Attempted to instantiate GdkGraphics2D" - + " but Graphics2D not enabled. Try again with" - + " -Dgnu.java.awt.peer.gtk.Graphics=Graphics2D"); - System.exit (1); + /** + * Sets up the default values and allocates the native cairographics2d structure + * @param cairo_t_pointer, a native pointer to a cairo_t of the context. + */ + public void setup(long cairo_t_pointer) + { + nativePointer = init(cairo_t_pointer); + setRenderingHints(new RenderingHints(getDefaultHints())); + font = new Font("SansSerif", Font.PLAIN, 12); + setColor(Color.black); + setBackground(Color.white); + setPaint(Color.black); + setStroke(new BasicStroke()); + setTransform(new AffineTransform()); } - GdkGraphics2D(GdkGraphics2D g) + /** + * Same as above, but copies the state of another CairoGraphics2D. + */ + public void copy(CairoGraphics2D g, long cairo_t_pointer) { - if (!GtkToolkit.useGraphics2D ()) - fail_g2d (); - + nativePointer = init(cairo_t_pointer); paint = g.paint; stroke = g.stroke; setRenderingHints(g.hints); + + Color foreground; if (g.fg.getAlpha() != -1) - fg = new Color(g.fg.getRed(), g.fg.getGreen(), g.fg.getBlue(), + foreground = new Color(g.fg.getRed(), g.fg.getGreen(), g.fg.getBlue(), g.fg.getAlpha()); else - fg = new Color(g.fg.getRGB()); + foreground = new Color(g.fg.getRGB()); if (g.bg != null) { @@ -199,496 +252,319 @@ public class GdkGraphics2D extends Graphics2D transform = new AffineTransform(g.transform); font = g.font; - component = g.component; - copyState(g); - setColor(fg); + setColor(foreground); setBackground(bg); setPaint(paint); setStroke(stroke); setTransform(transform); - setClip(clip); - stateStack = new Stack(); } - GdkGraphics2D(GdkGraphics2D g, int x, int y, int widht, int height) - { - this(g); - translate(x, y); - clipRect(0, 0, widht, height); - } - - GdkGraphics2D(int width, int height) - { - if (!GtkToolkit.useGraphics2D ()) - fail_g2d (); - - initState(width, height); - - setColor(Color.black); - setBackground(new Color(0, 0, 0, 0)); - setPaint(getColor()); - setFont(new Font("SansSerif", Font.PLAIN, 12)); - setTransform(new AffineTransform()); - setStroke(new BasicStroke()); - setRenderingHints(getDefaultHints()); - - stateStack = new Stack(); - } - - GdkGraphics2D(GtkComponentPeer component) + /** + * Generic destructor - call the native dispose() method. + */ + public void finalize() { - if (!GtkToolkit.useGraphics2D ()) - fail_g2d (); - - this.component = component; - - if (component.isRealized()) - initComponentGraphics2D(); - else - connectSignals(component); + dispose(); } - void initComponentGraphics2D() - { - initState(component); - - setColor(component.awtComponent.getForeground()); - setBackground(component.awtComponent.getBackground()); - setPaint(getColor()); - setTransform(new AffineTransform()); - setStroke(new BasicStroke()); - setRenderingHints(getDefaultHints()); - setFont(new Font("SansSerif", Font.PLAIN, 12)); - - stateStack = new Stack(); + /** + * Disposes the native cairographics2d structure, including the + * cairo_t and any gradient stuff, if allocated. + * Subclasses should of course overload and call this if + * they have additional native structures. + */ + public void dispose() + { + disposeNative(); + nativePointer = 0; } - void initComponentGraphics2DUnlocked() - { - initStateUnlocked(component); - - setColorUnlocked(component.awtComponent.getForeground()); - setBackgroundUnlocked(component.awtComponent.getBackground()); - setPaintUnlocked(getColorUnlocked()); - setTransformUnlocked(new AffineTransform()); - setStrokeUnlocked(new BasicStroke()); - setRenderingHintsUnlocked(getDefaultHints()); - setFontUnlocked(new Font("SansSerif", Font.PLAIN, 12)); - - stateStack = new Stack(); - } + /** + * Allocate the cairographics2d structure and set the cairo_t pointer in it. + * @param pointer - a cairo_t pointer, casted to a long. + */ + private native long init(long pointer); - GdkGraphics2D(BufferedImage bimage) - { - this.bimage = bimage; - this.pixelBuffer = findSimpleIntegerArray(bimage.getColorModel(), - bimage.getRaster()); - if (this.pixelBuffer == null) - { - this.pixelBuffer = new int[bimage.getRaster().getWidth() * bimage.getRaster() - .getHeight()]; - this.pixelConversionRequired = true; - } - else - { - this.pixelConversionRequired = false; - } + /** + * These are declared abstract as there may be context-specific issues. + */ + public abstract Graphics create(); - initState(this.pixelBuffer, bimage.getWidth(), bimage.getHeight()); + public abstract GraphicsConfiguration getDeviceConfiguration(); - setColor(Color.black); - setBackground(new Color(0, 0, 0, 0)); - setPaint(getColor()); - setFont(new Font("SansSerif", Font.PLAIN, 12)); - setTransform(new AffineTransform()); - setStroke(new BasicStroke()); - setRenderingHints(getDefaultHints()); + protected abstract void copyAreaImpl(int x, int y, + int width, int height, int dx, int dy); - stateStack = new Stack(); - // draw current buffered image to the pixmap associated - // with it, if the image is not equal to our paint buffer. - if (pixelConversionRequired) - drawImage(bimage, new AffineTransform(1, 0, 0, 1, 0, 0), bg, null); - } + protected abstract Rectangle2D getRealBounds(); - //////////////////////////////////// - ////// Native Drawing Methods ////// - //////////////////////////////////// + ////// Native Methods //////////////////////////////////////////////////// - // GDK drawing methods - private native void gdkDrawDrawable(GdkGraphics2D other, int x, int y); + /** + * Dispose of allocate native resouces. + */ + public native void disposeNative(); - // drawing utility methods + /** + * Draw pixels as an RGBA int matrix + * @param w, h - width and height + * @param stride - stride of the array width + * @param i2u - affine transform array + */ private native void drawPixels(int[] pixels, int w, int h, int stride, double[] i2u); - private native void setTexturePixelsUnlocked(int[] pixels, int w, int h, int stride); - private native void setTexturePixels(int[] pixels, int w, int h, int stride); + private native void setGradient(double x1, double y1, double x2, double y2, int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2, boolean cyclic); - private native void setGradientUnlocked(double x1, double y1, double x2, double y2, - int r1, int g1, int b1, int a1, int r2, - int g2, int b2, int a2, boolean cyclic); + + private native void setTexturePixels(int[] pixels, int w, int h, int stride); - // simple passthroughs to cairo - private native void cairoSave(); - private native void cairoRestore(); + /** + * Set the current transform matrix + */ private native void cairoSetMatrix(double[] m); - private native void cairoSetMatrixUnlocked(double[] m); + + /** + * Set the compositing operator + */ private native void cairoSetOperator(int cairoOperator); + + /** + * Sets the current color in RGBA as a 0.0-1.0 double + */ private native void cairoSetRGBAColor(double red, double green, double blue, double alpha); - private native void cairoSetRGBAColorUnlocked(double red, double green, - double blue, double alpha); - private native void cairoSetFillRule(int cairoFillRule); - private native void cairoSetLineWidth(double width); - private native void cairoSetLineWidthUnlocked(double width); - private native void cairoSetLineCap(int cairoLineCap); - private native void cairoSetLineCapUnlocked(int cairoLineCap); - private native void cairoSetLineJoin(int cairoLineJoin); - private native void cairoSetLineJoinUnlocked(int cairoLineJoin); - private native void cairoSetDash(double[] dashes, int ndash, double offset); - private native void cairoSetDashUnlocked(double[] dashes, int ndash, double offset); - private native void cairoSetMiterLimit(double limit); - private native void cairoSetMiterLimitUnlocked(double limit); - private native void cairoNewPath(); - private native void cairoMoveTo(double x, double y); - private native void cairoLineTo(double x, double y); - private native void cairoCurveTo(double x1, double y1, double x2, double y2, - double x3, double y3); - private native void cairoRelMoveTo(double dx, double dy); - private native void cairoRelLineTo(double dx, double dy); - private native void cairoRelCurveTo(double dx1, double dy1, double dx2, - double dy2, double dx3, double dy3); - private native void cairoRectangle(double x, double y, double width, - double height); - private native void cairoClosePath(); - private native void cairoStroke(); - private native void cairoFill(); - private native void cairoClip(); - - ///////////////////////////////////////////// - ////// General Drawing Support Methods ////// - ///////////////////////////////////////////// - - private class DrawState - { - private Paint paint; - private Stroke stroke; - private Color fg; - private Color bg; - private Shape clip; - private AffineTransform transform; - private Font font; - private Composite comp; - - DrawState(GdkGraphics2D g) - { - this.paint = g.paint; - this.stroke = g.stroke; - this.fg = g.fg; - this.bg = g.bg; - this.clip = g.clip; - if (g.transform != null) - this.transform = (AffineTransform) g.transform.clone(); - this.font = g.font; - this.comp = g.comp; - } - - public void restore(GdkGraphics2D g) - { - g.paint = this.paint; - g.stroke = this.stroke; - g.fg = this.fg; - g.bg = this.bg; - g.clip = this.clip; - g.transform = this.transform; - g.font = this.font; - g.comp = this.comp; - } - } - - private void stateSave() - { - stateStack.push(new DrawState(this)); - cairoSave(); - } - - private void stateRestore() - { - ((DrawState) (stateStack.pop())).restore(this); - cairoRestore(); - } - - // Some operations (drawing rather than filling) require that their - // 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; - - private double shifted(double coord, boolean doShift) - { - if (doShift) - return Math.floor(coord) + 0.5; - else - return coord; - } - - private void walkPath(PathIterator p, boolean doShift) - { - double x = 0; - double y = 0; - double[] coords = new double[6]; - - cairoSetFillRule(p.getWindingRule()); - for (; ! p.isDone(); p.next()) - { - int seg = p.currentSegment(coords); - switch (seg) - { - case PathIterator.SEG_MOVETO: - x = shifted(coords[0], doShift); - y = shifted(coords[1], doShift); - cairoMoveTo(x, y); - break; - case PathIterator.SEG_LINETO: - x = shifted(coords[0], doShift); - y = shifted(coords[1], doShift); - cairoLineTo(x, y); - break; - case PathIterator.SEG_QUADTO: - // splitting a quadratic bezier into a cubic: - // see: http://pfaedit.sourceforge.net/bezier.html - double x1 = x + (2.0 / 3.0) * (shifted(coords[0], doShift) - x); - double y1 = y + (2.0 / 3.0) * (shifted(coords[1], doShift) - y); - - double x2 = x1 + (1.0 / 3.0) * (shifted(coords[2], doShift) - x); - double y2 = y1 + (1.0 / 3.0) * (shifted(coords[3], doShift) - y); - - x = shifted(coords[2], doShift); - y = shifted(coords[3], doShift); - cairoCurveTo(x1, y1, x2, y2, x, y); - break; - case PathIterator.SEG_CUBICTO: - x = shifted(coords[4], doShift); - y = shifted(coords[5], doShift); - cairoCurveTo(shifted(coords[0], doShift), - shifted(coords[1], doShift), - shifted(coords[2], doShift), - shifted(coords[3], doShift), x, y); - break; - case PathIterator.SEG_CLOSE: - cairoClosePath(); - break; - } - } - } + /** + * Sets the current winding rule in Cairo + */ + private native void cairoSetFillRule(int cairoFillRule); - private Map getDefaultHints() - { - HashMap defaultHints = new HashMap(); + /** + * Set the line style, cap, join and miter limit. + * Cap and join parameters are in the BasicStroke enumerations. + */ + private native void cairoSetLine(double width, int cap, int join, double miterLimit); - defaultHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + /** + * Set the dash style + */ + private native void cairoSetDash(double[] dashes, int ndash, double offset); - defaultHints.put(RenderingHints.KEY_STROKE_CONTROL, - RenderingHints.VALUE_STROKE_DEFAULT); + /* + * Draws a Glyph Vector + */ + native void cairoDrawGlyphVector(GdkFontPeer font, + float x, float y, int n, + int[] codes, float[] positions); - defaultHints.put(RenderingHints.KEY_FRACTIONALMETRICS, - RenderingHints.VALUE_FRACTIONALMETRICS_OFF); - defaultHints.put(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); + private native void cairoRelCurveTo(double dx1, double dy1, double dx2, + double dy2, double dx3, double dy3); - defaultHints.put(RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_DEFAULT); + /** + * Appends a rectangle to the current path + */ + private native void cairoRectangle(double x, double y, double width, + double height); - return defaultHints; - } + /** + * New current path + */ + private native void cairoNewPath(); - public static int[] findSimpleIntegerArray (ColorModel cm, Raster raster) - { - if (cm == null || raster == null) - return null; + /** + * Close current path + */ + private native void cairoClosePath(); - if (! cm.getColorSpace().isCS_sRGB()) - return null; + /** moveTo */ + private native void cairoMoveTo(double x, double y); - if (! (cm instanceof DirectColorModel)) - return null; + /** relative moveTo */ + private native void cairoRelMoveTo(double dx, double dy); - DirectColorModel dcm = (DirectColorModel) cm; + /** lineTo */ + private native void cairoLineTo(double x, double y); - if (dcm.getRedMask() != 0x00FF0000 || dcm.getGreenMask() != 0x0000FF00 - || dcm.getBlueMask() != 0x000000FF) - return null; + /** relative lineTo */ + private native void cairoRelLineTo(double dx, double dy); - if (! (raster instanceof WritableRaster)) - return null; + /** Cubic curve-to */ + private native void cairoCurveTo(double x1, double y1, double x2, double y2, + double x3, double y3); - if (raster.getSampleModel().getDataType() != DataBuffer.TYPE_INT) - return null; + /** + * Stroke current path + */ + private native void cairoStroke(); - if (! (raster.getDataBuffer() instanceof DataBufferInt)) - return null; + /** + * Fill current path + */ + private native void cairoFill(); - DataBufferInt db = (DataBufferInt) raster.getDataBuffer(); + /** + * Clip current path + */ + private native void cairoClip(); - if (db.getNumBanks() != 1) - return null; + /** + * Save clip + */ + private native void cairoPreserveClip(); - // Finally, we have determined that this is a single bank, [A]RGB-int - // buffer in sRGB space. It's worth checking all this, because it means - // that cairo can paint directly into the data buffer, which is very - // fast compared to all the normal copying and converting. + /** + * Save clip + */ + private native void cairoResetClip(); - return db.getData(); - } + /** + * Set interpolation types + */ + private native void cairoSurfaceSetFilter(int filter); - private void updateBufferedImage() + ///////////////////////// TRANSFORMS /////////////////////////////////// + /** + * Set the current transform + */ + public void setTransform(AffineTransform tx) { - if (bimage != null && pixelConversionRequired) + transform = tx; + if (transform != null) { - int height = bimage.getHeight(); - int width = bimage.getWidth(); - int index = 0; - for (int y = 0; y < height; ++y) - for (int x = 0; x < width; ++x) - bimage.setRGB(x, y, pixelBuffer[index++]); + double[] m = new double[6]; + transform.getMatrix(m); + cairoSetMatrix(m); } } - - private boolean drawImage(Image img, AffineTransform xform, - Color bgcolor, ImageObserver obs) + + public void transform(AffineTransform tx) { - if (img == null) - return false; - - // FIXME: I'll fix this, /Sven -// if (img instanceof GtkOffScreenImage -// && img.getGraphics() instanceof GdkGraphics2D -// && (xform == null || xform.getType() == AffineTransform.TYPE_IDENTITY -// || xform.getType() == AffineTransform.TYPE_TRANSLATION)) -// { -// // we are being asked to flush a double buffer from Gdk -// GdkGraphics2D g2 = (GdkGraphics2D) img.getGraphics(); -// gdkDrawDrawable(g2, (int) xform.getTranslateX(), -// (int) xform.getTranslateY()); - -// updateBufferedImage(); - -// return true; -// } -// else + if (transform == null) + transform = new AffineTransform(tx); + else + transform.concatenate(tx); + setTransform(transform); + if (clip != null) { - // In this case, xform is an AffineTransform that transforms bounding - // box of the specified image from image space to user space. However - // when we pass this transform to cairo, cairo will use this transform - // to map "user coordinates" to "pixel" coordinates, which is the - // other way around. Therefore to get the "user -> pixel" transform - // that cairo wants from "image -> user" transform that we currently - // have, we will need to invert the transformation matrix. - AffineTransform invertedXform = new AffineTransform(); - + // FIXME: this should actuall try to transform the shape + // rather than degrade to bounds. + Rectangle2D r = clip.getBounds2D(); + double[] coords = new double[] + { + r.getX(), r.getY(), r.getX() + r.getWidth(), + r.getY() + r.getHeight() + }; try { - invertedXform = xform.createInverse(); - if (img instanceof BufferedImage) - { - // draw an image which has actually been loaded - // into memory fully - BufferedImage b = (BufferedImage) img; - return drawRaster(b.getColorModel(), b.getTile(0, 0), - invertedXform, bgcolor); - } - else - return this.drawImage(GdkPixbufDecoder.createBufferedImage(img - .getSource()), - xform, bgcolor, obs); + tx.createInverse().transform(coords, 0, coords, 0, 2); + r.setRect(coords[0], coords[1], coords[2] - coords[0], + coords[3] - coords[1]); + clip = r; } - catch (NoninvertibleTransformException e) + catch (java.awt.geom.NoninvertibleTransformException e) { - throw new ImagingOpException("Unable to invert transform " - + xform.toString()); } } } - ////////////////////////////////////////////////// - ////// Implementation of Graphics2D Methods ////// - ////////////////////////////////////////////////// - - public void draw(Shape s) + public void rotate(double theta) { - if (stroke != null && ! (stroke instanceof BasicStroke)) - { - fill(stroke.createStrokedShape(s)); - return; - } - - cairoNewPath(); + transform(AffineTransform.getRotateInstance(theta)); + } - if (s instanceof Rectangle2D) - { - Rectangle2D r = (Rectangle2D) s; - cairoRectangle(shifted(r.getX(), shiftDrawCalls), - shifted(r.getY(), shiftDrawCalls), r.getWidth(), - r.getHeight()); - } - else - walkPath(s.getPathIterator(null), shiftDrawCalls); - cairoStroke(); + public void rotate(double theta, double x, double y) + { + transform(AffineTransform.getRotateInstance(theta, x, y)); + } - updateBufferedImage(); + public void scale(double sx, double sy) + { + transform(AffineTransform.getScaleInstance(sx, sy)); } - public void fill(Shape s) + /** + * Translate the system of the co-ordinates. As translation is a frequent + * operation, it is done in an optimised way, unlike scaling and rotating. + */ + public void translate(double tx, double ty) { - cairoNewPath(); - if (s instanceof Rectangle2D) + if (transform != null) + transform.translate(tx, ty); + else + transform = AffineTransform.getTranslateInstance(tx, ty); + + if (clip != null) { - Rectangle2D r = (Rectangle2D) s; - cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + // FIXME: this should actuall try to transform the shape + // rather than degrade to bounds. + Rectangle2D r; + + if (clip instanceof Rectangle2D) + r = (Rectangle2D) clip; + else + r = clip.getBounds2D(); + + r.setRect(r.getX() - tx, r.getY() - ty, r.getWidth(), r.getHeight()); + clip = r; } - else - walkPath(s.getPathIterator(null), false); - cairoFill(); + setTransform(transform); + } + + public void translate(int x, int y) + { + translate((double) x, (double) y); + } - updateBufferedImage(); + public void shear(double shearX, double shearY) + { + transform(AffineTransform.getShearInstance(shearX, shearY)); } + ///////////////////////// DRAWING STATE /////////////////////////////////// + public void clip(Shape s) { - // update it - if (clip == null || s == null) - clip = s; - else if (s instanceof Rectangle2D && clip instanceof Rectangle2D) - { - Rectangle2D r = (Rectangle2D) s; - Rectangle2D curr = (Rectangle2D) clip; - clip = curr.createIntersection(r); - } - else - throw new UnsupportedOperationException(); + // Do not touch clip when s == null. + if (s == null) + return; - // draw it - if (clip != null) + // If the current clip is still null, initialize it. + if (clip == null) + clip = originalClip; + + // This is so common, let's optimize this. + else if (clip instanceof Rectangle2D && s instanceof Rectangle2D) { - cairoNewPath(); - if (clip instanceof Rectangle2D) - { - Rectangle2D r = (Rectangle2D) clip; - cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - else - walkPath(clip.getPathIterator(null), false); - - // cairoClosePath (); - cairoClip(); + Rectangle2D clipRect = (Rectangle2D) clip; + Rectangle2D r = (Rectangle2D) s; + Rectangle2D.intersect(clipRect, r, clipRect); + // Call setClip so that subclasses get notified. + setClip(clipRect); } + else + { + Area current; + if (clip instanceof Area) + current = (Area) clip; + else + current = new Area(clip); + + Area intersect; + if (s instanceof Area) + intersect = (Area) s; + else + intersect = new Area(s); + + current.intersect(intersect); + clip = current; + // Call setClip so that the native side gets notified. + setClip(clip); + } } public Paint getPaint() @@ -720,8 +596,8 @@ public class GdkGraphics2D extends Graphics2D int width = (int) tp.getAnchorRect().getWidth(); int height = (int) tp.getAnchorRect().getHeight(); - double scaleX = width / (double) img.getWidth(); - double scaleY = width / (double) img.getHeight(); + double scaleX = (width+1) / (double) img.getWidth(); + double scaleY = (height+1) / (double) img.getHeight(); AffineTransform at = new AffineTransform(scaleX, 0, 0, scaleY, 0, 0); AffineTransformOp op = new AffineTransformOp(at, getRenderingHints()); @@ -744,131 +620,6 @@ public class GdkGraphics2D extends Graphics2D throw new java.lang.UnsupportedOperationException(); } - public void setPaintUnlocked(Paint p) - { - if (paint == null) - return; - - paint = p; - if (paint instanceof Color) - { - setColorUnlocked((Color) paint); - } - else if (paint instanceof TexturePaint) - { - TexturePaint tp = (TexturePaint) paint; - BufferedImage img = tp.getImage(); - - // map the image to the anchor rectangle - int width = (int) tp.getAnchorRect().getWidth(); - int height = (int) tp.getAnchorRect().getHeight(); - - double scaleX = width / (double) img.getWidth(); - double scaleY = width / (double) img.getHeight(); - - AffineTransform at = new AffineTransform(scaleX, 0, 0, scaleY, 0, 0); - AffineTransformOp op = new AffineTransformOp(at, getRenderingHints()); - BufferedImage texture = op.filter(img, null); - int[] pixels = texture.getRGB(0, 0, width, height, null, 0, width); - setTexturePixelsUnlocked(pixels, width, height, width); - } - else if (paint instanceof GradientPaint) - { - GradientPaint gp = (GradientPaint) paint; - Point2D p1 = gp.getPoint1(); - Point2D p2 = gp.getPoint2(); - Color c1 = gp.getColor1(); - Color c2 = gp.getColor2(); - setGradientUnlocked(p1.getX(), p1.getY(), p2.getX(), p2.getY(), c1.getRed(), - c1.getGreen(), c1.getBlue(), c1.getAlpha(), c2.getRed(), - c2.getGreen(), c2.getBlue(), c2.getAlpha(), gp.isCyclic()); - } - else - throw new java.lang.UnsupportedOperationException(); - } - - public void setTransform(AffineTransform tx) - { - transform = tx; - if (transform != null) - { - double[] m = new double[6]; - transform.getMatrix(m); - cairoSetMatrix(m); - } - } - - public void setTransformUnlocked(AffineTransform tx) - { - transform = tx; - if (transform != null) - { - double[] m = new double[6]; - transform.getMatrix(m); - cairoSetMatrixUnlocked(m); - } - } - - public void transform(AffineTransform tx) - { - if (transform == null) - transform = new AffineTransform(tx); - else - transform.concatenate(tx); - setTransform(transform); - if (clip != null) - { - // FIXME: this should actuall try to transform the shape - // rather than degrade to bounds. - Rectangle2D r = clip.getBounds2D(); - double[] coords = new double[] - { - r.getX(), r.getY(), r.getX() + r.getWidth(), - r.getY() + r.getHeight() - }; - try - { - tx.createInverse().transform(coords, 0, coords, 0, 2); - r.setRect(coords[0], coords[1], coords[2] - coords[0], - coords[3] - coords[1]); - clip = r; - } - catch (java.awt.geom.NoninvertibleTransformException e) - { - } - } - } - - public void rotate(double theta) - { - transform(AffineTransform.getRotateInstance(theta)); - } - - public void rotate(double theta, double x, double y) - { - transform(AffineTransform.getRotateInstance(theta, x, y)); - } - - public void scale(double sx, double sy) - { - transform(AffineTransform.getScaleInstance(sx, sy)); - } - - public void translate(double tx, double ty) - { - transform(AffineTransform.getTranslateInstance(tx, ty)); - } - - public void translate(int x, int y) - { - translate((double) x, (double) y); - } - - public void shear(double shearX, double shearY) - { - transform(AffineTransform.getShearInstance(shearX, shearY)); - } - public Stroke getStroke() { return stroke; @@ -880,10 +631,9 @@ public class GdkGraphics2D extends Graphics2D if (stroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke) stroke; - cairoSetLineCap(bs.getEndCap()); - cairoSetLineWidth(bs.getLineWidth()); - cairoSetLineJoin(bs.getLineJoin()); - cairoSetMiterLimit(bs.getMiterLimit()); + cairoSetLine(bs.getLineWidth(), bs.getEndCap(), + bs.getLineJoin(), bs.getMiterLimit()); + float[] dashes = bs.getDashArray(); if (dashes != null) { @@ -898,42 +648,14 @@ public class GdkGraphics2D extends Graphics2D } } - public void setStrokeUnlocked(Stroke st) - { - stroke = st; - if (stroke instanceof BasicStroke) - { - BasicStroke bs = (BasicStroke) stroke; - cairoSetLineCapUnlocked(bs.getEndCap()); - cairoSetLineWidthUnlocked(bs.getLineWidth()); - cairoSetLineJoinUnlocked(bs.getLineJoin()); - cairoSetMiterLimitUnlocked(bs.getMiterLimit()); - float[] dashes = bs.getDashArray(); - if (dashes != null) - { - double[] double_dashes = new double[dashes.length]; - for (int i = 0; i < dashes.length; i++) - double_dashes[i] = dashes[i]; - cairoSetDashUnlocked(double_dashes, double_dashes.length, - (double) bs.getDashPhase()); - } - else - cairoSetDashUnlocked(new double[0], 0, 0.0); - } - } - - //////////////////////////////////////////////// - ////// Implementation of Graphics Methods ////// - //////////////////////////////////////////////// - public void setPaintMode() { - setComposite(java.awt.AlphaComposite.SrcOver); + setComposite(AlphaComposite.SrcOver); } public void setXORMode(Color c) { - setComposite(new gnu.java.awt.BitwiseXORComposite(c)); + // FIXME: implement } public void setColor(Color c) @@ -943,18 +665,17 @@ public class GdkGraphics2D extends Graphics2D fg = c; paint = c; - cairoSetRGBAColor(fg.getRed() / 255.0, fg.getGreen() / 255.0, - fg.getBlue() / 255.0, fg.getAlpha() / 255.0); + updateColor(); } - - public void setColorUnlocked(Color c) + + /** + * Set the current fg value as the cairo color. + */ + void updateColor() { - if (c == null) - c = Color.BLACK; - - fg = c; - paint = c; - cairoSetRGBAColorUnlocked(fg.getRed() / 255.0, fg.getGreen() / 255.0, + if (fg == null) + fg = Color.BLACK; + cairoSetRGBAColor(fg.getRed() / 255.0, fg.getGreen() / 255.0, fg.getBlue() / 255.0, fg.getAlpha() / 255.0); } @@ -963,11 +684,6 @@ public class GdkGraphics2D extends Graphics2D return fg; } - public Color getColorUnlocked() - { - return getColor(); - } - public void clipRect(int x, int y, int width, int height) { clip(new Rectangle(x, y, width, height)); @@ -1011,81 +727,41 @@ public class GdkGraphics2D extends Graphics2D public void setClip(int x, int y, int width, int height) { - setClip(new Rectangle2D.Double((double) x, (double) y, (double) width, - (double) height)); + if( width < 0 || height < 0 ) + return; + + setClip(new Rectangle2D.Double(x, y, width, height)); } public void setClip(Shape s) - { - clip = s; - if (clip == null) - { - // Reset clipping. - if (component != null) - { - Dimension d = component.awtComponent.getSize(); - setClip(0, 0, d.width, d.height); - } - } - else + { + // The first time the clip is set, save it as the original clip + // to reset to on s == null. We can rely on this being non-null + // because the constructor in subclasses is expected to set the + // initial clip properly. + if( firstClip ) { - cairoNewPath(); - if (s instanceof Rectangle2D) - { - Rectangle2D r = (Rectangle2D) s; - cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - else - walkPath(s.getPathIterator(null), false); - - // cairoClosePath (); - cairoClip(); + originalClip = s; + firstClip = false; } - } - - private static BasicStroke draw3DRectStroke = new BasicStroke(); - - public void draw3DRect(int x, int y, int width, int height, boolean raised) - { - Stroke tmp = stroke; - setStroke(draw3DRectStroke); - super.draw3DRect(x, y, width, height, raised); - setStroke(tmp); - updateBufferedImage(); - } - - public void fill3DRect(int x, int y, int width, int height, boolean raised) - { - Stroke tmp = stroke; - setStroke(draw3DRectStroke); - super.fill3DRect(x, y, width, height, raised); - setStroke(tmp); - updateBufferedImage(); - } - public void drawRect(int x, int y, int width, int height) - { - draw(new Rectangle(x, y, width, height)); - } + if (s == null) + clip = originalClip; + else + clip = s; - public void fillRect(int x, int y, int width, int height) - { - cairoNewPath(); - cairoRectangle(x, y, width, height); - cairoFill(); - } + cairoResetClip(); - public void clearRect(int x, int y, int width, int height) - { - if (bg != null) - cairoSetRGBAColor(bg.getRed() / 255.0, bg.getGreen() / 255.0, - bg.getBlue() / 255.0, 1.0); cairoNewPath(); - cairoRectangle(x, y, width, height); - cairoFill(); - setColor(fg); - - updateBufferedImage(); + if (clip instanceof Rectangle2D) + { + Rectangle2D r = (Rectangle2D) clip; + cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + else + walkPath(clip.getPathIterator(null), false); + + cairoClip(); } public void setBackground(Color c) @@ -1095,203 +771,232 @@ public class GdkGraphics2D extends Graphics2D bg = c; } - public void setBackgroundUnlocked(Color c) - { - setBackground(c); - } - public Color getBackground() { return bg; } - private void doPolygon(int[] xPoints, int[] yPoints, int nPoints, - boolean close, boolean fill) + /** + * Return the current composite. + */ + public Composite getComposite() { - if (nPoints < 1) - return; - GeneralPath gp = new GeneralPath(PathIterator.WIND_EVEN_ODD); - gp.moveTo((float) xPoints[0], (float) yPoints[0]); - for (int i = 1; i < nPoints; i++) - gp.lineTo((float) xPoints[i], (float) yPoints[i]); + if (comp == null) + return AlphaComposite.SrcOver; + else + return comp; + } - if (close) - gp.closePath(); + /** + * Sets the current composite context. + */ + public void setComposite(Composite comp) + { + this.comp = comp; - Shape sh = gp; - if (fill == false && stroke != null && ! (stroke instanceof BasicStroke)) + if (comp instanceof AlphaComposite) { - sh = stroke.createStrokedShape(gp); - fill = true; + AlphaComposite a = (AlphaComposite) comp; + cairoSetOperator(a.getRule()); + Color c = getColor(); + setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), + (int) (a.getAlpha() * ((float) c.getAlpha())))); } - - if (fill) - fill(sh); else - draw(sh); + { + // FIXME: implement general Composite support + throw new java.lang.UnsupportedOperationException(); + } } - public void drawLine(int x1, int y1, int x2, int y2) + ///////////////////////// DRAWING PRIMITIVES /////////////////////////////////// + + public void draw(Shape s) { - int[] xp = new int[2]; - int[] yp = new int[2]; + if (stroke != null && ! (stroke instanceof BasicStroke)) + { + fill(stroke.createStrokedShape(s)); + return; + } - xp[0] = x1; - xp[1] = x2; - yp[0] = y1; - yp[1] = y2; + cairoNewPath(); - doPolygon(xp, yp, 2, false, false); + if (s instanceof Rectangle2D) + { + Rectangle2D r = (Rectangle2D) s; + cairoRectangle(shifted(r.getX(), shiftDrawCalls), + shifted(r.getY(), shiftDrawCalls), r.getWidth(), + r.getHeight()); + } + else + walkPath(s.getPathIterator(null), shiftDrawCalls); + cairoStroke(); } - public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) + public void fill(Shape s) { - doPolygon(xPoints, yPoints, nPoints, true, true); + cairoNewPath(); + if (s instanceof Rectangle2D) + { + Rectangle2D r = (Rectangle2D) s; + cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + else + walkPath(s.getPathIterator(null), false); + + cairoFill(); } - public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) + /** + * Note that the rest of the drawing methods go via fill() or draw() for the drawing, + * although subclasses may with to overload these methods where context-specific + * optimizations are possible (e.g. bitmaps and fillRect(int, int, int, int) + */ + + public void clearRect(int x, int y, int width, int height) { - doPolygon(xPoints, yPoints, nPoints, true, false); + if (bg != null) + cairoSetRGBAColor(bg.getRed() / 255.0, bg.getGreen() / 255.0, + bg.getBlue() / 255.0, 1.0); + fillRect(x, y, width, height); + updateColor(); } - public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) + public void draw3DRect(int x, int y, int width, int height, boolean raised) { - doPolygon(xPoints, yPoints, nPoints, false, false); + Stroke tmp = stroke; + setStroke(draw3DRectStroke); + super.draw3DRect(x, y, width, height, raised); + setStroke(tmp); } - private boolean drawRaster(ColorModel cm, Raster r, - AffineTransform imageToUser, Color bgcolor) + public void drawArc(int x, int y, int width, int height, int startAngle, + int arcAngle) { - if (r == null) - return false; - - SampleModel sm = r.getSampleModel(); - DataBuffer db = r.getDataBuffer(); - - if (db == null || sm == null) - return false; - - if (cm == null) - cm = ColorModel.getRGBdefault(); - - double[] i2u = new double[6]; - if (imageToUser != null) - imageToUser.getMatrix(i2u); - else - { - i2u[0] = 1; - i2u[1] = 0; - i2u[2] = 0; - i2u[3] = 1; - i2u[4] = 0; - i2u[5] = 0; - } - - int[] pixels = findSimpleIntegerArray(cm, r); - - if (pixels == null) - { - // FIXME: I don't think this code will work correctly with a non-RGB - // MultiPixelPackedSampleModel. Although this entire method should - // probably be rewritten to better utilize Cairo's different supported - // data formats. - if (sm instanceof MultiPixelPackedSampleModel) - { - pixels = r.getPixels(0, 0, r.getWidth(), r.getHeight(), pixels); - for (int i = 0; i < pixels.length; i++) - pixels[i] = cm.getRGB(pixels[i]); - } - else - { - pixels = new int[r.getWidth() * r.getHeight()]; - for (int i = 0; i < pixels.length; i++) - pixels[i] = cm.getRGB(db.getElem(i)); - } - } - - // Change all transparent pixels in the image to the specified bgcolor, - // or (if there's no alpha) fill in an alpha channel so that it paints - // correctly. - if (cm.hasAlpha()) - { - if (bgcolor != null && cm.hasAlpha()) - for (int i = 0; i < pixels.length; i++) - { - if (cm.getAlpha(pixels[i]) == 0) - pixels[i] = bgcolor.getRGB(); - } - } - else - for (int i = 0; i < pixels.length; i++) - pixels[i] |= 0xFF000000; + draw(new Arc2D.Double((double) x, (double) y, (double) width, + (double) height, (double) startAngle, + (double) arcAngle, Arc2D.OPEN)); + } - drawPixels(pixels, r.getWidth(), r.getHeight(), r.getWidth(), i2u); + public void drawLine(int x1, int y1, int x2, int y2) + { + draw(new Line2D.Double(x1, y1, x2, y2)); + } - updateBufferedImage(); - - // Cairo seems loosing the current color. - setColor(fg); - - return true; + public void drawRect(int x, int y, int width, int height) + { + draw(new Rectangle(x, y, width, height)); } - public void drawRenderedImage(RenderedImage image, AffineTransform xform) + public void fillArc(int x, int y, int width, int height, int startAngle, + int arcAngle) { - drawRaster(image.getColorModel(), image.getData(), xform, bg); + fill(new Arc2D.Double((double) x, (double) y, (double) width, + (double) height, (double) startAngle, + (double) arcAngle, Arc2D.OPEN)); } - public void drawRenderableImage(RenderableImage image, AffineTransform xform) + public void fillRect(int x, int y, int width, int height) { - drawRenderedImage(image.createRendering(new RenderContext(xform)), xform); + fill(new Rectangle(x, y, width, height)); } - public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { - return drawImage(img, xform, bg, obs); + fill(new Polygon(xPoints, yPoints, nPoints)); } - public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y) + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { - Image filtered = op.filter(image, null); - drawImage(filtered, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg, null); + draw(new Polygon(xPoints, yPoints, nPoints)); } - public boolean drawImage(Image img, int x, int y, ImageObserver observer) + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { - return drawImage(img, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg, - observer); + draw(new Polygon(xPoints, yPoints, nPoints)); } - /////////////////////////////////////////////// - ////// Unimplemented Stubs and Overloads ////// - /////////////////////////////////////////////// + public void drawOval(int x, int y, int width, int height) + { + drawArc(x, y, width, height, 0, 360); + } - public boolean hit(Rectangle rect, Shape text, boolean onStroke) + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) { - throw new java.lang.UnsupportedOperationException(); + draw(new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight)); } - public GraphicsConfiguration getDeviceConfiguration() + public void fillOval(int x, int y, int width, int height) { - throw new java.lang.UnsupportedOperationException(); + fillArc(x, y, width, height, 0, 360); } - public void setComposite(Composite comp) + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) { - this.comp = comp; + fill(new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight)); + } + + /** + * CopyArea - performs clipping to the native surface as a convenience + * (requires getRealBounds). Then calls copyAreaImpl. + */ + public void copyArea(int ox, int oy, int owidth, int oheight, + int odx, int ody) + { + Point2D pos = transform.transform(new Point2D.Double(ox, oy), + (Point2D) null); + Point2D dim = transform.transform(new Point2D.Double(ox + owidth, + oy + oheight), + (Point2D) null); + Point2D p2 = transform.transform(new Point2D.Double(ox + odx, oy + ody), + (Point2D) null); + int x = (int)pos.getX(); + int y = (int)pos.getY(); + int width = (int)(dim.getX() - pos.getX()); + int height = (int)(dim.getY() - pos.getY()); + int dx = (int)(p2.getX() - pos.getX()); + int dy = (int)(p2.getY() - pos.getY()); + + Rectangle2D r = getRealBounds(); + + if( width < 0 || height < 0 ) + return; + // Return if outside the surface + if( x + dx > r.getWidth() || y + dy > r.getHeight() ) + return; - if (comp instanceof AlphaComposite) + if( x + dx + width < r.getX() || y + dy + height < r.getY() ) + return; + + // Clip edges if necessary + if( x + dx < r.getX() ) // left { - AlphaComposite a = (AlphaComposite) comp; - cairoSetOperator(a.getRule()); - Color c = getColor(); - setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), - (int) (a.getAlpha() * ((float) c.getAlpha())))); + width = x + dx + width; + x = (int)r.getX() - dx; } - else - throw new java.lang.UnsupportedOperationException(); + + if( y + dy < r.getY() ) // top + { + height = y + dy + height; + y = (int)r.getY() - dy; + } + + if( x + dx + width >= r.getWidth() ) // right + width = (int)r.getWidth() - dx - x; + + if( y + dy + height >= r.getHeight() ) // bottom + height = (int)r.getHeight() - dy - y; + + copyAreaImpl(x, y, width, height, dx, dy); } + ///////////////////////// RENDERING HINTS /////////////////////////////////// + + /** + * FIXME- support better + */ public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { hints.put(hintKey, hintValue); @@ -1316,7 +1021,7 @@ public class GdkGraphics2D extends Graphics2D } shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE) - || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); + || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); } public Object getRenderingHint(RenderingHints.Key hintKey) @@ -1351,74 +1056,137 @@ public class GdkGraphics2D extends Graphics2D } shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE) - || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); + || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); } - public void setRenderingHintsUnlocked(Map hints) + public void addRenderingHints(Map hints) { - this.hints = new RenderingHints(getDefaultHints()); this.hints.add(new RenderingHints(hints)); + } - if (hints.containsKey(RenderingHints.KEY_INTERPOLATION)) - { - if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) - cairoSurfaceSetFilterUnlocked(0); + public RenderingHints getRenderingHints() + { + return hints; + } - else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) - cairoSurfaceSetFilterUnlocked(1); + ///////////////////////// IMAGE. METHODS /////////////////////////////////// + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (img == null) + return false; + + // In this case, xform is an AffineTransform that transforms bounding + // box of the specified image from image space to user space. However + // when we pass this transform to cairo, cairo will use this transform + // to map "user coordinates" to "pixel" coordinates, which is the + // other way around. Therefore to get the "user -> pixel" transform + // that cairo wants from "image -> user" transform that we currently + // have, we will need to invert the transformation matrix. + AffineTransform invertedXform = new AffineTransform(); + + try + { + invertedXform = xform.createInverse(); + } + catch (NoninvertibleTransformException e) + { + throw new ImagingOpException("Unable to invert transform " + + xform.toString()); } - if (hints.containsKey(RenderingHints.KEY_ALPHA_INTERPOLATION)) + // Unrecognized image - convert to a BufferedImage and come back. + if( !(img instanceof BufferedImage) ) + return this.drawImage(Toolkit.getDefaultToolkit(). + createImage(img.getSource()), + xform, bgcolor, obs); + + BufferedImage b = (BufferedImage) img; + DataBuffer db; + double[] i2u = new double[6]; + int width = b.getWidth(); + int height = b.getHeight(); + + // If this BufferedImage has a BufferedImageGraphics object, + // use the cached CairoSurface that BIG is drawing onto + if( BufferedImageGraphics.bufferedImages.get( b ) != null ) + db = (DataBuffer)BufferedImageGraphics.bufferedImages.get( b ); + else + db = b.getRaster().getDataBuffer(); + + invertedXform.getMatrix(i2u); + + if(db instanceof CairoSurface) { - if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) - cairoSurfaceSetFilterUnlocked(2); + ((CairoSurface)db).drawSurface(this, i2u); + 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 ); + } - else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) - cairoSurfaceSetFilterUnlocked(3); + int[] pixels; - else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) - cairoSurfaceSetFilterUnlocked(4); + // Shortcut for easy color models. + if( b.getColorModel().equals(rgb32) ) + { + pixels = ((DataBufferInt)db).getData(); + for(int i = 0; i < pixels.length; i++) + pixels[i] |= 0xFF000000; + } + else if( b.getColorModel().equals(argb32) ) + { + pixels = ((DataBufferInt)db).getData(); + } + else + { + pixels = b.getRGB(0, 0, width, height, + null, 0, width); } - shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE) - || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); - } + drawPixels(pixels, width, height, width, i2u); - public void addRenderingHints(Map hints) - { - this.hints.add(new RenderingHints(hints)); + // Cairo seems to lose the current color which must be restored. + updateColor(); + return true; } - public RenderingHints getRenderingHints() + public void drawRenderedImage(RenderedImage image, AffineTransform xform) { - return hints; + drawRaster(image.getColorModel(), image.getData(), xform, null); } - public Composite getComposite() + public void drawRenderableImage(RenderableImage image, AffineTransform xform) { - if (comp == null) - return AlphaComposite.SrcOver; - else - return comp; + drawRenderedImage(image.createRendering(new RenderContext(xform)), xform); } - public FontRenderContext getFontRenderContext() + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { - return new FontRenderContext(transform, true, true); + return drawImage(img, xform, null, obs); } - public void copyArea(int x, int y, int width, int height, int dx, int dy) + public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y) { - GdkGraphics2D g = (GdkGraphics2D) create(x, y, width, height); - gdkDrawDrawable(g, x + dx, y + dy); + Image filtered = op.filter(image, null); + drawImage(filtered, new AffineTransform(1f, 0f, 0f, 1f, x, y), null, null); } - public void drawArc(int x, int y, int width, int height, int startAngle, - int arcAngle) + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - draw(new Arc2D.Double((double) x, (double) y, (double) width, - (double) height, (double) startAngle, - (double) arcAngle, Arc2D.OPEN)); + return drawImage(img, new AffineTransform(1f, 0f, 0f, 1f, x, y), null, + observer); } public boolean drawImage(Image img, int x, int y, Color bgcolor, @@ -1433,6 +1201,8 @@ public class GdkGraphics2D extends Graphics2D { double scaleX = width / (double) img.getWidth(observer); double scaleY = height / (double) img.getHeight(observer); + if( scaleX == 0 || scaleY == 0 ) + return true; return drawImage(img, new AffineTransform(scaleX, 0f, 0f, scaleY, x, y), bgcolor, observer); @@ -1441,7 +1211,7 @@ public class GdkGraphics2D extends Graphics2D public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { - return drawImage(img, x, y, width, height, bg, observer); + return drawImage(img, x, y, width, height, null, observer); } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, @@ -1451,101 +1221,51 @@ public class GdkGraphics2D extends Graphics2D if (img == null) return false; - Image subImage; - int sourceWidth = sx2 - sx1; int sourceHeight = sy2 - sy1; int destWidth = dx2 - dx1; int destHeight = dy2 - dy1; + if(destWidth == 0 || destHeight == 0 || sourceWidth == 0 || + sourceHeight == 0) + return true; + double scaleX = destWidth / (double) sourceWidth; double scaleY = destHeight / (double) sourceHeight; - // Get the subimage of the source enclosed in the - // rectangle specified by sx1, sy1, sx2, sy2 - - if (img instanceof BufferedImage) - { - BufferedImage b = (BufferedImage) img; - subImage = b.getSubimage(sx1, sy1, sx2, sy2); - } + // FIXME: Avoid using an AT if possible here - it's at least twice as slow. + + Shape oldClip = getClip(); + int cx, cy, cw, ch; + if( dx1 < dx2 ) + { cx = dx1; cw = dx2 - dx1; } else - { - // FIXME: This code currently doesn't work. Null Pointer - // exception is thrown in this case. This happens - // because img.getSource() always returns null, since source gets - // never initialized when it is created with the help of - // createImage(int width, int height). - CropImageFilter filter = new CropImageFilter(sx1, sx2, sx2, sy2); - FilteredImageSource src = new FilteredImageSource(img.getSource(), - filter); - - subImage = Toolkit.getDefaultToolkit().createImage(src); - } + { cx = dx2; cw = dx1 - dx2; } + if( dy1 < dy2 ) + { cy = dy1; ch = dy2 - dy1; } + else + { cy = dy2; ch = dy1 - dy2; } + + setClip( cx, cy, cw, ch ); - return drawImage(subImage, - new AffineTransform(scaleX, 0, 0, scaleY, dx1, dy1), - bgcolor, observer); + AffineTransform tx = new AffineTransform(); + tx.translate( dx1 - sx1*scaleX, dy1 - sy1*scaleY ); + tx.scale( scaleX, scaleY ); + + boolean retval = drawImage(img, tx, bgcolor, observer); + setClip( oldClip ); + return retval; } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { - return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bg, observer); + return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, observer); } - public void drawOval(int x, int y, int width, int height) - { - drawArc(x, y, width, height, 0, 360); - } - - public void drawRoundRect(int x, int y, int width, int height, int arcWidth, - int arcHeight) - { - if (arcWidth > width) - arcWidth = width; - if (arcHeight > height) - arcHeight = height; - - int xx = x + width - arcWidth; - int yy = y + height - arcHeight; - - drawArc(x, y, arcWidth, arcHeight, 90, 90); - drawArc(xx, y, arcWidth, arcHeight, 0, 90); - drawArc(xx, yy, arcWidth, arcHeight, 270, 90); - drawArc(x, yy, arcWidth, arcHeight, 180, 90); - - int y1 = y + arcHeight / 2; - int y2 = y + height - arcHeight / 2; - drawLine(x, y1, x, y2); - drawLine(x + width, y1, x + width, y2); - - int x1 = x + arcWidth / 2; - int x2 = x + width - arcWidth / 2; - drawLine(x1, y, x2, y); - drawLine(x1, y + height, x2, y + height); - } - - // these are the most accelerated painting paths - native void cairoDrawGlyphVector(GdkFontPeer font, - float x, float y, int n, - int[] codes, float[] positions); - - native void cairoDrawGdkTextLayout(GdkTextLayout gl, - float x, float y); - - GdkFontPeer getFontPeer() - { - return (GdkFontPeer) getFont().getPeer(); - } - - public void drawGdkTextLayout(GdkTextLayout gl, float x, float y) - { - cairoDrawGdkTextLayout (gl, x, y); - updateBufferedImage (); - } + ///////////////////////// TEXT METHODS //////////////////////////////////// public void drawString(String str, float x, float y) { @@ -1553,7 +1273,6 @@ public class GdkGraphics2D extends Graphics2D return; drawGlyphVector(getFont().createGlyphVector(null, str), x, y); - updateBufferedImage (); } public void drawString(String str, int x, int y) @@ -1573,8 +1292,7 @@ public class GdkGraphics2D extends Graphics2D float[] positions = gv.getGlyphPositions (0, n, null); setFont (gv.getFont ()); - cairoDrawGlyphVector (getFontPeer(), x, y, n, codes, positions); - updateBufferedImage (); + cairoDrawGlyphVector( (GdkFontPeer)getFont().getPeer(), x, y, n, codes, positions); } public void drawString(AttributedCharacterIterator ci, float x, float y) @@ -1583,52 +1301,19 @@ public class GdkGraphics2D extends Graphics2D drawGlyphVector(gv, x, y); } - public void fillArc(int x, int y, int width, int height, int startAngle, - int arcAngle) - { - fill(new Arc2D.Double((double) x, (double) y, (double) width, - (double) height, (double) startAngle, - (double) arcAngle, Arc2D.OPEN)); - } - - public void fillOval(int x, int y, int width, int height) - { - fillArc(x, y, width, height, 0, 360); - } - - public void fillRoundRect(int x, int y, int width, int height, int arcWidth, - int arcHeight) - { - if (arcWidth > width) - arcWidth = width; - if (arcHeight > height) - arcHeight = height; - - int xx = x + width - arcWidth; - int yy = y + height - arcHeight; - - fillArc(x, y, arcWidth, arcHeight, 90, 90); - fillArc(xx, y, arcWidth, arcHeight, 0, 90); - fillArc(xx, yy, arcWidth, arcHeight, 270, 90); - fillArc(x, yy, arcWidth, arcHeight, 180, 90); - - fillRect(x, y + arcHeight / 2, width, height - arcHeight + 1); - fillRect(x + arcWidth / 2, y, width - arcWidth + 1, height); - } - - public Font getFont() + /** + * Should perhaps be contexct dependent, but this is left for now as an + * overloadable default implementation. + */ + public FontRenderContext getFontRenderContext() { - if (font == null) - return new Font("SansSerif", Font.PLAIN, 12); - return font; + return new FontRenderContext(transform, true, true); } // Until such time as pango is happy to talk directly to cairo, we // actually need to redirect some calls from the GtkFontPeer and // GtkFontMetrics into the drawing kit and ask cairo ourselves. - static native void releasePeerGraphicsResource(GdkFontPeer f); - public FontMetrics getFontMetrics() { return getFontMetrics(getFont()); @@ -1656,9 +1341,25 @@ public class GdkGraphics2D extends Graphics2D .getFont(f.getName(), f.getAttributes()); } - public void setFontUnlocked(Font f) + public Font getFont() + { + if (font == null) + return new Font("SansSerif", Font.PLAIN, 12); + return font; + } + + /////////////////////// MISC. PUBLIC METHODS ///////////////////////////////// + + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { - setFont (f); + if( onStroke ) + { + Shape stroked = stroke.createStrokedShape( s ); + return stroked.intersects( (double)rect.x, (double)rect.y, + (double)rect.width, (double)rect.height ); + } + return s.intersects( (double)rect.x, (double)rect.y, + (double)rect.width, (double)rect.height ); } public String toString() @@ -1668,4 +1369,218 @@ public class GdkGraphics2D extends Graphics2D + ",color=" + fg.toString() + "]"); } + + ///////////////////////// PRIVATE METHODS /////////////////////////////////// + + /** + * All the drawImage() methods eventually get delegated here if the image + * is not a Cairo surface. + * + * @param bgcolor - if non-null draws the background color before + * drawing the image. + */ + private boolean drawRaster(ColorModel cm, Raster r, + AffineTransform imageToUser, Color bgcolor) + { + if (r == null) + return false; + + SampleModel sm = r.getSampleModel(); + DataBuffer db = r.getDataBuffer(); + + if (db == null || sm == null) + return false; + + if (cm == null) + cm = ColorModel.getRGBdefault(); + + double[] i2u = new double[6]; + if (imageToUser != null) + imageToUser.getMatrix(i2u); + else + { + i2u[0] = 1; + i2u[1] = 0; + i2u[2] = 0; + i2u[3] = 1; + i2u[4] = 0; + i2u[5] = 0; + } + + int[] pixels = findSimpleIntegerArray(cm, r); + + if (pixels == null) + { + // FIXME: I don't think this code will work correctly with a non-RGB + // MultiPixelPackedSampleModel. Although this entire method should + // probably be rewritten to better utilize Cairo's different supported + // data formats. + if (sm instanceof MultiPixelPackedSampleModel) + { + pixels = r.getPixels(0, 0, r.getWidth(), r.getHeight(), pixels); + for (int i = 0; i < pixels.length; i++) + pixels[i] = cm.getRGB(pixels[i]); + } + else + { + pixels = new int[r.getWidth() * r.getHeight()]; + for (int i = 0; i < pixels.length; i++) + pixels[i] = cm.getRGB(db.getElem(i)); + } + } + + // Change all transparent pixels in the image to the specified bgcolor, + // or (if there's no alpha) fill in an alpha channel so that it paints + // correctly. + if (cm.hasAlpha()) + { + if (bgcolor != null && cm.hasAlpha()) + for (int i = 0; i < pixels.length; i++) + { + if (cm.getAlpha(pixels[i]) == 0) + pixels[i] = bgcolor.getRGB(); + } + } + else + for (int i = 0; i < pixels.length; i++) + pixels[i] |= 0xFF000000; + + drawPixels(pixels, r.getWidth(), r.getHeight(), r.getWidth(), i2u); + + // Cairo seems to lose the current color which must be restored. + updateColor(); + + return true; + } + + /** + * Shifts coordinates by 0.5. + */ + private double shifted(double coord, boolean doShift) + { + if (doShift) + return Math.floor(coord) + 0.5; + else + return coord; + } + + /** + * Adds a pathIterator to the current Cairo path, also sets the cairo winding rule. + */ + private void walkPath(PathIterator p, boolean doShift) + { + double x = 0; + double y = 0; + double[] coords = new double[6]; + + cairoSetFillRule(p.getWindingRule()); + for (; ! p.isDone(); p.next()) + { + int seg = p.currentSegment(coords); + switch (seg) + { + case PathIterator.SEG_MOVETO: + x = shifted(coords[0], doShift); + y = shifted(coords[1], doShift); + cairoMoveTo(x, y); + break; + case PathIterator.SEG_LINETO: + x = shifted(coords[0], doShift); + y = shifted(coords[1], doShift); + cairoLineTo(x, y); + break; + case PathIterator.SEG_QUADTO: + // splitting a quadratic bezier into a cubic: + // see: http://pfaedit.sourceforge.net/bezier.html + double x1 = x + (2.0 / 3.0) * (shifted(coords[0], doShift) - x); + double y1 = y + (2.0 / 3.0) * (shifted(coords[1], doShift) - y); + + double x2 = x1 + (1.0 / 3.0) * (shifted(coords[2], doShift) - x); + double y2 = y1 + (1.0 / 3.0) * (shifted(coords[3], doShift) - y); + + x = shifted(coords[2], doShift); + y = shifted(coords[3], doShift); + cairoCurveTo(x1, y1, x2, y2, x, y); + break; + case PathIterator.SEG_CUBICTO: + x = shifted(coords[4], doShift); + y = shifted(coords[5], doShift); + cairoCurveTo(shifted(coords[0], doShift), + shifted(coords[1], doShift), + shifted(coords[2], doShift), + shifted(coords[3], doShift), x, y); + break; + case PathIterator.SEG_CLOSE: + cairoClosePath(); + break; + } + } + } + + /** + * Used by setRenderingHints() + */ + private Map getDefaultHints() + { + HashMap defaultHints = new HashMap(); + + defaultHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + + defaultHints.put(RenderingHints.KEY_STROKE_CONTROL, + RenderingHints.VALUE_STROKE_DEFAULT); + + defaultHints.put(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_OFF); + + defaultHints.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + + defaultHints.put(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_DEFAULT); + + return defaultHints; + } + + /** + * Used by drawRaster and GdkPixbufDecoder + */ + public static int[] findSimpleIntegerArray (ColorModel cm, Raster raster) + { + if (cm == null || raster == null) + return null; + + if (! cm.getColorSpace().isCS_sRGB()) + return null; + + if (! (cm instanceof DirectColorModel)) + return null; + + DirectColorModel dcm = (DirectColorModel) cm; + + if (dcm.getRedMask() != 0x00FF0000 || dcm.getGreenMask() != 0x0000FF00 + || dcm.getBlueMask() != 0x000000FF) + return null; + + if (! (raster instanceof WritableRaster)) + return null; + + if (raster.getSampleModel().getDataType() != DataBuffer.TYPE_INT) + return null; + + if (! (raster.getDataBuffer() instanceof DataBufferInt)) + return null; + + DataBufferInt db = (DataBufferInt) raster.getDataBuffer(); + + if (db.getNumBanks() != 1) + return null; + + // Finally, we have determined that this is a single bank, [A]RGB-int + // buffer in sRGB space. It's worth checking all this, because it means + // that cairo can paint directly into the data buffer, which is very + // fast compared to all the normal copying and converting. + + return db.getData(); + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java new file mode 100644 index 0000000..e19c9b9 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java @@ -0,0 +1,288 @@ +/* CairoSurface.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.peer.gtk; + +import java.awt.Graphics; +import java.awt.Color; +import java.awt.Image; +import java.awt.Point; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Vector; +import java.io.ByteArrayOutputStream; +import java.io.BufferedInputStream; +import java.net.URL; +import gnu.classpath.Pointer; + +/** + * CairoSurface - wraps a Cairo surface. + * + * @author Sven de Marothy + */ +public class CairoSurface extends DataBuffer +{ + int width = -1, height = -1; + + /** + * The native pointer to the Cairo surface. + */ + long surfacePointer; + + /** + * The native pointer to the image's data buffer + */ + long bufferPointer; + + + static ColorModel nativeModel = new DirectColorModel(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + + /** + * Allocates and clears the buffer and creates the cairo surface. + * @param width, height - the image size + * @param stride - the buffer row stride. + */ + private native void create(int width, int height, int stride); + + /** + * Destroys the cairo surface and frees the buffer. + */ + private native void destroy(); + + /** + * Gets buffer elements + */ + private native int nativeGetElem(int i); + + /** + * Sets buffer elements. + */ + private native void nativeSetElem(int i, int val); + + /** + * Draws this image to a given CairoGraphics context, + * with an affine transform given by i2u. + */ + public native void drawSurface(CairoGraphics2D context, double[] i2u); + + /** + * getPixels -return the pixels as a java array. + */ + native int[] getPixels(int size); + + /** + * getPixels -return the pixels as a java array. + */ + native void setPixels(int[] pixels); + + native long getFlippedBuffer(int size); + + /** + * Create a cairo_surface_t with specified width and height. + * The format will be ARGB32 with premultiplied alpha and native bit + * and word ordering. + */ + CairoSurface(int width, int height) + { + super(DataBuffer.TYPE_INT, width * height); + + if(width <= 0 || height <= 0) + throw new IllegalArgumentException("Image must be at least 1x1 pixels."); + + this.width = width; + this.height = height; + + create(width, height, width * 4); + + if(surfacePointer == 0 || bufferPointer == 0) + throw new Error("Could not allocate bitmap."); + } + + /** + * Create a cairo_surface_t from a GtkImage instance. + * (data is copied, not shared) + */ + CairoSurface(GtkImage image) + { + super(DataBuffer.TYPE_INT, image.width * image.height); + + if(image.width <= 0 || image.height <= 0) + throw new IllegalArgumentException("Image must be at least 1x1 pixels."); + + width = image.width; + height = image.height; + + create(width, height, width * 4); + + if(surfacePointer == 0 || bufferPointer == 0) + throw new Error("Could not allocate bitmap."); + + // Copy the pixel data from the GtkImage. + int[] data = image.getPixels(); + + // Swap ordering from GdkPixbuf to Cairo + for(int i = 0; i < data.length; i++ ) + { + int alpha = (data[i] & 0xFF000000) >> 24; + if( alpha == 0 ) // I do not know why we need this, but it works. + data[i] = 0; + else + { + int r = (((data[i] & 0x00FF0000) >> 16) ); + int g = (((data[i] & 0x0000FF00) >> 8) ); + int b = ((data[i] & 0x000000FF) ); + data[i] = (( alpha << 24 ) & 0xFF000000) + | (( b << 16 ) & 0x00FF0000) + | (( g << 8 ) & 0x0000FF00) + | ( r & 0x000000FF); + } + } + + setPixels( data ); + } + + /** + * Dispose of the native data. + */ + public void dispose() + { + if(surfacePointer != 0) + destroy(); + } + + /** + * Call dispose() to clean up any native resources allocated. + */ + protected void finalize() + { + dispose(); + } + + /** + * Return a GtkImage from this Cairo surface. + */ + public GtkImage getGtkImage() + { + return new GtkImage( width, height, getFlippedBuffer( width * height )); + } + + /** + * Returns a BufferedImage backed by a Cairo surface. + */ + public static BufferedImage getBufferedImage(int width, int height) + { + return getBufferedImage(new CairoSurface(width, height)); + } + + /** + * Returns a BufferedImage backed by a Cairo surface, + * created from a GtkImage. + */ + public static BufferedImage getBufferedImage(GtkImage image) + { + return getBufferedImage(new CairoSurface(image)); + } + + /** + * Returns a BufferedImage backed by a Cairo surface. + */ + public static BufferedImage getBufferedImage(CairoSurface surface) + { + WritableRaster raster = Raster.createPackedRaster + (surface, surface.width, surface.height, surface.width, + new int[]{ 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 }, + new Point(0,0)); + + return new BufferedImage(nativeModel, raster, true, new Hashtable()); + } + + /** + * DataBank.getElem implementation + */ + public int getElem(int bank, int i) + { + if(bank != 0 || i < 0 || i >= width*height) + throw new IndexOutOfBoundsException(i+" size: "+width*height); + return nativeGetElem(i); + } + + /** + * DataBank.setElem implementation + */ + public void setElem(int bank, int i, int val) + { + if(bank != 0 || i < 0 || i >= width*height) + throw new IndexOutOfBoundsException(i+" size: "+width*height); + nativeSetElem(i, val); + } + + /** + * Return a Graphics2D drawing to the CairoSurface. + */ + public Graphics2D getGraphics() + { + return new CairoSurfaceGraphics(this); + } + + ///// Methods used by CairoSurfaceGraphics ///// + /** + * Creates a cairo_t drawing context, returns the pointer as a long. + * Used by CairoSurfaceGraphics. + */ + native long newCairoContext(); + + /** + * Copy an area of the surface. Expects parameters must be within bounds. + * Count on a segfault otherwise. + */ + native void copyAreaNative(int x, int y, int width, int height, + int dx, int dy, int stride); +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java new file mode 100644 index 0000000..38c549d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java @@ -0,0 +1,100 @@ +/* CairoSurfaceGraphics.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.peer.gtk; + +import java.awt.Graphics; +import java.awt.Color; +import java.awt.Image; +import java.awt.Point; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.geom.Rectangle2D; +import java.awt.image.*; + +/** + * Implementation of Graphics2D on a Cairo surface. + */ +public class CairoSurfaceGraphics extends CairoGraphics2D +{ + protected CairoSurface surface; + private long cairo_t; + + /** + * Create a graphics context from a cairo surface + */ + public CairoSurfaceGraphics(CairoSurface surface) + { + this.surface = surface; + cairo_t = surface.newCairoContext(); + setup( cairo_t ); + setClip(0, 0, surface.width, surface.height); + } + + /** + * Creates another context from a surface. + * Used by create(). + */ + private CairoSurfaceGraphics(CairoSurfaceGraphics copyFrom) + { + surface = copyFrom.surface; + cairo_t = surface.newCairoContext(); + copy( copyFrom, cairo_t ); + setClip(0, 0, surface.width, surface.height); + } + + public Graphics create() + { + return new CairoSurfaceGraphics(this); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + throw new UnsupportedOperationException(); + } + + protected Rectangle2D getRealBounds() + { + return new Rectangle2D.Double(0.0, 0.0, surface.width, surface.height); + } + + public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy) + { + surface.copyAreaNative(x, y, width, height, dx, dy, surface.width); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java new file mode 100644 index 0000000..c6cf494 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java @@ -0,0 +1,243 @@ +/* ComponentGraphics.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.peer.gtk; + +import java.awt.Color; +import java.awt.Font; +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.Point; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.awt.image.ImagingOpException; +import java.awt.image.RenderedImage; + +/** + * ComponentGraphics - context for drawing directly to a component, + * as this is an X drawable, it requires that we use GTK locks. + * + * This context draws directly to the drawable and requires xrender. + */ +public class ComponentGraphics extends CairoGraphics2D +{ + private GtkComponentPeer component; + protected long cairo_t; + + ComponentGraphics() + { + } + + private ComponentGraphics(GtkComponentPeer component) + { + this.component = component; + cairo_t = initState(component); + setup( cairo_t ); + Rectangle bounds = component.awtComponent.getBounds(); + setClip( new Rectangle( 0, 0, bounds.width, bounds.height) ); + setBackground(component.awtComponent.getBackground()); + setColor(component.awtComponent.getForeground()); + } + + private ComponentGraphics(ComponentGraphics cg) + { + component = cg.component; + cairo_t = initState(component); + copy( cg, cairo_t ); + Rectangle bounds = component.awtComponent.getBounds(); + setClip( new Rectangle( 0, 0, bounds.width, bounds.height) ); + setBackground(component.awtComponent.getBackground()); + setColor(component.awtComponent.getForeground()); + } + + /** + * Creates a cairo_t for the component surface and return it. + */ + private native long initState(GtkComponentPeer component); + + /** + * Destroys the component surface and calls dispose on the cairo + * graphics2d to destroy any super class resources. + */ + public void dispose() + { + disposeSurface(nativePointer); + super.dispose(); + } + + /** + * Destroys the component surface. + */ + private native void disposeSurface(long nativePointer); + + /** + * Creates a cairo_t for a volatile image + */ + protected native long initFromVolatile( long pixmapPtr, int width, int height); + + /** + * Grab lock + */ + private native void start_gdk_drawing(); + + /** + * Release lock + */ + private native void end_gdk_drawing(); + + /** + * Query if the system has the XRender extension. + */ + public static native boolean hasXRender(); + + + private native void copyAreaNative(GtkComponentPeer component, int x, int y, + int width, int height, int dx, int dy); + + private native void drawVolatile(GtkComponentPeer component, + Image vimg, int x, int y, + int width, int height); + + /** + * Returns a Graphics2D object for a component, either an instance of this + * class (if xrender is supported), or a context which copies. + */ + public static Graphics2D getComponentGraphics(GtkComponentPeer component) + { + if( hasXRender() ) + return new ComponentGraphics(component); + + Rectangle r = component.awtComponent.getBounds(); + return new ComponentGraphicsCopy(r.width, r.height, component); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + return component.getGraphicsConfiguration(); + } + + public Graphics create() + { + return new ComponentGraphics(this); + } + + protected Rectangle2D getRealBounds() + { + return component.awtComponent.getBounds(); + } + + public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy) + { + copyAreaNative(component, x, y, width, height, dx, dy); + } + + /** + * Overloaded methods that do actual drawing need to enter the gdk threads + * and also do certain things before and after. + */ + public void draw(Shape s) + { + start_gdk_drawing(); + super.draw(s); + end_gdk_drawing(); + } + + public void fill(Shape s) + { + start_gdk_drawing(); + super.fill(s); + end_gdk_drawing(); + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + start_gdk_drawing(); + super.drawRenderedImage(image, xform); + end_gdk_drawing(); + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + start_gdk_drawing(); + boolean rv = super.drawImage(img, xform, bgcolor, obs); + end_gdk_drawing(); + return rv; + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + start_gdk_drawing(); + super.drawGlyphVector(gv, x, y); + end_gdk_drawing(); + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + if( img instanceof GtkVolatileImage ) + { + drawVolatile( component, img, x, y - 20, + ((GtkVolatileImage)img).width, + ((GtkVolatileImage)img).height ); + return true; + } + return super.drawImage( img, x, y, observer ); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) + { + if( img instanceof GtkVolatileImage ) + { + drawVolatile( component, img, x, y - 20, + width, height ); + return true; + } + return super.drawImage( img, x, y, width, height, observer ); + } + +} + diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphicsCopy.java b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphicsCopy.java new file mode 100644 index 0000000..286fbea --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphicsCopy.java @@ -0,0 +1,129 @@ +/* ComponentGraphicsCopy.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.peer.gtk; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.RenderedImage; +import java.awt.image.ImageObserver; + +/** + * Implementation of Graphics2D for Components for servers which + * do not have xrender. + * + * A mirrored GtkImage of the component is stored in memory + * and copied back. Yay. + */ +public class ComponentGraphicsCopy extends CairoSurfaceGraphics +{ + private GtkComponentPeer component; + + /** + * GtkImage sharing its data buffer with this Cairo surface. + */ + private GtkImage gtkimage; + + private int width, height; + + native void getPixbuf( GtkComponentPeer component, GtkImage image ); + + native void copyPixbuf( GtkComponentPeer component, GtkImage image, + int x, int y, int w, int h ); + + public ComponentGraphicsCopy(int width, int height, + GtkComponentPeer component) + { + super( new CairoSurface( width, height ) ); + this.component = component; + this.width = width; + this.height = height; + gtkimage = surface.getGtkImage(); + getPixbuf( component, gtkimage ); + } + + /** + * Overloaded methods that do actual drawing need to enter the gdk threads + * and also do certain things before and after. + */ + public void draw(Shape s) + { + super.draw(s); + Rectangle r = s.getBounds(); + copyPixbuf(component, gtkimage, r.x, r.y, r.width, r.height); + } + + public void fill(Shape s) + { + super.fill(s); + Rectangle r = s.getBounds(); + copyPixbuf(component, gtkimage, r.x, r.y, r.width, r.height); + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + super.drawRenderedImage(image, xform); + copyPixbuf(component, gtkimage, 0, 0, width, height); + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + boolean rv = super.drawImage(img, xform, bgcolor, obs); + copyPixbuf(component, gtkimage, 0, 0, width, height); + return rv; + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + super.drawGlyphVector(gv, x, y); + Rectangle r = gv.getPixelBounds(getFontRenderContext(), x , y); + copyPixbuf(component, gtkimage, r.x, r.y, r.width, r.height); + } +} + diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java b/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java new file mode 100644 index 0000000..0f8ce6d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java @@ -0,0 +1,392 @@ +/* FreetypeGlyphVector.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.peer.gtk; + +import java.awt.Font; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.GeneralPath; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.font.FontRenderContext; + +public class FreetypeGlyphVector extends GlyphVector +{ + /** + * The associated font and its peer. + */ + private Font font; + private GdkFontPeer peer; // ATTN: Accessed from native code. + + /** + * The string represented by this GlyphVector. + */ + private String s; + + /** + * The font render context + */ + private FontRenderContext frc; + + /** + * The total # of glyphs. + */ + private int nGlyphs; + + /** + * The glyph codes + */ + private int[] glyphCodes; + + /** + * Glyph transforms. (de facto only the translation is used) + */ + private AffineTransform[] glyphTransforms; + + /** + * Create a glyphvector from a given (Freetype) font and a String. + */ + public FreetypeGlyphVector(Font f, String s, FontRenderContext frc) + { + this.s = s; + this.font = f; + this.frc = frc; + if( !(font.getPeer() instanceof GdkFontPeer ) ) + throw new IllegalArgumentException("Not a valid font."); + peer = (GdkFontPeer)font.getPeer(); + + getGlyphs(); + performDefaultLayout(); + } + + /** + * Create a glyphvector from a given set of glyph codes. + */ + public FreetypeGlyphVector(Font f, int[] codes, FontRenderContext frc) + { + this.font = f; + this.frc = frc; + if( !(font.getPeer() instanceof GdkFontPeer ) ) + throw new IllegalArgumentException("Not a valid font."); + peer = (GdkFontPeer)font.getPeer(); + + glyphCodes = new int[ codes.length ]; + System.arraycopy(codes, 0, glyphCodes, 0, codes.length); + nGlyphs = glyphCodes.length; + performDefaultLayout(); + } + + /** + * Create the array of glyph codes. + */ + private void getGlyphs() + { + nGlyphs = s.codePointCount( 0, s.length() ); + glyphCodes = new int[ nGlyphs ]; + int stringIndex = 0; + for(int i = 0; i < nGlyphs; i++) + { + glyphCodes[i] = getGlyph( s.codePointAt(stringIndex) ); + // UTF32 surrogate handling + if( s.codePointAt( stringIndex ) != (int)s.charAt( stringIndex ) ) + stringIndex ++; + stringIndex ++; + } + } + + /** + * Returns the glyph code within the font for a given character + */ + public native int getGlyph(int codepoint); + + /** + * Returns the kerning of a glyph pair + */ + private native Point2D getKerning(int leftGlyph, int rightGlyph); + + private native double[] getMetricsNative( int glyphCode ); + + private native GeneralPath getGlyphOutlineNative(int glyphIndex); + + /** + * Duh, compares two instances. + */ + public boolean equals(GlyphVector gv) + { + if( ! (gv instanceof FreetypeGlyphVector) ) + return false; + + return (((FreetypeGlyphVector)gv).font.equals(font) && + ((FreetypeGlyphVector)gv).frc.equals(frc) + && ((FreetypeGlyphVector)gv).s.equals(s)); + } + + /** + * Returns the associated Font + */ + public Font getFont() + { + return font; + } + + /** + * Returns the associated FontRenderContext + */ + public FontRenderContext getFontRenderContext() + { + return frc; + } + + /** + * Layout the glyphs. + */ + public void performDefaultLayout() + { + glyphTransforms = new AffineTransform[ nGlyphs ]; + double x = 0; + for(int i = 0; i < nGlyphs; i++) + { + GlyphMetrics gm = getGlyphMetrics( i ); + Rectangle2D r = gm.getBounds2D(); + glyphTransforms[ i ] = AffineTransform.getTranslateInstance(x, 0); + x += gm.getAdvanceX(); + if( i > 0 ) + { + Point2D p = getKerning( glyphCodes[ i - 1 ], glyphCodes[ i ] ); + x += p.getX(); + } + } + } + + /** + * Returns the code of the glyph at glyphIndex; + */ + public int getGlyphCode(int glyphIndex) + { + return glyphCodes[ glyphIndex ]; + } + + /** + * Returns multiple glyphcodes. + */ + public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, + int[] codeReturn) + { + int[] rval; + + if( codeReturn == null ) + rval = new int[ numEntries ]; + else + rval = codeReturn; + + System.arraycopy(glyphCodes, beginGlyphIndex, rval, 0, numEntries); + + return rval; + } + + /** + * FIXME: Implement me. + */ + public Shape getGlyphLogicalBounds(int glyphIndex) + { + GlyphMetrics gm = getGlyphMetrics( glyphIndex ); + if( gm == null ) + return null; + Rectangle2D r = gm.getBounds2D(); + return new Rectangle2D.Double( r.getX() - gm.getLSB(), r.getY(), + gm.getAdvanceX(), r.getHeight() ); + } + + /** + * Returns the metrics of a single glyph. + */ + public GlyphMetrics getGlyphMetrics(int glyphIndex) + { + double[] val = getMetricsNative( glyphCodes[ glyphIndex ] ); + if( val == null ) + return null; + + return new GlyphMetrics( true, (float)val[1], (float)val[2], + new Rectangle2D.Double( val[3], val[4], + val[5], val[6] ), + GlyphMetrics.STANDARD ); + } + + /** + * Returns the outline of a single glyph. + */ + public Shape getGlyphOutline(int glyphIndex) + { + GeneralPath gp = getGlyphOutlineNative( glyphCodes[ glyphIndex ] ); + gp.transform( glyphTransforms[ glyphIndex ] ); + return gp; + } + + /** + * Returns the position of a single glyph. + */ + public Point2D getGlyphPosition(int glyphIndex) + { + return glyphTransforms[ glyphIndex ].transform( new Point2D.Double(0, 0), + null ); + } + + /** + * Returns the positions of multiple glyphs. + */ + public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, + float[] positionReturn) + { + float[] rval; + + if( positionReturn == null ) + rval = new float[2 * numEntries]; + else + rval = positionReturn; + + for( int i = beginGlyphIndex; i < numEntries; i++ ) + { + Point2D p = getGlyphPosition( i ); + rval[i * 2] = (float)p.getX(); + rval[i * 2 + 1] = (float)p.getY(); + } + + return rval; + } + + /** + * Returns the transform of a glyph. + */ + public AffineTransform getGlyphTransform(int glyphIndex) + { + return new AffineTransform( glyphTransforms[ glyphIndex ] ); + } + + /** + * Returns the visual bounds of a glyph + * May be off by a pixel or two due to hinting/rasterization. + */ + public Shape getGlyphVisualBounds(int glyphIndex) + { + return getGlyphOutline( glyphIndex ).getBounds2D(); + } + + /** + * Return the logical bounds of the whole thing. + */ + public Rectangle2D getLogicalBounds() + { + if( nGlyphs == 0 ) + return new Rectangle2D.Double(0, 0, 0, 0); + + Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 ); + for( int i = 1; i < nGlyphs; i++ ) + rect = rect.createUnion( (Rectangle2D)getGlyphLogicalBounds( i ) ); + + return rect; + } + + /** + * Returns the number of glyphs. + */ + public int getNumGlyphs() + { + return glyphCodes.length; + } + + /** + * Returns the outline of the entire GlyphVector. + */ + public Shape getOutline() + { + GeneralPath path = new GeneralPath(); + for( int i = 0; i < getNumGlyphs(); i++ ) + path.append( getGlyphOutline( i ), false ); + return path; + } + + /** + * TODO: + * FreeType does not currently have an API for the JSTF table. We should + * probably get the table ourselves from FT and pass it to some parser + * which the native font peers will need. + */ + public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) + { + return null; + } + + /** + * Returns the outline of the entire vector, drawn at (x,y). + */ + public Shape getOutline(float x, float y) + { + AffineTransform tx = AffineTransform.getTranslateInstance( x, y ); + return tx.createTransformedShape( getOutline() ); + } + + /** + * Returns the visual bounds of the entire GlyphVector. + * May be off by a pixel or two due to hinting/rasterization. + */ + public Rectangle2D getVisualBounds() + { + return getOutline().getBounds2D(); + } + + /** + * Sets the position of a glyph. + */ + public void setGlyphPosition(int glyphIndex, Point2D newPos) + { + // FIXME: Scaling, etc.? + glyphTransforms[ glyphIndex ].setToTranslation( newPos.getX(), + newPos.getY() ); + } + + /** + * Sets the transform of a single glyph. + */ + public void setGlyphTransform(int glyphIndex, AffineTransform newTX) + { + glyphTransforms[ glyphIndex ].setTransform( newTX ); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java index 36986d5..7aa5e7a 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java @@ -40,19 +40,23 @@ package gnu.java.awt.peer.gtk; import gnu.classpath.Configuration; import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.font.opentype.NameDecoder; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; +import java.awt.font.GlyphMetrics; import java.awt.font.LineMetrics; import java.awt.geom.Rectangle2D; +import java.awt.geom.Point2D; import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; +import java.nio.ByteBuffer; public class GdkFontPeer extends ClasspathFontPeer { @@ -76,17 +80,21 @@ public class GdkFontPeer extends ClasspathFontPeer } } + private ByteBuffer nameTable = null; + private native void initState (); private native void dispose (); - private native void setFont (String family, int style, int size, boolean useGraphics2D); + private native void setFont (String family, int style, int size); native void getFontMetrics(double [] metrics); native void getTextMetrics(String str, double [] metrics); + native void releasePeerGraphicsResource(); + + protected void finalize () { - if (GtkToolkit.useGraphics2D ()) - GdkGraphics2D.releasePeerGraphicsResource(this); + releasePeerGraphicsResource(); dispose (); } @@ -136,26 +144,84 @@ public class GdkFontPeer extends ClasspathFontPeer { super(name, style, size); initState (); - setFont (this.familyName, this.style, (int)this.size, - GtkToolkit.useGraphics2D()); + setFont (this.familyName, this.style, (int)this.size); } public GdkFontPeer (String name, Map attributes) { super(name, attributes); initState (); - setFont (this.familyName, this.style, (int)this.size, - GtkToolkit.useGraphics2D()); + setFont (this.familyName, this.style, (int)this.size); } - + + /** + * Unneeded, but implemented anyway. + */ public String getSubFamilyName(Font font, Locale locale) { - return null; + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_SUBFAMILY, locale); + if (name == null) + { + name = getName(NameDecoder.NAME_SUBFAMILY, Locale.ENGLISH); + if ("Regular".equals(name)) + name = null; + } + + return name; } + /** + * Returns the bytes belonging to a TrueType/OpenType table, + * Parameters n,a,m,e identify the 4-byte ASCII tag of the table. + * + * Returns null if the font is not TT, the table is nonexistant, + * or if some other unexpected error occured. + * + */ + private native byte[] getTrueTypeTable(byte n, byte a, byte m, byte e); + + /** + * Returns the PostScript name of the font, defaults to the familyName if + * a PS name could not be retrieved. + */ public String getPostScriptName(Font font) { - return this.familyName; + String name = getName(NameDecoder.NAME_POSTSCRIPT, + /* any language */ null); + if( name == null ) + return this.familyName; + + return name; + } + + /** + * Extracts a String from the font’s name table. + * + * @param name the numeric TrueType or OpenType name ID. + * + * @param locale the locale for which names shall be localized, or + * <code>null</code> if the locale does mot matter because the name + * is known to be language-independent (for example, because it is + * the PostScript name). + */ + private String getName(int name, Locale locale) + { + if (nameTable == null) + { + byte[] data = getTrueTypeTable((byte)'n', (byte) 'a', + (byte) 'm', (byte) 'e'); + if( data == null ) + return null; + + nameTable = ByteBuffer.wrap( data ); + } + + return NameDecoder.getName(nameTable, name, locale); } public boolean canDisplay (Font font, char c) @@ -170,23 +236,18 @@ public class GdkFontPeer extends ClasspathFontPeer return -1; } - private native GdkGlyphVector getGlyphVector(String txt, - Font f, - FontRenderContext ctx); - public GlyphVector createGlyphVector (Font font, FontRenderContext ctx, CharacterIterator i) { - return getGlyphVector(buildString (i), font, ctx); + return new FreetypeGlyphVector(font, buildString (i), ctx); } public GlyphVector createGlyphVector (Font font, FontRenderContext ctx, int[] glyphCodes) { - return null; - // return new GdkGlyphVector (font, this, ctx, glyphCodes); + return new FreetypeGlyphVector(font, glyphCodes, ctx); } public byte getBaselineFor (Font font, char c) @@ -262,13 +323,21 @@ public class GdkFontPeer extends ClasspathFontPeer public int getNumGlyphs (Font font) { - throw new UnsupportedOperationException (); + byte[] data = getTrueTypeTable((byte)'m', (byte) 'a', + (byte)'x', (byte) 'p'); + if( data == null ) + return -1; + + ByteBuffer buf = ByteBuffer.wrap( data ); + return buf.getShort(4); } public Rectangle2D getStringBounds (Font font, CharacterIterator ci, int begin, int limit, FontRenderContext frc) { - GdkGlyphVector gv = getGlyphVector(buildString (ci, begin, limit), font, frc); + GlyphVector gv = new FreetypeGlyphVector( font, + buildString(ci, begin, limit), + frc); return gv.getVisualBounds(); } @@ -303,5 +372,4 @@ public class GdkFontPeer extends ClasspathFontPeer // the metrics cache. return Toolkit.getDefaultToolkit().getFontMetrics (font); } - } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGlyphVector.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGlyphVector.java deleted file mode 100644 index f0ddea4..0000000 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGlyphVector.java +++ /dev/null @@ -1,359 +0,0 @@ -/* GdkGlyphVector.java -- Glyph vector object - Copyright (C) 2003, 2004, 2005 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.gtk; - -import java.awt.Font; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphJustificationInfo; -import java.awt.font.GlyphMetrics; -import java.awt.font.GlyphVector; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -public class GdkGlyphVector extends GlyphVector -{ - - /* We use a simple representation for glyph vectors here. Glyph i - * consumes 8 doubles: - * - * logical x: extents[ 10*i ] - * logical y: extents[ 10*i + 1 ] - * logical width: extents[ 10*i + 2 ] - * logical height: extents[ 10*i + 3 ] - * - * visual x: extents[ 10*i + 4 ] - * visual y: extents[ 10*i + 5 ] - * visual width: extents[ 10*i + 6 ] - * visual height: extents[ 10*i + 7 ] - * - * origin pos x: extents[ 10*i + 8 ] - * origin pos y: extents[ 10*i + 9 ] - * - * as well as one int, code[i], representing the glyph code in the font. - */ - - double [] extents; - int [] codes; - - Font font; - FontRenderContext fontRenderContext; - - Rectangle2D allLogical; - Rectangle2D allVisual; - - public GdkGlyphVector(double[] extents, int[] codes, Font font, FontRenderContext frc) - { - this.extents = extents; - this.codes = codes; - this.font = font; - this.fontRenderContext = frc; - - allLogical = new Rectangle2D.Double(); - allVisual = new Rectangle2D.Double(); - - for (int i = 0; i < codes.length; ++i) - { - allLogical.add (new Rectangle2D.Double(extents[10*i ] + extents[10*i + 8], - extents[10*i + 1] + extents[10*i + 9], - extents[10*i + 2], - extents[10*i + 3])); - - allVisual.add (new Rectangle2D.Double(extents[10*i + 4] + extents[10*i + 8], - extents[10*i + 5] + extents[10*i + 9], - extents[10*i + 6], - extents[10*i + 7])); - } - } - - /* - geometric notes: - - the FRC contains a mapping from points -> pixels. - - typographics points are typically 1/72 of an inch. - - pixel displays are often around 72 dpi. - - so the FRC can get away with using an identity transform on a screen, - often. behavior is documented by sun to fall back to an identity - transform if the internal transformation is null. - - coordinates coming up from pango are expressed as floats -- in device - space, so basically pixels-with-fractional-bits -- derived from their - storage format in pango (1024ths of pixels). - - it is not clear from the javadocs whether the results of methods like - getGlyphPositions ought to return coordinates in device space, or - "point" space, or what. for now I'm returning them in device space. - - */ - - public double[] getExtents() - { - return extents; - } - - public int[] getCodes() - { - return codes; - } - - public Font getFont () - { - return font; - } - - public FontRenderContext getFontRenderContext () - { - return fontRenderContext; - } - - public int getGlyphCharIndex (int glyphIndex) - { - // FIXME: currently pango does not provide glyph-by-glyph - // reverse mapping information, so we assume a broken 1:1 - // glyph model here. This is plainly wrong. - return glyphIndex; - } - - public int[] getGlyphCharIndices (int beginGlyphIndex, - int numEntries, - int[] codeReturn) - { - int ix[] = codeReturn; - if (ix == null) - ix = new int[numEntries]; - - for (int i = 0; i < numEntries; i++) - ix[i] = getGlyphCharIndex (beginGlyphIndex + i); - return ix; - } - - public int getGlyphCode (int glyphIndex) - { - return codes[glyphIndex]; - } - - public int[] getGlyphCodes (int beginGlyphIndex, int numEntries, - int[] codeReturn) - { - if (codeReturn == null) - codeReturn = new int[numEntries]; - - System.arraycopy(codes, beginGlyphIndex, codeReturn, 0, numEntries); - return codeReturn; - } - - public Shape getGlyphLogicalBounds (int i) - { - return new Rectangle2D.Double (extents[8*i], extents[8*i + 1], - extents[8*i + 2], extents[8*i + 3]); - } - - public GlyphMetrics getGlyphMetrics (int i) - { - // FIXME: pango does not yield vertical layout information at the - // moment. - - boolean is_horizontal = true; - double advanceX = extents[8*i + 2]; // "logical width" == advanceX - double advanceY = 0; - - return new GlyphMetrics (is_horizontal, - (float) advanceX, (float) advanceY, - (Rectangle2D) getGlyphVisualBounds(i), - GlyphMetrics.STANDARD); - } - - public Shape getGlyphOutline (int glyphIndex) - { - throw new UnsupportedOperationException (); - } - - public Shape getGlyphOutline (int glyphIndex, float x, float y) - { - throw new UnsupportedOperationException (); - } - - public Rectangle getGlyphPixelBounds (int i, - FontRenderContext renderFRC, - float x, float y) - { - return new Rectangle((int) x, (int) y, - (int) extents[8*i + 6], (int) extents[8*i + 7]); - } - - public Point2D getGlyphPosition (int i) - { - return new Point2D.Double (extents[10*i + 8], - extents[10*i + 9]); - } - - public float[] getGlyphPositions (int beginGlyphIndex, - int numEntries, - float[] positionReturn) - { - float fx[] = positionReturn; - if (fx == null) - fx = new float[numEntries * 2]; - - for (int i = 0; i < numEntries; ++i) - { - fx[2*i ] = (float) extents[10*i + 8]; - fx[2*i + 1] = (float) extents[10*i + 9]; - } - return fx; - } - - public AffineTransform getGlyphTransform (int glyphIndex) - { - // Glyphs don't have independent transforms in these simple glyph - // vectors; docs specify null is an ok return here. - return null; - } - - public Shape getGlyphVisualBounds (int i) - { - return new Rectangle2D.Double(extents[8*i + 4], extents[8*i + 5], - extents[8*i + 6], extents[8*i + 7]); - } - - public int getLayoutFlags () - { - return 0; - } - - public Rectangle2D getLogicalBounds () - { - return allLogical; - } - - public int getNumGlyphs () - { - return codes.length; - } - - public Shape getOutline () - { - throw new UnsupportedOperationException (); - } - - public Rectangle getPixelBounds (FontRenderContext renderFRC, - float x, float y) - { - return new Rectangle((int)x, - (int)y, - (int) allVisual.getWidth(), - (int) allVisual.getHeight()); - } - - public Rectangle2D getVisualBounds () - { - return allVisual; - } - - public void performDefaultLayout () - { - } - - public void setGlyphPosition (int i, Point2D newPos) - { - extents[8*i ] = newPos.getX(); - extents[8*i + 1] = newPos.getY(); - - extents[8*i + 4] = newPos.getX(); - extents[8*i + 5] = newPos.getY(); - } - - public void setGlyphTransform (int glyphIndex, - AffineTransform newTX) - { - // not yet.. maybe not ever? - throw new UnsupportedOperationException (); - } - - public boolean equals(GlyphVector gv) - { - if (gv == null) - return false; - - if (! (gv instanceof GdkGlyphVector)) - return false; - - GdkGlyphVector ggv = (GdkGlyphVector) gv; - - if ((ggv.codes.length != this.codes.length) - || (ggv.extents.length != this.extents.length)) - return false; - - if ((ggv.font == null && this.font != null) - || (ggv.font != null && this.font == null) - || (!ggv.font.equals(this.font))) - return false; - - if ((ggv.fontRenderContext == null && this.fontRenderContext != null) - || (ggv.fontRenderContext != null && this.fontRenderContext == null) - || (!ggv.fontRenderContext.equals(this.fontRenderContext))) - return false; - - for (int i = 0; i < ggv.codes.length; ++i) - if (ggv.codes[i] != this.codes[i]) - return false; - - for (int i = 0; i < ggv.extents.length; ++i) - if (ggv.extents[i] != this.extents[i]) - return false; - - return true; - } - - public GlyphJustificationInfo getGlyphJustificationInfo(int idx) - { - throw new UnsupportedOperationException (); - } - - public Shape getOutline(float x, float y) - { - throw new UnsupportedOperationException (); - } - -} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java deleted file mode 100644 index 50066ff..0000000 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java +++ /dev/null @@ -1,494 +0,0 @@ -/* GdkGraphics.java - Copyright (C) 1998, 1999, 2002, 2005 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.gtk; - -import gnu.classpath.Configuration; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.Toolkit; -import java.awt.image.ImageObserver; -import java.text.AttributedCharacterIterator; - -public class GdkGraphics extends Graphics -{ - static - { - System.loadLibrary("gtkpeer"); - - initStaticState (); - } - - static native void initStaticState(); - private final int native_state = GtkGenericPeer.getUniqueInteger (); - - Color color, xorColor; - GtkComponentPeer component; - Font font = new Font ("Dialog", Font.PLAIN, 12); - Rectangle clip; - GtkImage image; - - int xOffset = 0; - int yOffset = 0; - - static final int GDK_COPY = 0, GDK_XOR = 2; - - native void initState (GtkComponentPeer component); - native void initStateUnlocked (GtkComponentPeer component); - native void initState (int width, int height); - native void initFromImage (GtkImage image); - native void nativeCopyState (GdkGraphics g); - - /** - * A cached instance that is used by {@link #create} in order to avoid - * massive allocation of graphics contexts. - */ - GdkGraphics cached = null; - - /** - * A link to the parent context. This is used in {@link #dispose} to put - * this graphics context into the cache. - */ - GdkGraphics parent = null; - - GdkGraphics (GdkGraphics g) - { - parent = g; - copyState (g); - } - - GdkGraphics (int width, int height) - { - initState (width, height); - color = Color.black; - clip = new Rectangle (0, 0, width, height); - font = new Font ("Dialog", Font.PLAIN, 12); - } - - GdkGraphics (GtkImage image) - { - this.image = image; - initFromImage (image); - color = Color.black; - clip = new Rectangle (0, 0, - image.getWidth(null), image.getHeight(null)); - font = new Font ("Dialog", Font.PLAIN, 12); - } - - GdkGraphics (GtkComponentPeer component) - { - this.component = component; - color = Color.black; - - if (component.isRealized ()) - initComponentGraphics (); - else - connectSignals (component); - } - - void initComponentGraphics () - { - initState (component); - color = component.awtComponent.getForeground (); - if (color == null) - color = Color.BLACK; - Dimension d = component.awtComponent.getSize (); - clip = new Rectangle (0, 0, d.width, d.height); - } - - // called back by native side: realize_cb - void initComponentGraphicsUnlocked () - { - initStateUnlocked (component); - color = component.awtComponent.getForeground (); - if (color == null) - color = Color.BLACK; - Dimension d = component.awtComponent.getSize (); - clip = new Rectangle (0, 0, d.width, d.height); - } - - native void connectSignals (GtkComponentPeer component); - - public native void clearRect(int x, int y, int width, int height); - - public void clipRect (int x, int y, int width, int height) - { - if (component != null && ! component.isRealized ()) - return; - - clip = clip.intersection (new Rectangle (x, y, width, height)); - setClipRectangle (clip.x, clip.y, clip.width, clip.height); - } - - public native void copyArea(int x, int y, int width, int height, - int dx, int dy); - - /** - * Creates a copy of this GdkGraphics instance. This implementation can - * reuse a cached instance to avoid massive instantiation of Graphics objects - * during painting. - * - * @return a copy of this graphics context - */ - public Graphics create() - { - GdkGraphics copy = cached; - if (copy == null) - copy = new GdkGraphics(this); - else - { - copy.copyState(this); - cached = null; - } - return copy; - } - - public native void nativeDispose(); - - /** - * Disposes this graphics object. This puts this graphics context into the - * cache of its parent graphics if there is one. - */ - public void dispose() - { - if (parent != null) - { - parent.cached = this; - parent = null; - } - else - nativeDispose(); - } - - /** - * This is called when this object gets finalized by the garbage collector. - * In addition to {@link Graphics#finalize()} this calls nativeDispose() to - * make sure the native resources are freed before the graphics context is - * thrown away. - */ - public void finalize() - { - super.finalize(); - nativeDispose(); - } - - public boolean drawImage (Image img, int x, int y, - Color bgcolor, ImageObserver observer) - { - if (img != null) - return drawImage(img, x, y, img.getWidth(null), img.getHeight(null), - bgcolor, observer); - return false; - } - - public boolean drawImage (Image img, int x, int y, ImageObserver observer) - { - return drawImage (img, x, y, null, observer); - } - - public boolean drawImage(Image img, int x, int y, int width, int height, - Color bgcolor, ImageObserver observer) - { - if (img != null) - { - if (img instanceof GtkImage) - return ((GtkImage) img).drawImage(this, x, y, width, height, bgcolor, - observer); - return (new GtkImage(img.getSource())).drawImage(this, x, y, width, - height, bgcolor, - observer); - } - return false; - } - - public boolean drawImage (Image img, int x, int y, int width, int height, - ImageObserver observer) - { - return drawImage (img, x, y, width, height, null, observer); - } - - public boolean drawImage (Image img, int dx1, int dy1, int dx2, int dy2, - int sx1, int sy1, int sx2, int sy2, - Color bgcolor, ImageObserver observer) - { - if (img != null) - { - if (img instanceof GtkImage) - return ((GtkImage) img).drawImage(this, dx1, dy1, dx2, dy2, sx1, sy1, - sx2, sy2, bgcolor, observer); - return (new GtkImage(img.getSource())).drawImage(this, dx1, dy1, dx2, - dy2, sx1, sy1, sx2, - sy2, bgcolor, observer); - } - return false; - } - - public boolean drawImage (Image img, int dx1, int dy1, int dx2, int dy2, - int sx1, int sy1, int sx2, int sy2, - ImageObserver observer) - { - return drawImage (img, dx1, dy1, dx2, dy2, - sx1, sy1, sx2, sy2, - null, observer); - } - - public native void drawLine(int x1, int y1, int x2, int y2); - - public native void drawArc(int x, int y, int width, int height, - int startAngle, int arcAngle); - public native void fillArc(int x, int y, int width, int height, - int startAngle, int arcAngle); - public native void drawOval(int x, int y, int width, int height); - public native void fillOval(int x, int y, int width, int height); - - public native void drawPolygon(int[] xPoints, int[] yPoints, int nPoints); - public native void fillPolygon(int[] xPoints, int[] yPoints, int nPoints); - - public native void drawPolyline(int[] xPoints, int[] yPoints, int nPoints); - - public native void drawRect(int x, int y, int width, int height); - public native void fillRect(int x, int y, int width, int height); - - GdkFontPeer getFontPeer() - { - return (GdkFontPeer) getFont().getPeer(); - } - - native void drawString (GdkFontPeer f, String str, int x, int y); - public void drawString (String str, int x, int y) - { - drawString(getFontPeer(), str, x, y); - } - - public void drawString (AttributedCharacterIterator ci, int x, int y) - { - throw new Error ("not implemented"); - } - - public void drawRoundRect(int x, int y, int width, int height, - int arcWidth, int arcHeight) - { - if (arcWidth > width) - arcWidth = width; - if (arcHeight > height) - arcHeight = height; - - int xx = x + width - arcWidth; - int yy = y + height - arcHeight; - - drawArc (x, y, arcWidth, arcHeight, 90, 90); - drawArc (xx, y, arcWidth, arcHeight, 0, 90); - drawArc (xx, yy, arcWidth, arcHeight, 270, 90); - drawArc (x, yy, arcWidth, arcHeight, 180, 90); - - int y1 = y + arcHeight / 2; - int y2 = y + height - arcHeight / 2; - drawLine (x, y1, x, y2); - drawLine (x + width, y1, x + width, y2); - - int x1 = x + arcWidth / 2; - int x2 = x + width - arcWidth / 2; - drawLine (x1, y, x2, y); - drawLine (x1, y + height, x2, y + height); - } - - public void fillRoundRect (int x, int y, int width, int height, - int arcWidth, int arcHeight) - { - if (arcWidth > width) - arcWidth = width; - if (arcHeight > height) - arcHeight = height; - - int xx = x + width - arcWidth; - int yy = y + height - arcHeight; - - fillArc (x, y, arcWidth, arcHeight, 90, 90); - fillArc (xx, y, arcWidth, arcHeight, 0, 90); - fillArc (xx, yy, arcWidth, arcHeight, 270, 90); - fillArc (x, yy, arcWidth, arcHeight, 180, 90); - - fillRect (x, y + arcHeight / 2, width, height - arcHeight + 1); - fillRect (x + arcWidth / 2, y, width - arcWidth + 1, height); - } - - public Shape getClip () - { - return getClipBounds (); - } - - public Rectangle getClipBounds () - { - if (clip == null) - return null; - else - return clip.getBounds(); - } - - public Color getColor () - { - return color; - } - - public Font getFont () - { - return font; - } - - public FontMetrics getFontMetrics (Font font) - { - // Get the font metrics through GtkToolkit to take advantage of - // the metrics cache. - return Toolkit.getDefaultToolkit().getFontMetrics (font); - } - - native void setClipRectangle (int x, int y, int width, int height); - - public void setClip (int x, int y, int width, int height) - { - if ((component != null && ! component.isRealized ()) - || clip == null) - return; - - clip.x = x; - clip.y = y; - clip.width = width; - clip.height = height; - - setClipRectangle (x, y, width, height); - } - - public void setClip (Rectangle clip) - { - setClip (clip.x, clip.y, clip.width, clip.height); - } - - public void setClip (Shape clip) - { - if (clip == null) - { - // Reset clipping. - Dimension d = component.awtComponent.getSize(); - setClip(new Rectangle (0, 0, d.width, d.height)); - } - else - setClip(clip.getBounds()); - } - - private native void setFGColor(int red, int green, int blue); - - public void setColor (Color c) - { - if (c == null) - color = Color.BLACK; - else - color = c; - - if (xorColor == null) /* paint mode */ - setFGColor (color.getRed (), color.getGreen (), color.getBlue ()); - else /* xor mode */ - setFGColor (color.getRed () ^ xorColor.getRed (), - color.getGreen () ^ xorColor.getGreen (), - color.getBlue () ^ xorColor.getBlue ()); - } - - public void setFont (Font font) - { - if (font != null) - this.font = font; - } - - native void setFunction (int gdk_func); - - public void setPaintMode () - { - xorColor = null; - - setFunction (GDK_COPY); - setFGColor (color.getRed (), color.getGreen (), color.getBlue ()); - } - - public void setXORMode (Color c) - { - xorColor = c; - - setFunction (GDK_XOR); - setFGColor (color.getRed () ^ xorColor.getRed (), - color.getGreen () ^ xorColor.getGreen (), - color.getBlue () ^ xorColor.getBlue ()); - } - - public native void translateNative(int x, int y); - - public void translate (int x, int y) - { - if (component != null && ! component.isRealized ()) - return; - - clip.x -= x; - clip.y -= y; - - translateNative (x, y); - } - - /** - * Copies over the state of another GdkGraphics to this instance. This is - * used by the {@link #GdkGraphics(GdkGraphics)} constructor and the - * {@link #create()} method. - * - * @param g the GdkGraphics object to copy the state from - */ - private void copyState(GdkGraphics g) - { - color = g.color; - xorColor = g.xorColor; - font = g.font; - if (font == null) - font = new Font ("Dialog", Font.PLAIN, 12); - clip = new Rectangle (g.clip); - component = g.component; - nativeCopyState(g); - } -} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java index 6cf7310..147f8f3 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java @@ -1,5 +1,5 @@ /* GdkGraphicsConfiguration.java -- describes characteristics of graphics - Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -42,26 +42,33 @@ import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.ImageCapabilities; import java.awt.Rectangle; -import java.awt.Toolkit; +import java.awt.Transparency; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; import java.awt.image.VolatileImage; public class GdkGraphicsConfiguration extends GraphicsConfiguration { GdkScreenGraphicsDevice gdkScreenGraphicsDevice; - ColorModel cm; - Rectangle bounds; + + ColorModel opaqueColorModel; + ColorModel bitmaskColorModel; + + ColorModel translucentColorModel; + public GdkGraphicsConfiguration(GdkScreenGraphicsDevice dev) { - this.gdkScreenGraphicsDevice = dev; - cm = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getColorModel(); - bounds = ((GtkToolkit) Toolkit.getDefaultToolkit()).getBounds(); + gdkScreenGraphicsDevice = dev; + + opaqueColorModel = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, 0); + bitmaskColorModel = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, 0x1000000); + translucentColorModel = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000); } public GraphicsDevice getDevice() @@ -94,12 +101,21 @@ public class GdkGraphicsConfiguration public ColorModel getColorModel() { - return cm; + return opaqueColorModel; } public ColorModel getColorModel(int transparency) { - return getColorModel(); + switch (transparency) + { + case Transparency.OPAQUE: + return opaqueColorModel; + case Transparency.BITMASK: + return bitmaskColorModel; + default: + case Transparency.TRANSLUCENT: + return translucentColorModel; + } } public AffineTransform getDefaultTransform() @@ -116,7 +132,7 @@ public class GdkGraphicsConfiguration public Rectangle getBounds() { - return bounds; + return gdkScreenGraphicsDevice.getBounds(); } public BufferCapabilities getBufferCapabilities() @@ -133,8 +149,8 @@ public class GdkGraphicsConfiguration public VolatileImage createCompatibleVolatileImage(int width, int height, int transparency) { - // FIXME: implement - return null; + // FIXME: support the transparency argument + return new GtkVolatileImage(width, height); } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java index 4b0b5d3..e1c076c 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java @@ -1,5 +1,5 @@ /* GdkGraphicsEnvironment.java -- information about the graphics environment - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,33 +43,80 @@ import java.awt.Graphics2D; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; -import java.awt.Toolkit; import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; import java.util.Locale; public class GdkGraphicsEnvironment extends GraphicsEnvironment { + private final int native_state = GtkGenericPeer.getUniqueInteger (); + + private GdkScreenGraphicsDevice defaultDevice; + + private GdkScreenGraphicsDevice[] devices; + + static + { + System.loadLibrary("gtkpeer"); + + initStaticState (); + } + + static native void initStaticState(); + public GdkGraphicsEnvironment () { + nativeInitState(); } + + native void nativeInitState(); public GraphicsDevice[] getScreenDevices () { - // FIXME: Support multiple screens, since GDK can. - return new GraphicsDevice[] { new GdkScreenGraphicsDevice (this) }; + if (devices == null) + { + devices = nativeGetScreenDevices(); + } + + return (GraphicsDevice[]) devices.clone(); } + + private native GdkScreenGraphicsDevice[] nativeGetScreenDevices(); public GraphicsDevice getDefaultScreenDevice () { if (GraphicsEnvironment.isHeadless ()) throw new HeadlessException (); - - return new GdkScreenGraphicsDevice (this); + + // GCJ LOCAL: workaround a GCJ problem accessing + // GdkGraphicsEnvironment.class + try + { + synchronized (Class.forName ("gnu.java.awt.peer.gtk.GdkGraphicsEnvironment")) + { + if (defaultDevice == null) + { + defaultDevice = nativeGetDefaultScreenDevice(); + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return defaultDevice; } + + private native GdkScreenGraphicsDevice nativeGetDefaultScreenDevice(); public Graphics2D createGraphics (BufferedImage image) { - return new GdkGraphics2D (image); + DataBuffer db = image.getRaster().getDataBuffer(); + if(db instanceof CairoSurface) + return ((CairoSurface)db).getGraphics(); + + return new BufferedImageGraphics( image ); } private native int nativeGetNumFontFamilies(); @@ -80,20 +127,21 @@ public class GdkGraphicsEnvironment extends GraphicsEnvironment throw new java.lang.UnsupportedOperationException (); } - public String[] getAvailableFontFamilyNames () - { - String[] family_names; - int array_size; + public String[] getAvailableFontFamilyNames () + { + String[] family_names; + int array_size; - array_size = nativeGetNumFontFamilies(); - family_names = new String[array_size]; + array_size = nativeGetNumFontFamilies(); + family_names = new String[array_size]; - nativeGetFontFamilies(family_names); - return family_names; - } + nativeGetFontFamilies(family_names); + return family_names; + } public String[] getAvailableFontFamilyNames (Locale l) { throw new java.lang.UnsupportedOperationException (); } + } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java index 2e3eee4..58b2dec 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java @@ -247,12 +247,23 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder public static ImageFormatSpec registerFormat(String name, boolean writable) { ImageFormatSpec ifs = new ImageFormatSpec(name, writable); - synchronized(GdkPixbufDecoder.class) + + // GCJ LOCAL: workaround a GCJ problem accessing + // GdkPixbufDecoder.class + try + { + synchronized(Class.forName ("gnu.java.awt.peer.gtk.GdkPixbufDecoder")) { if (imageFormatSpecs == null) imageFormatSpecs = new ArrayList(); imageFormatSpecs.add(ifs); } + } + catch (Exception e) + { + e.printStackTrace(); + } + return ifs; } @@ -502,19 +513,19 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder int width = ras.getWidth(); int height = ras.getHeight(); ColorModel model = image.getColorModel(); - int[] pixels = GdkGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras); + int[] pixels = CairoGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras); if (pixels == null) { - BufferedImage img = new BufferedImage(width, height, - (model != null && model.hasAlpha() ? - BufferedImage.TYPE_INT_ARGB - : BufferedImage.TYPE_INT_RGB)); + BufferedImage img; + if(model != null && model.hasAlpha()) + img = CairoSurface.getBufferedImage(width, height); + img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); int[] pix = new int[4]; for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) img.setRGB(x, y, model.getRGB(ras.getPixel(x, y, pix))); - pixels = GdkGraphics2D.findSimpleIntegerArray (img.getColorModel(), + pixels = CairoGraphics2D.findSimpleIntegerArray (img.getColorModel(), img.getRaster()); model = img.getColorModel(); } @@ -584,9 +595,10 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder if (bufferedImage == null) { - bufferedImage = new BufferedImage (width, height, (model != null && model.hasAlpha() ? - BufferedImage.TYPE_INT_ARGB - : BufferedImage.TYPE_INT_RGB)); + if(model != null && model.hasAlpha()) + bufferedImage = new BufferedImage (width, height, BufferedImage.TYPE_INT_ARGB); + else + bufferedImage = new BufferedImage (width, height, BufferedImage.TYPE_INT_RGB); } int pixels2[]; @@ -680,43 +692,4 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder return getBufferedImage (); } } - - // remaining helper class and static method is a convenience for the Gtk - // peers, for loading a BufferedImage in off a disk file without going - // through the whole imageio system. - - public static BufferedImage createBufferedImage (String filename) - { - GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), - "png", // reader auto-detects, doesn't matter - new GdkPixbufDecoder (filename)); - return r.getBufferedImage (); - } - - public static BufferedImage createBufferedImage (URL u) - { - GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), - "png", // reader auto-detects, doesn't matter - new GdkPixbufDecoder (u)); - return r.getBufferedImage (); - } - - public static BufferedImage createBufferedImage (byte[] imagedata, int imageoffset, - int imagelength) - { - GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), - "png", // reader auto-detects, doesn't matter - new GdkPixbufDecoder (imagedata, - imageoffset, - imagelength)); - return r.getBufferedImage (); - } - - public static BufferedImage createBufferedImage (ImageProducer producer) - { - GdkPixbufReader r = new GdkPixbufReader (getReaderSpi(), "png" /* ignored */, null); - producer.startProduction(r); - return r.getBufferedImage (); - } - } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java index b5d1237..62116a3 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java @@ -1,5 +1,5 @@ /* GdkScreenGraphicsDevice.java -- information about a screen device - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,44 +38,110 @@ exception statement from your version. */ package gnu.java.awt.peer.gtk; -import java.awt.Dimension; import java.awt.DisplayMode; +import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; -import java.awt.Toolkit; +import java.awt.Rectangle; +import java.awt.Window; +import java.util.ArrayList; -public class GdkScreenGraphicsDevice extends GraphicsDevice +class GdkScreenGraphicsDevice extends GraphicsDevice { + private final int native_state = GtkGenericPeer.getUniqueInteger (); + + private Window fullscreenWindow; + + private boolean oldWindowDecorationState; + + private Rectangle oldWindowBounds; + + private Rectangle bounds; + + private GdkGraphicsConfiguration[] configurations; + + /** The <code>GdkGraphicsEnvironment</code> instance that created this + * <code>GdkScreenGraphicsDevice</code>. This is only needed for native + * methods which need to access the 'native_state' field storing a pointer + * to a GdkDisplay object. + */ GdkGraphicsEnvironment env; + + /** An identifier that is created by Gdk + */ + String idString; + + /** The display modes supported by this <code>GdkScreenGraphicsDevice</code>. + * If the array is <code>null</code> <code>nativeGetDisplayModes</code> has + * to be called. + */ + X11DisplayMode[] displayModes; - public GdkScreenGraphicsDevice (GdkGraphicsEnvironment e) - { - super (); + /** The non-changeable display mode of this <code>GdkScreenGraphicsDevice + * </code>. This field gets initialized by the {@link #init()} method. If it + * is still <code>null</code> afterwards, the XRandR extension is available + * and display mode changes are possible. If it is non-null XRandR is not + * available, no display mode changes are possible and no other native + * method must be called. + */ + DisplayMode fixedDisplayMode; + + static + { + System.loadLibrary("gtkpeer"); + + initStaticState (); + } + + static native void initStaticState(); + + GdkScreenGraphicsDevice (GdkGraphicsEnvironment e) + { + super(); env = e; + + configurations = new GdkGraphicsConfiguration[1]; + configurations[0] = new GdkGraphicsConfiguration(this); } + /** This method is called from the native side immediately after + * the constructor is run. + */ + void init() + { + fixedDisplayMode = nativeGetFixedDisplayMode(env); + } + + /** Depending on the availability of the XRandR extension the method returns + * the screens' non-changeable display mode or null, meaning that XRandR can + * handle display mode changes. + */ + native DisplayMode nativeGetFixedDisplayMode(GdkGraphicsEnvironment env); + public int getType () { + // Gdk manages only raster screens. return GraphicsDevice.TYPE_RASTER_SCREEN; } public String getIDstring () { - // FIXME: query X for this string - return "default GDK device ID string"; + if (idString == null) + idString = nativeGetIDString(); + + return idString; } + + private native String nativeGetIDString(); public GraphicsConfiguration[] getConfigurations () { - // FIXME: query X for the list of possible configurations - return new GraphicsConfiguration [] { new GdkGraphicsConfiguration(this) }; + return (GraphicsConfiguration[]) configurations.clone(); } - + public GraphicsConfiguration getDefaultConfiguration () { - - // FIXME: query X for default configuration - return new GdkGraphicsConfiguration(this); + return configurations[0]; } @@ -89,23 +155,193 @@ public class GdkScreenGraphicsDevice extends GraphicsDevice */ public DisplayMode getDisplayMode() { - // determine display mode - Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); - DisplayMode mode = new DisplayMode(dim.width, dim.height, 0, - DisplayMode.REFRESH_RATE_UNKNOWN); - return mode; + if (fixedDisplayMode != null) + return fixedDisplayMode; + + synchronized (this) + { + if (displayModes == null) + displayModes = nativeGetDisplayModes(env); + } + + int index = nativeGetDisplayModeIndex(env); + int rate = nativeGetDisplayModeRate(env); + + return new DisplayMode(displayModes[index].width, + displayModes[index].height, + DisplayMode.BIT_DEPTH_MULTI, + rate); + } + + native int nativeGetDisplayModeIndex(GdkGraphicsEnvironment env); + + native int nativeGetDisplayModeRate(GdkGraphicsEnvironment env); + + public DisplayMode[] getDisplayModes() + { + if (fixedDisplayMode != null) + return new DisplayMode[] { fixedDisplayMode }; + + synchronized (this) + { + if (displayModes == null) + displayModes = nativeGetDisplayModes(env); + } + + ArrayList list = new ArrayList(); + for(int i=0;i<displayModes.length;i++) + for(int j=0;j<displayModes[i].rates.length;j++) + list.add(new DisplayMode(displayModes[i].width, + displayModes[i].height, + DisplayMode.BIT_DEPTH_MULTI, + displayModes[i].rates[j])); + + return (DisplayMode[]) list.toArray(new DisplayMode[list.size()]); } + + native X11DisplayMode[] nativeGetDisplayModes(GdkGraphicsEnvironment env); /** - * This device does not yet support fullscreen exclusive mode, so this - * returns <code>false</code>. + * Real fullscreen exclusive mode is not supported. * * @return <code>false</code> * @since 1.4 */ public boolean isFullScreenSupported() { - return false; + return true; + } + + public boolean isDisplayChangeSupported() + { + return fixedDisplayMode == null; + } + + public void setDisplayMode(DisplayMode dm) + { + if (fixedDisplayMode != null) + throw new UnsupportedOperationException("Cannnot change display mode."); + + if (dm == null) + throw new IllegalArgumentException("DisplayMode must not be null."); + + synchronized (this) + { + if (displayModes == null) + displayModes = nativeGetDisplayModes(env); + } + + for (int i=0; i<displayModes.length; i++) + if (displayModes[i].width == dm.getWidth() + && displayModes[i].height == dm.getHeight()) + { + synchronized (this) + { + nativeSetDisplayMode(env, + i, + (short) dm.getRefreshRate()); + + bounds = null; + } + + return; + } + + throw new IllegalArgumentException("Mode not supported by this device."); + } + + native void nativeSetDisplayMode(GdkGraphicsEnvironment env, + int index, short rate); + + /** A class that simply encapsulates the X11 display mode data. + */ + static class X11DisplayMode + { + short[] rates; + int width; + int height; + + X11DisplayMode(int width, int height, short[] rates) + { + this.width = width; + this.height = height; + this.rates = rates; + } + + } + + public void setFullScreenWindow(Window w) + { + // Bring old fullscreen window back into its original state. + if (fullscreenWindow != null && w != fullscreenWindow) + { + if (fullscreenWindow instanceof Frame) + { + // Decoration state can only be switched when the peer is + // non-existent. That means we have to dispose the + // Frame. + Frame f = (Frame) fullscreenWindow; + if (oldWindowDecorationState != f.isUndecorated()) + { + f.dispose(); + f.setUndecorated(oldWindowDecorationState); + } + } + + fullscreenWindow.setBounds(oldWindowBounds); + + if (!fullscreenWindow.isVisible()) + fullscreenWindow.setVisible(true); + } + + // If applicable remove decoration, then maximize the window and + // bring it to the foreground. + if (w != null) + { + if (w instanceof Frame) + { + Frame f = (Frame) w; + oldWindowDecorationState = f.isUndecorated(); + if (!oldWindowDecorationState) + { + f.dispose(); + f.setUndecorated(true); + } + } + + oldWindowBounds = w.getBounds(); + + DisplayMode dm = getDisplayMode(); + + w.setBounds(0, 0, dm.getWidth(), dm.getHeight()); + + if (!w.isVisible()) + w.setVisible(true); + + w.requestFocus(); + w.toFront(); + + } + + fullscreenWindow = w; + } + + public Window getFullScreenWindow() + { + return fullscreenWindow; + } + + Rectangle getBounds() + { + synchronized(this) + { + if (bounds == null) + bounds = nativeGetBounds(); + } + + return bounds; } + + native Rectangle nativeGetBounds(); } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkTextLayout.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkTextLayout.java index c5e751f..d6b3de8 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkTextLayout.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkTextLayout.java @@ -75,13 +75,21 @@ public class GdkTextLayout initStaticState (); } private native void setText(String str); + private native void setFont(GdkFontPeer font); private native void getExtents(double[] inkExtents, double[] logExtents); private native void indexToPos(int idx, double[] pos); + private native void initState (); + private native void dispose (); + + private native void cairoDrawGdkTextLayout(CairoGraphics2D g, float x, float y); + static native void initStaticState(); + private final int native_state = GtkGenericPeer.getUniqueInteger (); + protected void finalize () { dispose (); @@ -97,6 +105,15 @@ public class GdkTextLayout initState(); attributedString = str; fontRenderContext = frc; + AttributedCharacterIterator aci = str.getIterator(); + char[] chars = new char[aci.getEndIndex() - aci.getBeginIndex()]; + for(int i = aci.getBeginIndex(); i < aci.getEndIndex(); i++) + chars[i] = aci.setIndex(i); + setText(new String(chars)); + + Object fnt = aci.getAttribute(TextAttribute.FONT); + if (fnt != null && fnt instanceof Font) + setFont( (GdkFontPeer) ((Font)fnt).getPeer() ); } protected class CharacterIteratorProxy @@ -199,60 +216,7 @@ public class GdkTextLayout public void draw (Graphics2D g2, float x, float y) { - if (g2 instanceof GdkGraphics2D) - { - // we share pango structures directly with GdkGraphics2D - // when legal - GdkGraphics2D gg2 = (GdkGraphics2D) g2; - gg2.drawGdkTextLayout(this, x, y); - } - else - { - // falling back to a rather tedious layout algorithm when - // not legal - AttributedCharacterIterator ci = attributedString.getIterator (); - CharacterIteratorProxy proxy = new CharacterIteratorProxy (ci); - Font defFont = g2.getFont (); - - /* Note: this implementation currently only interprets FONT text - * attributes. There is a reasonable argument to be made for some - * attributes being interpreted out here, where we have control of the - * Graphics2D and can construct or derive new fonts, and some - * attributes being interpreted by the GlyphVector itself. So far, for - * all attributes except FONT we do neither. - */ - - for (char c = ci.first (); - c != CharacterIterator.DONE; - c = ci.next ()) - { - proxy.begin = ci.getIndex (); - proxy.limit = ci.getRunLimit(TextAttribute.FONT); - if (proxy.limit <= proxy.begin) - continue; - - proxy.index = proxy.begin; - - Object fnt = ci.getAttribute(TextAttribute.FONT); - GlyphVector gv; - if (fnt instanceof Font) - gv = ((Font)fnt).createGlyphVector (fontRenderContext, proxy); - else - gv = defFont.createGlyphVector (fontRenderContext, proxy); - - g2.drawGlyphVector (gv, x, y); - - int n = gv.getNumGlyphs (); - for (int i = 0; i < n; ++i) - { - GlyphMetrics gm = gv.getGlyphMetrics (i); - if (gm.getAdvanceX() == gm.getAdvance ()) - x += gm.getAdvanceX (); - else - y += gm.getAdvanceY (); - } - } - } + cairoDrawGdkTextLayout((CairoGraphics2D)g2, x, y); } public TextHitInfo getStrongCaret (TextHitInfo hit1, diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java index 797d653..edfc9ce 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java @@ -45,7 +45,6 @@ import java.awt.peer.CanvasPeer; public class GtkCanvasPeer extends GtkComponentPeer implements CanvasPeer { native void create (); - native void realize (); public GtkCanvasPeer (Canvas c) { diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java index 1a85de5..625855f 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java @@ -109,14 +109,7 @@ public class GtkComponentPeer extends GtkGenericPeer native void gtkWidgetRequestFocus (); native void gtkWidgetDispatchKeyEvent (int id, long when, int mods, int keyCode, int keyLocation); - - native boolean isRealized (); - - void realize () - { - // Default implementation does nothing - } - + native void realize(); native void setNativeEventMask (); void create () @@ -149,6 +142,9 @@ public class GtkComponentPeer extends GtkGenericPeer setNativeEventMask (); + // This peer is guaranteed to have an X window upon construction. + // That is, native methods such as those in GdkGraphics can rely + // on this component's widget->window field being non-null. realize (); if (awtComponent.isCursorSet()) @@ -211,16 +207,7 @@ public class GtkComponentPeer extends GtkGenericPeer public Image createImage (int width, int height) { - Image image; - if (GtkToolkit.useGraphics2D ()) - image = new BufferedImage (width, height, BufferedImage.TYPE_INT_RGB); - else - image = new GtkImage (width, height); - - Graphics g = image.getGraphics(); - g.setColor(getBackground()); - g.fillRect(0, 0, width, height); - return image; + return CairoSurface.getBufferedImage(width, height); } public void disable () @@ -247,10 +234,7 @@ public class GtkComponentPeer extends GtkGenericPeer // never return null. public Graphics getGraphics () { - if (GtkToolkit.useGraphics2D ()) - return new GdkGraphics2D (this); - else - return new GdkGraphics (this); + return ComponentGraphics.getComponentGraphics(this); } public Point getLocationOnScreen () @@ -713,7 +697,7 @@ public class GtkComponentPeer extends GtkGenericPeer // on which this component is displayed. public VolatileImage createVolatileImage (int width, int height) { - return new GtkVolatileImage (width, height); + return new GtkVolatileImage (this, width, height, null); } // Creates buffers used in a buffering strategy. @@ -723,7 +707,7 @@ public class GtkComponentPeer extends GtkGenericPeer // numBuffers == 2 implies double-buffering, meaning one back // buffer and one front buffer. if (numBuffers == 2) - backBuffer = new GtkVolatileImage(awtComponent.getWidth(), + backBuffer = new GtkVolatileImage(this, awtComponent.getWidth(), awtComponent.getHeight(), caps.getBackBufferCapabilities()); else diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java index 5e5f1de..ef96518 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java @@ -57,14 +57,7 @@ import java.net.URL; import gnu.classpath.Pointer; /** - * GtkImage - wraps a GdkPixbuf or GdkPixmap. - * - * The constructor GtkImage(int, int) creates an 'off-screen' GdkPixmap, - * this can be drawn to (it's a GdkDrawable), and correspondingly, you can - * create a GdkGraphics object for it. - * - * This corresponds to the Image implementation returned by - * Component.createImage(int, int). + * GtkImage - wraps a GdkPixbuf. * * A GdkPixbuf is 'on-screen' and the gdk cannot draw to it, * this is used for the other constructors (and other createImage methods), and @@ -88,9 +81,10 @@ public class GtkImage extends Image boolean isLoaded; /** - * Pointer to the GdkPixbuf + * Pointer to the GdkPixbuf - + * don't change the name without changing the native code. */ - Pointer pixmap; + Pointer pixbuf; /** * Observer queue. @@ -98,11 +92,6 @@ public class GtkImage extends Image Vector observers; /** - * If offScreen is set, a GdkBitmap is wrapped and not a Pixbuf. - */ - boolean offScreen; - - /** * Error flag for loading. */ boolean errorLoading; @@ -122,71 +111,64 @@ public class GtkImage extends Image 0xFF000000); /** + * The singleton GtkImage that is returned on errors by GtkToolkit. + */ + private static GtkImage errorImage; + + /** + * Lock that should be held for all gdkpixbuf operations. We don't use + * the global gdk_threads_enter/leave functions in most places since + * most gdkpixbuf operations can be done in parallel to drawing and + * manipulating gtk widgets. + */ + static Object pixbufLock = new Object(); + + /** + * Allocate a PixBuf from a given ARGB32 buffer pointer. + */ + private native void initFromBuffer( long bufferPointer ); + + /** * Returns a copy of the pixel data as a java array. - * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Should be called with the pixbufLock held. */ - private native int[] getPixels(); + native int[] getPixels(); /** * Sets the pixel data from a java array. - * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Should be called with the pixbufLock held. */ private native void setPixels(int[] pixels); /** * Loads an image using gdk-pixbuf from a file. - * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Should be called with the pixbufLock held. */ private native boolean loadPixbuf(String name); /** * Loads an image using gdk-pixbuf from data. - * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Should be called with the pixbufLock held. */ private native boolean loadImageFromData(byte[] data); /** - * Allocates a Gtk Pixbuf or pixmap - * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Allocates a Gtk Pixbuf + * Should be called with the pixbufLock held. */ - private native void createPixmap(); + private native void createPixbuf(); /** * Frees the above. - * Should be called with the GdkPixbufDecoder.pixbufLock held. - */ - private native void freePixmap(); - - /** - * Sets the pixmap to scaled copy of src image. hints are rendering hints. - * Should be called with the GdkPixbufDecoder.pixbufLock held. - */ - private native void createScaledPixmap(GtkImage src, int hints); - - /** - * Draws the image, optionally scaled and composited. - * Should be called with the GdkPixbufDecoder.pixbufLock held. - * Also acquires global gdk lock for drawing. + * Should be called with the pixbufLock held. */ - private native void drawPixelsScaled (GdkGraphics gc, - int bg_red, int bg_green, int bg_blue, - int x, int y, int width, int height, - boolean composite); + private native void freePixbuf(); /** - * Draws the image, optionally scaled flipped and composited. - * Should be called with the GdkPixbufDecoder.pixbufLock held. - * Also acquires global gdk lock for drawing. + * Sets the pixbuf to scaled copy of src image. hints are rendering hints. + * Should be called with the pixbufLock held. */ - private native void drawPixelsScaledFlipped (GdkGraphics gc, - int bg_red, int bg_green, - int bg_blue, - boolean flipX, boolean flipY, - int srcX, int srcY, - int srcWidth, int srcHeight, - int dstX, int dstY, - int dstWidth, int dstHeight, - boolean composite); + private native void createScaledPixbuf(GtkImage src, int hints); /** * Constructs a GtkImage from an ImageProducer. Asynchronity is handled in @@ -202,7 +184,6 @@ public class GtkImage extends Image source = producer; errorLoading = false; source.startProduction(new GtkImageConsumer(this, source)); - offScreen = false; } /** @@ -215,7 +196,6 @@ public class GtkImage extends Image { isLoaded = true; observers = null; - offScreen = false; props = new Hashtable(); errorLoading = false; } @@ -231,7 +211,7 @@ public class GtkImage extends Image try { String path = f.getCanonicalPath(); - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized(pixbufLock) { if (loadPixbuf(f.getCanonicalPath()) != true) throw new IllegalArgumentException("Couldn't load image: " @@ -249,7 +229,6 @@ public class GtkImage extends Image isLoaded = true; observers = null; - offScreen = false; props = new Hashtable(); } @@ -261,7 +240,7 @@ public class GtkImage extends Image */ public GtkImage (byte[] data) { - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized(pixbufLock) { if (loadImageFromData (data) != true) throw new IllegalArgumentException ("Couldn't load image."); @@ -269,7 +248,6 @@ public class GtkImage extends Image isLoaded = true; observers = null; - offScreen = false; props = new Hashtable(); errorLoading = false; } @@ -301,7 +279,7 @@ public class GtkImage extends Image throw new IllegalArgumentException ("Couldn't load image."); } byte[] array = baos.toByteArray(); - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized(pixbufLock) { if (loadImageFromData(array) != true) throw new IllegalArgumentException ("Couldn't load image."); @@ -313,23 +291,6 @@ public class GtkImage extends Image } /** - * Constructs an empty GtkImage. - */ - public GtkImage (int width, int height) - { - this.width = width; - this.height = height; - props = new Hashtable(); - isLoaded = true; - observers = null; - offScreen = true; - synchronized(GdkPixbufDecoder.pixbufLock) - { - createPixmap(); - } - } - - /** * Constructs a scaled version of the src bitmap, using the GDK. */ private GtkImage (GtkImage src, int width, int height, int hints) @@ -339,12 +300,11 @@ public class GtkImage extends Image props = new Hashtable(); isLoaded = true; observers = null; - offScreen = false; // Use the GDK scaling method. - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized(pixbufLock) { - createScaledPixmap(src, hints); + createScaledPixbuf(src, hints); } } @@ -354,19 +314,30 @@ public class GtkImage extends Image */ GtkImage (Pointer pixbuf) { - pixmap = pixbuf; - synchronized(GdkPixbufDecoder.pixbufLock) + this.pixbuf = pixbuf; + synchronized(pixbufLock) { createFromPixbuf(); } isLoaded = true; observers = null; - offScreen = false; props = new Hashtable(); } - // The singleton GtkImage that is returned on errors by GtkToolkit. - private static GtkImage errorImage; + /** + * Wraps a buffer with a GtkImage. + * + * @param bufferPointer a pointer to an ARGB32 buffer + */ + GtkImage(int width, int height, long bufferPointer) + { + this.width = width; + this.height = height; + props = new Hashtable(); + isLoaded = true; + observers = null; + initFromBuffer( bufferPointer ); + } /** * Returns an empty GtkImage with the errorLoading flag set. @@ -385,7 +356,7 @@ public class GtkImage extends Image /** * Native helper function for constructor that takes a pixbuf Pointer. - * Should be called with the GdkPixbufDecoder.pixbufLock held. + * Should be called with the pixbufLock held. */ private native void createFromPixbuf(); @@ -407,9 +378,9 @@ public class GtkImage extends Image isLoaded = true; deliver(); - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized(pixbufLock) { - createPixmap(); + createPixbuf(); setPixels(pixels); } } @@ -450,30 +421,28 @@ public class GtkImage extends Image return null; int[] pixels; - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized (pixbufLock) { - pixels = getPixels(); + if (!errorLoading) + pixels = getPixels(); + else + return null; } return new MemoryImageSource(width, height, nativeModel, pixels, 0, width); } /** - * Creates a GdkGraphics context for this pixmap. + * Does nothing. Should not be called. */ public Graphics getGraphics () { - if (!isLoaded) - return null; - if (offScreen) - return new GdkGraphics(this); - else - throw new IllegalAccessError("This method only works for off-screen" - +" Images."); + throw new IllegalAccessError("This method only works for off-screen" + +" Images."); } /** - * Returns a scaled instance of this pixmap. + * Returns a scaled instance of this pixbuf. */ public Image getScaledInstance(int width, int height, @@ -500,9 +469,9 @@ public class GtkImage extends Image { observers = new Vector(); isLoaded = false; - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized(pixbufLock) { - freePixmap(); + freePixbuf(); } source.startProduction(new GtkImageConsumer(this, source)); } @@ -512,9 +481,9 @@ public class GtkImage extends Image { if (isLoaded) { - synchronized(GdkPixbufDecoder.pixbufLock) + synchronized(pixbufLock) { - freePixmap(); + freePixbuf(); } } } @@ -535,104 +504,6 @@ public class GtkImage extends Image return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT; } - // Drawing methods //////////////////////////////////////////////// - - /** - * Draws an image with eventual scaling/transforming. - */ - public boolean drawImage (GdkGraphics g, int dx1, int dy1, int dx2, int dy2, - int sx1, int sy1, int sx2, int sy2, - Color bgcolor, ImageObserver observer) - { - if (addObserver(observer)) - return false; - - boolean flipX = (dx1 > dx2)^(sx1 > sx2); - boolean flipY = (dy1 > dy2)^(sy1 > sy2); - int dstWidth = Math.abs (dx2 - dx1); - int dstHeight = Math.abs (dy2 - dy1); - int srcWidth = Math.abs (sx2 - sx1); - int srcHeight = Math.abs (sy2 - sy1); - int srcX = (sx1 < sx2) ? sx1 : sx2; - int srcY = (sy1 < sy2) ? sy1 : sy2; - int dstX = (dx1 < dx2) ? dx1 : dx2; - int dstY = (dy1 < dy2) ? dy1 : dy2; - - // Clipping. This requires the dst to be scaled as well, - if (srcWidth > width) - { - dstWidth = (int)((double)dstWidth*((double)width/(double)srcWidth)); - srcWidth = width - srcX; - } - - if (srcHeight > height) - { - dstHeight = (int)((double)dstHeight*((double)height/(double)srcHeight)); - srcHeight = height - srcY; - } - - if (srcWidth + srcX > width) - { - dstWidth = (int)((double)dstWidth * (double)(width - srcX)/(double)srcWidth); - srcWidth = width - srcX; - } - - if (srcHeight + srcY > height) - { - dstHeight = (int)((double)dstHeight * (double)(width - srcY)/(double)srcHeight); - srcHeight = height - srcY; - } - - if ( this.width <= 0 || this.height <= 0 ) - return true; - - if ( srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0) - return true; - - synchronized(GdkPixbufDecoder.pixbufLock) - { - if(bgcolor != null) - drawPixelsScaledFlipped (g, bgcolor.getRed (), bgcolor.getGreen (), - bgcolor.getBlue (), - flipX, flipY, - srcX, srcY, - srcWidth, srcHeight, - dstX, dstY, - dstWidth, dstHeight, - true); - else - drawPixelsScaledFlipped (g, 0, 0, 0, flipX, flipY, - srcX, srcY, srcWidth, srcHeight, - dstX, dstY, dstWidth, dstHeight, - false); - } - return true; - } - - /** - * Draws an image to the GdkGraphics context, at (x,y) scaled to - * width and height, with optional compositing with a background color. - */ - public boolean drawImage (GdkGraphics g, int x, int y, int width, int height, - Color bgcolor, ImageObserver observer) - { - if (addObserver(observer)) - return false; - - if ( this.width <= 0 || this.height <= 0 ) - return true; - - synchronized(GdkPixbufDecoder.pixbufLock) - { - if(bgcolor != null) - drawPixelsScaled(g, bgcolor.getRed (), bgcolor.getGreen (), - bgcolor.getBlue (), x, y, width, height, true); - else - drawPixelsScaled(g, 0, 0, 0, x, y, width, height, false); - } - - return true; - } // Private methods //////////////////////////////////////////////// diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java index 4c41f97..688af00 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java @@ -78,31 +78,12 @@ import javax.imageio.spi.IIORegistry; this class. If getPeer() ever goes away, we can implement a hash table that will keep up with every window's peer, but for now this is faster. */ -/** - * This class accesses a system property called - * <tt>gnu.java.awt.peer.gtk.Graphics</tt>. If the property is defined and - * equal to "Graphics2D", the cairo-based GdkGraphics2D will be used in - * drawing contexts. Any other value will cause the older GdkGraphics - * object to be used. - */ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit { Hashtable containers = new Hashtable(); static EventQueue q; - static boolean useGraphics2dSet; - static boolean useGraphics2d; static Thread mainThread; - public static boolean useGraphics2D() - { - if (useGraphics2dSet) - return useGraphics2d; - useGraphics2d = System.getProperty("gnu.java.awt.peer.gtk.Graphics", - "Graphics").equals("Graphics2D"); - useGraphics2dSet = true; - return useGraphics2d; - } - static native void gtkInit(int portableNativeSync); static @@ -178,10 +159,7 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit Image image; try { - if (useGraphics2D()) - image = GdkPixbufDecoder.createBufferedImage(filename); - else - image = new GtkImage(filename); + image = CairoSurface.getBufferedImage( new GtkImage( filename ) ); } catch (IllegalArgumentException iae) { @@ -195,10 +173,7 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit Image image; try { - if (useGraphics2D()) - image = GdkPixbufDecoder.createBufferedImage(url); - else - image = new GtkImage(url); + image = CairoSurface.getBufferedImage( new GtkImage( url ) ); } catch (IllegalArgumentException iae) { @@ -209,13 +184,13 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit public Image createImage (ImageProducer producer) { + if (producer == null) + return null; + Image image; try { - if (useGraphics2D()) - image = GdkPixbufDecoder.createBufferedImage(producer); - else - image = new GtkImage(producer); + image = CairoSurface.getBufferedImage( new GtkImage( producer ) ); } catch (IllegalArgumentException iae) { @@ -230,16 +205,9 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit Image image; try { - if (useGraphics2D()) - image = GdkPixbufDecoder.createBufferedImage(imagedata, - imageoffset, - imagelength); - else - { - byte[] datacopy = new byte[imagelength]; - System.arraycopy(imagedata, imageoffset, datacopy, 0, imagelength); - return new GtkImage(datacopy); - } + byte[] data = new byte[ imagelength ]; + System.arraycopy(imagedata, imageoffset, data, 0, imagelength); + image = CairoSurface.getBufferedImage( new GtkImage( data ) ); } catch (IllegalArgumentException iae) { @@ -256,7 +224,7 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit */ public ImageProducer createImageProducer(URL url) { - return new GdkPixbufDecoder(url); + return createImage( url ).getSource(); } /** @@ -568,13 +536,23 @@ public class GtkToolkit extends gnu.java.awt.ClasspathToolkit protected EventQueue getSystemEventQueueImpl() { - synchronized (GtkToolkit.class) + // GCJ LOCAL: workaround a GCJ problem accessing + // GtkToolkit.class + try + { + synchronized (Class.forName ("gnu.java.awt.peer.gtk.GtkToolkit")) { if (q == null) { q = new EventQueue(); } } + } + catch (Exception e) + { + e.printStackTrace(); + } + return q; } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java index 496090a..53bcd73 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java @@ -1,4 +1,4 @@ -/* GtkVolatileImage.java -- a hardware-accelerated image buffer +/* GtkVolatileImage.java -- wraps an X pixmap Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,7 @@ 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.image.BufferedImage; @@ -46,44 +47,68 @@ import java.awt.image.VolatileImage; public class GtkVolatileImage extends VolatileImage { - private int width; - private int height; + int width, height; private ImageCapabilities caps; - public GtkVolatileImage(int width, int height) - { - this(width, height, null); - } + /** + * Don't touch, accessed from native code. + */ + long nativePointer; - public GtkVolatileImage(int width, int height, ImageCapabilities caps) + native long init(GtkComponentPeer component, int width, int height); + + native void destroy(); + + native int[] getPixels(); + + native void copyArea( int x, int y, int w, int h, int dx, int dy ); + + native void drawVolatile( long ptr, int x, int y, int w, int h ); + + public GtkVolatileImage(GtkComponentPeer component, + int width, int height, ImageCapabilities caps) { this.width = width; this.height = height; this.caps = caps; + nativePointer = init( component, width, height ); } - // FIXME: should return a buffered image snapshot of the accelerated - // visual - public BufferedImage getSnapshot() + public GtkVolatileImage(int width, int height, ImageCapabilities caps) { - return null; + this(null, width, height, caps); } - public int getWidth() + public GtkVolatileImage(int width, int height) { - return width; + this(null, width, height, null); } - public int getHeight() + public void finalize() { - return height; + dispose(); + } + + public void dispose() + { + destroy(); + } + + public BufferedImage getSnapshot() + { + CairoSurface cs = new CairoSurface( width, height ); + cs.setPixels( getPixels() ); + return CairoSurface.getBufferedImage( cs ); + } + + public Graphics getGraphics() + { + return createGraphics(); } - // FIXME: should return a graphics wrapper around this image's - // visual public Graphics2D createGraphics() { - return null; + return new VolatileImageGraphics( this ); } public int validate(GraphicsConfiguration gc) @@ -101,18 +126,28 @@ public class GtkVolatileImage extends VolatileImage return caps; } - public synchronized Object getProperty (String name, ImageObserver observer) + public int getWidth() { - return null; + return width; + } + + public int getHeight() + { + return height; } - public synchronized int getWidth (ImageObserver observer) + public int getWidth(java.awt.image.ImageObserver observer) { return width; } - public synchronized int getHeight (ImageObserver observer) + public int getHeight(java.awt.image.ImageObserver observer) { return height; } + + public Object getProperty(String name, ImageObserver observer) + { + return null; + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java new file mode 100644 index 0000000..d5adfcf --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java @@ -0,0 +1,122 @@ +/* VolatileImageGraphics.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.peer.gtk; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.RenderedImage; +import java.awt.image.ImageObserver; +import java.util.WeakHashMap; + +public class VolatileImageGraphics extends ComponentGraphics +{ + private GtkVolatileImage owner; + + public VolatileImageGraphics(GtkVolatileImage img) + { + this.owner = img; + cairo_t = initFromVolatile( owner.nativePointer, img.width, img.height ); + setup( cairo_t ); + setClip( new Rectangle( 0, 0, img.width, img.height) ); + } + + private VolatileImageGraphics(VolatileImageGraphics copy) + { + this.owner = copy.owner; + initFromVolatile( owner.nativePointer, owner.width, owner.height ); + setClip( new Rectangle( 0, 0, owner.width, owner.height) ); + copy( copy, cairo_t ); + } + + public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy) + { + owner.copyArea(x, y, width, height, dx, dy); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + return null; + } + + public Graphics create() + { + return new VolatileImageGraphics( this ); + } + + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + if( img instanceof GtkVolatileImage ) + { + owner.drawVolatile( ((GtkVolatileImage)img).nativePointer, + x, y, + ((GtkVolatileImage)img).width, + ((GtkVolatileImage)img).height ); + return true; + } + return super.drawImage( img, x, y, observer ); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) + { + if( img instanceof GtkVolatileImage ) + { + owner.drawVolatile( ((GtkVolatileImage)img).nativePointer, + x, y, width, height ); + return true; + } + return super.drawImage( img, x, y, width, height, observer ); + } +} + diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java index 04ca729..a51b758 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java @@ -62,7 +62,7 @@ public interface SwingComponent /** * Handles a mouse event. This is usually forwarded to - * {@link Component#processMouseMotionEvent(MouseEvent)} of the swing + * {@link java.awt.Component#processMouseMotionEvent(MouseEvent)} of the swing * component. * * @param ev the mouse event @@ -71,7 +71,7 @@ public interface SwingComponent /** * Handles a mouse motion event. This is usually forwarded to - * {@link Component#processMouseEvent(MouseEvent)} of the swing + * {@link java.awt.Component#processMouseEvent(MouseEvent)} of the swing * component. * * @param ev the mouse motion event @@ -80,7 +80,7 @@ public interface SwingComponent /** * Handles a key event. This is usually forwarded to - * {@link Component#processKeyEvent(KeyEvent)} of the swing + * {@link java.awt.Component#processKeyEvent(KeyEvent)} of the swing * component. * * @param ev the key event diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java index 5d484e0..f60c8e9 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java @@ -48,6 +48,8 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; @@ -98,8 +100,9 @@ public class SwingComponentPeer /** * Creates a SwingComponentPeer instance. Subclasses are expected to call - * this constructor and thereafter call {@link #init(Component, JComponent)} - * in order to setup the AWT and Swing components properly. + * this constructor and thereafter call + * {@link #init(Component, SwingComponent)} in order to setup the AWT and + * Swing components properly. */ protected SwingComponentPeer() { @@ -164,9 +167,12 @@ public class SwingComponentPeer */ public Image createImage(int width, int height) { - Component parent = awtComponent.getParent(); - ComponentPeer parentPeer = parent.getPeer(); - return parentPeer.createImage(width, height); + GraphicsEnvironment graphicsEnv = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice dev = graphicsEnv.getDefaultScreenDevice(); + GraphicsConfiguration conf = dev.getDefaultConfiguration(); + Image image = conf.createCompatibleImage(width, height); + return image; } /** @@ -442,20 +448,6 @@ public class SwingComponentPeer return retVal; } - /** - * Prepares an image for rendering on this component. This is called by - * {@link Component#prepareImage(Image, int, int, ImageObserver)}. - * - * @param img the image to prepare - * @param width the desired width of the rendered image - * @param height the desired height of the rendered image - * @param ob the image observer to be notified of updates in the preparation - * process - * - * @return <code>true</code> if the image has been fully prepared, - * <code>false</code> otherwise (in which case the image observer - * receives updates) - */ public void paint(Graphics graphics) { // FIXME: I don't know what this method is supposed to do. @@ -478,8 +470,17 @@ public class SwingComponentPeer public boolean prepareImage(Image img, int width, int height, ImageObserver ob) { Component parent = awtComponent.getParent(); - ComponentPeer parentPeer = parent.getPeer(); - return parentPeer.prepareImage(img, width, height, ob); + boolean res; + if(parent != null) + { + ComponentPeer parentPeer = parent.getPeer(); + res = parentPeer.prepareImage(img, width, height, ob); + } + else + { + res = Toolkit.getDefaultToolkit().prepareImage(img, width, height, ob); + } + return res; } public void print(Graphics graphics) diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java index 0b2fb99..f433e1b 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java @@ -92,7 +92,12 @@ public class SwingContainerPeer */ public Insets getInsets() { - return insets(); + Insets retVal; + if (swingComponent != null) + retVal = swingComponent.getJComponent().getInsets(); + else + retVal = new Insets(0, 0, 0, 0); + return retVal; } /** @@ -209,6 +214,8 @@ public class SwingContainerPeer protected void handleMouseEvent(MouseEvent ev) { Component comp = awtComponent.getComponentAt(ev.getPoint()); + if(comp == null) + comp = awtComponent; if (comp != null) { ComponentPeer peer = comp.getPeer(); diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java index fea1b50..0d5a02d 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java @@ -53,9 +53,9 @@ import java.awt.peer.FramePeer; * As a minimum, a subclass must implement all the remaining abstract methods * as well as the following methods: * <ul> - * <li>{@link ComponentPeer#getLocationOnScreen()}</li> - * <li>{@link ComponentPeer#getGraphics()}</li> - * <li>{@link ComponentPeer#createImage(int, int)}</li> + * <li>{@link java.awt.peer.ComponentPeer#getLocationOnScreen()}</li> + * <li>{@link java.awt.peer.ComponentPeer#getGraphics()}</li> + * <li>{@link java.awt.peer.ComponentPeer#createImage(int, int)}</li> * </ul> * * @author Roman Kennke (kennke@aicas.com) diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java index bd9dcd7..0033efb 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java @@ -174,7 +174,7 @@ public class SwingMenuBarPeer /** * Adds a help menu to the menu bar. * - * @param m the menu to add + * @param menu the menu to add */ public void addHelpMenu(Menu menu) { diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java index a4c6d82..0c3b4e7 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java @@ -283,7 +283,7 @@ public class SwingTextFieldPeer * @param startPos the start index of the selection * @param endPos the start index of the selection */ - public void select(int start_pos, int endPos) + public void select(int startPos, int endPos) { // TODO: Must be implemented. } diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java index 2f89795..43a509b 100644 --- a/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java @@ -48,9 +48,9 @@ import java.awt.peer.WindowPeer; * As a minimum, a subclass must implement all the remaining abstract methods * as well as the following methods: * <ul> - * <li>{@link ComponentPeer#getLocationOnScreen()}</li> - * <li>{@link ComponentPeer#getGraphics()}</li> - * <li>{@link ComponentPeer#createImage(int, int)}</li> + * <li>{@link java.awt.peer.ComponentPeer#getLocationOnScreen()}</li> + * <li>{@link java.awt.peer.ComponentPeer#getGraphics()}</li> + * <li>{@link java.awt.peer.ComponentPeer#createImage(int, int)}</li> * </ul> * * @author Roman Kennke (kennke@aicas.com) diff --git a/libjava/classpath/gnu/java/awt/print/JavaPrinterGraphics.java b/libjava/classpath/gnu/java/awt/print/JavaPrinterGraphics.java new file mode 100644 index 0000000..9a3db01 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/print/JavaPrinterGraphics.java @@ -0,0 +1,518 @@ +/* JavaPrinterGraphics.java -- AWT printer rendering class. + 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.print; + +import gnu.java.awt.peer.gtk.CairoSurface; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.image.ImageObserver; +import java.awt.image.PixelGrabber; +import java.awt.print.PageFormat; +import java.awt.print.Pageable; +import java.awt.print.Paper; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterGraphics; +import java.awt.print.PrinterJob; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.text.AttributedCharacterIterator; + +/** + * Graphics context to draw to PostScript. + * + * @author Sven de Marothy + */ +public class JavaPrinterGraphics extends Graphics implements PrinterGraphics +{ + + /** + * The used graphics context. + */ + private Graphics g; + + /** + * The associated printer job. + */ + private PrinterJob printerJob; + + /** + * Rendering resolution + */ + private static final double DPI = 72.0; + + /** + * Rendered image size. + */ + private int xSize, ySize; + + /** + * The image to render to. + */ + private Image image; + + public JavaPrinterGraphics( PrinterJob printerJob ) + { + this.printerJob = printerJob; + } + + /** + * Spool a document to PostScript. + * If Pageable is non-null, it will print that, otherwise it will use + * the supplied printable and pageFormat. + */ + public SpooledDocument spoolPostScript(Printable printable, + PageFormat pageFormat, + Pageable pageable) + throws PrinterException + { + try + { + // spool to a temporary file + File temp = File.createTempFile("cpspool", ".ps"); + temp.deleteOnExit(); + + PrintWriter out = new PrintWriter + (new BufferedWriter + (new OutputStreamWriter + (new FileOutputStream(temp), "ISO8859_1"), 1000000)); + + writePSHeader(out); + + if(pageable != null) + { + for(int index = 0; index < pageable.getNumberOfPages(); index++) + spoolPage(out, pageable.getPrintable(index), + pageable.getPageFormat(index), index); + } + else + { + int index = 0; + while(spoolPage(out, printable, pageFormat, index++) == + Printable.PAGE_EXISTS); + } + out.println("%%Trailer"); + out.println("%%EOF"); + out.close(); + return new SpooledDocument( temp ); + } + catch (IOException e) + { + PrinterException pe = new PrinterException(); + pe.initCause(e); + throw pe; + } + } + + /** + * Spools a single page, returns NO_SUCH_PAGE unsuccessful, + * PAGE_EXISTS if it was. + */ + public int spoolPage(PrintWriter out, + Printable printable, + PageFormat pageFormat, + int index) throws IOException, PrinterException + { + initImage( pageFormat ); + if(printable.print(this, pageFormat, index) == Printable.NO_SUCH_PAGE) + return Printable.NO_SUCH_PAGE; + g.dispose(); + g = null; + writePage( out, pageFormat ); + return Printable.PAGE_EXISTS; + } + + private void initImage(PageFormat pageFormat) + { + // Create a really big image and draw to that. + xSize = (int)(DPI*pageFormat.getWidth()/72.0); + ySize = (int)(DPI*pageFormat.getHeight()/72.0); + + // Swap X and Y sizes if it's a Landscape page. + if( pageFormat.getOrientation() != PageFormat.PORTRAIT ) + { + int t = xSize; + xSize = ySize; + ySize = t; + } + + // FIXME: This should at least be BufferedImage. + // Fix once we have a working B.I. + // Graphics2D should also be supported of course. + image = CairoSurface.getBufferedImage(xSize, ySize); + + g = image.getGraphics(); + setColor(Color.white); + fillRect(0, 0, xSize, ySize); + setColor(Color.black); + } + + private void writePSHeader(PrintWriter out) + { + out.println("%!PS-Adobe-3.0"); + out.println("%%Title: "+printerJob.getJobName()); + out.println("%%Creator: GNU Classpath "); + out.println("%%DocumentData: Clean8Bit"); + + out.println("%%DocumentNeededResources: font Times-Roman Helvetica Courier"); + // out.println("%%Pages: "+); // FIXME # pages. + out.println("%%EndComments"); + + out.println("%%BeginProlog"); + out.println("%%EndProlog"); + out.println("%%BeginSetup"); + + // FIXME: Paper name + // E.g. "A4" "Letter" + // out.println("%%BeginFeature: *PageSize A4"); + + out.println("%%EndFeature"); + + out.println("%%EndSetup"); + + // out.println("%%Page: 1 1"); + } + + private void writePage(PrintWriter out, PageFormat pageFormat) + { + out.println("%%BeginPageSetup"); + + Paper p = pageFormat.getPaper(); + double pWidth = p.getWidth(); + double pHeight = p.getHeight(); + + if( pageFormat.getOrientation() == PageFormat.PORTRAIT ) + out.println( "%%Orientation: Portrait" ); + else + { + out.println( "%%Orientation: Landscape" ); + double t = pWidth; + pWidth = pHeight; + pHeight = t; + } + + out.println("gsave % first save"); + + // 595x842; 612x792 respectively + out.println("<< /PageSize [" +pWidth + " "+pHeight+ "] >> setpagedevice"); + + // invert the Y axis so that we get screen-like coordinates instead. + AffineTransform pageTransform = new AffineTransform(); + if( pageFormat.getOrientation() == PageFormat.REVERSE_LANDSCAPE ) + { + pageTransform.translate(pWidth, pHeight); + pageTransform.scale(-1.0, -1.0); + } + concatCTM(out, pageTransform); + out.println("%%EndPageSetup"); + + out.println("gsave"); + + + // Draw the image + out.println(xSize+" "+ySize+" 8 [1 0 0 -1 0 "+ySize+" ]"); + out.println("{currentfile 3 string readhexstring pop} bind"); + out.println("false 3 colorimage"); + int[] pixels = new int[xSize * ySize]; + PixelGrabber pg = new PixelGrabber(image, 0, 0, xSize, ySize, pixels, 0, xSize); + + try { + pg.grabPixels(); + } catch (InterruptedException e) { + out.println("% Bug getting pixels!"); + } + + int n = 0; + for (int j = 0; j < ySize; j++) { + for (int i = 0; i < xSize; i++) { + out.print( colorTripleHex(pixels[j * xSize + i]) ); + if(((++n)%11) == 0) out.println(); + } + } + + out.println(); + out.println("%%EOF"); + out.println("grestore"); + out.println("showpage"); + } + + /** + * Get a nonsperated hex RGB triple, e.g. FFFFFF = white + */ + private String colorTripleHex(int num){ + String s = ""; + + try { + s = Integer.toHexString( ( num & 0x00FFFFFF ) ); + if( s.length() < 6 ) + { + s = "000000"+s; + return s.substring(s.length()-6); + } + } catch (Exception e){ + s = "FFFFFF"; + } + + return s; + } + + private void concatCTM(PrintWriter out, AffineTransform Tx){ + double[] matrixElements = new double[6]; + Tx.getMatrix(matrixElements); + + out.print("[ "); + for(int i=0;i<6;i++) + out.print(matrixElements[i]+" "); + out.println("] concat"); + } + + //----------------------------------------------------------------------------- + /** + * PrinterGraphics method - Returns the printer job associated with this object. + */ + public PrinterJob getPrinterJob() + { + return printerJob; + } + + /** + * The rest of the methods here are just pass-throughs to g. + */ + public void clearRect(int x, int y, int width, int height) + { + g.clearRect(x, y, width, height); + } + + public void clipRect(int x, int y, int width, int height) + { + g.clipRect(x, y, width, height); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) + { + g.copyArea(x, y, width, height, dx, dy); + } + + public Graphics create() + { + return g.create(); + } + + public void dispose() + { + } + + public void drawArc(int x, int y, int width, int height, int startAngle, + int arcAngle) + { + g.drawArc(x, y, width, height, startAngle, arcAngle); + } + + public boolean drawImage(Image img, int x, int y, Color bgcolor, + ImageObserver observer) + { + return g.drawImage(img, x, y, bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + return g.drawImage(img, x, y, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) + { + return g.drawImage(img, x, y, width, height, bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) + { + return g.drawImage(img, x, y, width, height, observer); + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver observer) + { + return g.drawImage(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, bgcolor, observer); + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, ImageObserver observer) + { + return g.drawImage(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, observer); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + g.drawLine(x1, y1, x2, y2); + } + + public void drawOval(int x, int y, int width, int height) + { + g.drawOval(x, y, width, height); + } + + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + g.drawPolygon(xPoints, yPoints, nPoints); + } + + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) + { + g.drawPolyline(xPoints, yPoints, nPoints); + } + + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + g.drawRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + public void drawString(AttributedCharacterIterator iterator, int x, int y) + { + g.drawString(iterator, x, y); + } + + public void drawString(String str, int x, int y) + { + g.drawString(str, x, y); + } + + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) + { + g.fillArc(x, y, width, height, startAngle, arcAngle); + } + + public void fillOval(int x, int y, int width, int height) + { + g.fillOval(x, y, width, height); + } + + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + g.fillPolygon(xPoints, yPoints, nPoints); + } + + public void fillRect(int x, int y, int width, int height) + { + g.fillRect(x, y, width, height); + } + + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + g.fillRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + public Shape getClip() + { + return g.getClip(); + } + + public Rectangle getClipBounds() + { + return g.getClipBounds(); + } + + public Color getColor() + { + return g.getColor(); + } + + public Font getFont() + { + return g.getFont(); + } + + public FontMetrics getFontMetrics(Font f) + { + return g.getFontMetrics(f); + } + + public void setClip(int x, int y, int width, int height) + { + g.setClip(x, y, width, height); + } + + public void setClip(Shape clip) + { + g.setClip(clip); + } + + public void setColor(Color c) + { + g.setColor(c); + } + + public void setFont(Font font) + { + g.setFont(font); + } + + public void setPaintMode() + { + g.setPaintMode(); + } + + public void setXORMode(Color c1) + { + g.setXORMode(c1); + } + + public void translate(int x, int y) + { + g.translate(x, y); + } +} + diff --git a/libjava/classpath/gnu/java/awt/print/JavaPrinterJob.java b/libjava/classpath/gnu/java/awt/print/JavaPrinterJob.java new file mode 100644 index 0000000..adeeba0 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/print/JavaPrinterJob.java @@ -0,0 +1,403 @@ +/* JavaPrinterJob.java -- AWT printing implemented on javax.print. + 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.print; + +import java.awt.HeadlessException; +import java.awt.print.PageFormat; +import java.awt.print.Pageable; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.util.Locale; + +import javax.print.CancelablePrintJob; +import javax.print.DocFlavor; +import javax.print.DocPrintJob; +import javax.print.PrintException; +import javax.print.PrintService; +import javax.print.PrintServiceLookup; +import javax.print.ServiceUI; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.IntegerSyntax; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.TextSyntax; +import javax.print.attribute.standard.Copies; +import javax.print.attribute.standard.JobName; +import javax.print.attribute.standard.OrientationRequested; +import javax.print.attribute.standard.RequestingUserName; + +/** + * This is the default implementation of PrinterJob + * + * @author Sven de Marothy + */ +public class JavaPrinterJob extends PrinterJob +{ + /** + * The print service associated with this job + */ + private PrintService printer = null; + + /** + * Printing options; + */ + private PrintRequestAttributeSet attributes; + + /** + * Available print services + */ + private static PrintService[] services; + + /** + * The actual print job. + */ + private DocPrintJob printJob; + + /** + * The Printable object to print. + */ + private Printable printable; + + /** + * Page format. + */ + private PageFormat pageFormat; + + /** + * A pageable, or null + */ + private Pageable pageable = null; + + /** + * Cancelled or not + */ + private boolean cancelled = false; + + static + { + // lookup all services without any constraints + services = PrintServiceLookup.lookupPrintServices + (DocFlavor.INPUT_STREAM.POSTSCRIPT, null); + } + + private static final Class copyClass = (new Copies(1)).getClass(); + private static final Class jobNameClass = (new JobName("", null)).getClass(); + private static final Class userNameClass = (new RequestingUserName("", null)).getClass(); + + /** + * Initializes a new instance of <code>PrinterJob</code>. + */ + public JavaPrinterJob() + { + attributes = new HashPrintRequestAttributeSet(); + setCopies(1); + setJobName("Java Printing"); + pageFormat = new PageFormat(); // default page format. + } + + private void getPageAttributes() + { + OrientationRequested orientation = (OrientationRequested) + attributes.get( OrientationRequested.LANDSCAPE.getCategory() ); + if( orientation == null) + return; + + if( orientation.equals(OrientationRequested.PORTRAIT) ) + pageFormat.setOrientation(PageFormat.PORTRAIT); + else if( orientation.equals(OrientationRequested.LANDSCAPE) ) + pageFormat.setOrientation(PageFormat.LANDSCAPE); + else if( orientation.equals(OrientationRequested.REVERSE_LANDSCAPE) ) + pageFormat.setOrientation(PageFormat.REVERSE_LANDSCAPE); + } + + /** + * Returns the number of copies to be printed. + * + * @return The number of copies to be printed. + */ + public int getCopies() + { + return ((IntegerSyntax)attributes.get( jobNameClass )).getValue(); + } + + /** + * Sets the number of copies to be printed. + * + * @param copies The number of copies to be printed. + */ + public void setCopies(int copies) + { + attributes.add( new Copies( copies ) ); + } + + /** + * Returns the name of the print job. + * + * @return The name of the print job. + */ + public String getJobName() + { + return ((TextSyntax)attributes.get( jobNameClass )).getValue(); + } + + /** + * Sets the name of the print job. + * + * @param job_name The name of the print job. + */ + public void setJobName(String job_name) + { + attributes.add( new JobName(job_name, Locale.getDefault()) ); + } + + /** + * Returns the printing user name. + * + * @return The printing username. + */ + public String getUserName() + { + return ((TextSyntax)attributes.get( userNameClass )).getValue(); + } + + /** + * Cancels an in progress print job. + */ + public void cancel() + { + try + { + if(printJob != null && (printJob instanceof CancelablePrintJob)) + { + ((CancelablePrintJob)printJob).cancel(); + cancelled = true; + } + } + catch(PrintException pe) + { + } + } + + /** + * Tests whether or not this job has been cancelled. + * + * @return <code>true</code> if this job has been cancelled, <code>false</code> + * otherwise. + */ + public boolean isCancelled() + { + return cancelled; + } + + /** + * Clones the specified <code>PageFormat</code> object then alters the + * clone so that it represents the default page format. + * + * @param page_format The <code>PageFormat</code> to clone. + * + * @return A new default page format. + */ + public PageFormat defaultPage(PageFormat page_format) + { + return new PageFormat(); + } + + /** + * Displays a dialog box to the user which allows the page format + * attributes to be modified. + * + * @param page_format The <code>PageFormat</code> object to modify. + * + * @return The modified <code>PageFormat</code>. + */ + public PageFormat pageDialog(PageFormat page_format) + throws HeadlessException + { + return defaultPage(null); + } + + /** + * Prints the pages. + */ + public void print() throws PrinterException + { + if( printable == null && pageable == null ) // nothing to print? + return; + + PostScriptGraphics2D pg = new PostScriptGraphics2D( this ); + SpooledDocument doc = pg.spoolPostScript( printable, pageFormat, + pageable ); + + cancelled = false; + printJob = printer.createPrintJob(); + try + { + printJob.print(doc, attributes); + } + catch (PrintException pe) + { + PrinterException p = new PrinterException(); + p.initCause(pe); + throw p; + } + // no printjob active. + printJob = null; + } + + /** + * Prints the page with given attributes. + */ + public void print (PrintRequestAttributeSet attributes) + throws PrinterException + { + this.attributes = attributes; + print(); + } + + /** + * Displays a dialog box to the user which allows the print job + * attributes to be modified. + * + * @return <code>false</code> if the user cancels the dialog box, + * <code>true</code> otherwise. + */ + public boolean printDialog() throws HeadlessException + { + return printDialog( attributes ); + } + + /** + * Displays a dialog box to the user which allows the print job + * attributes to be modified. + * + * @return <code>false</code> if the user cancels the dialog box, + * <code>true</code> otherwise. + */ + public boolean printDialog(PrintRequestAttributeSet attributes) + throws HeadlessException + { + PrintService chosenPrinter = ServiceUI.printDialog + (null, 50, 50, services, null, + DocFlavor.INPUT_STREAM.POSTSCRIPT, attributes); + + getPageAttributes(); + + if( chosenPrinter != null ) + { + try + { + setPrintService( chosenPrinter ); + } + catch(PrinterException pe) + { + // Should not happen. + } + return true; + } + return false; + } + + /** + * This sets the pages that are to be printed. + * + * @param pageable The pages to be printed, which may not be <code>null</code>. + */ + public void setPageable(Pageable pageable) + { + if( pageable == null ) + throw new NullPointerException("Pageable cannot be null."); + this.pageable = pageable; + } + + /** + * Sets this specified <code>Printable</code> as the one to use for + * rendering the pages on the print device. + * + * @param printable The <code>Printable</code> for the print job. + */ + public void setPrintable(Printable printable) + { + this.printable = printable; + } + + /** + * Sets the <code>Printable</code> and the page format for the pages + * to be printed. + * + * @param printable The <code>Printable</code> for the print job. + * @param page_format The <code>PageFormat</code> for the print job. + */ + public void setPrintable(Printable printable, PageFormat page_format) + { + this.printable = printable; + this.pageFormat = page_format; + } + + /** + * Makes any alterations to the specified <code>PageFormat</code> + * necessary to make it work with the current printer. The alterations + * are made to a clone of the input object, which is then returned. + * + * @param page_format The <code>PageFormat</code> to validate. + * + * @return The validated <code>PageFormat</code>. + */ + public PageFormat validatePage(PageFormat page_format) + { + // FIXME + return page_format; + } + + /** + * Change the printer for this print job to service. Subclasses that + * support setting the print service override this method. Throws + * PrinterException when the class doesn't support setting the printer, + * the service doesn't support Pageable or Printable interfaces for 2D + * print output. + * @param service The new printer to use. + * @throws PrinterException if service is not valid. + */ + public void setPrintService(PrintService service) + throws PrinterException + { + if(!service.isDocFlavorSupported(DocFlavor.INPUT_STREAM.POSTSCRIPT)) + throw new PrinterException("This printer service is not supported."); + printer = service; + } +} diff --git a/libjava/classpath/gnu/java/awt/print/PostScriptGraphics2D.java b/libjava/classpath/gnu/java/awt/print/PostScriptGraphics2D.java new file mode 100644 index 0000000..2303f44 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/print/PostScriptGraphics2D.java @@ -0,0 +1,1349 @@ +/* PostScriptGraphics2D.java -- AWT printer rendering class. + 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.print; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Paint; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.renderable.RenderableImage; +import java.awt.image.RenderedImage; +import java.awt.image.ImageObserver; +import java.awt.image.PixelGrabber; +import java.awt.print.PageFormat; +import java.awt.print.Pageable; +import java.awt.print.Paper; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterGraphics; +import java.awt.print.PrinterJob; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * Class PostScriptGraphics2D - Class that implements the Graphics2D object, + * writing the output to a PostScript or EPS file + * + * @author Sven de Marothy + * + */ +class PostScriptGraphics2D extends Graphics2D +{ + /** + * The associated printer job. + */ + private PrinterJob printerJob; + + /** + * Output file. + */ + private PrintWriter out; + + // Graphics data + private AffineTransform currentTransform = new AffineTransform(); + private AffineTransform pageTransform; + private RenderingHints renderingHints; + private Paint currentPaint = null; + private Shape clipShape = null; + private Font currentFont = null; + private Color currentColor = Color.black; + private Color backgroundColor = Color.white; + private Stroke currentStroke = null; + private static Stroke ordinaryStroke = new BasicStroke(0.0f, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER); + private float cx; // current drawing position + private float cy; // current drawing position + private boolean currentFontIsPS; // set if currentFont is one of the above + + // settings + private double pageX = 595; + private double pageY = 842; + private double Y = pageY; + private boolean gradientOn = false; + + /** + * Constructor + * + */ + public PostScriptGraphics2D( PrinterJob pg ) + { + printerJob = pg; + // create transform objects + pageTransform = new AffineTransform(); + currentTransform = new AffineTransform(); + + /* + Create Rendering hints + No text aliasing + Quality color and rendering + Bicubic interpolation + Fractional metrics supported + */ + renderingHints = new RenderingHints(null); + renderingHints.put(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + renderingHints.put(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + renderingHints.put(RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_QUALITY); + } + + /** + * Spool a document to PostScript. + * If Pageable is non-null, it will print that, otherwise it will use + * the supplied printable and pageFormat. + */ + public SpooledDocument spoolPostScript(Printable printable, + PageFormat pageFormat, + Pageable pageable) + throws PrinterException + { + try + { + // spool to a temporary file + File temp = File.createTempFile("cpspool", ".ps"); + temp.deleteOnExit(); + + out = new PrintWriter(new BufferedWriter + (new OutputStreamWriter + (new FileOutputStream(temp), + "ISO8859_1"), 1000000)); + + writePSHeader(); + + if(pageable != null) + { + for(int index = 0; index < pageable.getNumberOfPages(); index++) + spoolPage(out, pageable.getPrintable(index), + pageable.getPageFormat(index), index); + } + else + { + int index = 0; + while(spoolPage(out, printable, pageFormat, index++) == + Printable.PAGE_EXISTS); + } + out.println("%%Trailer"); + out.println("%%EOF"); + out.close(); + return new SpooledDocument( temp ); + } + catch (IOException e) + { + PrinterException pe = new PrinterException(); + pe.initCause(e); + throw pe; + } + } + + //-------------------------------------------------------------------------- + + /** + * Write the postscript file header, + * setup the page format and transforms. + */ + private void writePSHeader() + { + out.println("%!PS-Adobe-3.0"); + out.println("%%Title: "+printerJob.getJobName()); + out.println("%%Creator: GNU Classpath "); + out.println("%%DocumentData: Clean8Bit"); + + out.println("%%DocumentNeededResources: font Times-Roman Helvetica Courier"); + out.println("%%EndComments"); + + out.println("%%BeginProlog"); + out.println("%%EndProlog"); + out.println("%%BeginSetup"); + + out.println("%%EndFeature"); + setupFonts(); + out.println("%%EndSetup"); + + // set default fonts and colors + setFont( new Font("Dialog", Font.PLAIN, 12) ); + currentColor = Color.white; + currentStroke = new BasicStroke(); + setPaint(currentColor); + setStroke(currentStroke); + } + + /** + * setupFonts - set up the font dictionaries for + * helvetica, times and courier + */ + private void setupFonts() + { + out.println("/helveticaISO"); + out.println("/Helvetica findfont dup length dict begin"); + out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall"); + out.println("/Encoding ISOLatin1Encoding def"); + out.println("currentdict end definefont pop"); + + out.println("/timesISO"); + out.println("/Times-Roman findfont dup length dict begin"); + out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall"); + out.println("/Encoding ISOLatin1Encoding def"); + out.println("currentdict end definefont pop"); + + out.println("/courierISO"); + out.println("/Courier findfont dup length dict begin"); + out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall"); + out.println("/Encoding ISOLatin1Encoding def"); + out.println("currentdict end definefont pop"); + } + + /** + * Spools a single page, returns NO_SUCH_PAGE unsuccessful, + * PAGE_EXISTS if it was. + */ + public int spoolPage(PrintWriter out, + Printable printable, + PageFormat pageFormat, + int index) throws IOException, PrinterException + { + out.println("%%BeginPageSetup"); + + Paper p = pageFormat.getPaper(); + pageX = p.getWidth(); + pageY = p.getHeight(); + + if( pageFormat.getOrientation() == PageFormat.PORTRAIT ) + out.println( "%%Orientation: Portrait" ); + else + { + out.println( "%%Orientation: Landscape" ); + double t = pageX; + pageX = pageY; + pageY = t; + } + + setClip(0, 0, (int)pageX, (int)pageY); + + out.println("gsave % first save"); + + // 595x842; 612x792 respectively + out.println("<< /PageSize [" +pageX + " "+pageY+ "] >> setpagedevice"); + + if( pageFormat.getOrientation() != PageFormat.LANDSCAPE ) + { + pageTransform.translate(pageX, 0); + pageTransform.scale(-1.0, 1.0); + } + + // save the original CTM + pushCTM(); + concatCTM(pageTransform); + setTransform(new AffineTransform()); + + out.println("%%EndPageSetup"); + + out.println("gsave"); + + if( printable.print(this, pageFormat, index) == Printable.NO_SUCH_PAGE ) + return Printable.NO_SUCH_PAGE; + + out.println("grestore"); + out.println("showpage"); + + return Printable.PAGE_EXISTS; + } + + /** push the Current Transformation Matrix onto the PS stack */ + private void pushCTM() + { + out.println("matrix currentmatrix % pushCTM()"); + } + + /** pop the Current Transformation Matrix from the PS stack */ + private void popCTM() + { + out.println("setmatrix % restore CTM"); + } + + /////////////////////////////////////////////////////////////////////////// + + public Graphics create() + { + return null; + } + + public void drawOval(int x, int y, int width, int height) + { + out.println("% drawOval()"); + setStroke(ordinaryStroke); + draw(new Ellipse2D.Double(x, y, width, height)); + setStroke(currentStroke); + } + + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) + { + if (nPoints <= 0 || xPoints.length < nPoints || yPoints.length < nPoints) + return; + out.println("newpath % drawPolyLine()"); + out.println(xPoints[0] + " " + yPoints[0] + " moveto"); + for (int i = 1; i < nPoints; i++) + out.println(xPoints[i] + " " + yPoints[i] + " lineto"); + out.println("closepath"); + out.println("stroke"); + } + + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) + { + out.println("% drawRoundRect()"); + RoundRectangle2D.Double rr = new RoundRectangle2D.Double(x, y, width, + height, arcWidth, + arcHeight); + setStroke(ordinaryStroke); + draw(rr); + setStroke(currentStroke); + } + + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) + { + out.println("% fillRoundRect()"); + RoundRectangle2D.Double rr = new RoundRectangle2D.Double(x, y, width, + height, arcWidth, + arcHeight); + fill(rr); + } + + public void drawArc(int x, int y, int width, int height, int startAngle, + int arcAngle) + { + setStroke(ordinaryStroke); + draw(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN)); + setStroke(currentStroke); + } + + public void fillArc(int x, int y, int width, int height, int startAngle, + int arcAngle) + { + fill(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE)); + } + + public void fillOval(int x, int y, int width, int height) + { + out.println("% fillOval()"); + fill( new Ellipse2D.Double(x, y, width, height) ); + } + + public void fillPolygon(int[] x, int[] y, int nPoints) + { + out.println("% fillPolygon()"); + fill( new Polygon(x, y, nPoints) ); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + out.println("% drawLine()"); + setStroke(ordinaryStroke); + out.println("newpath"); + out.println(x1 + " " + (y1) + " moveto"); + out.println(x2 + " " + (y2) + " lineto"); + out.println("stroke"); + setStroke(currentStroke); + } + + //--------------- Image drawing ------------------------------------------ + public boolean drawImage(Image img, int x, int y, Color bgcolor, + ImageObserver observer) + { + int w = img.getWidth(null); + int h = img.getHeight(null); + + return drawImage(img, x, y, x + w, y + h, 0, 0, w - 1, h - 1, bgcolor, + observer); + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver observer) + { + int n = 0; + boolean flipx = false; + boolean flipy = false; + + // swap X and Y's + if (sx1 > sx2) + { + n = sx1; + sx1 = sx2; + sx2 = n; + flipx = ! flipx; + } + if (sy1 > sy2) + { + n = sy1; + sy1 = sy2; + sy2 = n; + flipy = ! flipy; + } + if (dx1 > dx2) + { + n = dx1; + dx1 = dx2; + dx2 = n; + flipx = ! flipx; + } + if (dy1 > dy2) + { + n = dy1; + dy1 = dy2; + dy2 = n; + flipy = ! flipy; + } + n = 0; + int sw = sx2 - sx1; // source width + int sh = sy2 - sy1; // source height + int[] pixels = new int[sw * sh]; // pixel buffer + int dw = dx2 - dx1; // destination width + int dh = dy2 - dy1; // destination height + double x_scale = ((double) dw) / ((double) sw); + double y_scale = ((double) dh) / ((double) sh); + + out.println("% drawImage() 2"); + out.println("gsave"); + out.println(dx1 + " " + dy1 + " translate"); + out.println(dw + " " + dh + " scale"); + out.println(sw + " " + sh + " 8 [" + (flipx ? -sw : sw) + " 0 0 " + + (flipy ? -sh : sh) + " " + (flipx ? sw : 0) + " " + + (flipy ? sh : 0) + " ]"); + out.println("{currentfile 3 string readhexstring pop} bind"); + out.println("false 3 colorimage"); + + PixelGrabber pg = new PixelGrabber(img, sx1, sy1, sw, sh, pixels, 0, sw); + try + { + pg.grabPixels(); + } + catch (InterruptedException e) + { + System.err.println("interrupted waiting for pixels!"); + return (false); + } + + if ((pg.getStatus() & ImageObserver.ABORT) != 0) + { + System.err.println("image fetch aborted or errored"); + return (false); + } + + for (int j = 0; j < sh; j++) + { + for (int i = 0; i < sw; i++) + { + out.print(colorTripleHex(new Color(pixels[j * sw + i]))); + if (((++n) % 11) == 0) + out.println(); + } + } + + out.println(); + out.println("%%EOF"); + out.println("grestore"); + return true; + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + ImageObserver observer) + { + return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, + observer); + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + return drawImage(img, x, y, null, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) + { + int sw = img.getWidth(null); + int sh = img.getHeight(null); + return drawImage(img, x, y, x + width, y + height, /* destination */ + 0, 0, sw - 1, sh - 1, /* source */ + bgcolor, observer); + // correct? + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) + { + return drawImage(img, x, y, width, height, null, observer); + } + + /** Renders a BufferedImage that is filtered with a BufferedImageOp. */ + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) + { + BufferedImage result = op.filter(img, null); + drawImage(result, x, y, null); + } + + /** Renders an image, applying a transform from image space + into user space before drawing. */ + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) + { + AffineTransform oldTransform = new AffineTransform(currentTransform); + boolean ret; + + transform(xform); + ret = drawImage(img, 0, 0, null, obs); + setTransform(oldTransform); + + return ret; + } + + /** Renders a RenderableImage, applying a transform from image + space into user space before drawing. */ + public void drawRenderableImage(RenderableImage img, AffineTransform xform) + { + // FIXME + } + + /** Renders a RenderedImage, applying a transform from + image space into user space before drawing. */ + public void drawRenderedImage(RenderedImage img, AffineTransform xform) + { + // FIXME + } + + //------------------------------------------------------------------------- + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + setStroke(ordinaryStroke); + draw(new Polygon(xPoints, yPoints, nPoints)); + setStroke(currentStroke); + } + + public void drawString(String str, int x, int y) + { + drawString(str, (float) x, (float) y); + } + + public void drawString(String str, float x, float y) + { + if( str.trim().equals("") ) + return; // don't draw whitespace, silly! + + if( currentFontIsPS ) + { + drawStringPSFont(str, x, y); + return; + } + + TextLayout text = new TextLayout(str, currentFont, getFontRenderContext()); + Shape s = text.getOutline(AffineTransform.getTranslateInstance(x, y)); + drawStringShape(s); + } + + private void drawStringPSFont(String str, float x, float y) + { + out.println("% drawString PS font"); + out.println(x + " " + y + " moveto"); + saveAndInvertAxis(); + out.println("(" + str + ") show"); + restoreAxis(); + } + + private void saveAndInvertAxis() + { + // Invert the Y axis of the CTM. + popCTM(); + pushCTM(); + + double[] test = + { + pageTransform.getScaleX(), pageTransform.getShearY(), + pageTransform.getShearX(), pageTransform.getScaleY(), + pageTransform.getTranslateX(), + -pageTransform.getTranslateY() + pageY + }; + + double[] test2 = + { + currentTransform.getScaleX(), + currentTransform.getShearY(), + -currentTransform.getShearX(), + -currentTransform.getScaleY(), + currentTransform.getTranslateX(), + currentTransform.getTranslateY() + }; + + AffineTransform total = new AffineTransform(test); + total.concatenate(new AffineTransform(test2)); + concatCTM(total); + } + + private void restoreAxis() + { + // reset the CTM + popCTM(); + pushCTM(); + AffineTransform total = new AffineTransform(pageTransform); + total.concatenate(currentTransform); + concatCTM(total); + } + + /** + * special drawing routine for string shapes, + * which need to be drawn with the Y axis uninverted. + */ + private void drawStringShape(Shape s) + { + saveAndInvertAxis(); + + // draw the shape s with an inverted Y axis. + PathIterator pi = s.getPathIterator(new AffineTransform()); + float[] coords = new float[6]; + + while (! pi.isDone()) + { + switch (pi.currentSegment(coords)) + { + case PathIterator.SEG_MOVETO: + out.println((coords[0]) + " " + (Y - coords[1]) + " moveto"); + cx = coords[0]; + cy = coords[1]; + break; + case PathIterator.SEG_LINETO: + out.println((coords[0]) + " " + (Y - coords[1]) + " lineto"); + cx = coords[0]; + cy = coords[1]; + break; + case PathIterator.SEG_QUADTO: + // convert to cubic bezier points + float x1 = (cx + 2 * coords[0]) / 3; + float y1 = (cy + 2 * coords[1]) / 3; + float x2 = (2 * coords[2] + coords[0]) / 3; + float y2 = (2 * coords[3] + coords[1]) / 3; + + out.print((x1) + " " + (Y - y1) + " "); + out.print((x2) + " " + (Y - y2) + " "); + out.println((coords[2]) + " " + (Y - coords[3]) + " curveto"); + cx = coords[2]; + cy = coords[3]; + break; + case PathIterator.SEG_CUBICTO: + out.print((coords[0]) + " " + (Y - coords[1]) + " "); + out.print((coords[2]) + " " + (Y - coords[3]) + " "); + out.println((coords[4]) + " " + (Y - coords[5]) + " curveto"); + cx = coords[4]; + cy = coords[5]; + break; + case PathIterator.SEG_CLOSE: + out.println("closepath"); + break; + } + pi.next(); + } + out.println("fill"); + + restoreAxis(); + } + + public void setColor(Color c) + { + /* don't set the color if it's already set */ + if (c.equals(currentColor)) + return; + gradientOn = false; + currentColor = c; + currentPaint = c; // Graphics2D extends colors to paint + + out.println(colorTriple(c) + " setrgbcolor"); + } + + public void clearRect(int x, int y, int width, int height) + { + out.println("% clearRect"); + Color c = currentColor; + setColor(backgroundColor); + fill(new Rectangle2D.Double(x, y, width, height)); + setColor(c); + } + + public void clipRect(int x, int y, int width, int height) + { + clip(new Rectangle2D.Double(x, y, width, height)); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) + { + // FIXME + } + + public void fillRect(int x, int y, int width, int height) + { + fill(new Rectangle2D.Double(x, y, width, height)); + } + + public void dispose() + { + } + + public void setClip(int x, int y, int width, int height) + { + out.println("% setClip()"); + setClip(new Rectangle2D.Double(x, y, width, height)); + } + + public void setClip(Shape s) + { + clip(s); + } + + public Shape getClip() + { + return clipShape; + } + + public Rectangle getClipBounds() + { + return clipShape.getBounds(); + } + + public Color getColor() + { + return currentColor; + } + + public Font getFont() + { + return currentFont; + } + + public FontMetrics getFontMetrics() + { + return getFontMetrics(currentFont); + } + + public FontMetrics getFontMetrics(Font f) + { + // FIXME + return null; + } + + public void setFont(Font font) + { + out.println("% setfont()"); + if (font == null) + // use the default font + font = new Font("Dialog", Font.PLAIN, 12); + currentFont = font; + setPSFont(); // set up the PostScript fonts + } + + /** + * Setup the postscript font if the current font is one + */ + private void setPSFont() + { + currentFontIsPS = false; + + String s = currentFont.getName(); + out.println("% setPSFont: Fontname: " + s); + if (s.equalsIgnoreCase("Helvetica") || s.equalsIgnoreCase("SansSerif")) + out.print("/helveticaISO findfont "); + else if (s.equalsIgnoreCase("Times New Roman")) + out.print("/timesISO findfont "); + else if (s.equalsIgnoreCase("Courier")) + out.print("/courierISO findfont "); + else + return; + + currentFontIsPS = true; + + out.print(currentFont.getSize() + " scalefont "); + out.println("setfont"); + } + + /** XOR mode is not supported */ + public void setPaintMode() + { + } + + /** XOR mode is not supported */ + public void setXORMode(Color c1) + { + } + + public void close() + { + out.println("showpage"); + out.println("%%Trailer"); + out.println("grestore % restore original stuff"); + out.println("%%EOF"); + + try + { + out.close(); + } + catch (Exception e) + { + } + out = null; + } + + //---------------------------------------------------------------- + // Graphics2D stuff ---------------------------------------------- + + /** Sets the values of an arbitrary number of + preferences for the rendering algorithms. */ + public void addRenderingHints(Map hints) + { + /* rendering hint changes are disallowed */ + } + + /** write a shape to the file */ + private void writeShape(Shape s) + { + PathIterator pi = s.getPathIterator(new AffineTransform()); + float[] coords = new float[6]; + + while (! pi.isDone()) + { + switch (pi.currentSegment(coords)) + { + case PathIterator.SEG_MOVETO: + out.println(coords[0] + " " + (coords[1]) + " moveto"); + cx = coords[0]; + cy = coords[1]; + break; + case PathIterator.SEG_LINETO: + out.println(coords[0] + " " + (coords[1]) + " lineto"); + cx = coords[0]; + cy = coords[1]; + break; + case PathIterator.SEG_QUADTO: + // convert to cubic bezier points + float x1 = (cx + 2 * coords[0]) / 3; + float y1 = (cy + 2 * coords[1]) / 3; + float x2 = (2 * coords[2] + coords[0]) / 3; + float y2 = (2 * coords[3] + coords[1]) / 3; + + out.print(x1 + " " + (Y - y1) + " "); + out.print(x2 + " " + (Y - y2) + " "); + out.println(coords[2] + " " + (Y - coords[3]) + " curveto"); + cx = coords[2]; + cy = coords[3]; + break; + case PathIterator.SEG_CUBICTO: + out.print(coords[0] + " " + coords[1] + " "); + out.print(coords[2] + " " + coords[3] + " "); + out.println(coords[4] + " " + coords[5] + " curveto"); + cx = coords[4]; + cy = coords[5]; + break; + case PathIterator.SEG_CLOSE: + out.println("closepath"); + break; + } + pi.next(); + } + } + + /** Intersects the current Clip with the interior of + the specified Shape and sets the Clip to the resulting intersection. */ + public void clip(Shape s) + { + clipShape = s; + out.println("% clip INACTIVE"); + // writeShape(s); + // out.println("clip"); + } + + /** Strokes the outline of a Shape using the + settings of the current Graphics2D context.*/ + public void draw(Shape s) + { + if(!(currentStroke instanceof BasicStroke)) + fill(currentStroke.createStrokedShape(s)); + + out.println("% draw"); + writeShape(s); + out.println("stroke"); + } + + /** Renders the text of the specified GlyphVector using the + Graphics2D context's rendering attributes. */ + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + out.println("% drawGlyphVector"); + Shape s = gv.getOutline(); + drawStringShape(AffineTransform.getTranslateInstance(x, y) + .createTransformedShape(s)); + } + + /** Renders the text of the specified iterator, + using the Graphics2D context's current Paint.*/ + public void drawString(AttributedCharacterIterator iterator, float x, float y) + { + TextLayout text = new TextLayout(iterator, getFontRenderContext()); + Shape s = text.getOutline(AffineTransform.getTranslateInstance(x, y)); + drawStringShape(s); + } + + /** Renders the text of the specified iterator, + using the Graphics2D context's current Paint. */ + public void drawString(AttributedCharacterIterator iterator, int x, int y) + { + drawString(iterator, (float) x, (float) y); + } + + /** Fills the interior of a Shape using the settings of the Graphics2D context. */ + public void fill(Shape s) + { + out.println("% fill"); + if (! gradientOn) + { + writeShape(s); + out.println("fill"); + } + else + { + out.println("gsave"); + writeShape(s); + out.println("clip"); + writeGradient(); + out.println("shfill"); + out.println("grestore"); + } + } + + /** Returns the background color used for clearing a region. */ + public Color getBackground() + { + return backgroundColor; + } + + /** Returns the current Composite in the Graphics2D context. */ + public Composite getComposite() + { + // FIXME + return null; + } + + /** Returns the device configuration associated with this Graphics2D. */ + public GraphicsConfiguration getDeviceConfiguration() + { + // FIXME + out.println("% getDeviceConfiguration()"); + return null; + } + + /** Get the rendering context of the Font within this Graphics2D context. */ + public FontRenderContext getFontRenderContext() + { + out.println("% getFontRenderContext()"); + + double[] scaling = + { + pageTransform.getScaleX(), 0, 0, + -pageTransform.getScaleY(), 0, 0 + }; + + return (new FontRenderContext(new AffineTransform(scaling), false, true)); + } + + /** Returns the current Paint of the Graphics2D context. */ + public Paint getPaint() + { + return currentPaint; + } + + /** Returns the value of a single preference for the rendering algorithms. */ + public Object getRenderingHint(RenderingHints.Key hintKey) + { + return renderingHints.get(hintKey); + } + + /** Gets the preferences for the rendering algorithms. */ + public RenderingHints getRenderingHints() + { + return renderingHints; + } + + /** Returns the current Stroke in the Graphics2D context. */ + public Stroke getStroke() + { + return currentStroke; + } + + /** Returns a copy of the current Transform in the Graphics2D context. */ + public AffineTransform getTransform() + { + return currentTransform; + } + + /** + * Checks whether or not the specified Shape intersects + * the specified Rectangle, which is in device space. + */ + public boolean hit(Rectangle rect, Shape s, boolean onStroke) + { + Rectangle2D.Double r = new Rectangle2D.Double(rect.getX(), rect.getY(), + rect.getWidth(), + rect.getHeight()); + return s.intersects(r); + } + + /** Sets the background color for the Graphics2D context.*/ + public void setBackground(Color color) + { + out.println("% setBackground(" + color + ")"); + backgroundColor = color; + } + + /** Sets the Composite for the Graphics2D context. + Not supported. */ + public void setComposite(Composite comp) + { + } + + /** Sets the Paint attribute for the Graphics2D context.*/ + public void setPaint(Paint paint) + { + currentPaint = paint; + gradientOn = false; + if (paint instanceof Color) + { + setColor((Color) paint); + return; + } + if (paint instanceof GradientPaint) + { + gradientOn = true; + return; + } + } + + /* get a space seperated 0.0 - 1.0 color RGB triple */ + private String colorTriple(Color c) + { + return (((double) c.getRed() / 255.0) + " " + + ((double) c.getGreen() / 255.0) + " " + + ((double) c.getBlue() / 255.0)); + } + + /** + * Get a nonsperated hex RGB triple, eg FFFFFF = white + * used by writeGradient and drawImage + */ + private String colorTripleHex(Color c) + { + String r = "00" + Integer.toHexString(c.getRed()); + r = r.substring(r.length() - 2); + String g = "00" + Integer.toHexString(c.getGreen()); + g = g.substring(g.length() - 2); + String b = "00" + Integer.toHexString(c.getBlue()); + b = b.substring(b.length() - 2); + return r + g + b; + } + + /* write the current gradient fill */ + private void writeGradient() + { + GradientPaint paint = (GradientPaint) currentPaint; + out.println("% writeGradient()"); + + int n = 1; + double x; + double y; + double dx; + double dy; + Point2D p1 = currentTransform.transform(paint.getPoint1(), null); + Point2D p2 = currentTransform.transform(paint.getPoint2(), null); + x = p1.getX(); + y = p1.getY(); + dx = p2.getX() - x; + dy = p2.getY() - y; + + // get number of repetitions + while (x + n * dx < pageY && y + n * dy < pageX && x + n * dx > 0 + && y + n * dy > 0) + n++; + + out.println("<<"); // start + out.println("/ShadingType 2"); // gradient fill + out.println("/ColorSpace [ /DeviceRGB ]"); // RGB colors + out.print("/Coords ["); + out.print(x + " " + y + " " + (x + n * dx) + " " + (y + n * dy) + " "); + out.println("]"); // coordinates defining the axis + out.println("/Function <<"); + out.println("/FunctionType 0"); + out.println("/Order 1"); + out.println("/Domain [ 0 1 ]"); + out.println("/Range [ 0 1 0 1 0 1 ]"); + out.println("/BitsPerSample 8"); + out.println("/Size [ " + (1 + n) + " ]"); + out.print("/DataSource < " + colorTripleHex(paint.getColor1()) + " " + + colorTripleHex(paint.getColor2()) + " "); + for (; n > 1; n--) + if (paint.isCyclic()) + { + if ((n % 2) == 1) + out.print(colorTripleHex(paint.getColor1()) + " "); + else + out.print(colorTripleHex(paint.getColor2()) + " "); + } + else + out.print(colorTripleHex(paint.getColor2()) + " "); + out.println(">"); + out.println(">>"); + out.println(">>"); + } + + /** Sets the value of a single preference for the rendering algorithms. */ + public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) + { + /* we don't allow the changing of rendering hints. */ + } + + /** Replaces the values of all preferences for the rendering algorithms + with the specified hints. */ + public void setRenderingHints(Map hints) + { + /* we don't allow the changing of rendering hints. */ + } + + /** + * Sets the Stroke for the Graphics2D context. BasicStroke fully implemented. + */ + public void setStroke(Stroke s) + { + currentStroke = s; + + if (! (s instanceof BasicStroke)) + return; + + BasicStroke bs = (BasicStroke) s; + out.println("% setStroke()"); + try + { + // set the line width + out.println(bs.getLineWidth() + " setlinewidth"); + + // set the line dash + float[] dashArray = bs.getDashArray(); + if (dashArray != null) + { + out.print("[ "); + for (int i = 0; i < dashArray.length; i++) + out.print(dashArray[i] + " "); + out.println("] " + bs.getDashPhase() + " setdash"); + } + else + out.println("[] 0 setdash"); // set solid + + // set the line cap + switch (bs.getEndCap()) + { + case BasicStroke.CAP_BUTT: + out.println("0 setlinecap"); + break; + case BasicStroke.CAP_ROUND: + out.println("1 setlinecap"); + break; + case BasicStroke.CAP_SQUARE: + out.println("2 setlinecap"); + break; + } + + // set the line join + switch (bs.getLineJoin()) + { + case BasicStroke.JOIN_BEVEL: + out.println("2 setlinejoin"); + break; + case BasicStroke.JOIN_MITER: + out.println("0 setlinejoin"); + out.println(bs.getMiterLimit() + " setmiterlimit"); + break; + case BasicStroke.JOIN_ROUND: + out.println("1 setlinejoin"); + break; + } + } + catch (Exception e) + { + out.println("% Exception in setStroke()"); + } + } + + //////////////////// TRANSFORM SETTING ///////////////////////////////////// + private void concatCTM(AffineTransform Tx) + { + double[] matrixElements = new double[6]; + Tx.getMatrix(matrixElements); + + out.print("[ "); + for (int i = 0; i < 6; i++) + out.print(matrixElements[i] + " "); + out.println("] concat"); + } + + /** Sets the Transform in the Graphics2D context. */ + public void setTransform(AffineTransform Tx) + { + // set the transformation matrix; + currentTransform = Tx; + + // concatenate the current transform and the page transform + AffineTransform totalTransform = new AffineTransform(pageTransform); + totalTransform.concatenate(currentTransform); + out.println("% setTransform()"); + out.println("% pageTransform:" + pageTransform); + out.println("% currentTransform:" + currentTransform); + out.println("% totalTransform:" + totalTransform); + + popCTM(); + pushCTM(); // set the CTM to it's original state + concatCTM(totalTransform); // apply our transforms + } + + /** Composes an AffineTransform object with the Transform + in this Graphics2D according to the rule last-specified-first-applied. */ + public void transform(AffineTransform Tx) + { + // concatenate the current transform + currentTransform.concatenate(Tx); + // and the PS CTM + concatCTM(Tx); + } + + ////////////////////////// TRANSFORMS ////////////////////////////////////// + + /** shear transform */ + public void shear(double shx, double shy) + { + out.println("% shear()"); + AffineTransform Tx = new AffineTransform(); + Tx.shear(shx, shy); + transform(Tx); + } + + /** Translates the origin of the Graphics2D context + to the point (x, y) in the current coordinate system. */ + public void translate(int x, int y) + { + out.println("% translate()"); + AffineTransform Tx = new AffineTransform(); + Tx.translate(x, y); + transform(Tx); + } + + /** Translates the origin of the Graphics2D context + to the point (x, y) in the current coordinate system. */ + public void translate(double x, double y) + { + out.println("% translate(" + x + ", " + y + ")"); + AffineTransform Tx = new AffineTransform(); + Tx.translate(x, y); + transform(Tx); + } + + /** Concatenates the current Graphics2D Transform with a rotation transform.*/ + public void rotate(double theta) + { + out.println("% rotate(" + theta + ")"); + AffineTransform Tx = new AffineTransform(); + Tx.rotate(theta); + transform(Tx); + } + + /** Concatenates the current Graphics2D Transform with + a translated rotation transform.*/ + public void rotate(double theta, double x, double y) + { + out.println("% rotate()"); + AffineTransform Tx = new AffineTransform(); + Tx.rotate(theta, x, y); + transform(Tx); + } + + /** Concatenates the current Graphics2D Transform with a scaling + transformation Subsequent rendering is resized according to the + specified scaling factors relative to the previous scaling.*/ + public void scale(double sx, double sy) + { + out.println("% scale(" + sx + ", " + sy + ")"); + AffineTransform Tx = new AffineTransform(); + Tx.scale(sx, sy); + transform(Tx); + } +} diff --git a/libjava/classpath/gnu/java/awt/print/SpooledDocument.java b/libjava/classpath/gnu/java/awt/print/SpooledDocument.java new file mode 100644 index 0000000..b606a2e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/print/SpooledDocument.java @@ -0,0 +1,91 @@ +/* SpooledDocument.java -- Reurgitate a spooled PostScript file + 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.print; + +import javax.print.Doc; +import javax.print.DocFlavor; +import javax.print.attribute.DocAttributeSet; +import java.io.File; +import java.io.IOException; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.Reader; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class SpooledDocument implements Doc +{ + private FileInputStream fis; + + public SpooledDocument(File file) + { + try + { + fis = new FileInputStream(file); + } + catch (FileNotFoundException ffne) + { + // Shouldn't happen. + } + } + + public DocAttributeSet getAttributes() + { + return null; + } + + public DocFlavor getDocFlavor() + { + return DocFlavor.INPUT_STREAM.POSTSCRIPT; + } + + public Object getPrintData() + { + return fis; + } + + public Reader getReaderForText() + { + return new InputStreamReader(fis); + } + + public InputStream getStreamForBytes() + { + return fis; + } +} |