diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/gnu/java/awt | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/gnu/java/awt')
236 files changed, 63220 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/java/awt/AWTUtilities.java b/libjava/classpath/gnu/java/awt/AWTUtilities.java new file mode 100644 index 000000000..1b308cef4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/AWTUtilities.java @@ -0,0 +1,898 @@ +/* AWTUtilities.java -- Common utility methods for AWT and Swing. + Copyright (C) 2005, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt; + +import java.applet.Applet; +import java.awt.Component; +import java.awt.Container; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.AbstractSequentialList; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.WeakHashMap; +import java.lang.reflect.InvocationTargetException; + +/** + * This class mirrors the javax.swing.SwingUtilities class. It + * provides commonly needed functionalities for AWT classes without + * the need to reference classes in the javax.swing package. + */ +public class AWTUtilities +{ + + /** + * This List implementation wraps the Component[] returned by + * {@link Container#getComponents()} and iterates over the visible Components + * in that array. This class is used in {@link #getVisibleChildren}. + */ + static class VisibleComponentList extends AbstractSequentialList + { + /** + * The ListIterator for this List. + */ + class VisibleComponentIterator implements ListIterator + { + /** The current index in the Component[]. */ + int index; + + /** The index in the List of visible Components. */ + int listIndex; + + /** + * Creates a new VisibleComponentIterator that starts at the specified + * <code>listIndex</code>. The array of Components is searched from + * the beginning to find the matching array index. + * + * @param listIndex the index from where to begin iterating + */ + VisibleComponentIterator(int listIndex) + { + this.listIndex = listIndex; + int visibleComponentsFound = 0; + for (index = 0; visibleComponentsFound != listIndex; index++) + { + if (components[index].isVisible()) + visibleComponentsFound++; + } + } + + /** + * Returns <code>true</code> if there are more visible components in the + * array, <code>false</code> otherwise. + * + * @return <code>true</code> if there are more visible components in the + * array, <code>false</code> otherwise + */ + public boolean hasNext() + { + boolean hasNext = false; + for (int i = index; i < components.length; i++) + { + if (components[i].isVisible()) + { + hasNext = true; + break; + } + } + return hasNext; + } + + /** + * Returns the next visible <code>Component</code> in the List. + * + * @return the next visible <code>Component</code> in the List + * + * @throws NoSuchElementException if there is no next element + */ + public Object next() + { + Object o = null; + for (; index < components.length; index++) + { + if (components[index].isVisible()) + { + o = components[index]; + break; + } + } + if (o != null) + { + index++; + listIndex++; + return o; + } + else + throw new NoSuchElementException(); + } + + /** + * Returns <code>true</code> if there are more visible components in the + * array in the reverse direction, <code>false</code> otherwise. + * + * @return <code>true</code> if there are more visible components in the + * array in the reverse direction, <code>false</code> otherwise + */ + public boolean hasPrevious() + { + boolean hasPrevious = false; + for (int i = index - 1; i >= 0; i--) + { + if (components[i].isVisible()) + { + hasPrevious = true; + break; + } + } + return hasPrevious; + } + + /** + * Returns the previous visible <code>Component</code> in the List. + * + * @return the previous visible <code>Component</code> in the List + * + * @throws NoSuchElementException if there is no previous element + */ + public Object previous() + { + Object o = null; + for (index--; index >= 0; index--) + { + if (components[index].isVisible()) + { + o = components[index]; + break; + } + } + if (o != null) + { + listIndex--; + return o; + } + else + throw new NoSuchElementException(); + } + + /** + * Returns the index of the next element in the List. + * + * @return the index of the next element in the List + */ + public int nextIndex() + { + return listIndex + 1; + } + + /** + * Returns the index of the previous element in the List. + * + * @return the index of the previous element in the List + */ + public int previousIndex() + { + return listIndex - 1; + } + + /** + * This operation is not supported because the List is immutable. + * + * @throws UnsupportedOperationException because the List is immutable + */ + public void remove() + { + throw new UnsupportedOperationException + ("VisibleComponentList is immutable"); + } + + /** + * This operation is not supported because the List is immutable. + * + * @param o not used here + * + * @throws UnsupportedOperationException because the List is immutable + */ + public void set(Object o) + { + throw new UnsupportedOperationException + ("VisibleComponentList is immutable"); + } + + /** + * This operation is not supported because the List is immutable. + * + * @param o not used here + * + * @throws UnsupportedOperationException because the List is immutable + */ + public void add(Object o) + { + throw new UnsupportedOperationException + ("VisibleComponentList is immutable"); + } + } + + /** + * The components over which we iterate. Only the visible components + * are returned by this List. + */ + Component[] components; + + /** + * Creates a new instance of VisibleComponentList that wraps the specified + * <code>Component[]</code>. + * + * @param c the <code>Component[]</code> to be wrapped. + */ + VisibleComponentList(Component[] c) + { + components = c; + } + + /** + * Returns a {@link ListIterator} for iterating over this List. + * + * @return a {@link ListIterator} for iterating over this List + */ + public ListIterator listIterator(int index) + { + return new VisibleComponentIterator(index); + } + + /** + * Returns the number of visible components in the wrapped Component[]. + * + * @return the number of visible components + */ + public int size() + { + int visibleComponents = 0; + for (int i = 0; i < components.length; i++) + if (components[i].isVisible()) + visibleComponents++; + return visibleComponents; + } + } + + /** + * The cache for our List instances. We try to hold one instance of + * VisibleComponentList for each Component[] that is requested. Note + * that we use a WeakHashMap for caching, so that the cache itself + * does not keep the array or the List from beeing garbage collected + * if no other objects hold references to it. + */ + static WeakHashMap visibleChildrenCache = new WeakHashMap(); + + /** + * Returns the visible children of a {@link Container}. This method is + * commonly needed in LayoutManagers, because they only have to layout + * the visible children of a Container. + * + * @param c the Container from which to extract the visible children + * + * @return the visible children of <code>c</code> + */ + public static List getVisibleChildren(Container c) + { + Component[] children = c.getComponents(); + Object o = visibleChildrenCache.get(children); + VisibleComponentList visibleChildren = null; + if (o == null) + { + visibleChildren = new VisibleComponentList(children); + visibleChildrenCache.put(children, visibleChildren); + } + else + visibleChildren = (VisibleComponentList) o; + + return visibleChildren; + } + + /** + * Calculates the portion of the base rectangle which is inside the + * insets. + * + * @param base The rectangle to apply the insets to + * @param insets The insets to apply to the base rectangle + * @param ret A rectangle to use for storing the return value, or + * <code>null</code> + * + * @return The calculated area inside the base rectangle and its insets, + * either stored in ret or a new Rectangle if ret is <code>null</code> + * + * @see #calculateInnerArea + */ + public static Rectangle calculateInsetArea(Rectangle base, Insets insets, + Rectangle ret) + { + if (ret == null) + ret = new Rectangle(); + ret.setBounds(base.x + insets.left, base.y + insets.top, + base.width - (insets.left + insets.right), + base.height - (insets.top + insets.bottom)); + return ret; + } + + /** + * Calculates the bounds of a component in the component's own coordinate + * space. The result has the same height and width as the component's + * bounds, but its location is set to (0,0). + * + * @param aComponent The component to measure + * + * @return The component's bounds in its local coordinate space + */ + public static Rectangle getLocalBounds(Component aComponent) + { + Rectangle bounds = aComponent.getBounds(); + return new Rectangle(0, 0, bounds.width, bounds.height); + } + + /** + * Returns the font metrics object for a given font. The metrics can be + * used to calculate crude bounding boxes and positioning information, + * for laying out components with textual elements. + * + * @param font The font to get metrics for + * + * @return The font's metrics + * + * @see java.awt.font.GlyphMetrics + */ + public static FontMetrics getFontMetrics(Font font) + { + return Toolkit.getDefaultToolkit().getFontMetrics(font); + } + + /** + * Returns the least ancestor of <code>comp</code> which has the + * specified name. + * + * @param name The name to search for + * @param comp The component to search the ancestors of + * + * @return The nearest ancestor of <code>comp</code> with the given + * name, or <code>null</code> if no such ancestor exists + * + * @see java.awt.Component#getName + * @see #getAncestorOfClass + */ + public static Container getAncestorNamed(String name, Component comp) + { + while (comp != null && (comp.getName() != name)) + comp = comp.getParent(); + return (Container) comp; + } + + /** + * Returns the least ancestor of <code>comp</code> which is an instance + * of the specified class. + * + * @param c The class to search for + * @param comp The component to search the ancestors of + * + * @return The nearest ancestor of <code>comp</code> which is an instance + * of the given class, or <code>null</code> if no such ancestor exists + * + * @see #getAncestorOfClass + * @see #windowForComponent + * @see + * + */ + public static Container getAncestorOfClass(Class c, Component comp) + { + while (comp != null && (! c.isInstance(comp))) + comp = comp.getParent(); + return (Container) comp; + } + + /** + * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>. + * + * @param comp The component to search for an ancestor window + * + * @return An ancestral window, or <code>null</code> if none exists + */ + public static Window windowForComponent(Component comp) + { + return (Window) getAncestorOfClass(Window.class, comp); + } + + /** + * Returns the "root" of the component tree containint <code>comp</code> + * The root is defined as either the <em>least</em> ancestor of + * <code>comp</code> which is a {@link Window}, or the <em>greatest</em> + * ancestor of <code>comp</code> which is a {@link Applet} if no {@link + * Window} ancestors are found. + * + * @param comp The component to search for a root + * + * @return The root of the component's tree, or <code>null</code> + */ + public static Component getRoot(Component comp) + { + Applet app = null; + Window win = null; + + while (comp != null) + { + if (win == null && comp instanceof Window) + win = (Window) comp; + else if (comp instanceof Applet) + app = (Applet) comp; + comp = comp.getParent(); + } + + if (win != null) + return win; + else + return app; + } + + /** + * Return true if a descends from b, in other words if b is an + * ancestor of a. + * + * @param a The child to search the ancestry of + * @param b The potential ancestor to search for + * + * @return true if a is a descendent of b, false otherwise + */ + public static boolean isDescendingFrom(Component a, Component b) + { + while (true) + { + if (a == null || b == null) + return false; + if (a == b) + return true; + a = a.getParent(); + } + } + + /** + * Returns the deepest descendent of parent which is both visible and + * contains the point <code>(x,y)</code>. Returns parent when either + * parent is not a container, or has no children which contain + * <code>(x,y)</code>. Returns <code>null</code> when either + * <code>(x,y)</code> is outside the bounds of parent, or parent is + * <code>null</code>. + * + * @param parent The component to search the descendents of + * @param x Horizontal coordinate to search for + * @param y Vertical coordinate to search for + * + * @return A component containing <code>(x,y)</code>, or + * <code>null</code> + * + * @see java.awt.Container#findComponentAt + */ + public static Component getDeepestComponentAt(Component parent, int x, int y) + { + if (parent == null || (! parent.contains(x, y))) + return null; + + if (! (parent instanceof Container)) + return parent; + + Container c = (Container) parent; + return c.findComponentAt(x, y); + } + + /** + * Converts a point from a component's local coordinate space to "screen" + * coordinates (such as the coordinate space mouse events are delivered + * in). This operation is equivalent to translating the point by the + * location of the component (which is the origin of its coordinate + * space). + * + * @param p The point to convert + * @param c The component which the point is expressed in terms of + * + * @see convertPointFromScreen + */ + public static void convertPointToScreen(Point p, Component c) + { + Point c0 = c.getLocationOnScreen(); + p.translate(c0.x, c0.y); + } + + /** + * Converts a point from "screen" coordinates (such as the coordinate + * space mouse events are delivered in) to a component's local coordinate + * space. This operation is equivalent to translating the point by the + * negation of the component's location (which is the origin of its + * coordinate space). + * + * @param p The point to convert + * @param c The component which the point should be expressed in terms of + */ + public static void convertPointFromScreen(Point p, Component c) + { + Point c0 = c.getLocationOnScreen(); + p.translate(-c0.x, -c0.y); + } + + /** + * Converts a point <code>(x,y)</code> from the coordinate space of one + * component to another. This is equivalent to converting the point from + * <code>source</code> space to screen space, then back from screen space + * to <code>destination</code> space. If exactly one of the two + * Components is <code>null</code>, it is taken to refer to the root + * ancestor of the other component. If both are <code>null</code>, no + * transformation is done. + * + * @param source The component which the point is expressed in terms of + * @param x Horizontal coordinate of point to transform + * @param y Vertical coordinate of point to transform + * @param destination The component which the return value will be + * expressed in terms of + * + * @return The point <code>(x,y)</code> converted from the coordinate + * space of the + * source component to the coordinate space of the destination component + * + * @see #convertPointToScreen + * @see #convertPointFromScreen + * @see #convertRectangle + * @see #getRoot + */ + public static Point convertPoint(Component source, int x, int y, + Component destination) + { + Point pt = new Point(x, y); + + if (source == null && destination == null) + return pt; + + if (source == null) + source = getRoot(destination); + + if (destination == null) + destination = getRoot(source); + + if (source.isShowing() && destination.isShowing()) + { + convertPointToScreen(pt, source); + convertPointFromScreen(pt, destination); + } + + return pt; + } + + + /** + * Converts a rectangle from the coordinate space of one component to + * another. This is equivalent to converting the rectangle from + * <code>source</code> space to screen space, then back from screen space + * to <code>destination</code> space. If exactly one of the two + * Components is <code>null</code>, it is taken to refer to the root + * ancestor of the other component. If both are <code>null</code>, no + * transformation is done. + * + * @param source The component which the rectangle is expressed in terms of + * @param rect The rectangle to convert + * @param destination The component which the return value will be + * expressed in terms of + * + * @return A new rectangle, equal in size to the input rectangle, but + * with its position converted from the coordinate space of the source + * component to the coordinate space of the destination component + * + * @see #convertPointToScreen + * @see #convertPointFromScreen + * @see #convertPoint + * @see #getRoot + */ + public static Rectangle convertRectangle(Component source, Rectangle rect, + Component destination) + { + Point pt = convertPoint(source, rect.x, rect.y, destination); + return new Rectangle(pt.x, pt.y, rect.width, rect.height); + } + + /** + * Convert a mouse event which refrers to one component to another. This + * includes changing the mouse event's coordinate space, as well as the + * source property of the event. If <code>source</code> is + * <code>null</code>, it is taken to refer to <code>destination</code>'s + * root component. If <code>destination</code> is <code>null</code>, the + * new event will remain expressed in <code>source</code>'s coordinate + * system. + * + * @param source The component the mouse event currently refers to + * @param sourceEvent The mouse event to convert + * @param destination The component the new mouse event should refer to + * + * @return A new mouse event expressed in terms of the destination + * component's coordinate space, and with the destination component as + * its source + * + * @see #convertPoint + */ + public static MouseEvent convertMouseEvent(Component source, + MouseEvent sourceEvent, + Component destination) + { + Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(), + destination); + + return new MouseEvent(destination, sourceEvent.getID(), + sourceEvent.getWhen(), sourceEvent.getModifiers(), + newpt.x, newpt.y, sourceEvent.getClickCount(), + sourceEvent.isPopupTrigger(), + sourceEvent.getButton()); + } + + + /** + * Calls {@link java.awt.EventQueue.invokeLater} with the + * specified {@link Runnable}. + */ + public static void invokeLater(Runnable doRun) + { + java.awt.EventQueue.invokeLater(doRun); + } + + /** + * Calls {@link java.awt.EventQueue.invokeAndWait} with the + * specified {@link Runnable}. + */ + public static void invokeAndWait(Runnable doRun) + throws InterruptedException, + InvocationTargetException + { + java.awt.EventQueue.invokeAndWait(doRun); + } + + /** + * Calls {@link java.awt.EventQueue.isEventDispatchThread}. + */ + public static boolean isEventDispatchThread() + { + return java.awt.EventQueue.isDispatchThread(); + } + + /** + * Returns whether the specified key code is valid. + */ + public static boolean isValidKey(int keyCode) + { + switch (keyCode) + { + case KeyEvent.VK_ENTER: + case KeyEvent.VK_BACK_SPACE: + case KeyEvent.VK_TAB: + case KeyEvent.VK_CANCEL: + case KeyEvent.VK_CLEAR: + case KeyEvent.VK_SHIFT: + case KeyEvent.VK_CONTROL: + case KeyEvent.VK_ALT: + case KeyEvent.VK_PAUSE: + case KeyEvent.VK_CAPS_LOCK: + case KeyEvent.VK_ESCAPE: + case KeyEvent.VK_SPACE: + case KeyEvent.VK_PAGE_UP: + case KeyEvent.VK_PAGE_DOWN: + case KeyEvent.VK_END: + case KeyEvent.VK_HOME: + case KeyEvent.VK_LEFT: + case KeyEvent.VK_UP: + case KeyEvent.VK_RIGHT: + case KeyEvent.VK_DOWN: + case KeyEvent.VK_COMMA: + case KeyEvent.VK_MINUS: + case KeyEvent.VK_PERIOD: + case KeyEvent.VK_SLASH: + case KeyEvent.VK_0: + case KeyEvent.VK_1: + case KeyEvent.VK_2: + case KeyEvent.VK_3: + case KeyEvent.VK_4: + case KeyEvent.VK_5: + case KeyEvent.VK_6: + case KeyEvent.VK_7: + case KeyEvent.VK_8: + case KeyEvent.VK_9: + case KeyEvent.VK_SEMICOLON: + case KeyEvent.VK_EQUALS: + case KeyEvent.VK_A: + case KeyEvent.VK_B: + case KeyEvent.VK_C: + case KeyEvent.VK_D: + case KeyEvent.VK_E: + case KeyEvent.VK_F: + case KeyEvent.VK_G: + case KeyEvent.VK_H: + case KeyEvent.VK_I: + case KeyEvent.VK_J: + case KeyEvent.VK_K: + case KeyEvent.VK_L: + case KeyEvent.VK_M: + case KeyEvent.VK_N: + case KeyEvent.VK_O: + case KeyEvent.VK_P: + case KeyEvent.VK_Q: + case KeyEvent.VK_R: + case KeyEvent.VK_S: + case KeyEvent.VK_T: + case KeyEvent.VK_U: + case KeyEvent.VK_V: + case KeyEvent.VK_W: + case KeyEvent.VK_X: + case KeyEvent.VK_Y: + case KeyEvent.VK_Z: + case KeyEvent.VK_OPEN_BRACKET: + case KeyEvent.VK_BACK_SLASH: + case KeyEvent.VK_CLOSE_BRACKET: + case KeyEvent.VK_NUMPAD0: + case KeyEvent.VK_NUMPAD1: + case KeyEvent.VK_NUMPAD2: + case KeyEvent.VK_NUMPAD3: + case KeyEvent.VK_NUMPAD4: + case KeyEvent.VK_NUMPAD5: + case KeyEvent.VK_NUMPAD6: + case KeyEvent.VK_NUMPAD7: + case KeyEvent.VK_NUMPAD8: + case KeyEvent.VK_NUMPAD9: + case KeyEvent.VK_MULTIPLY: + case KeyEvent.VK_ADD: + case KeyEvent.VK_SEPARATOR: + case KeyEvent.VK_SUBTRACT: + case KeyEvent.VK_DECIMAL: + case KeyEvent.VK_DIVIDE: + case KeyEvent.VK_DELETE: + case KeyEvent.VK_NUM_LOCK: + case KeyEvent.VK_SCROLL_LOCK: + case KeyEvent.VK_F1: + case KeyEvent.VK_F2: + case KeyEvent.VK_F3: + case KeyEvent.VK_F4: + case KeyEvent.VK_F5: + case KeyEvent.VK_F6: + case KeyEvent.VK_F7: + case KeyEvent.VK_F8: + case KeyEvent.VK_F9: + case KeyEvent.VK_F10: + case KeyEvent.VK_F11: + case KeyEvent.VK_F12: + case KeyEvent.VK_F13: + case KeyEvent.VK_F14: + case KeyEvent.VK_F15: + case KeyEvent.VK_F16: + case KeyEvent.VK_F17: + case KeyEvent.VK_F18: + case KeyEvent.VK_F19: + case KeyEvent.VK_F20: + case KeyEvent.VK_F21: + case KeyEvent.VK_F22: + case KeyEvent.VK_F23: + case KeyEvent.VK_F24: + case KeyEvent.VK_PRINTSCREEN: + case KeyEvent.VK_INSERT: + case KeyEvent.VK_HELP: + case KeyEvent.VK_META: + case KeyEvent.VK_BACK_QUOTE: + case KeyEvent.VK_QUOTE: + case KeyEvent.VK_KP_UP: + case KeyEvent.VK_KP_DOWN: + case KeyEvent.VK_KP_LEFT: + case KeyEvent.VK_KP_RIGHT: + case KeyEvent.VK_DEAD_GRAVE: + case KeyEvent.VK_DEAD_ACUTE: + case KeyEvent.VK_DEAD_CIRCUMFLEX: + case KeyEvent.VK_DEAD_TILDE: + case KeyEvent.VK_DEAD_MACRON: + case KeyEvent.VK_DEAD_BREVE: + case KeyEvent.VK_DEAD_ABOVEDOT: + case KeyEvent.VK_DEAD_DIAERESIS: + case KeyEvent.VK_DEAD_ABOVERING: + case KeyEvent.VK_DEAD_DOUBLEACUTE: + case KeyEvent.VK_DEAD_CARON: + case KeyEvent.VK_DEAD_CEDILLA: + case KeyEvent.VK_DEAD_OGONEK: + case KeyEvent.VK_DEAD_IOTA: + case KeyEvent.VK_DEAD_VOICED_SOUND: + case KeyEvent.VK_DEAD_SEMIVOICED_SOUND: + case KeyEvent.VK_AMPERSAND: + case KeyEvent.VK_ASTERISK: + case KeyEvent.VK_QUOTEDBL: + case KeyEvent.VK_LESS: + case KeyEvent.VK_GREATER: + case KeyEvent.VK_BRACELEFT: + case KeyEvent.VK_BRACERIGHT: + case KeyEvent.VK_AT: + case KeyEvent.VK_COLON: + case KeyEvent.VK_CIRCUMFLEX: + case KeyEvent.VK_DOLLAR: + case KeyEvent.VK_EURO_SIGN: + case KeyEvent.VK_EXCLAMATION_MARK: + case KeyEvent.VK_INVERTED_EXCLAMATION_MARK: + case KeyEvent.VK_LEFT_PARENTHESIS: + case KeyEvent.VK_NUMBER_SIGN: + case KeyEvent.VK_PLUS: + case KeyEvent.VK_RIGHT_PARENTHESIS: + case KeyEvent.VK_UNDERSCORE: + case KeyEvent.VK_FINAL: + case KeyEvent.VK_CONVERT: + case KeyEvent.VK_NONCONVERT: + case KeyEvent.VK_ACCEPT: + case KeyEvent.VK_MODECHANGE: + case KeyEvent.VK_KANA: + case KeyEvent.VK_KANJI: + case KeyEvent.VK_ALPHANUMERIC: + case KeyEvent.VK_KATAKANA: + case KeyEvent.VK_HIRAGANA: + case KeyEvent.VK_FULL_WIDTH: + case KeyEvent.VK_HALF_WIDTH: + case KeyEvent.VK_ROMAN_CHARACTERS: + case KeyEvent.VK_ALL_CANDIDATES: + case KeyEvent.VK_PREVIOUS_CANDIDATE: + case KeyEvent.VK_CODE_INPUT: + case KeyEvent.VK_JAPANESE_KATAKANA: + case KeyEvent.VK_JAPANESE_HIRAGANA: + case KeyEvent.VK_JAPANESE_ROMAN: + case KeyEvent.VK_KANA_LOCK: + case KeyEvent.VK_INPUT_METHOD_ON_OFF: + case KeyEvent.VK_CUT: + case KeyEvent.VK_COPY: + case KeyEvent.VK_PASTE: + case KeyEvent.VK_UNDO: + case KeyEvent.VK_AGAIN: + case KeyEvent.VK_FIND: + case KeyEvent.VK_PROPS: + case KeyEvent.VK_STOP: + case KeyEvent.VK_COMPOSE: + case KeyEvent.VK_ALT_GRAPH: + case KeyEvent.VK_BEGIN: + case KeyEvent.VK_CONTEXT_MENU: + case KeyEvent.VK_WINDOWS: + return true; + default: + return false; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/BitMaskExtent.java b/libjava/classpath/gnu/java/awt/BitMaskExtent.java new file mode 100644 index 000000000..be66fccc6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/BitMaskExtent.java @@ -0,0 +1,79 @@ +/* Copyright (C) 2000, 2002 Free Software Foundation + +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; + +/** + * Simple transparent utility class that can be used to perform bit + * mask extent calculations. + */ +public final class BitMaskExtent +{ + /** The number of the least significant bit of the bit mask extent. */ + public byte leastSignificantBit; + + /** The number of bits in the bit mask extent. */ + public byte bitWidth; + + /** + * Set the bit mask. This will calculate and set the leastSignificantBit + * and bitWidth fields. + * + * @see #leastSignificantBit + * @see #bitWidth + */ + public void setMask(long mask) + { + leastSignificantBit = 0; + bitWidth = 0; + if (mask == 0) return; + long shiftMask = mask; + for (; (shiftMask&1) == 0; shiftMask >>>=1) leastSignificantBit++; + for (; (shiftMask&1) != 0; shiftMask >>>=1) bitWidth++; + + if (shiftMask != 0) + throw new IllegalArgumentException("mask must be continuous"); + } + + /** + * Calculate the bit mask based on the values of the + * leastSignificantBit and bitWidth fields. + */ + public long toMask() + { + return ((1<<bitWidth)-1) << leastSignificantBit; + } +} diff --git a/libjava/classpath/gnu/java/awt/BitwiseXORComposite.java b/libjava/classpath/gnu/java/awt/BitwiseXORComposite.java new file mode 100644 index 000000000..e19b27fa9 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/BitwiseXORComposite.java @@ -0,0 +1,295 @@ +/* BitwiseXORComposite.java -- Composite for emulating old-style XOR. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt; + +import java.awt.Color; +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + + +/** + * A composite for emulating traditional bitwise XOR of pixel values. + * Please note that this composite does <i>not</i> implement the Porter-Duff + * XOR operator, but an exclusive or of overlapping subpixel regions. + * + * <p><img src="doc-files/BitwiseXORComposite-1.png" width="545" + * height="138" alt="A screen shot of BitwiseXORComposite in action" + * /> + * + * <p>The above screen shot shows the result of applying six different + * 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. + * + * <p>The purpose of this composite is to support the {@link + * Graphics#setXORMode(Color)} method in composite-aware graphics + * implementations. Applications typically would use + * <code>setXORMode</code> for drawing “highlights” such + * as text selections or cursors by inverting colors temporarily and + * then inverting them back. + * + * <p>A concrete <code>Graphics</code> implementation may contain + * the following code: + * + * <p><pre> public void setXORMode(Color xorColor) + * { + * setComposite(new gnu.java.awt.BitwiseXORComposite(xorColor)); + * } + * + * public void setPaintMode() + * { + * setComposite(java.awt.AlphaComposite.SrcOver); + * }</pre> + * + * @author Graydon Hoare (graydon@redhat.com) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class BitwiseXORComposite + implements Composite +{ + /** + * The color whose RGB value is xor-ed with the values of each + * pixel. + */ + protected Color xorColor; + + + /** + * Constructs a new composite for xor-ing the pixel value. + * + * @param xorColor the color whose pixel value will be bitwise + * xor-ed with the source and destination pixels. + */ + public BitwiseXORComposite(Color xorColor) + { + this.xorColor = xorColor; + } + + + /** + * Creates a context object for performing the compositing + * operation. Several contexts may co-exist for one composite; each + * context may simultaneously be called from concurrent threads. + * + * @param srcColorModel the color model of the source. + * @param dstColorModel the color model of the destination. + * @param hints hints for choosing between rendering alternatives. + */ + public CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints) + { + if (IntContext.isSupported(srcColorModel, dstColorModel, hints)) + return new IntContext(srcColorModel, xorColor); + + return new GeneralContext(srcColorModel, dstColorModel, xorColor); + } + + + /** + * A fallback CompositeContext that performs bitwise XOR of pixel + * values with the pixel value of the specified <code>xorColor</code>. + * + * <p>Applying this CompositeContext on a 1024x1024 BufferedImage of + * <code>TYPE_INT_RGB</code> took 611 ms on a lightly loaded 2.4 GHz + * Intel Pentium 4 CPU running Sun J2SE 1.4.1_01 on GNU/Linux + * 2.4.20. The timing is the average of ten runs on the same + * BufferedImage. Since the measurements were taken with {@link + * System#currentTimeMillis()}, they are rather inaccurate. + * + * @author Graydon Hoare (graydon@redhat.com) + */ + private static class GeneralContext + implements CompositeContext + { + ColorModel srcColorModel; + ColorModel dstColorModel; + Color xorColor; + + public GeneralContext(ColorModel srcColorModel, + ColorModel dstColorModel, + Color xorColor) + { + this.srcColorModel = srcColorModel; + this.dstColorModel = dstColorModel; + this.xorColor = xorColor; + } + + + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) + { + Rectangle srcRect = src.getBounds(); + Rectangle dstInRect = dstIn.getBounds(); + Rectangle dstOutRect = dstOut.getBounds(); + + int xp = xorColor.getRGB(); + int w = Math.min(Math.min(srcRect.width, dstOutRect.width), + dstInRect.width); + int h = Math.min(Math.min(srcRect.height, dstOutRect.height), + dstInRect.height); + + Object srcPix = null, dstPix = null, rpPix = null; + + // Re-using the rpPix object saved 1-2% of execution time in + // the 1024x1024 pixel benchmark. + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + srcPix = src.getDataElements(x + srcRect.x, y + srcRect.y, srcPix); + dstPix = dstIn.getDataElements(x + dstInRect.x, y + dstInRect.y, + dstPix); + int sp = srcColorModel.getRGB(srcPix); + int dp = dstColorModel.getRGB(dstPix); + int rp = sp ^ xp ^ dp; + dstOut.setDataElements(x + dstOutRect.x, y + dstOutRect.y, + dstColorModel.getDataElements(rp, rpPix)); + } + } + } + + + /** + * Disposes any cached resources. The default implementation does + * nothing because no resources are cached. + */ + public void dispose() + { + } + } + + + /** + * An optimized CompositeContext that performs bitwise XOR of + * <code>int</code> pixel values with the pixel value of a specified + * <code>xorColor</code>. This CompositeContext working only for + * rasters whose transfer format is {@link DataBuffer#TYPE_INT}. + * + * <p>Applying this CompositeContext on a 1024x1024 BufferedImage of + * <code>TYPE_INT_RGB</code> took 69 ms on a lightly loaded 2.4 GHz + * Intel Pentium 4 CPU running Sun J2SE 1.4.1_01 on GNU/Linux + * 2.4.20. The timing is the average of ten runs on the same + * BufferedImage. Since the measurements were taken with {@link + * System#currentTimeMillis()}, they are rather inaccurate. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static class IntContext + extends GeneralContext + { + public IntContext(ColorModel colorModel, Color xorColor) + { + super(colorModel, colorModel, xorColor); + } + + + public void compose(Raster src, Raster dstIn, + WritableRaster dstOut) + { + int aX, bX, dstX, aY, bY, dstY, width, height; + int xorPixel; + int[] srcLine, dstLine; + + aX = src.getMinX(); + aY = src.getMinY(); + bX = dstIn.getMinX(); + bY = dstIn.getMinY(); + dstX = dstOut.getMinX(); + dstY = dstOut.getMinY(); + width = Math.min(Math.min(src.getWidth(), dstIn.getWidth()), + dstOut.getWidth()); + height = Math.min(Math.min(src.getHeight(), dstIn.getHeight()), + dstOut.getHeight()); + if ((width < 1) || (height < 1)) + return; + + srcLine = new int[width]; + dstLine = new int[width]; + + /* We need an int[] array with at least one element here; + * srcLine is as good as any other. + */ + srcColorModel.getDataElements(this.xorColor.getRGB(), srcLine); + xorPixel = srcLine[0]; + + for (int y = 0; y < height; y++) + { + src.getDataElements(aX, y + aY, width, 1, srcLine); + dstIn.getDataElements(bX, y + bY, width, 1, dstLine); + + for (int x = 0; x < width; x++) + dstLine[x] ^= srcLine[x] ^ xorPixel; + + dstOut.setDataElements(dstX, y + dstY, width, 1, dstLine); + } + } + + + /** + * Determines whether an instance of this CompositeContext would + * be able to process the specified color models. + */ + public static boolean isSupported(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints) + { + // FIXME: It would be good if someone could review these checks. + // They probably need to be more restrictive. + + int transferType; + + transferType = srcColorModel.getTransferType(); + if (transferType != dstColorModel.getTransferType()) + return false; + + if (transferType != DataBuffer.TYPE_INT) + return false; + + return true; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/Buffers.java b/libjava/classpath/gnu/java/awt/Buffers.java new file mode 100644 index 000000000..0c8d438c7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/Buffers.java @@ -0,0 +1,225 @@ +/* Buffers.java -- + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt; + +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferDouble; +import java.awt.image.DataBufferFloat; +import java.awt.image.DataBufferInt; +import java.awt.image.DataBufferShort; +import java.awt.image.DataBufferUShort; + +/** + * Utility class for creating and accessing data buffers of arbitrary + * data types. + */ +public final class Buffers +{ + /** + * Create a data buffer of a particular type. + * + * @param dataType the desired data type of the buffer. + * @param data an array containing data, or null + * @param size the size of the data buffer bank + */ + public static DataBuffer createBuffer(int dataType, Object data, + int size) + { + if (data == null) return createBuffer(dataType, size, 1); + + return createBufferFromData(dataType, data, size); + } + + + /** + * Create a data buffer of a particular type. + * + * @param dataType the desired data type of the buffer. + * @param size the size of the data buffer bank + */ + public static DataBuffer createBuffer(int dataType, int size) { + return createBuffer(dataType, size, 1); + } + + /** + * Create a data buffer of a particular type. + * + * @param dataType the desired data type of the buffer. + * @param size the size of the data buffer bank + * @param numBanks the number of banks the buffer should have + */ + public static DataBuffer createBuffer(int dataType, int size, int numBanks) + { + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + return new DataBufferByte(size, numBanks); + case DataBuffer.TYPE_SHORT: + return new DataBufferShort(size, numBanks); + case DataBuffer.TYPE_USHORT: + return new DataBufferUShort(size, numBanks); + case DataBuffer.TYPE_INT: + return new DataBufferInt(size, numBanks); + case DataBuffer.TYPE_FLOAT: + return new DataBufferFloat(size, numBanks); + case DataBuffer.TYPE_DOUBLE: + return new DataBufferDouble(size, numBanks); + default: + throw new UnsupportedOperationException(); + } + } + + /** + * Create a data buffer of a particular type. + * + * @param dataType the desired data type of the buffer + * @param data an array containing the data + * @param size the size of the data buffer bank + */ + public static DataBuffer createBufferFromData(int dataType, Object data, + int size) + { + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + return new DataBufferByte((byte[]) data, size); + case DataBuffer.TYPE_SHORT: + return new DataBufferShort((short[]) data, size); + case DataBuffer.TYPE_USHORT: + return new DataBufferUShort((short[]) data, size); + case DataBuffer.TYPE_INT: + return new DataBufferInt((int[]) data, size); + case DataBuffer.TYPE_FLOAT: + return new DataBufferFloat((float[]) data, size); + case DataBuffer.TYPE_DOUBLE: + return new DataBufferDouble((double[]) data, size); + default: + throw new UnsupportedOperationException(); + } + } + + /** + * Return the data array of a data buffer, regardless of the data + * type. + * + * @return an array of primitive values. The actual array type + * depends on the data type of the buffer. + */ + public static Object getData(DataBuffer buffer) + { + return getData(buffer, 0, null, 0, buffer.getSize()); + } + + + /** + * Copy data from array contained in data buffer, much like + * System.arraycopy. Create a suitable destination array if the + * given destination array is null. + */ + public static Object getData(DataBuffer src, int srcOffset, + Object dest, int dstOffset, + int length) + { + Object from; + 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"); + } + return dest; + } + + /** + * @param bits the width of a data element measured in bits + * + * @return the smallest data type that can store data elements of + * the given number of bits, without any truncation. + */ + public static int smallestAppropriateTransferType(int bits) + { + if (bits <= 8) + { + return DataBuffer.TYPE_BYTE; + } + else if (bits <= 16) + { + return DataBuffer.TYPE_USHORT; + } + else if (bits <= 32) + { + return DataBuffer.TYPE_INT; + } + else + { + return DataBuffer.TYPE_UNDEFINED; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/ClasspathGraphicsEnvironment.java b/libjava/classpath/gnu/java/awt/ClasspathGraphicsEnvironment.java new file mode 100644 index 000000000..fecefa03e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/ClasspathGraphicsEnvironment.java @@ -0,0 +1,67 @@ +/* ClasspathGraphicsEnvironment.java + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt; + +import java.awt.GraphicsEnvironment; +import java.awt.image.ColorModel; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; + +/** + * This class extends the GraphicsEnvironment API with some Classpath-specific + * methods, in order to provide optimized graphics handling. + * + * @author Francis Kung <fkung@redhat.com> + */ +public abstract class ClasspathGraphicsEnvironment + extends GraphicsEnvironment +{ + /** + * Returns an appropriate Raster that can efficiently back a + * BufferedImage with the given ColorModel and SampleModel. + * + * @param cm The color model. + * @param sm The samepl model. + * @return An appropriate WritableRaster, or null if acceleration/optimization + * is not available for the given colour model / sample model. + */ + public WritableRaster createRaster(ColorModel cm, SampleModel sm) + { + return null; + } +} diff --git a/libjava/classpath/gnu/java/awt/ClasspathToolkit.java b/libjava/classpath/gnu/java/awt/ClasspathToolkit.java new file mode 100644 index 000000000..99c186aaa --- /dev/null +++ b/libjava/classpath/gnu/java/awt/ClasspathToolkit.java @@ -0,0 +1,231 @@ +/* ClasspathToolkit.java -- Abstract superclass for Classpath toolkits. + Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt; + +import gnu.java.awt.peer.ClasspathDesktopPeer; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.peer.EmbeddedWindowPeer; +import gnu.java.security.action.SetAccessibleAction; + +import java.awt.AWTException; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.peer.DesktopPeer; +import java.awt.peer.RobotPeer; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.util.Map; + +import javax.imageio.spi.IIORegistry; + +/** + * An abstract superclass for Classpath toolkits. + * + * <p>There exist some parts of AWT and Java2D that are specific to + * the underlying platform, but for which the {@link Toolkit} class + * does not provide suitable abstractions. Examples include some + * methods of {@link Font} or {@link GraphicsEnvironment}. Those + * methods use ClasspathToolkit as a central place for obtaining + * platform-specific functionality. + * + * <p>In addition, ClasspathToolkit implements some abstract methods + * of {@link java.awt.Toolkit} that are not really platform-specific, + * such as the maintenance of a cache of loaded images. + * + * <p><b>Thread Safety:</b> The methods of this class may safely be + * called without external synchronization. This also hold for any + * inherited {@link Toolkit} methods. Subclasses are responsible for + * the necessary synchronization. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class ClasspathToolkit + extends Toolkit +{ + /** + * Returns a shared instance of the local, platform-specific + * graphics environment. + * + * <p>This method is specific to GNU Classpath. It gets called by + * the Classpath implementation of {@link + * GraphicsEnvironment.getLocalGraphcisEnvironment()}. + */ + public abstract GraphicsEnvironment getLocalGraphicsEnvironment(); + + /** + * Acquires an appropriate {@link ClasspathFontPeer}, for use in + * classpath's implementation of {@link java.awt.Font}. + * + * @param name The logical name of the font. This may be either a face + * name or a logical font name, or may even be null. A default + * implementation of name decoding is provided in + * {@link ClasspathFontPeer}, but may be overridden in other toolkits. + * + * @param attrs Any extra {@link java.awt.font.TextAttribute} attributes + * this font peer should have, such as size, weight, family name, or + * transformation. + */ + public abstract ClasspathFontPeer getClasspathFontPeer (String name, + Map<?,?> attrs); + + /** + * Creates a {@link Font}, in a platform-specific manner. + * + * The default implementation simply constructs a {@link Font}, but some + * toolkits may wish to override this, to return {@link Font} subclasses + * which implement {@link java.awt.font.OpenType} or + * {@link java.awt.font.MultipleMaster}. + */ + public Font getFont (String name, Map attrs) + { + Font f = null; + + // Circumvent the package-privateness of the + // java.awt.Font.Font(String,Map) constructor. + try + { + Constructor fontConstructor = Font.class.getDeclaredConstructor + (new Class[] { String.class, Map.class }); + AccessController.doPrivileged(new SetAccessibleAction(fontConstructor)); + f = (Font) fontConstructor.newInstance(new Object[] { name, attrs }); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch (InstantiationException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + return f; + } + + /** + * Creates a font, reading the glyph definitions from a stream. + * + * <p>This method provides the platform-specific implementation for + * the static factory method {@link Font#createFont(int, + * java.io.InputStream)}. + * + * @param format the format of the font data, such as {@link + * Font#TRUETYPE_FONT}. An implementation may ignore this argument + * if it is able to automatically recognize the font format from the + * provided data. + * + * @param stream an input stream from where the font data is read + * in. The stream will be advanced to the position after the font + * data, but not closed. + * + * @throws IllegalArgumentException if <code>format</code> is + * not supported. + * + * @throws FontFormatException if <code>stream</code> does not + * contain data in the expected format, or if required tables are + * missing from a font. + * + * @throws IOException if a problem occurs while reading in the + * contents of <code>stream</code>. + */ + public abstract Font createFont(int format, InputStream stream); + + /** + * Creates a RobotPeer on a given GraphicsDevice. + */ + public abstract RobotPeer createRobot (GraphicsDevice screen) + throws AWTException; + + /** + * Creates an embedded window peer, and associates it with an + * EmbeddedWindow object. + * + * @param w The embedded window with which to associate a peer. + */ + public abstract EmbeddedWindowPeer createEmbeddedWindow (EmbeddedWindow w); + + /** + * Used to register ImageIO SPIs provided by the toolkit. + * + * Our default implementation does nothing. + */ + public void registerImageIOSpis(IIORegistry reg) + { + } + + /** + * Returns the number of mouse buttons. + * (used by java.awt.MouseInfo). + * + * This dummy implementation returns -1 (no mouse). + * toolkit implementors should overload this method if possible. + * @since 1.5 + */ + public int getMouseNumberOfButtons() + { + return -1; + } + + /* (non-Javadoc) + * @see java.awt.Toolkit#createDesktopPeer(java.awt.Desktop) + */ + protected DesktopPeer createDesktopPeer(Desktop target) + throws HeadlessException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + + return ClasspathDesktopPeer.getDesktop(); + } + +} diff --git a/libjava/classpath/gnu/java/awt/ComponentDataBlitOp.java b/libjava/classpath/gnu/java/awt/ComponentDataBlitOp.java new file mode 100644 index 000000000..becf54167 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/ComponentDataBlitOp.java @@ -0,0 +1,156 @@ +/* Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.RasterOp; +import java.awt.image.WritableRaster; + +/** + * This raster copy operation assumes that both source and destination + * sample models are tightly pixel packed and contain the same number + * of bands. + * + * @throws java.lang.ClassCastException if the sample models of the + * rasters are not of type ComponentSampleModel. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class ComponentDataBlitOp implements RasterOp +{ + public static final ComponentDataBlitOp INSTANCE = new ComponentDataBlitOp(); + + public WritableRaster filter(Raster src, WritableRaster dest) + { + if (dest == null) + dest = createCompatibleDestRaster(src); + + DataBuffer srcDB = src.getDataBuffer(); + DataBuffer destDB = dest.getDataBuffer(); + + ComponentSampleModel srcSM = (ComponentSampleModel) src.getSampleModel(); + ComponentSampleModel destSM = (ComponentSampleModel) dest.getSampleModel(); + + + // Calculate offset to data in the underlying arrays: + + int srcScanlineStride = srcSM.getScanlineStride(); + int destScanlineStride = destSM.getScanlineStride(); + int srcX = src.getMinX() - src.getSampleModelTranslateX(); + int srcY = src.getMinY() - src.getSampleModelTranslateY(); + int destX = dest.getMinX() - dest.getSampleModelTranslateX(); + int destY = dest.getMinY() - dest.getSampleModelTranslateY(); + + int numBands = srcSM.getNumBands(); + + /* We can't use getOffset(x, y) from the sample model since we + don't want the band offset added in. */ + + int srcOffset = + numBands*srcX + srcScanlineStride*srcY + // from sample model + srcDB.getOffset(); // from data buffer + + int destOffset = + numBands*destX + destScanlineStride*destY + // from sample model + destDB.getOffset(); // from data buffer + + // Determine how much, and how many times to blit. + + int rowSize = src.getWidth()*numBands; + int h = src.getHeight(); + + if ((rowSize == srcScanlineStride) && + (rowSize == destScanlineStride)) + { + // collapse scan line blits to one large blit. + rowSize *= h; + h = 1; + } + + + // Do blitting + + Object srcArray = Buffers.getData(srcDB); + Object destArray = Buffers.getData(destDB); + + for (int yd = 0; yd<h; yd++) + { + System.arraycopy(srcArray, srcOffset, + destArray, destOffset, + rowSize); + srcOffset += srcScanlineStride; + destOffset += destScanlineStride; + } + + + return dest; + } + + public Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + public WritableRaster createCompatibleDestRaster(Raster src) { + + /* FIXME: Maybe we should explicitly create a raster with a + tightly pixel packed sample model, rather than assuming + that the createCompatibleWritableRaster() method in Raster + will create one. */ + + return src.createCompatibleWritableRaster(); + } + + public Point2D getPoint2D(Point2D srcPoint, Point2D destPoint) + { + if (destPoint == null) + return (Point2D) srcPoint.clone(); + + destPoint.setLocation(srcPoint); + return destPoint; + } + + public RenderingHints getRenderingHints() + { + throw new UnsupportedOperationException("not implemented"); + } +} diff --git a/libjava/classpath/gnu/java/awt/ComponentReshapeEvent.java b/libjava/classpath/gnu/java/awt/ComponentReshapeEvent.java new file mode 100644 index 000000000..8f15c8519 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/ComponentReshapeEvent.java @@ -0,0 +1,85 @@ +/* WindowResizeEvent.java -- Used to synchronize the AWT and peer sizes + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt; + +import java.awt.AWTEvent; +import java.awt.Component; + +/** + * This is used to update the AWT's knowledge about a Window's size when + * the user changes the window bounds. + * + * This event is _not_ posted to the eventqueue, but rather dispatched directly + * via Window.dispatchEvent(). It is the cleanest way we could find to update + * the AWT's knowledge of the window size. Small testprograms showed the + * following: + * - Component.reshape() and its derivatives are _not_ called. This makes sense + * as it could end up in loops,because this calls back into the peers. + * - Intercepting event dispatching for any events in + * EventQueue.dispatchEvent() showed that the size is still updated. So it + * is not done via an event dispatched over the eventqueue. + * + * Possible other candidates for implementation would have been: + * - Call a (private) callback method in Window/Component from the native + * side. + * - Call a (private) callback method in Window/Component via reflection. + * + * Both is uglier than sending this event directly. Note however that this + * is impossible to test, as Component.dispatchEvent() is final and can't be + * intercepted from outside code. But this impossibility to test the issue from + * outside code also means that this shouldn't raise any compatibility issues. + */ +public class ComponentReshapeEvent + extends AWTEvent +{ + + public int x; + public int y; + public int width; + public int height; + + public ComponentReshapeEvent(Component c, int x, int y, int width, int height) + { + super(c, 1999); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} diff --git a/libjava/classpath/gnu/java/awt/EmbeddedWindow.java b/libjava/classpath/gnu/java/awt/EmbeddedWindow.java new file mode 100644 index 000000000..6734a7b1b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/EmbeddedWindow.java @@ -0,0 +1,138 @@ +/* EmbeddedWindow.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt; + +import gnu.java.awt.peer.EmbeddedWindowPeer; +import gnu.java.security.action.SetAccessibleAction; + +import java.awt.Component; +import java.awt.Frame; +import java.lang.reflect.Field; +import java.security.AccessController; + +/** + * Represents an AWT window that can be embedded into another + * application. + * + * @author Michael Koch (konqueror@gmx.de) + */ +public class EmbeddedWindow extends Frame +{ + private long handle; + + /** + * Creates a window to be embedded into another application. The + * window will only be embedded after its setHandle method has been + * called. + */ + public EmbeddedWindow () + { + super(); + this.handle = 0; + } + + /** + * Creates a window to be embedded into another application. + * + * @param handle the native handle to the screen area where the AWT + * window should be embedded + */ + public EmbeddedWindow (long handle) + { + super(); + this.handle = handle; + } + + /** + * Creates the native peer for this embedded window. + */ + public void addNotify() + { + // Assume we're using ClasspathToolkit + ClasspathToolkit tk = (ClasspathToolkit) getToolkit(); + + // Circumvent the package-privateness of the AWT internal + // java.awt.Component.peer member variable. + try + { + Field peerField = Component.class.getDeclaredField("peer"); + AccessController.doPrivileged(new SetAccessibleAction(peerField)); + peerField.set(this, tk.createEmbeddedWindow (this)); + } + catch (IllegalAccessException e) + { + throw new AssertionError (e); + } + catch (NoSuchFieldException e) + { + throw new AssertionError (e); + } + + super.addNotify(); + } + + /** + * If the native peer for this embedded window has been created, + * then setHandle will embed the window. If not, setHandle tells + * us where to embed ourselves when our peer is created. + * + * @param handle the native handle to the screen area where the AWT + * window should be embedded + */ + public void setHandle(long handle) + { + if (this.handle != 0) + throw new RuntimeException ("EmbeddedWindow is already embedded"); + + this.handle = handle; + if (getPeer() != null) + ((EmbeddedWindowPeer) getPeer()).embed (this.handle); + } + + /** + * Gets the native handle of the screen area where the window will + * be embedded. + * + * @return The native handle that was passed to the constructor. + */ + public long getHandle() + { + return handle; + } +} diff --git a/libjava/classpath/gnu/java/awt/EventModifier.java b/libjava/classpath/gnu/java/awt/EventModifier.java new file mode 100644 index 000000000..565fcbc32 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/EventModifier.java @@ -0,0 +1,107 @@ +/* EventModifier.java -- tool for converting modifier bits to 1.4 syle + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt; + +import java.awt.event.InputEvent; + +public class EventModifier +{ + /** The mask for old events. */ + public static final int OLD_MASK = 0x3f; + + /** The mask for new events. */ + public static final int NEW_MASK = 0x3fc0; + + /** + * Non-instantiable. + */ + private EventModifier() + { + throw new InternalError(); + } + + /** + * Converts the old style modifiers (0x3f) to the new style (0xffffffc0). + * + * @param mod the modifiers to convert + * @return the adjusted modifiers + */ + public static int extend(int mod) + { + // Favor what we hope will be the common case. + if ((mod & OLD_MASK) == 0) + return mod; + if ((mod & InputEvent.SHIFT_MASK) != 0) + mod |= InputEvent.SHIFT_DOWN_MASK; + if ((mod & InputEvent.CTRL_MASK) != 0) + mod |= InputEvent.CTRL_DOWN_MASK; + if ((mod & InputEvent.META_MASK) != 0) + mod |= InputEvent.META_DOWN_MASK; + if ((mod & InputEvent.ALT_MASK) != 0) + mod |= InputEvent.ALT_DOWN_MASK; + if ((mod & InputEvent.BUTTON1_MASK) != 0) + mod |= InputEvent.BUTTON1_DOWN_MASK; + if ((mod & InputEvent.ALT_GRAPH_MASK) != 0) + mod |= InputEvent.ALT_GRAPH_DOWN_MASK; + return mod & ~OLD_MASK; + } + + /** + * Converts the new style modifiers (0xffffffc0) to the old style (0x3f). + * + * @param mod the modifiers to convert + * @return the adjusted modifiers + */ + public static int revert(int mod) + { + if ((mod & InputEvent.SHIFT_DOWN_MASK) != 0) + mod |= InputEvent.SHIFT_MASK; + if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) + mod |= InputEvent.CTRL_MASK; + if ((mod & InputEvent.META_DOWN_MASK) != 0) + mod |= InputEvent.META_MASK; + if ((mod & InputEvent.ALT_DOWN_MASK) != 0) + mod |= InputEvent.ALT_MASK; + if ((mod & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) + mod |= InputEvent.ALT_GRAPH_MASK; + if ((mod & InputEvent.BUTTON1_DOWN_MASK) != 0) + mod |= InputEvent.BUTTON1_MASK; + return mod & OLD_MASK; + } +} // class EventModifier diff --git a/libjava/classpath/gnu/java/awt/GradientPaintContext.java b/libjava/classpath/gnu/java/awt/GradientPaintContext.java new file mode 100644 index 000000000..f33926c12 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/GradientPaintContext.java @@ -0,0 +1,164 @@ +/* GradientPaintContext.java -- + Copyright (C) 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; + +import java.awt.geom.Point2D; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.awt.PaintContext; +import java.awt.Color; + +/** + * A {@link PaintContext} used by the {@link GradientPaint} class. + */ +public class GradientPaintContext implements PaintContext +{ + + // This implementation follows the technique described in + // "Java(tm) 2D Graphics" by Jonathan Knudsen (O'Reilly 1999). + + /** The x-coordinate of the anchor point for color 1. */ + private final float x1; + + /** The y-coordinate of the anchor point for color 1. */ + private final float y1; + + /** Color 1. */ + private final Color c1; + + /** The x-coordinate of the anchor point for color 2. */ + private final float x2; + + /** The y-coordinate of the anchor point for color 2. */ + private final float y2; + + /** Color 2. */ + private final Color c2; + + /** A flag indicating whether the gradient is cyclic or acyclic. */ + private final boolean cyclic; + + /** The length of the gradient line - computed from the two anchor points. */ + private final double length; + + /** + * Creates a new instance. + * + * @param x1 the x-coordinate for the anchor point for color 1. + * @param y1 the y-coordinate for the anchor point for color 1. + * @param c1 color 1. + * @param x2 the x-coordinate for the anchor point for color 2. + * @param y2 the y-coordinate for the anchor point for color 2. + * @param c2 color 2. + * @param cyclic a flag that determines whether the gradient is cyclic + * or acyclic. + */ + public GradientPaintContext(float x1, float y1, Color c1, + float x2, float y2, Color c2, boolean cyclic) + { + this.x1 = x1; + this.y1 = y1; + this.c1 = c1; + this.x2 = x2; + this.y2 = y2; + this.c2 = c2; + this.cyclic = cyclic; + length = Point2D.distance(x1, y1, x2, y2); + } + + /** + * Return the color model of this context. It may be different from the + * hint specified during createContext, as not all contexts can generate + * color patterns in an arbitrary model. + * + * @return the context color model + */ + public ColorModel getColorModel() + { + return ColorModel.getRGBdefault(); + } + + /** + * Return a raster containing the colors for the graphics operation. + * + * @param x the x-coordinate, in device space + * @param y the y-coordinate, in device space + * @param w the width, in device space + * @param h the height, in device space + * @return a raster for the given area and color + */ + public Raster getRaster(int x, int y, int w, int h) { + ColorModel cm = getColorModel(); + WritableRaster raster = cm.createCompatibleWritableRaster(w, h); + int[] data = new int[w * h * 4]; + double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + for (int r = 0; r < h; r++) { + for (int c = 0; c < w; c++) { + double u = 0.0; + if (pd2 != 0) + u = (((x + c) - x1) * (x2 - x1) + ((y + r) - y1) * (y2 - y1)) + / Math.sqrt(pd2); + double ratio = u / length; + if (cyclic) + ratio = Math.abs(ratio - Math.floor((ratio + 1.0) / 2.0) * 2.0); + else + ratio = Math.max(0.0, Math.min(1.0, ratio)); + int base = (r * w + c) * 4; + data[base] = (int) (c1.getRed() + ratio * (c2.getRed() - c1.getRed())); + data[base + 1] + = (int) (c1.getGreen() + ratio * (c2.getGreen() - c1.getGreen())); + data[base + 2] + = (int) (c1.getBlue() + ratio * (c2.getBlue() - c1.getBlue())); + data[base + 3] + = (int) (c1.getAlpha() + ratio * (c2.getAlpha() - c1.getAlpha())); + } + } + raster.setPixels(0, 0, w, h, data); + return raster; + } + + /** + * Release the resources allocated for the paint (none in this + * implementation). + */ + public void dispose() { + // nothing to do + } + +} diff --git a/libjava/classpath/gnu/java/awt/LowPriorityEvent.java b/libjava/classpath/gnu/java/awt/LowPriorityEvent.java new file mode 100644 index 000000000..c1558f6ff --- /dev/null +++ b/libjava/classpath/gnu/java/awt/LowPriorityEvent.java @@ -0,0 +1,48 @@ +/* LowPriorityEvent.java -- Marks events with low priority + 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; + +/** + * A marker interface that marks events with low priority. LowPriority events + * are dispatched _after_ other (normal priority) events by the EventQueue. + */ +public interface LowPriorityEvent +{ + // Empty marker interface. +} diff --git a/libjava/classpath/gnu/java/awt/color/CieXyzConverter.java b/libjava/classpath/gnu/java/awt/color/CieXyzConverter.java new file mode 100644 index 000000000..e1b548e98 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/CieXyzConverter.java @@ -0,0 +1,73 @@ +/* CieXyzConverter.java -- CieXyz conversion class + Copyright (C) 2004 Free Software Foundation + +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.color; + + +/** + * CieXyzConverter - converts to/from a D50-relative CIE XYZ color space. + * + * The sRGB<->CIE XYZ conversions in SrgbConverter are used. + * + * @author Sven de Marothy + */ +public class CieXyzConverter implements ColorSpaceConverter +{ + public float[] toCIEXYZ(float[] in) + { + float[] out = new float[3]; + System.arraycopy(in, 0, out, 0, 3); + return out; + } + + public float[] fromCIEXYZ(float[] in) + { + float[] out = new float[3]; + System.arraycopy(in, 0, out, 0, 3); + return out; + } + + public float[] toRGB(float[] in) + { + return SrgbConverter.XYZtoRGB(in); + } + + public float[] fromRGB(float[] in) + { + return SrgbConverter.RGBtoXYZ(in); + } +} diff --git a/libjava/classpath/gnu/java/awt/color/ClutProfileConverter.java b/libjava/classpath/gnu/java/awt/color/ClutProfileConverter.java new file mode 100644 index 000000000..5229ce804 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/ClutProfileConverter.java @@ -0,0 +1,152 @@ +/* ClutProfileConverter.java -- Conversion routines for CLUT-Based profiles + Copyright (C) 2004 Free Software Foundation + +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.color; + +import java.awt.color.ICC_Profile; + + +/** + * ClutProfileConverter - conversions through a CLUT-based profile + * + * @author Sven de Marothy + */ +public class ClutProfileConverter implements ColorSpaceConverter +{ + private ColorLookUpTable toPCS; + private ColorLookUpTable fromPCS; + private int nChannels; + + public ClutProfileConverter(ICC_Profile profile) + { + nChannels = profile.getNumComponents(); + + // Sun does not specifiy which rendering intent should be used, + // neither does the ICC v2 spec really. + // Try intent 0 + try + { + toPCS = new ColorLookUpTable(profile, ICC_Profile.icSigAToB0Tag); + } + catch (Exception e) + { + toPCS = null; + } + + try + { + fromPCS = new ColorLookUpTable(profile, ICC_Profile.icSigBToA0Tag); + } + catch (Exception e) + { + fromPCS = null; + } + + if (toPCS != null || fromPCS != null) + return; + + // If no intent 0 clut is available, look for a intent 1 clut. + try + { + toPCS = new ColorLookUpTable(profile, ICC_Profile.icSigAToB1Tag); + } + catch (Exception e) + { + toPCS = null; + } + + try + { + fromPCS = new ColorLookUpTable(profile, ICC_Profile.icSigBToA1Tag); + } + catch (Exception e) + { + fromPCS = null; + } + + if (toPCS != null || fromPCS != null) + return; + + // Last shot.. intent 2 CLUT. + try + { + toPCS = new ColorLookUpTable(profile, ICC_Profile.icSigAToB2Tag); + } + catch (Exception e) + { + toPCS = null; + } + + try + { + fromPCS = new ColorLookUpTable(profile, ICC_Profile.icSigBToA2Tag); + } + catch (Exception e) + { + fromPCS = null; + } + + if (toPCS == null && fromPCS == null) + throw new IllegalArgumentException("No CLUTs in profile!"); + } + + public float[] toCIEXYZ(float[] in) + { + if (toPCS != null) + return toPCS.lookup(in); + else + return new float[3]; + } + + public float[] toRGB(float[] in) + { + return SrgbConverter.XYZtoRGB(toCIEXYZ(in)); + } + + public float[] fromCIEXYZ(float[] in) + { + if (fromPCS != null) + return fromPCS.lookup(in); + else + return new float[nChannels]; + } + + public float[] fromRGB(float[] in) + { + return fromCIEXYZ(SrgbConverter.RGBtoXYZ(in)); + } +} diff --git a/libjava/classpath/gnu/java/awt/color/ColorLookUpTable.java b/libjava/classpath/gnu/java/awt/color/ColorLookUpTable.java new file mode 100644 index 000000000..581320c3e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/ColorLookUpTable.java @@ -0,0 +1,429 @@ +/* ColorLookUpTable.java -- ICC v2 CLUT + Copyright (C) 2004 Free Software Foundation + +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.color; + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.nio.ByteBuffer; + + +/** + * ColorLookUpTable handles color lookups through a color lookup table, + * as defined in the ICC specification. + * Both 'mft2' and 'mft1' (8 and 16-bit) type CLUTs are handled. + * + * This will have to be updated later for ICC 4.0.0 + * + * @author Sven de Marothy + */ +public class ColorLookUpTable +{ + /** + * CIE 1931 D50 white point (in Lab coordinates) + */ + private static float[] D50 = { 0.96422f, 1.00f, 0.82521f }; + + /** + * Number of input/output channels + */ + int nIn; + + /** + * Number of input/output channels + */ + int nOut; + int nInTableEntries; // Number of input table entries + int nOutTableEntries; // Number of output table entries + int gridpoints; // Number of gridpoints + int nClut; // This is nOut*(gridpoints**nIn) + double[][] inTable; // 1D input table ([channel][table]) + short[][] outTable; // 1D input table ([channel][table]) + double[] clut; // The color lookup table + float[][] inMatrix; // input matrix (XYZ only) + boolean useMatrix; // Whether to use the matrix or not. + int[] multiplier; + int[] offsets; // Hypercube offsets + boolean inputLab; // Set if the CLUT input CS is Lab + boolean outputLab; // Set if the CLUT output CS is Lab + + /** + * Constructor + * Requires a profile file to get the CLUT from and the tag of the + * CLUT to create. (icSigXToYZTag where X,Y = [A | B], Z = [0,1,2]) + */ + public ColorLookUpTable(ICC_Profile profile, int tag) + { + useMatrix = false; + + switch (tag) + { + case ICC_Profile.icSigAToB0Tag: + case ICC_Profile.icSigAToB1Tag: + case ICC_Profile.icSigAToB2Tag: + if (profile.getColorSpaceType() == ColorSpace.TYPE_XYZ) + useMatrix = true; + inputLab = false; + outputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab); + break; + case ICC_Profile.icSigBToA0Tag: + case ICC_Profile.icSigBToA1Tag: + case ICC_Profile.icSigBToA2Tag: + if (profile.getPCSType() == ColorSpace.TYPE_XYZ) + useMatrix = true; + inputLab = (profile.getPCSType() == ColorSpace.TYPE_Lab); + outputLab = false; + break; + default: + throw new IllegalArgumentException("Not a clut-type tag."); + } + + byte[] data = profile.getData(tag); + if (data == null) + throw new IllegalArgumentException("Unsuitable profile, does not contain a CLUT."); + + // check 'mft' + if (data[0] != 0x6d || data[1] != 0x66 || data[2] != 0x74) + throw new IllegalArgumentException("Unsuitable profile, invalid CLUT data."); + + if (data[3] == 0x32) + readClut16(data); + else if (data[3] == 0x31) + readClut8(data); + else + throw new IllegalArgumentException("Unknown/invalid CLUT type."); + } + + /** + * Loads a 16-bit CLUT into our data structures + */ + private void readClut16(byte[] data) + { + ByteBuffer buf = ByteBuffer.wrap(data); + + nIn = data[8] & (0xFF); + nOut = data[9] & (0xFF); + nInTableEntries = buf.getShort(48); + nOutTableEntries = buf.getShort(50); + gridpoints = data[10] & (0xFF); + + inMatrix = new float[3][3]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f; + + inTable = new double[nIn][nInTableEntries]; + for (int channel = 0; channel < nIn; channel++) + for (int i = 0; i < nInTableEntries; i++) + inTable[channel][i] = (double) ((int) buf.getShort(52 + + (channel * nInTableEntries + + i) * 2) + & (0xFFFF)) / 65536.0; + + nClut = nOut; + multiplier = new int[nIn]; + multiplier[nIn - 1] = nOut; + for (int i = 0; i < nIn; i++) + { + nClut *= gridpoints; + if (i > 0) + multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints; + } + + int clutOffset = 52 + nIn * nInTableEntries * 2; + clut = new double[nClut]; + for (int i = 0; i < nClut; i++) + clut[i] = (double) ((int) buf.getShort(clutOffset + i * 2) & (0xFFFF)) / 65536.0; + + outTable = new short[nOut][nOutTableEntries]; + for (int channel = 0; channel < nOut; channel++) + for (int i = 0; i < nOutTableEntries; i++) + outTable[channel][i] = buf.getShort(clutOffset + + (nClut + + channel * nOutTableEntries + i) * 2); + + // calculate the hypercube corner offsets + offsets = new int[(1 << nIn)]; + offsets[0] = 0; + for (int j = 0; j < nIn; j++) + { + int factor = 1 << j; + for (int i = 0; i < factor; i++) + offsets[factor + i] = offsets[i] + multiplier[j]; + } + } + + /** + * Loads a 8-bit CLUT into our data structures. + */ + private void readClut8(byte[] data) + { + ByteBuffer buf = ByteBuffer.wrap(data); + + nIn = (data[8] & (0xFF)); + nOut = (data[9] & (0xFF)); + nInTableEntries = 256; // always 256 + nOutTableEntries = 256; // always 256 + gridpoints = (data[10] & (0xFF)); + + inMatrix = new float[3][3]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + inMatrix[i][j] = ((float) (buf.getInt(12 + (i * 3 + j) * 4))) / 65536.0f; + + inTable = new double[nIn][nInTableEntries]; + for (int channel = 0; channel < nIn; channel++) + for (int i = 0; i < nInTableEntries; i++) + inTable[channel][i] = (double) ((int) buf.get(48 + + (channel * nInTableEntries + + i)) & (0xFF)) / 255.0; + + nClut = nOut; + multiplier = new int[nIn]; + multiplier[nIn - 1] = nOut; + for (int i = 0; i < nIn; i++) + { + nClut *= gridpoints; + if (i > 0) + multiplier[nIn - i - 1] = multiplier[nIn - i] * gridpoints; + } + + int clutOffset = 48 + nIn * nInTableEntries; + clut = new double[nClut]; + for (int i = 0; i < nClut; i++) + clut[i] = (double) ((int) buf.get(clutOffset + i) & (0xFF)) / 255.0; + + outTable = new short[nOut][nOutTableEntries]; + for (int channel = 0; channel < nOut; channel++) + for (int i = 0; i < nOutTableEntries; i++) + outTable[channel][i] = (short) (buf.get(clutOffset + nClut + + channel * nOutTableEntries + + i) * 257); + + // calculate the hypercube corner offsets + offsets = new int[(1 << nIn)]; + offsets[0] = 0; + for (int j = 0; j < nIn; j++) + { + int factor = 1 << j; + for (int i = 0; i < factor; i++) + offsets[factor + i] = offsets[i] + multiplier[j]; + } + } + + /** + * Performs a lookup through the Color LookUp Table. + * If the CLUT tag type is AtoB the conversion will be from the device + * color space to the PCS, BtoA type goes in the opposite direction. + * + * For convenience, the PCS values for input or output will always be + * CIE XYZ (D50), if the actual PCS is Lab, the values will be converted. + * + * N-dimensional linear interpolation is used. + */ + float[] lookup(float[] in) + { + float[] in2 = new float[in.length]; + if (useMatrix) + { + for (int i = 0; i < 3; i++) + in2[i] = in[0] * inMatrix[i][0] + in[1] * inMatrix[i][1] + + in[2] * inMatrix[i][2]; + } + else if (inputLab) + in2 = XYZtoLab(in); + else + System.arraycopy(in, 0, in2, 0, in.length); + + // input table + for (int i = 0; i < nIn; i++) + { + int index = (int) Math.floor(in2[i] * (double) (nInTableEntries - 1)); // floor in + + // clip values. + if (index >= nInTableEntries - 1) + in2[i] = (float) inTable[i][nInTableEntries - 1]; + else if (index < 0) + in2[i] = (float) inTable[i][0]; + else + { + // linear interpolation + double alpha = in2[i] * ((double) nInTableEntries - 1.0) - index; + in2[i] = (float) (inTable[i][index] * (1 - alpha) + + inTable[i][index + 1] * alpha); + } + } + + // CLUT lookup + double[] output2 = new double[nOut]; + double[] weights = new double[(1 << nIn)]; + double[] clutalpha = new double[nIn]; // interpolation values + int offset = 0; // = gp + for (int i = 0; i < nIn; i++) + { + int index = (int) Math.floor(in2[i] * ((double) gridpoints - 1.0)); + double alpha = in2[i] * ((double) gridpoints - 1.0) - (double) index; + + // clip values. + if (index >= gridpoints - 1) + { + index = gridpoints - 1; + alpha = 1.0; + } + else if (index < 0) + index = 0; + clutalpha[i] = alpha; + offset += index * multiplier[i]; + } + + // Calculate interpolation weights + weights[0] = 1.0; + for (int j = 0; j < nIn; j++) + { + int factor = 1 << j; + for (int i = 0; i < factor; i++) + { + weights[factor + i] = weights[i] * clutalpha[j]; + weights[i] *= (1.0 - clutalpha[j]); + } + } + + for (int i = 0; i < nOut; i++) + output2[i] = weights[0] * clut[offset + i]; + + for (int i = 1; i < (1 << nIn); i++) + { + int offset2 = offset + offsets[i]; + for (int f = 0; f < nOut; f++) + output2[f] += weights[i] * clut[offset2 + f]; + } + + // output table + float[] output = new float[nOut]; + for (int i = 0; i < nOut; i++) + { + int index = (int) Math.floor(output2[i] * ((double) nOutTableEntries + - 1.0)); + + // clip values. + if (index >= nOutTableEntries - 1) + output[i] = outTable[i][nOutTableEntries - 1]; + else if (index < 0) + output[i] = outTable[i][0]; + else + { + // linear interpolation + double a = output2[i] * ((double) nOutTableEntries - 1.0) + - (double) index; + output[i] = (float) ((double) ((int) outTable[i][index] & (0xFFFF)) * (1 + - a) + + (double) ((int) outTable[i][index + 1] & (0xFFFF)) * a) / 65536f; + } + } + + if (outputLab) + return LabtoXYZ(output); + return output; + } + + /** + * Converts CIE Lab coordinates to (D50) XYZ ones. + */ + private float[] LabtoXYZ(float[] in) + { + // Convert from byte-packed format to a + // more convenient one (actual Lab values) + // (See ICC spec for details) + // factor is 100 * 65536 / 65280 + in[0] = (float) (100.392156862745 * in[0]); + in[1] = (in[1] * 256.0f) - 128.0f; + in[2] = (in[2] * 256.0f) - 128.0f; + + float[] out = new float[3]; + + out[1] = (in[0] + 16.0f) / 116.0f; + out[0] = in[1] / 500.0f + out[1]; + out[2] = out[1] - in[2] / 200.0f; + + for (int i = 0; i < 3; i++) + { + double exp = out[i] * out[i] * out[i]; + if (exp <= 0.008856) + out[i] = (out[i] - 16.0f / 116.0f) / 7.787f; + else + out[i] = (float) exp; + out[i] = D50[i] * out[i]; + } + return out; + } + + /** + * Converts CIE XYZ coordinates to Lab ones. + */ + private float[] XYZtoLab(float[] in) + { + float[] temp = new float[3]; + + for (int i = 0; i < 3; i++) + { + temp[i] = in[i] / D50[i]; + + if (temp[i] <= 0.008856f) + temp[i] = (7.7870689f * temp[i]) + (16f / 116.0f); + else + temp[i] = (float) Math.exp((1.0 / 3.0) * Math.log(temp[i])); + } + + float[] out = new float[3]; + out[0] = (116.0f * temp[1]) - 16f; + out[1] = 500.0f * (temp[0] - temp[1]); + out[2] = 200.0f * (temp[1] - temp[2]); + + // Normalize to packed format + out[0] = (float) (out[0] / 100.392156862745); + out[1] = (out[1] + 128f) / 256f; + out[2] = (out[2] + 128f) / 256f; + for (int i = 0; i < 3; i++) + { + if (out[i] < 0f) + out[i] = 0f; + if (out[i] > 1f) + out[i] = 1f; + } + return out; + } +} diff --git a/libjava/classpath/gnu/java/awt/color/ColorSpaceConverter.java b/libjava/classpath/gnu/java/awt/color/ColorSpaceConverter.java new file mode 100644 index 000000000..63ba08a4f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/ColorSpaceConverter.java @@ -0,0 +1,69 @@ +/* ColorSpaceConverter.java -- an interface for colorspace conversion + Copyright (C) 2004 Free Software Foundation + +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.color; + + +/** + * ColorSpaceConverter - used by java.awt.color.ICC_ColorSpace + * + * Color space conversion can occur in several ways: + * + * -Directly (for the built in spaces sRGB, linear RGB, gray, CIE XYZ and PYCC + * -ICC_ProfileRGB works through TRC curves and a matrix + * -ICC_ProfileGray works through a single TRC + * -Everything else is done through Color lookup tables. + * + * The different conversion methods are implemented through + * an interface. The built-in colorspaces are implemented directly + * with the relevant conversion equations. + * + * In this way, we hopefully will always use the fastest and most + * accurate method available. + * + * @author Sven de Marothy + */ +public interface ColorSpaceConverter +{ + float[] toCIEXYZ(float[] in); + + float[] fromCIEXYZ(float[] in); + + float[] toRGB(float[] in); + + float[] fromRGB(float[] in); +} diff --git a/libjava/classpath/gnu/java/awt/color/GrayProfileConverter.java b/libjava/classpath/gnu/java/awt/color/GrayProfileConverter.java new file mode 100644 index 000000000..3f95b07d7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/GrayProfileConverter.java @@ -0,0 +1,137 @@ +/* GrayProfileConverter.java -- Gray profile conversion class + Copyright (C) 2004 Free Software Foundation + +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.color; + +import java.awt.color.ICC_Profile; +import java.awt.color.ICC_ProfileGray; +import java.awt.color.ProfileDataException; + +/** + * GrayProfileConverter - converts Grayscale profiles (ICC_ProfileGray) + * + * This type of profile contains a single tone reproduction curve (TRC). + * Conversion consists of simple TRC lookup. + * + * This implementation is very lazy and does everything applying the TRC and + * utilizing the built-in linear grayscale color space. + * + * @author Sven de Marothy + */ +public class GrayProfileConverter implements ColorSpaceConverter +{ + private GrayScaleConverter gc; + private ToneReproductionCurve trc; + private ColorLookUpTable toPCS; + private ColorLookUpTable fromPCS; + + /** + * Constructs the converter described by an ICC_ProfileGray object + */ + public GrayProfileConverter(ICC_ProfileGray profile) + { + try + { + trc = new ToneReproductionCurve(profile.getGamma()); + } + catch (ProfileDataException e) + { + trc = new ToneReproductionCurve(profile.getTRC()); + } + + // linear grayscale converter + gc = new GrayScaleConverter(); + + // If a CLUT is available, it should be used, and the TRCs ignored. + // Note: A valid profile may only have CLUTs in one direction, and + // TRC:s without useful info, making reverse-transforms impossible. + // In this case the TRC will be used for the reverse-transform with + // unpredictable results. This is in line with the Java specification, + try + { + toPCS = new ColorLookUpTable(profile, ICC_Profile.icSigAToB0Tag); + } + catch (Exception e) + { + toPCS = null; + } + + try + { + fromPCS = new ColorLookUpTable(profile, ICC_Profile.icSigBToA0Tag); + } + catch (Exception e) + { + fromPCS = null; + } + } + + public float[] toCIEXYZ(float[] in) + { + if (toPCS != null) + return toPCS.lookup(in); + float[] gray = new float[1]; + gray[0] = trc.lookup(in[0]); + return gc.toCIEXYZ(gray); + } + + public float[] toRGB(float[] in) + { + float[] gray = new float[1]; + gray[0] = trc.lookup(in[0]); + return gc.toRGB(gray); + } + + public float[] fromRGB(float[] in) + { + // get linear grayscale value + float[] gray = gc.fromRGB(in); + gray[0] = trc.reverseLookup(gray[0]); + return gray; + } + + public float[] fromCIEXYZ(float[] in) + { + if (fromPCS != null) + return fromPCS.lookup(in); + + float[] gray = gc.fromCIEXYZ(in); + gray[0] = trc.reverseLookup(gray[0]); + return gray; + } +} diff --git a/libjava/classpath/gnu/java/awt/color/GrayScaleConverter.java b/libjava/classpath/gnu/java/awt/color/GrayScaleConverter.java new file mode 100644 index 000000000..beea9d287 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/GrayScaleConverter.java @@ -0,0 +1,110 @@ +/* GrayScaleConverter.java -- Linear grayscale conversion class + Copyright (C) 2004 Free Software Foundation + +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.color; + + +/** + * Linear Grayscale converter + * + * @author Sven de Marothy + */ +public class GrayScaleConverter implements ColorSpaceConverter +{ + // intensity factors (ITU Rec. BT.709) + double[] coeff = { 0.2125f, 0.7154f, 0.0721f }; + + /** + * CIE 1931 D50 white point (in Lab coordinates) + */ + private static float[] D50 = { 0.96422f, 1.00f, 0.82521f }; + + public float[] toCIEXYZ(float[] in) + { + float g = in[0]; + if (g < 0) + g = 1 + g; + float[] out = { g * D50[0], g * D50[1], g * D50[2] }; // White spot + return out; + } + + public float[] toRGB(float[] in) + { + float[] out = new float[3]; + if (in[0] <= 0.00304f) + out[0] = in[0] * 12.92f; + else + out[0] = 1.055f * ((float) Math.exp((1 / 2.4) * Math.log(in[0]))) + - 0.055f; + out[1] = out[2] = out[0]; + return out; + } + + public float[] fromCIEXYZ(float[] in) + { + float[] temp = new float[3]; + temp[0] = 3.1338f * in[0] - 1.6171f * in[1] - 0.4907f * in[2]; + temp[1] = -0.9785f * in[0] + 1.9160f * in[1] + 0.0334f * in[2]; + temp[2] = 0.0720f * in[0] - 0.2290f * in[1] + 1.4056f * in[2]; + float[] out = new float[1]; + for (int i = 0; i < 3; i++) + out[0] = (float) (temp[i] * coeff[i]); + return out; + } + + public float[] fromRGB(float[] in) + { + float[] out = new float[1]; + + // Convert non-linear RGB coordinates to linear ones, + // numbers from the w3 spec. + out[0] = 0; + for (int i = 0; i < 3; i++) + { + float n = in[i]; + if (n < 0) + n = 0f; + if (n > 1) + n = 1f; + if (n <= 0.03928f) + out[0] += (float) (coeff[i] * n / 12.92); + else + out[0] += (float) (coeff[i] * Math.exp(2.4 * Math.log((n + 0.055) / 1.055))); + } + return out; + } +} diff --git a/libjava/classpath/gnu/java/awt/color/LinearRGBConverter.java b/libjava/classpath/gnu/java/awt/color/LinearRGBConverter.java new file mode 100644 index 000000000..1eaf6479e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/LinearRGBConverter.java @@ -0,0 +1,152 @@ +/* LinearRGBConverter.java -- conversion to a linear RGB color space + Copyright (C) 2004 Free Software Foundation + +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.color; + + +/** + * LinearRGBConverter - conversion routines for a linear sRGB colorspace + * sRGB is a standard for RGB colorspaces, adopted by the w3c. + * + * The specification is available at: + * http://www.w3.org/Graphics/Color/sRGB.html + * + * @author Sven de Marothy + */ +public class LinearRGBConverter implements ColorSpaceConverter +{ + /** + * linear RGB --> sRGB + * Use the inverse gamma curve + */ + public float[] toRGB(float[] in) + { + float[] out = new float[3]; + for (int i = 0; i < 3; i++) + { + float n = in[i]; + if (n < 0) + n = 0f; + if (n > 1) + n = 1f; + if (n <= 0.00304f) + out[i] = in[0] * 12.92f; + else + out[i] = 1.055f * ((float) Math.exp((1 / 2.4) * Math.log(n))) + - 0.055f; + } + return out; + } + + /** + * sRGB --> linear RGB + * Use the gamma curve (gamma=2.4 in sRGB) + */ + public float[] fromRGB(float[] in) + { + float[] out = new float[3]; + + // Convert non-linear RGB coordinates to linear ones, + // numbers from the w3 spec. + for (int i = 0; i < 3; i++) + { + float n = in[i]; + if (n < 0) + n = 0f; + if (n > 1) + n = 1f; + if (n <= 0.03928f) + out[i] = (float) (n / 12.92); + else + out[i] = (float) (Math.exp(2.4 * Math.log((n + 0.055) / 1.055))); + } + return out; + } + + /** + * Linear RGB --> CIE XYZ (D50 relative) + * This is a simple matrix transform, the matrix (relative D65) + * is given in the sRGB spec. This has been combined with a + * linear Bradford transform for the D65-->D50 mapping, resulting + * in a single matrix which does the whole thing. + * + */ + public float[] fromCIEXYZ(float[] in) + { + /* + * Note: The numbers which were used to calculate this only had four + * digits of accuracy. So don't be fooled by the number of digits here. + * If someone has more accurate source, feel free to update this. + */ + float[] out = new float[3]; + out[0] = (float) (3.13383065124221 * in[0] - 1.61711949411313 * in[1] + - 0.49071914111101 * in[2]); + out[1] = (float) (-0.97847026691142 * in[0] + 1.91597856031996 * in[1] + + 0.03340430640699 * in[2]); + out[2] = (float) (0.07203679486279 * in[0] - 0.22903073553113 * in[1] + + 1.40557835776234 * in[2]); + if (out[0] < 0) + out[0] = 0f; + if (out[1] < 0) + out[1] = 0f; + if (out[2] < 0) + out[2] = 0f; + if (out[0] > 1.0f) + out[0] = 1.0f; + if (out[1] > 1.0f) + out[1] = 1.0f; + if (out[2] > 1.0f) + out[2] = 1.0f; + return out; + } + + /** + * Linear RGB --> CIE XYZ (D50 relative) + * Uses the inverse of the above matrix. + */ + public float[] toCIEXYZ(float[] in) + { + float[] out = new float[3]; + out[0] = (float) (0.43606375022190 * in[0] + 0.38514960146481 * in[1] + + 0.14308641888799 * in[2]); + out[1] = (float) (0.22245089403542 * in[0] + 0.71692584775182 * in[1] + + 0.06062451125578 * in[2]); + out[2] = (float) (0.01389851860679 * in[0] + 0.09707969011198 * in[1] + + 0.71399604572506 * in[2]); + return out; + } +} diff --git a/libjava/classpath/gnu/java/awt/color/ProfileHeader.java b/libjava/classpath/gnu/java/awt/color/ProfileHeader.java new file mode 100644 index 000000000..2a6402dbc --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/ProfileHeader.java @@ -0,0 +1,398 @@ +/* ProfileHeader.java -- Encapsules ICC Profile header data + Copyright (C) 2004 Free Software Foundation + +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.color; + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.nio.ByteBuffer; + + +/** + * Header, abstracts and validates the header data. + * + * @author Sven de Marothy + */ +public class ProfileHeader +{ + /** + * Magic identifier (ASCII 'acsp') + */ + private static final int icMagicNumber = 0x61637370; + + /** + * Mapping from ICC Profile signatures to ColorSpace types + */ + private static final int[] csTypeMap = + { + ICC_Profile.icSigXYZData, + ColorSpace.TYPE_XYZ, + ICC_Profile.icSigLabData, + ColorSpace.TYPE_Lab, + ICC_Profile.icSigLuvData, + ColorSpace.TYPE_Luv, + ICC_Profile.icSigYCbCrData, + ColorSpace.TYPE_YCbCr, + ICC_Profile.icSigYxyData, + ColorSpace.TYPE_Yxy, + ICC_Profile.icSigRgbData, + ColorSpace.TYPE_RGB, + ICC_Profile.icSigGrayData, + ColorSpace.TYPE_GRAY, + ICC_Profile.icSigHsvData, + ColorSpace.TYPE_HSV, + ICC_Profile.icSigHlsData, + ColorSpace.TYPE_HLS, + ICC_Profile.icSigCmykData, + ColorSpace.TYPE_CMYK, + ICC_Profile.icSigCmyData, + ColorSpace.TYPE_CMY, + ICC_Profile.icSigSpace2CLR, + ColorSpace.TYPE_2CLR, + ICC_Profile.icSigSpace3CLR, + ColorSpace.TYPE_3CLR, + ICC_Profile.icSigSpace4CLR, + ColorSpace.TYPE_4CLR, + ICC_Profile.icSigSpace5CLR, + ColorSpace.TYPE_5CLR, + ICC_Profile.icSigSpace6CLR, + ColorSpace.TYPE_6CLR, + ICC_Profile.icSigSpace7CLR, + ColorSpace.TYPE_7CLR, + ICC_Profile.icSigSpace8CLR, + ColorSpace.TYPE_8CLR, + ICC_Profile.icSigSpace9CLR, + ColorSpace.TYPE_9CLR, + ICC_Profile.icSigSpaceACLR, + ColorSpace.TYPE_ACLR, + ICC_Profile.icSigSpaceBCLR, + ColorSpace.TYPE_BCLR, + ICC_Profile.icSigSpaceCCLR, + ColorSpace.TYPE_CCLR, + ICC_Profile.icSigSpaceDCLR, + ColorSpace.TYPE_DCLR, + ICC_Profile.icSigSpaceECLR, + ColorSpace.TYPE_ECLR, + ICC_Profile.icSigSpaceFCLR, + ColorSpace.TYPE_FCLR + }; + + /** + * Size of an ICC header (128 bytes) + */ + public static final int HEADERSIZE = 128; + + /** + * Mapping of ICC class signatures to profile class constants + */ + private static final int[] classMap = + { + ICC_Profile.icSigInputClass, + ICC_Profile.CLASS_INPUT, + ICC_Profile.icSigDisplayClass, + ICC_Profile.CLASS_DISPLAY, + ICC_Profile.icSigOutputClass, + ICC_Profile.CLASS_OUTPUT, + ICC_Profile.icSigLinkClass, + ICC_Profile.CLASS_DEVICELINK, + ICC_Profile.icSigColorSpaceClass, + ICC_Profile.CLASS_COLORSPACECONVERSION, + ICC_Profile.icSigAbstractClass, + ICC_Profile.CLASS_ABSTRACT, + ICC_Profile.icSigNamedColorClass, + ICC_Profile.CLASS_NAMEDCOLOR + }; + private int size; + private int cmmId; + + // Major/Minor version, The ICC-1998 spec is major v2 + private int majorVersion; + + // Major/Minor version, The ICC-1998 spec is major v2 + private int minorVersion; + private int profileClass; // profile device class + private int colorSpace; // data color space type + private int profileColorSpace; // profile connection space (PCS) type + private byte[] timestamp; // original creation timestamp + private int platform; // platform signature + private int flags; // flags + private int magic; // magic number. + private int manufacturerSig; // manufacturer sig + private int modelSig; // model sig + private byte[] attributes; // Attributes + private int intent; // rendering intent + private byte[] illuminant; // illuminant info (Coordinates of D50 in the PCS) + private int creatorSig; // Creator sig (same type as manufacturer) + + /** + * Creates a 'default' header for use with our predefined profiles. + * Note the device and profile color spaces are not set. + */ + public ProfileHeader() + { + creatorSig = 0; + intent = 0; + modelSig = manufacturerSig = (int) 0x6E6f6E65; // 'none' + magic = icMagicNumber; + cmmId = 0; + platform = 0; // no preferred platform + timestamp = new byte[8]; + majorVersion = 2; + minorVersion = 0x10; + flags = 0; + + // D50 in XYZ format (encoded) + illuminant = new byte[] + { + (byte) 0x00, (byte) 0x00, (byte) 0xf6, (byte) 0xd6, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xd3, (byte) 0x2d + }; + attributes = new byte[8]; + profileClass = ICC_Profile.CLASS_DISPLAY; + } + + /** + * Creates a header from profile data. Only the header portion (128 bytes) + * is read, so the array passed need not be the full profile. + */ + public ProfileHeader(byte[] data) + { + ByteBuffer buf = ByteBuffer.wrap(data); + + // Get size (the sign bit shouldn't matter. + // A valid profile can never be +2Gb) + size = buf.getInt(ICC_Profile.icHdrSize); + + // CMM ID + cmmId = buf.getInt(ICC_Profile.icHdrCmmId); + + // Version number + majorVersion = (int) (data[ICC_Profile.icHdrVersion]); + minorVersion = (int) (data[ICC_Profile.icHdrVersion + 1]); + + // Profile/Device class + int classSig = buf.getInt(ICC_Profile.icHdrDeviceClass); + profileClass = -1; + for (int i = 0; i < classMap.length; i += 2) + if (classMap[i] == classSig) + { + profileClass = classMap[i + 1]; + break; + } + + // get the data color space + int csSig = buf.getInt(ICC_Profile.icHdrColorSpace); + colorSpace = -1; + for (int i = 0; i < csTypeMap.length; i += 2) + if (csTypeMap[i] == csSig) + { + colorSpace = csTypeMap[i + 1]; + break; + } + + // get the profile color space (PCS), must be xyz or lab except + // for device-link-class profiles + int pcsSig = buf.getInt(ICC_Profile.icHdrPcs); + profileColorSpace = -1; + if (profileClass != ICC_Profile.CLASS_DEVICELINK) + { + if (pcsSig == ICC_Profile.icSigXYZData) + profileColorSpace = ColorSpace.TYPE_XYZ; + if (pcsSig == ICC_Profile.icSigLabData) + profileColorSpace = ColorSpace.TYPE_Lab; + } + else + { + for (int i = 0; i < csTypeMap.length; i += 2) + if (csTypeMap[i] == pcsSig) + { + profileColorSpace = csTypeMap[i + 1]; + break; + } + } + + // creation timestamp + timestamp = new byte[8]; + System.arraycopy(data, ICC_Profile.icHdrDate, timestamp, 0, 8); + + // magic number + magic = buf.getInt(ICC_Profile.icHdrMagic); + + // platform info + platform = buf.getInt(ICC_Profile.icHdrPlatform); + // get flags + flags = buf.getInt(ICC_Profile.icHdrFlags); + // get manufacturer sign + manufacturerSig = buf.getInt(ICC_Profile.icHdrManufacturer); + // get header model + modelSig = buf.getInt(ICC_Profile.icHdrModel); + // attributes + attributes = new byte[8]; + System.arraycopy(data, ICC_Profile.icHdrAttributes, attributes, 0, 8); + // rendering intent + intent = buf.getInt(ICC_Profile.icHdrRenderingIntent); + // illuminant info + illuminant = new byte[12]; + System.arraycopy(data, ICC_Profile.icHdrIlluminant, illuminant, 0, 12); + // Creator signature + creatorSig = buf.getInt(ICC_Profile.icHdrCreator); + // The rest of the header (Total size: 128 bytes) is unused.. + } + + /** + * Verify that the header is valid + * @param size equals the file size if it is to be verified, -1 otherwise + * @throws IllegalArgumentException if the header is found to be invalid. + */ + public void verifyHeader(int size) throws IllegalArgumentException + { + // verify size + if (size != -1 && this.size != size) + throw new IllegalArgumentException("Invalid profile length:" + size); + + // Check version number + if (majorVersion != 2) + throw new IllegalArgumentException("Wrong major version number:" + + majorVersion); + + // Profile/Device class + if (profileClass == -1) + throw new IllegalArgumentException("Invalid profile/device class"); + + // get the data color space + if (colorSpace == -1) + throw new IllegalArgumentException("Invalid colorspace"); + + // profile color space + if (profileColorSpace == -1) + throw new IllegalArgumentException("Invalid PCS."); + + // check magic number + if (magic != icMagicNumber) + throw new IllegalArgumentException("Invalid magic number!"); + } + + /** + * Creates a header, setting the header file size at the same time. + * @param size the profile file size. + */ + public byte[] getData(int size) + { + byte[] data = new byte[HEADERSIZE]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.putInt(ICC_Profile.icHdrSize, size); + buf.putInt(ICC_Profile.icHdrCmmId, cmmId); + buf.putShort(ICC_Profile.icHdrVersion, + (short) (majorVersion << 8 | minorVersion)); + for (int i = 1; i < classMap.length; i += 2) + if (profileClass == classMap[i]) + buf.putInt(ICC_Profile.icHdrDeviceClass, classMap[i - 1]); + for (int i = 1; i < csTypeMap.length; i += 2) + if (csTypeMap[i] == colorSpace) + buf.putInt(ICC_Profile.icHdrColorSpace, csTypeMap[i - 1]); + for (int i = 1; i < csTypeMap.length; i += 2) + if (csTypeMap[i] == profileColorSpace) + buf.putInt(ICC_Profile.icHdrPcs, csTypeMap[i - 1]); + + System.arraycopy(timestamp, 0, data, ICC_Profile.icHdrDate, + timestamp.length); + buf.putInt(ICC_Profile.icHdrMagic, icMagicNumber); + buf.putInt(ICC_Profile.icHdrPlatform, platform); + buf.putInt(ICC_Profile.icHdrFlags, flags); + buf.putInt(ICC_Profile.icHdrManufacturer, manufacturerSig); + buf.putInt(ICC_Profile.icHdrModel, modelSig); + System.arraycopy(attributes, 0, data, ICC_Profile.icHdrAttributes, + attributes.length); + buf.putInt(ICC_Profile.icHdrRenderingIntent, intent); + System.arraycopy(illuminant, 0, data, ICC_Profile.icHdrIlluminant, + illuminant.length); + buf.putInt(ICC_Profile.icHdrCreator, creatorSig); + return buf.array(); + } + + public int getSize() + { + return size; + } + + public void setSize(int s) + { + size = s; + } + + public int getMajorVersion() + { + return majorVersion; + } + + public int getMinorVersion() + { + return minorVersion; + } + + public int getProfileClass() + { + return profileClass; + } + + public void setProfileClass(int pc) + { + profileClass = pc; + } + + public int getColorSpace() + { + return colorSpace; + } + + public int getProfileColorSpace() + { + return profileColorSpace; + } + + public void setColorSpace(int cs) + { + colorSpace = cs; + } + + public void setProfileColorSpace(int pcs) + { + profileColorSpace = pcs; + } + +} diff --git a/libjava/classpath/gnu/java/awt/color/PyccConverter.java b/libjava/classpath/gnu/java/awt/color/PyccConverter.java new file mode 100644 index 000000000..77ea28a3e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/PyccConverter.java @@ -0,0 +1,71 @@ +/* PyccConverter.java -- PhotoYCC conversion class + Copyright (C) 2004 Free Software Foundation + +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.color; + +/** + * PyccConverter - conversion routines for the PhotoYCC colorspace + * + * Also known as PhotoCD YCC, it is an expansion of the conventional + * YCC color space to also include colors with over 100% white. + * + * XXX FIXME: Not yet implemented, implementation pending. + * + * @author Sven de Marothy + */ +public class PyccConverter implements ColorSpaceConverter +{ + public float[] toRGB(float[] in) + { + throw new UnsupportedOperationException(); + } + + public float[] fromRGB(float[] in) + { + throw new UnsupportedOperationException(); + } + + public float[] toCIEXYZ(float[] in) + { + throw new UnsupportedOperationException(); + } + + public float[] fromCIEXYZ(float[] in) + { + throw new UnsupportedOperationException(); + } +} diff --git a/libjava/classpath/gnu/java/awt/color/RgbProfileConverter.java b/libjava/classpath/gnu/java/awt/color/RgbProfileConverter.java new file mode 100644 index 000000000..7623890a4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/RgbProfileConverter.java @@ -0,0 +1,244 @@ +/* RgbProfileConverter.java -- RGB Profile conversion class + Copyright (C) 2004 Free Software Foundation + +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.color; + +import java.awt.color.ICC_Profile; +import java.awt.color.ICC_ProfileRGB; +import java.awt.color.ProfileDataException; + +/** + * RgbProfileConverter - converts RGB profiles (ICC_ProfileRGB) + * + * This type of profile contains a matrix and three + * tone reproduction curves (TRCs). + * + * Device RGB --> CIE XYZ is done through first multiplying with + * a matrix, then each component is looked-up against it's TRC. + * + * The opposite transform is done using the inverse of the matrix, + * and TRC:s. + * + * @author Sven de Marothy + */ +public class RgbProfileConverter implements ColorSpaceConverter +{ + private float[][] matrix; + private float[][] inv_matrix; + private ToneReproductionCurve rTRC; + private ToneReproductionCurve gTRC; + private ToneReproductionCurve bTRC; + private ColorLookUpTable toPCS; + private ColorLookUpTable fromPCS; + + /** + * CIE 1931 D50 white point (in Lab coordinates) + */ + private static float[] D50 = { 0.96422f, 1.00f, 0.82521f }; + + /** + * Constructs an RgbProfileConverter from a given ICC_ProfileRGB + */ + public RgbProfileConverter(ICC_ProfileRGB profile) + { + toPCS = fromPCS = null; + matrix = profile.getMatrix(); + + // get TRCs + try + { + rTRC = new ToneReproductionCurve(profile.getGamma(ICC_ProfileRGB.REDCOMPONENT)); + } + catch (ProfileDataException e) + { + rTRC = new ToneReproductionCurve(profile.getTRC(ICC_ProfileRGB.REDCOMPONENT)); + } + try + { + gTRC = new ToneReproductionCurve(profile.getGamma(ICC_ProfileRGB.GREENCOMPONENT)); + } + catch (ProfileDataException e) + { + gTRC = new ToneReproductionCurve(profile.getTRC(ICC_ProfileRGB.GREENCOMPONENT)); + } + try + { + bTRC = new ToneReproductionCurve(profile.getGamma(ICC_ProfileRGB.BLUECOMPONENT)); + } + catch (ProfileDataException e) + { + bTRC = new ToneReproductionCurve(profile.getTRC(ICC_ProfileRGB.BLUECOMPONENT)); + } + + // If a CLUT is available, it should be used, and the TRCs ignored. + // Note: A valid profile may only have CLUTs in one direction, and + // TRC:s without useful info, making reverse-transforms impossible. + // In this case the TRC will be used for the reverse-transform with + // unpredictable results. This is in line with the Java specification, + try + { + toPCS = new ColorLookUpTable(profile, ICC_Profile.icSigAToB0Tag); + } + catch (Exception e) + { + toPCS = null; + } + + try + { + fromPCS = new ColorLookUpTable(profile, ICC_Profile.icSigBToA0Tag); + } + catch (Exception e) + { + fromPCS = null; + } + + // Calculate the inverse matrix if no reverse CLUT is available + if(fromPCS == null) + inv_matrix = invertMatrix(matrix); + else + { + // otherwise just set it to an identity matrix + inv_matrix = new float[3][3]; + inv_matrix[0][0] = inv_matrix[1][1] = inv_matrix[2][2] = 1.0f; + } + } + + public float[] toCIEXYZ(float[] in) + { + // CLUT takes precedence + if (toPCS != null) + return toPCS.lookup(in); + + float[] temp = new float[3]; + float[] out = new float[3]; + + // device space --> linear gamma + temp[0] = rTRC.lookup(in[0]); + temp[1] = gTRC.lookup(in[1]); + temp[2] = bTRC.lookup(in[2]); + + // matrix multiplication + out[0] = matrix[0][0] * temp[0] + matrix[0][1] * temp[1] + + matrix[0][2] * temp[2]; + out[1] = matrix[1][0] * temp[0] + matrix[1][1] * temp[1] + + matrix[1][2] * temp[2]; + out[2] = matrix[2][0] * temp[0] + matrix[2][1] * temp[1] + + matrix[2][2] * temp[2]; + + return out; + } + + public float[] toRGB(float[] in) + { + return SrgbConverter.XYZtoRGB(toCIEXYZ(in)); + } + + public float[] fromCIEXYZ(float[] in) + { + if (fromPCS != null) + return fromPCS.lookup(in); + + float[] temp = new float[3]; + float[] out = new float[3]; + + // matrix multiplication + temp[0] = inv_matrix[0][0] * in[0] + inv_matrix[0][1] * in[1] + + inv_matrix[0][2] * in[2]; + temp[1] = inv_matrix[1][0] * in[0] + inv_matrix[1][1] * in[1] + + inv_matrix[1][2] * in[2]; + temp[2] = inv_matrix[2][0] * in[0] + inv_matrix[2][1] * in[1] + + inv_matrix[2][2] * in[2]; + + // device space --> linear gamma + out[0] = rTRC.reverseLookup(temp[0]); + out[1] = gTRC.reverseLookup(temp[1]); + out[2] = bTRC.reverseLookup(temp[2]); + + // FIXME: Sun appears to clip the return values to [0,1] + // I don't believe that is a Good Thing, + // (some colorspaces may allow values outside that range.) + // So we return the actual values here. + return out; + } + + public float[] fromRGB(float[] in) + { + return fromCIEXYZ(SrgbConverter.RGBtoXYZ(in)); + } + + /** + * Inverts a 3x3 matrix, returns the inverse, + * throws an IllegalArgumentException if the matrix is not + * invertible (this shouldn't happen for a valid profile) + */ + private float[][] invertMatrix(float[][] matrix) + { + float[][] out = new float[3][3]; + double determinant = matrix[0][0] * (matrix[1][1] * matrix[2][2] + - matrix[2][1] * matrix[1][2]) + - matrix[0][1] * (matrix[1][0] * matrix[2][2] + - matrix[2][0] * matrix[1][2]) + + matrix[0][2] * (matrix[1][0] * matrix[2][1] + - matrix[2][0] * matrix[1][1]); + + if (determinant == 0.0) + throw new IllegalArgumentException("Can't invert conversion matrix."); + float invdet = (float) (1.0 / determinant); + + out[0][0] = invdet * (matrix[1][1] * matrix[2][2] + - matrix[1][2] * matrix[2][1]); + out[0][1] = invdet * (matrix[0][2] * matrix[2][1] + - matrix[0][1] * matrix[2][2]); + out[0][2] = invdet * (matrix[0][1] * matrix[1][2] + - matrix[0][2] * matrix[1][1]); + out[1][0] = invdet * (matrix[1][2] * matrix[2][0] + - matrix[1][0] * matrix[2][2]); + out[1][1] = invdet * (matrix[0][0] * matrix[2][2] + - matrix[0][2] * matrix[2][0]); + out[1][2] = invdet * (matrix[0][2] * matrix[1][0] + - matrix[0][0] * matrix[1][2]); + out[2][0] = invdet * (matrix[1][0] * matrix[2][1] + - matrix[1][1] * matrix[2][0]); + out[2][1] = invdet * (matrix[0][1] * matrix[2][0] + - matrix[0][0] * matrix[2][1]); + out[2][2] = invdet * (matrix[0][0] * matrix[1][1] + - matrix[0][1] * matrix[1][0]); + return out; + } +} diff --git a/libjava/classpath/gnu/java/awt/color/SrgbConverter.java b/libjava/classpath/gnu/java/awt/color/SrgbConverter.java new file mode 100644 index 000000000..76831c0ea --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/SrgbConverter.java @@ -0,0 +1,152 @@ +/* SrgbConverter.java -- sRGB conversion class + Copyright (C) 2004 Free Software Foundation + +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.color; + + +/** + * SrgbConverter - conversion routines for the sRGB colorspace + * sRGB is a standard for RGB colorspaces, adopted by the w3c. + * + * The specification is available at: + * http://www.w3.org/Graphics/Color/sRGB.html + * + * @author Sven de Marothy + */ +/** + * + * Note the matrix numbers used here are NOT identical to those in the + * w3 spec, as those numbers are CIE XYZ relative a D65 white point. + * The CIE XYZ we use is relative a D50 white point, so therefore a + * linear Bradford transform matrix for D65->D50 mapping has been applied. + * (The ICC documents describe this transform) + * + * Linearized Bradford transform: + * 0.8951 0.2664 -0.1614 + * -0.7502 1.7135 0.0367 + * 0.0389 -0.0685 1.0296 + * + * Inverse: + * 0.9870 -0.1471 0.1600 + * 0.4323 0.5184 0.0493 + * -0.00853 0.0400 0.9685 + */ +public class SrgbConverter implements ColorSpaceConverter +{ + public float[] fromCIEXYZ(float[] in) + { + return XYZtoRGB(in); + } + + public float[] toCIEXYZ(float[] in) + { + return RGBtoXYZ(in); + } + + public float[] toRGB(float[] in) + { + float[] out = new float[3]; + System.arraycopy(in, 0, out, 0, 3); + return out; + } + + public float[] fromRGB(float[] in) + { + float[] out = new float[3]; + System.arraycopy(in, 0, out, 0, 3); + return out; + } + + /** + * CIE XYZ (D50 relative) --> sRGB + * + * Static as it's used by other ColorSpaceConverters to + * convert to sRGB if the color space is defined in XYZ. + */ + public static float[] XYZtoRGB(float[] in) + { + float[] temp = new float[3]; + temp[0] = 3.1338f * in[0] - 1.6171f * in[1] - 0.4907f * in[2]; + temp[1] = -0.9785f * in[0] + 1.9160f * in[1] + 0.0334f * in[2]; + temp[2] = 0.0720f * in[0] - 0.2290f * in[1] + 1.4056f * in[2]; + + float[] out = new float[3]; + for (int i = 0; i < 3; i++) + { + if (temp[i] < 0) + temp[i] = 0.0f; + if (temp[i] > 1) + temp[i] = 1.0f; + if (temp[i] <= 0.00304f) + out[i] = temp[i] * 12.92f; + else + out[i] = 1.055f * ((float) Math.exp((1 / 2.4) * Math.log(temp[i]))) + - 0.055f; + } + return out; + } + + /** + * sRGB --> CIE XYZ (D50 relative) + * + * Static as it's used by other ColorSpaceConverters to + * convert to XYZ if the color space is defined in RGB. + */ + public static float[] RGBtoXYZ(float[] in) + { + float[] temp = new float[3]; + float[] out = new float[3]; + for (int i = 0; i < 3; i++) + if (in[i] <= 0.03928f) + temp[i] = in[i] / 12.92f; + else + temp[i] = (float) Math.exp(2.4 * Math.log((in[i] + 0.055) / 1.055)); + + /* + * Note: The numbers which were used to calculate this only had four + * digits of accuracy. So don't be fooled by the number of digits here. + * If someone has more accurate source, feel free to update this. + */ + out[0] = (float) (0.436063750222 * temp[0] + 0.385149601465 * temp[1] + + 0.143086418888 * temp[2]); + out[1] = (float) (0.222450894035 * temp[0] + 0.71692584775 * temp[1] + + 0.060624511256 * temp[2]); + out[2] = (float) (0.0138985186 * temp[0] + 0.097079690112 * temp[1] + + 0.713996045725 * temp[2]); + return out; + } +} diff --git a/libjava/classpath/gnu/java/awt/color/TagEntry.java b/libjava/classpath/gnu/java/awt/color/TagEntry.java new file mode 100644 index 000000000..a97864683 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/TagEntry.java @@ -0,0 +1,121 @@ +/* TagEntry.java -- A utility class used for storing the tags in ICC_Profile + Copyright (C) 2004 Free Software Foundation + +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.color; + + +/** + * TagEntry - stores a profile tag. + * These are conveniently stored in a hashtable with the tag signature + * as a key. A legal profile can only have one tag with a given sig, + * so we can conveniently ignore collisions. + * + * @author Sven de Marothy + */ +public class TagEntry +{ + // tag table entry size + public static final int entrySize = 12; + private int signature; + private int size; + private int offset; + private byte[] data; + + public TagEntry(int sig, int offset, int size, byte[] data) + { + this.signature = sig; + this.offset = offset; + this.size = size; + this.data = new byte[size]; + System.arraycopy(data, offset, this.data, 0, size); + } + + public TagEntry(int sig, byte[] data) + { + this.signature = sig; + this.size = data.length; + this.data = new byte[size]; + System.arraycopy(data, offset, this.data, 0, size); + } + + public byte[] getData() + { + byte[] d = new byte[size]; + System.arraycopy(this.data, 0, d, 0, size); + return d; + } + + public String hashKey() + { + return tagHashKey(signature); + } + + public String toString() + { + String s = ""; + s = s + (char) ((byte) ((signature >> 24) & 0xFF)); + s = s + (char) ((byte) ((signature >> 16) & 0xFF)); + s = s + (char) ((byte) ((signature >> 8) & 0xFF)); + s = s + (char) ((byte) (signature & 0xFF)); + return s; + } + + public int getSignature() + { + return signature; + } + + public int getSize() + { + return size; + } + + public int getOffset() + { + return offset; + } + + public void setOffset(int offset) + { + this.offset = offset; + } + + public static String tagHashKey(int sig) + { + return "" + sig; + } +} diff --git a/libjava/classpath/gnu/java/awt/color/ToneReproductionCurve.java b/libjava/classpath/gnu/java/awt/color/ToneReproductionCurve.java new file mode 100644 index 000000000..208e16883 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/ToneReproductionCurve.java @@ -0,0 +1,177 @@ +/* ToneReproductionCurve.java -- Representation of an ICC 'curv' type TRC + Copyright (C) 2004 Free Software Foundation + +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.color; + + +/** + * ToneReproductionCurve - TRCs are used to describe RGB + * and Grayscale profiles. The TRC is essentially the gamma + * function of the color space. + * + * For example, Apple RGB has a gamma of 1.8, most monitors are ~2.2, + * sRGB is 2.4 with a small linear part near 0. + * Linear spaces are of course 1.0. + * (The exact function is implemented in SrgbConverter) + * + * The ICC specification allows the TRC to be described as a single + * Gamma value, where the function is thus out = in**gamma. + * Alternatively, the gamma function may be represented by a lookup table + * of values, in which case linear interpolation is used. + * + * @author Sven de Marothy + */ +public class ToneReproductionCurve +{ + private float[] trc; + private float gamma; + private float[] reverseTrc; + + /** + * Constructs a TRC from a gamma values + */ + public ToneReproductionCurve(float gamma) + { + trc = null; + reverseTrc = null; + this.gamma = gamma; + } + + /** + * Constructs a TRC from a set of float values + */ + public ToneReproductionCurve(float[] trcValues) + { + trc = new float[trcValues.length]; + System.arraycopy(trcValues, 0, trc, 0, trcValues.length); + setupReverseTrc(); + } + + /** + * Constructs a TRC from a set of short values normalized to + * the 0-65535 range (as in the ICC profile file). + * (Note the values are treated as unsigned) + */ + public ToneReproductionCurve(short[] trcValues) + { + trc = new float[trcValues.length]; + for (int i = 0; i < trcValues.length; i++) + trc[i] = (float) ((int) trcValues[i] & (0xFFFF)) / 65535.0f; + setupReverseTrc(); + } + + /** + * Performs a TRC lookup + */ + public float lookup(float in) + { + float out; + + if (trc == null) + { + if (in == 0f) + return 0.0f; + return (float) Math.exp(gamma * Math.log(in)); + } + else + { + double alpha = in * (trc.length - 1); + int index = (int) Math.floor(alpha); + alpha = alpha - (double) index; + if (index >= trc.length - 1) + return trc[trc.length - 1]; + if (index <= 0) + return trc[0]; + out = (float) (trc[index] * (1.0 - alpha) + trc[index + 1] * alpha); + } + return out; + } + + /** + * Performs an reverse lookup + */ + public float reverseLookup(float in) + { + float out; + + if (trc == null) + { + if (in == 0f) + return 0.0f; + return (float) Math.exp((1.0 / gamma) * Math.log(in)); + } + else + { + double alpha = in * (reverseTrc.length - 1); + int index = (int) Math.floor(alpha); + alpha = alpha - (double) index; + if (index >= reverseTrc.length - 1) + return reverseTrc[reverseTrc.length - 1]; + if (index <= 0) + return reverseTrc[0]; + out = (float) (reverseTrc[index] * (1.0 - alpha) + + reverseTrc[index + 1] * alpha); + } + return out; + } + + /** + * Calculates a reverse-lookup table. + * We use a whopping 10,000 entries.. This is should be more than any + * real-life TRC table (typically around 256-1024) so we won't be losing + * any precision. + * + * This will of course generate completely invalid results if the curve + * is not monotonic and invertable. But what's the alternative? + */ + public void setupReverseTrc() + { + reverseTrc = new float[10000]; + int j = 0; + for (int i = 0; i < 10000; i++) + { + float n = ((float) i) / 10000f; + while (trc[j + 1] < n && j < trc.length - 2) + j++; + + if (j == trc.length - 2) + reverseTrc[i] = trc[trc.length - 1]; + else + reverseTrc[i] = (j + (n - trc[j]) / (trc[j + 1] - trc[j])) / ((float) trc.length); + } + } +} diff --git a/libjava/classpath/gnu/java/awt/color/package.html b/libjava/classpath/gnu/java/awt/color/package.html new file mode 100644 index 000000000..c4705cce5 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/color/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.awt.color package. + Copyright (C) 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.awt.color</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/awt/dnd/GtkMouseDragGestureRecognizer.java b/libjava/classpath/gnu/java/awt/dnd/GtkMouseDragGestureRecognizer.java new file mode 100644 index 000000000..4ef8c2918 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/dnd/GtkMouseDragGestureRecognizer.java @@ -0,0 +1,172 @@ +/* GtkMouseDragGestureRecognizer.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.dnd; + +import java.awt.Component; +import java.awt.Point; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.awt.event.MouseEvent; + +public class GtkMouseDragGestureRecognizer + extends MouseDragGestureRecognizer +{ + + public GtkMouseDragGestureRecognizer (DragSource ds) + { + this(ds, null, 0, null); + } + + public GtkMouseDragGestureRecognizer (DragSource ds, Component c) + { + this (ds, c, 0, null); + } + + public GtkMouseDragGestureRecognizer (DragSource ds, Component c, int act) + { + this(ds, c, act, null); + } + + public GtkMouseDragGestureRecognizer (DragSource ds, Component c, int act, + DragGestureListener dgl) + { + super(ds, c, act, dgl); + } + + public void registerListeners () + { + super.registerListeners(); + } + + public void unregisterListeners () + { + super.unregisterListeners(); + } + + public void mouseClicked (MouseEvent e) + { + // Nothing to do here. + } + + public void mousePressed (MouseEvent e) + { + events.clear(); + if (getDropActionFromEvent(e) != DnDConstants.ACTION_NONE) + appendEvent(e); + } + + public void mouseReleased (MouseEvent e) + { + events.clear(); + } + + public void mouseEntered (MouseEvent e) + { + events.clear(); + } + + public void mouseExited(MouseEvent e) + { + if (!events.isEmpty()) + if (getDropActionFromEvent(e) == DnDConstants.ACTION_NONE) + events.clear(); + } + + public void mouseDragged(MouseEvent e) + { + if (!events.isEmpty()) + { + int act = getDropActionFromEvent(e); + + if (act == DnDConstants.ACTION_NONE) + return; + + Point origin = ((MouseEvent) events.get(0)).getPoint(); + Point current = e.getPoint(); + int dx = Math.abs(origin.x - current.x); + int dy = Math.abs(origin.y - current.y); + int threshold = DragSource.getDragThreshold(); + + if (dx > threshold || dy > threshold) + fireDragGestureRecognized(act, origin); + else + appendEvent(e); + } + } + + public void mouseMoved (MouseEvent e) + { + // Nothing to do here. + } + + private int getDropActionFromEvent(MouseEvent e) + { + int modEx = e.getModifiersEx(); + int buttons = modEx & (MouseEvent.BUTTON1_DOWN_MASK + | MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK); + if (!(buttons == MouseEvent.BUTTON1_DOWN_MASK || + buttons == MouseEvent.BUTTON2_DOWN_MASK)) + return DnDConstants.ACTION_NONE; + + // Convert modifier to a drop action + int sourceActions = getSourceActions(); + int mod = modEx + & (MouseEvent.SHIFT_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK); + switch (mod) + { + case MouseEvent.SHIFT_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK: + return DnDConstants.ACTION_LINK & sourceActions; + case MouseEvent.CTRL_DOWN_MASK: + return DnDConstants.ACTION_COPY & sourceActions; + case MouseEvent.SHIFT_DOWN_MASK: + return DnDConstants.ACTION_MOVE & sourceActions; + default: + if ((sourceActions & DnDConstants.ACTION_MOVE) != 0) + return DnDConstants.ACTION_MOVE & sourceActions; + else if ((sourceActions & DnDConstants.ACTION_COPY) != 0) + return DnDConstants.ACTION_COPY & sourceActions; + else if ((sourceActions & DnDConstants.ACTION_LINK) != 0) + return DnDConstants.ACTION_LINK & sourceActions; + } + + return DnDConstants.ACTION_NONE & sourceActions; + } +} diff --git a/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDragSourceContextPeer.java b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDragSourceContextPeer.java new file mode 100644 index 000000000..4d1097626 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDragSourceContextPeer.java @@ -0,0 +1,184 @@ +/* GtkDragSourceContextPeer.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.dnd.peer.gtk; + +import gnu.java.awt.peer.gtk.GtkGenericPeer; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragSourceContext; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.peer.ComponentPeer; +import java.awt.peer.LightweightPeer; + +public class GtkDragSourceContextPeer + extends GtkGenericPeer + implements DragSourceContextPeer +{ + private ComponentPeer peer; + private Cursor cursor; + private DragSourceContext context; + public static Component target; + + native void nativeStartDrag(Image i, int x, int y, int action, String target); + native void connectSignals(ComponentPeer comp); + native void create(ComponentPeer comp); + native void nativeSetCursor(int cursor); + native void setTarget(GtkDropTargetContextPeer target); + + public GtkDragSourceContextPeer(DragGestureEvent e) + { + super(e.getComponent()); + Component comp = e.getComponent(); + peer = getComponentPeer(comp); + + create(peer); + connectSignals(peer); + cursor = comp.getCursor(); + + // FIXME: Where do we set the target? + + if ((target != null)) + setTarget(new GtkDropTargetContextPeer(target)); + } + + ComponentPeer getComponentPeer(Component c) + { + if (c == null) + return null; + + Component curr = c; + while (curr.getPeer() instanceof LightweightPeer) + curr = curr.getParent(); + + if (curr != null) + return curr.getPeer(); + return null; + } + + public void startDrag(DragSourceContext context, Cursor c, Image i, Point p) + throws InvalidDnDOperationException + { + this.context = context; + + if (p == null) + p = new Point(); + + // FIXME: use proper DataFlavor, not "text/plain". + // Also, add check to determine if dragging. + + setCursor(c); + nativeStartDrag(i, p.x, p.y, context.getTrigger().getDragAction(), + "text/plain"); + } + + public Cursor getCursor() + { + return cursor; + } + + public void setCursor(Cursor c) throws InvalidDnDOperationException + { + if (c != null) + { + nativeSetCursor(c.getType()); + cursor = c; + } + } + + public void transferablesFlavorsChanged() + { + // Nothing to do here. + } + + /** + * Called from native code. + */ + + public void dragEnter(int action, int modifiers) + { + context.dragEnter(new DragSourceDragEvent(context, action, + action + & context.getSourceActions(), + modifiers)); + } + + public void dragExit(int action, int x, int y) + { + context.dragExit(new DragSourceEvent(context, x, y)); + } + + public void dragDropEnd(int action, boolean success, int x, int y) + { + context.dragDropEnd(new DragSourceDropEvent(context, action, success, x, y)); + } + + public void dragMouseMoved(int action, int modifiers) + { + context.dragMouseMoved(new DragSourceDragEvent(context, + action, + action + & context.getSourceActions(), + modifiers)); + } + + public void dragOver(int action, int modifiers) + { + context.dragOver(new DragSourceDragEvent(context, action, + action + & context.getSourceActions(), + modifiers)); + } + + public void dragActionChanged(int newAction, int modifiers) + { + context.dropActionChanged(new DragSourceDragEvent(context, + newAction, + newAction + & context.getSourceActions(), + modifiers)); + } +} diff --git a/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetContextPeer.java b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetContextPeer.java new file mode 100644 index 000000000..315a2bdf1 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetContextPeer.java @@ -0,0 +1,125 @@ +/* GtkDropTargetContextPeer.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.dnd.peer.gtk; + +import gnu.java.awt.peer.gtk.GtkGenericPeer; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DropTarget; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.peer.DropTargetContextPeer; + +public class GtkDropTargetContextPeer + extends GtkGenericPeer + implements DropTargetContextPeer +{ + + public GtkDropTargetContextPeer(Object obj) + { + super(obj); + } + + public void setTargetActions(int actions) + { + // FIXME: Not Implemented + + } + + public int getTargetActions() + { + // FIXME: Not Implemented + return 0; + } + + public DropTarget getDropTarget() + { + // FIXME: Not Implemented + return null; + } + + public DataFlavor[] getTransferDataFlavors() + { + // FIXME: Not Implemented + return null; + } + + public Transferable getTransferable() throws InvalidDnDOperationException + { + // FIXME: Not Implemented + return null; + } + + public boolean isTransferableJVMLocal() + { + // FIXME: Not Implemented + return false; + } + + public void acceptDrag(int dragAction) + { + // FIXME: Not Implemented + + } + + public void rejectDrag() + { + // FIXME: Not Implemented + + } + + public void acceptDrop(int dropAction) + { + // FIXME: Not Implemented + + } + + public void rejectDrop() + { + // FIXME: Not Implemented + + } + + public void dropComplete(boolean success) + { + // FIXME: Not Implemented + + } + +} diff --git a/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetPeer.java b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetPeer.java new file mode 100644 index 000000000..0799df5ba --- /dev/null +++ b/libjava/classpath/gnu/java/awt/dnd/peer/gtk/GtkDropTargetPeer.java @@ -0,0 +1,68 @@ +/* GtkDropTargetPeer.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.dnd.peer.gtk; + +import gnu.java.awt.peer.gtk.GtkGenericPeer; + +import java.awt.dnd.DropTarget; +import java.awt.dnd.peer.DropTargetPeer; + +public class GtkDropTargetPeer + extends GtkGenericPeer + implements DropTargetPeer +{ + + public GtkDropTargetPeer() + { + super(null); + } + + public void addDropTarget(DropTarget target) + { + // FIXME: Not Implemented + + } + + public void removeDropTarget(DropTarget target) + { + // FIXME: Not Implemented + + } + +} diff --git a/libjava/classpath/gnu/java/awt/doc-files/BitwiseXORComposite-1.png b/libjava/classpath/gnu/java/awt/doc-files/BitwiseXORComposite-1.png Binary files differnew file mode 100644 index 000000000..588c910dd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/doc-files/BitwiseXORComposite-1.png diff --git a/libjava/classpath/gnu/java/awt/font/FontDelegate.java b/libjava/classpath/gnu/java/awt/font/FontDelegate.java new file mode 100644 index 000000000..e5c03a10f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/FontDelegate.java @@ -0,0 +1,329 @@ +/* FontDelegate.java -- Interface implemented by all font delegates. + 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.font; + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.text.CharacterIterator; +import java.util.Locale; + + +/** + * The interface that all font delegate objects implement, + * irrespective of where they get their information from. + * + * <p><b>Thread Safety:</b> All classes that implement the + * <code>FontDelegate</code> interface must allow calling these + * methods from multiple concurrent threads. The delegates are + * responsible for performing the necessary synchronization. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public interface FontDelegate +{ + public static final int FLAG_FITTED = 1 << 0; + public static final int FLAG_NO_HINT_HORIZONTAL = 1 << 1; + public static final int FLAG_NO_HINT_VERTICAL = 1 << 2; + public static final int FLAG_NO_HINT_EDGE_POINTS = 1 << 3; + public static final int FLAG_NO_HINT_STRONG_POINTS = 1 << 4; + public static final int FLAG_NO_HINT_WEAK_POINTS = 1 << 5; + + /** + * Returns the full name of this font face in the specified + * locale, for example <i>“Univers Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the face name. + */ + public String getFullName(Locale locale); + + + /** + * Returns the name of the family to which this font face belongs, + * for example <i>“Univers”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the family name. + */ + public String getFamilyName(Locale locale); + + + /** + * Returns the name of this font face inside the family, for example + * <i>“Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the name of the face inside its family. + */ + public String getSubFamilyName(Locale locale); + + + /** + * Returns the PostScript name of this font face, for example + * <i>“Helvetica-Bold”</i>. + * + * @return the PostScript name, or <code>null</code> if the font + * does not provide a PostScript name. + */ + public String getPostScriptName(); + + + /** + * Returns the number of glyphs in this font face. + */ + public int getNumGlyphs(); + + /** + * Returns the glyph code for the specified character. + * + * @param c the character to map + * + * @return the glyph code + */ + public int getGlyphIndex(int c); + + /** + * Returns the index of the glyph which gets displayed if the font + * cannot map a Unicode code point to a glyph. Many fonts show this + * glyph as an empty box. + */ + public int getMissingGlyphCode(); + + + /** + * Creates a GlyphVector by mapping each character in a + * CharacterIterator to the corresponding glyph. + * + * <p>The mapping takes only the font’s <code>cmap</code> + * tables into consideration. No other operations (such as glyph + * re-ordering, composition, or ligature substitution) are + * performed. This means that the resulting GlyphVector will not be + * correct for text in languages that have complex + * character-to-glyph mappings, such as Arabic, Hebrew, Hindi, or + * Thai. + * + * @param font the font object that the created GlyphVector + * will return when it gets asked for its font. This argument is + * needed because the public API works with java.awt.Font, + * not with some private delegate like OpenTypeFont. + * + * @param frc the font rendering parameters that are used for + * measuring glyphs. The exact placement of text slightly depends on + * device-specific characteristics, for instance the device + * resolution or anti-aliasing. For this reason, any measurements + * will only be accurate if the passed + * <code>FontRenderContext</code> correctly reflects the relevant + * parameters. Hence, <code>frc</code> should be obtained from the + * same <code>Graphics2D</code> that will be used for drawing, and + * any rendering hints should be set to the desired values before + * obtaining <code>frc</code>. + * + * @param ci a CharacterIterator for iterating over the + * characters to be displayed. + */ + public GlyphVector createGlyphVector(Font font, + FontRenderContext frc, + CharacterIterator ci); + + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is well + * possible that both values are non-zero, for example for rotated + * text or for Urdu fonts. + */ + public void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance); + + + /** + * Returns the shape of a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, this + * parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional + * metrics, <code>false</code> for rounding the result to a pixel + * boundary. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or <code>null</code> for bitmap fonts. + */ + public GeneralPath getGlyphOutline(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + int type); + + + /** + * Returns a name for the specified glyph. This is useful for + * generating PostScript or PDF files that embed some glyphs of a + * font. + * + * <p><b>Names are not unique:</b> Under some rare circumstances, + * the same name can be returned for different glyphs. It is + * therefore recommended that printer drivers check whether the same + * name has already been returned for antoher glyph, and make the + * name unique by adding the string ".alt" followed by the glyph + * index.</p> + * + * <p>This situation would occur for an OpenType or TrueType font + * that has a <code>post</code> table of format 3 and provides a + * mapping from glyph IDs to Unicode sequences through a + * <code>Zapf</code> table. If the same sequence of Unicode + * codepoints leads to different glyphs (depending on contextual + * position, for example, or on typographic sophistication level), + * the same name would get synthesized for those glyphs. + * + * @param glyphIndex the glyph whose name the caller wants to + * retrieve. + */ + public String getGlyphName(int glyphIndex); + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public float getAscent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); +} diff --git a/libjava/classpath/gnu/java/awt/font/FontFactory.java b/libjava/classpath/gnu/java/awt/font/FontFactory.java new file mode 100644 index 000000000..53eb5df5f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/FontFactory.java @@ -0,0 +1,90 @@ +/* FontFactory.java -- Factory for font delegates. + 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.font; + +import java.nio.ByteBuffer; + +import java.awt.FontFormatException; +import gnu.java.awt.font.opentype.OpenTypeFontFactory; + + +/** + * A factory for creating font delegate objects. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class FontFactory +{ + /** + * The constructor is private so nobody can construct an instance + */ + private FontFactory() + { + } + + + /** + * Creates FontDelegate objects for the fonts in the specified buffer. + * The following font formats are currently recognized: + * recognized font formats are: + * + * <p><ul> + * <li>OpenType (*.otf);</li> + * <li>TrueType (*.ttf);</li> + * <li>TrueType Collections (*.ttc);</li> + * <li>Apple MacOS X data-fork font (*.dfont).</li></ul> + * + * <p>Some formats may contain more than a single font, for example + * *.ttc and *.dfont files. This is the reason why this function + * returns an array. + * + * <p>The implementation reads data from the buffer only when + * needed. Therefore, it greatly increases efficiency if + * <code>buf</code> has been obtained through mapping a file into + * the virtual address space. + * + * @throws FontFormatException if the font data is not in one of the + * known formats. + */ + public static FontDelegate[] createFonts(ByteBuffer buf) + throws FontFormatException + { + return OpenTypeFontFactory.createFonts(buf); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java new file mode 100644 index 000000000..9fd80e79e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java @@ -0,0 +1,663 @@ +/* GNUGlyphVector.java -- The GNU implementation of GlyphVector. + 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.font; + +import gnu.java.awt.java2d.ShapeWrapper; + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphVector; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + + +/** + * The GNU implementation of the abstract GlyphVector class, which + * uses the services provided by a FontDelegate for its functionality. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class GNUGlyphVector + extends GlyphVector +{ + private FontDelegate fontDelegate; + private Font font; + private FontRenderContext renderContext; + private int[] glyphs; + private float fontSize; + private AffineTransform transform; + private boolean valid; + + + /** + * The position of each glyph. The horizontal position of the + * <code>i</code>-th glyph is at <code>pos[i * 2]</code>, its + * vertical position at <code>pos[i * 2 + 1]</code>. The total + * advance width of the entire vector is stored at + * <code>pos[numGlyphs]</code>, the total advance height at + * <code>pos[numGlyphs + 1]</code>. + */ + private float[] pos; + + + private AffineTransform[] transforms; + private int layoutFlags; + + /** + * The cached non-transformed outline of this glyph vector. + */ + private Shape cleanOutline; + + /** + * Constructs a new GNUGlyphVector. + * + * @param fontDelegate the FontDelegate that creates this vector. + * + * @param font the Font that this GlyphVector will return for {@link + * #getFont()}. That object is also used to determine the point + * size, which affects the affine transformation used by the font + * scaler. + * + * @param renderContext an object with parameters for font + * rendering, such as whether anti-aliasing is enabled. + * + * @param glyphs the glyphs in this vector. + */ + public GNUGlyphVector(FontDelegate fontDelegate, + Font font, + FontRenderContext renderContext, + int[] glyphs) + { + this.fontDelegate = fontDelegate; + this.font = font; + this.renderContext = renderContext; + this.glyphs = glyphs; + + fontSize = font.getSize2D(); + transform = font.getTransform(); // returns a modifiable copy + //transform.concatenate(renderContext.getTransform()); + } + + + + /** + * Returns the font of the glyphs in this GlyphVector. + */ + public Font getFont() + { + return font; + } + + + /** + * Returns the FontRenderContext that is used to calculate the + * extent and position of the glyphs. + */ + public FontRenderContext getFontRenderContext() + { + return renderContext; + } + + + /** + * Moves each glyph in the vector to its default position. + */ + public void performDefaultLayout() + { + float x, y, advanceWidth, advanceHeight; + int i, p; + AffineTransform tx; + Point2D.Float advance = new Point2D.Float(); + + pos = new float[(glyphs.length + 1) * 2]; + x = y = 0.0f; + p = 0; + for (i = p = 0; i < glyphs.length; i++) + { + p += 2; + + if ((transforms == null) || (tx = transforms[i]) == null) + tx = this.transform; + else + { + tx = new AffineTransform(tx); + tx.concatenate(this.transform); + } + + fontDelegate.getAdvance(glyphs[i], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true, + advance); + // FIXME: We shouldn't round here, but instead hint the metrics + // correctly. + pos[p] = x += Math.round(advance.x); + pos[p + 1] = y += advance.y; + } + valid = true; + } + + + /** + * Determines the number of glyphs in this GlyphVector. + */ + public int getNumGlyphs() + { + return glyphs.length; + } + + + /** + * Determines the glyph number by index in this vector. + * Glyph numbers are specific to each font, so two fonts + * will likely assign different numbers to the same glyph. + * + * @param glyphIndex the index of the glyph whose glyph number is to + * be retrieved. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> + * is not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public int getGlyphCode(int glyphIndex) + { + /* The exception is thrown automatically if the index is out + * of the valid bounds. + */ + return glyphs[glyphIndex]; + } + + + /** + * Returns a slice of this GlyphVector. + * + * @param firstGlyphIndex the index of the first glyph in the + * returned slice. + * + * @param numEntries the size of the returned slice. + * + * @param outCodes a pre-allocated array for storing the slice, + * or <code>null</code> to cause allocation of a new array. + * + * @return a slice of this GlyphVector. If <code>outCodes</code> + * is <code>null</code>, the slice will be stored into a freshly + * allocated array; otherwise, the result will be stored into + * <code>outCodes</code>. + */ + public int[] getGlyphCodes(int firstGlyphIndex, + int numEntries, + int[] outCodes) + { + if (numEntries < 0) + throw new IllegalArgumentException(); + if (outCodes == null) + outCodes = new int[numEntries]; + System.arraycopy(glyphs, firstGlyphIndex, outCodes, 0, numEntries); + return outCodes; + } + + + public Rectangle2D getLogicalBounds() + { + float ascent, descent; + + validate(); + + return new Rectangle2D.Float(0, 0, + pos[pos.length - 2], + getAscent() - getDescent()); + } + + + public Rectangle2D getVisualBounds() + { + validate(); + + // FIXME: Not yet implemented. + return getLogicalBounds(); + } + + + /** + * Returns the shape of this GlyphVector. + */ + public Shape getOutline() + { + return getOutline(0.0f, 0.0f); + } + + + /** + * Returns the shape of this GlyphVector, translated to the + * specified position. + * + * @param x the horizontal position for rendering this vector. + * @param y the vertical position for rendering this vector. + */ + public Shape getOutline(float x, float y) + { + validate(); + + Shape outline; + if (cleanOutline == null) + { + GeneralPath path = new GeneralPath(); + int len = glyphs.length; + for (int i = 0; i < len; i++) + { + GeneralPath p = new GeneralPath(getGlyphOutline(i)); + path.append(p, false); + } + // Protect the cached instance from beeing modified by application + // code. + cleanOutline = new ShapeWrapper(path); + outline = cleanOutline; + } + else + { + outline = cleanOutline; + } + if (x != 0 || y != 0) + { + GeneralPath path = new GeneralPath(outline); + AffineTransform t = new AffineTransform(); + t.translate(x, y); + path.transform(t); + outline = path; + } + return outline; + } + + public Shape getOutline(float x, float y, int type) + { + validate(); + + GeneralPath outline = new GeneralPath(); + int len = glyphs.length; + for (int i = 0; i < len; i++) + { + GeneralPath p = new GeneralPath(getGlyphOutline(i, type)); + outline.append(p, false); + } + AffineTransform t = new AffineTransform(); + t.translate(x, y); + outline.transform(t); + return outline; + } + + /** + * Determines the shape of the specified glyph. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs()]</code>. + */ + public Shape getGlyphOutline(int glyphIndex) + { + AffineTransform tx, glyphTx; + GeneralPath path; + + validate(); + + if ((transforms != null) + && ((glyphTx = transforms[glyphIndex]) != null)) + { + tx = new AffineTransform(transform); + tx.concatenate(glyphTx); + } + else + tx = transform; + + path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + FontDelegate.FLAG_FITTED); + + tx = new AffineTransform(); + tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]); + path.transform(tx); + return path; + } + + public Shape getGlyphOutline(int glyphIndex, int type) + { + AffineTransform tx, glyphTx; + GeneralPath path; + + validate(); + + if ((transforms != null) + && ((glyphTx = transforms[glyphIndex]) != null)) + { + tx = new AffineTransform(transform); + tx.concatenate(glyphTx); + } + else + tx = transform; + + path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + type); + + tx = new AffineTransform(); + tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]); + path.transform(tx); + return path; + } + + /** + * Determines the position of the specified glyph, or the + * total advance width and height of the vector. + * + * @param glyphIndex the index of the glyph in question. + * If this value equals <code>getNumGlyphs()</code>, the + * position <i>after</i> the last glyph will be returned, + * which is the total advance width and height of the vector. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs()]</code>. + */ + public Point2D getGlyphPosition(int glyphIndex) + { + validate(); + return new Point2D.Float(pos[glyphIndex * 2], + pos[glyphIndex * 2 + 1]); + } + + + /** + * Moves the specified glyph to a new position, or changes the + * advance width and height of the entire glyph vector. + * + * <p>Note that the position of an individual glyph may also + * affected by its affine transformation. + * + * @param glyphIndex the index of the moved glyph. If + * <code>glyphIndex</code> equals the total number of glyphs in this + * vector, the advance width and height of the vector is changed. + * + * @param position the new position of the glyph. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs()]</code>. + */ + public void setGlyphPosition(int glyphIndex, Point2D position) + { + validate(); + pos[glyphIndex * 2] = (float) position.getX(); + pos[glyphIndex * 2 + 1] = (float) position.getY(); + } + + + /** + * Returns the affine transformation that is applied to the + * glyph at the specified index. + * + * @param glyphIndex the index of the glyph whose transformation + * is to be retrieved. + * + * @return an affine transformation, or <code>null</code> + * for the identity transformation. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public AffineTransform getGlyphTransform(int glyphIndex) + { + if (transforms == null) + return null; + else + return transforms[glyphIndex]; + } + + + /** + * Applies an affine transformation to the glyph at the specified + * index. + * + * @param glyphIndex the index of the glyph to which the + * transformation is applied. + * + * @param transform the affine transformation for the glyph, or + * <code>null</code> for an identity transformation. + */ + public void setGlyphTransform(int glyphIndex, + AffineTransform transform) + { + if (transforms == null) + transforms = new AffineTransform[glyphs.length]; + transforms[glyphIndex] = transform; + + /* If the GlyphVector has only a transform for a single glyph, and + * the caller clears its transform, the FLAG_HAS_TRANSFORMS bit + * should be cleared in layoutFlags. However, this would require + * that we keep track of the number of transformed glyphs, or that + * we count them when a transform is cleared. This would + * complicate the code quite a bit. Note that the only drawback of + * wrongly setting FLAG_HAS_TRANSFORMS is that a slower code path + * might be taken for rendering the vector. Right now, we never + * really look at the flag, so it does not make any difference. + */ + if (transform != null) + layoutFlags |= FLAG_HAS_TRANSFORMS; + valid = false; + } + + + /** + * Returns flags that can be used for optimizing the rendering + * of this GlyphVector. + * + * @return a bit mask with the applicable flags set. + * + * @since 1.4 + * + * @see GlyphVector#FLAG_HAS_POSITION_ADJUSTMENTS + * @see GlyphVector#FLAG_HAS_TRANSFORMS + * @see GlyphVector#FLAG_RUN_RTL + * @see GlyphVector#FLAG_COMPLEX_GLYPHS + * @see GlyphVector#FLAG_MASK + */ + public int getLayoutFlags() + { + return layoutFlags; + } + + + /** + * Returns the positions of a range of glyphs in this vector. + * + * @param firstGlyphIndex the index of the first glyph whose + * position is retrieved. + * + * @param numGlyphs the number of glyphs whose positions + * are retrieved. + * + * @param outPositions an array for storing the results + * (the length must be at least twice <code>numGlyphs</code>), + * or <code>null</code> for freshly allocating an array. + * + * @return an array with the glyph positions. The horizontal + * position of the <code>i</code>-th glyph is at index <code>2 * + * i</code>, the vertical position at index <code>2 * i + 1</code>. + * + * @throws IllegalArgumentException if <code>numGlyphs</code> + * is less than zero. + * + * @throws IndexOutOfBoundsException if either + * <code>firstGlyphIndex</code> or <code>(firstGlyphIndex + + * numGlyphs)</code> is not in the range <code>[0 .. getNumGlyphs() - + * 1]</code>. + */ + public float[] getGlyphPositions(int firstGlyphIndex, + int numGlyphs, + float[] outPositions) + { + if (numGlyphs < 0) + throw new IllegalArgumentException(); + + validate(); + if (outPositions == null) + outPositions = new float[numGlyphs * 2]; + + System.arraycopy(/*src */ pos, /* srcStart */ firstGlyphIndex * 2, + /* dest */ outPositions, /* destStart */ 0, + /* length */ numGlyphs * 2); + return outPositions; + } + + + private float getAscent() + { + return fontDelegate.getAscent(fontSize, transform, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true); + } + + + private float getDescent() + { + return fontDelegate.getDescent(fontSize, transform, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true); + } + + + public Shape getGlyphLogicalBounds(int glyphIndex) + { + float x, y, ascent; + + validate(); + ascent = getAscent(); + x = pos[glyphIndex * 2]; + y = pos[glyphIndex * 2 + 1]; + + return new Rectangle2D.Float(x, y - ascent, + pos[(glyphIndex + 1) * 2] - x, + ascent - getDescent()); + } + + + public Shape getGlyphVisualBounds(int glyphIndex) + { + return getGlyphOutline(glyphIndex).getBounds2D(); + } + + + /** + * Determines the metrics of the glyph at the specified index. + * + * @param glyphIndex the index of the glyph whose metrics is to be + * retrieved. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public GlyphMetrics getGlyphMetrics(int glyphIndex) + { + // FIXME: Not yet implemented. + throw new UnsupportedOperationException(); + } + + + /** + * Determines the justification information for the glyph at the + * specified index. + * + * @param glyphIndex the index of the glyph whose justification + * information is to be retrieved. + * + * @throws IndexOutOfBoundsException if <code>glyphIndex</code> is + * not in the range <code[0 .. getNumGlyphs() - 1]</code>. + */ + public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) + { + // FIXME: Not yet implemented. + throw new UnsupportedOperationException(); + } + + + /** + * Determines whether another GlyphVector is for the same font and + * rendering context, uses the same glyphs and positions them to the + * same location. + * + * @param other the GlyphVector to compare with. + * + * @return <code>true</code> if the two vectors are equal, + * <code>false</code> otherwise. + */ + public boolean equals(GlyphVector other) + { + GNUGlyphVector o; + if (!(other instanceof GNUGlyphVector)) + return false; + + o = (GNUGlyphVector) other; + if ((this.font != o.font) + || (this.fontDelegate != o.fontDelegate) + || (this.renderContext != o.renderContext) + || (this.glyphs.length != o.glyphs.length)) + return false; + + for (int i = 0; i < glyphs.length; i++) + if (this.glyphs[i] != o.glyphs[i]) + return false; + + validate(); + o.validate(); + for (int i = 0; i < pos.length; i++) + if (this.pos[i] != o.pos[i]) + return false; + + return true; + } + + private void validate() + { + if (!valid) + performDefaultLayout(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/OpenTypeFontPeer.java b/libjava/classpath/gnu/java/awt/font/OpenTypeFontPeer.java new file mode 100644 index 000000000..d8bff1199 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/OpenTypeFontPeer.java @@ -0,0 +1,565 @@ +/* XFontPeer2.java -- A Java based TTF font peer for X + 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.font; + + +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.lang.CPStringBuilder; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +public class OpenTypeFontPeer + extends ClasspathFontPeer +{ + + /** + * The font mapping as specified in the file fonts.properties. + */ + private static Properties fontProperties; + + /** + * The available font family names. + */ + private static Set<String> availableFontNames; + + /** + * Font spec to file mapping. + */ + private static Map<String,Map<String,String>> fontToFileMap; + + static + { + fontProperties = new Properties(); + InputStream in = OpenTypeFontPeer.class.getResourceAsStream("fonts.properties"); + try + { + fontProperties.load(in); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + private class XLineMetrics + extends LineMetrics + { + + private Font font; + private GlyphVector glyphVector; +// private CharacterIterator characterIterator; +// private int begin; +// private int limit; + private FontRenderContext fontRenderContext; + XLineMetrics(Font f, CharacterIterator ci, int b, int l, + FontRenderContext rc) + { + font = f; +// characterIterator = ci; +// begin = b; +// limit = l; + fontRenderContext = rc; + glyphVector = fontDelegate.createGlyphVector(font, fontRenderContext, + ci); + } + + public float getAscent() + { + return fontDelegate.getAscent(font.getSize(), fontRenderContext.getTransform(), + fontRenderContext.isAntiAliased(), + fontRenderContext.usesFractionalMetrics(), true); + } + + public int getBaselineIndex() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public float[] getBaselineOffsets() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public float getDescent() + { + return (int) fontDelegate.getDescent(font.getSize(), IDENDITY, false, + false, false); + } + + public float getHeight() + { + return (float) glyphVector.getLogicalBounds().getHeight(); + } + + public float getLeading() + { + return getHeight() - getAscent() - getDescent(); + } + + public int getNumChars() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public float getStrikethroughOffset() + { + return 0.F; + } + + public float getStrikethroughThickness() + { + return 0.F; + } + + public float getUnderlineOffset() + { + return 0.F; + } + + public float getUnderlineThickness() + { + return 0.F; + } + + } + + private class XFontMetrics + extends FontMetrics + { + /** + * A cached point instance, to be used in #charWidth(). + */ + private Point2D cachedPoint = new Point2D.Double(); + + XFontMetrics(Font f) + { + super(f); + } + + public int getAscent() + { + return (int) fontDelegate.getAscent(getFont().getSize(), IDENDITY, + false, false, false); + } + + public int getDescent() + { + return (int) fontDelegate.getDescent(getFont().getSize(), IDENDITY, + false, false, false); + } + + public int getHeight() + { + GlyphVector gv = fontDelegate.createGlyphVector(getFont(), + new FontRenderContext(IDENDITY, false, false), + new StringCharacterIterator("m")); + Rectangle2D b = gv.getVisualBounds(); + return (int) b.getHeight(); + } + + public int charWidth(char c) + { + int code = fontDelegate.getGlyphIndex(c); + Point2D advance = cachedPoint; + fontDelegate.getAdvance(code, font.getSize2D(), IDENDITY, + false, false, true, advance); + return (int) advance.getX(); + } + + public int charsWidth(char[] chars, int offs, int len) + { + return stringWidth(new String(chars, offs, len)); + } + + public int stringWidth(String s) + { + GlyphVector gv = fontDelegate.createGlyphVector(getFont(), + new FontRenderContext(IDENDITY, false, false), + new StringCharacterIterator(s)); + Rectangle2D b = gv.getVisualBounds(); + return (int) b.getWidth(); + } + } + + /** + * The indendity transform, to be used in several methods. + */ + private static final AffineTransform IDENDITY = new AffineTransform(); + + private FontDelegate fontDelegate; + + public OpenTypeFontPeer(String name, int style, int size) + { + super(name, style, size); + try + { + String fontSpec = encodeFont(name, style); + String filename = mapFontToFilename(fontSpec); + File fontfile = new File(filename); + FileInputStream in = new FileInputStream(fontfile); + FileChannel ch = in.getChannel(); + ByteBuffer buffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, + fontfile.length()); + fontDelegate = FontFactory.createFonts(buffer)[0]; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + public OpenTypeFontPeer(String name, Map atts) + { + super(name, atts); + try + { + String fontSpec = encodeFont(name, atts); + String filename = mapFontToFilename(fontSpec); + File fontfile = new File(filename); + FileInputStream in = new FileInputStream(fontfile); + FileChannel ch = in.getChannel(); + ByteBuffer buffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, + fontfile.length()); + fontDelegate = FontFactory.createFonts(buffer)[0]; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + public boolean canDisplay(Font font, int c) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public int canDisplayUpTo(Font font, CharacterIterator i, int start, int limit) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public String getSubFamilyName(Font font, Locale locale) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public String getPostScriptName(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public int getNumGlyphs(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public int getMissingGlyphCode(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public byte getBaselineFor(Font font, char c) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public String getGlyphName(Font font, int glyphIndex) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public GlyphVector createGlyphVector(Font font, FontRenderContext frc, CharacterIterator ci) + { + return fontDelegate.createGlyphVector(font, frc, ci); + } + + public GlyphVector createGlyphVector(Font font, FontRenderContext ctx, int[] glyphCodes) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public GlyphVector layoutGlyphVector(Font font, FontRenderContext frc, char[] chars, int start, int limit, int flags) + { + StringCharacterIterator i = new StringCharacterIterator(new String(chars), start, limit, 0); + return fontDelegate.createGlyphVector(font, frc, i); + } + + public FontMetrics getFontMetrics(Font font) + { + return new XFontMetrics(font); + } + + public boolean hasUniformLineMetrics(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public LineMetrics getLineMetrics(Font font, CharacterIterator ci, int begin, int limit, FontRenderContext rc) + { + return new XLineMetrics(font, ci, begin, limit, rc); + } + + public Rectangle2D getMaxCharBounds(Font font, FontRenderContext rc) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Encodes a font name + style + size specification into a X logical font + * description (XLFD) as described here: + * + * http://www.meretrx.com/e93/docs/xlfd.html + * + * This is implemented to look up the font description in the + * fonts.properties of this package. + * + * @param name the font name + * @param atts the text attributes + * + * @return the encoded font description + */ + public static String encodeFont(String name, Map atts) + { + String family = name; + if (family == null || family.equals("")) + family = (String) atts.get(TextAttribute.FAMILY); + if (family == null) + family = "SansSerif"; + + int style = 0; + // Detect italic attribute. + Float posture = (Float) atts.get(TextAttribute.POSTURE); + if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR)) + style |= Font.ITALIC; + + // Detect bold attribute. + Float weight = (Float) atts.get(TextAttribute.WEIGHT); + if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0) + style |= Font.BOLD; + + return encodeFont(name, style); + } + + /** + * Encodes a font name + style into a combined string. + * + * This is implemented to look up the font description in the + * fonts.properties of this package. + * + * @param name the font name + * @param style the font style + * + * @return the encoded font description + */ + static String encodeFont(String name, int style) + { + CPStringBuilder key = new CPStringBuilder(); + key.append(validName(name)); + key.append('/'); + switch (style) + { + case Font.BOLD: + key.append("b"); + break; + case Font.ITALIC: + key.append("i"); + break; + case (Font.BOLD | Font.ITALIC): + key.append("bi"); + break; + case Font.PLAIN: + default: + key.append("p"); + + } + + return key.toString(); + } + + /** + * Checks the specified font name for a valid font name. If the font name + * is not known, then this returns 'sansserif' as fallback. + * + * @param name the font name to check + * + * @return a valid font name + */ + static String validName(String name) + { + String retVal; + Set<String> fontNames = getFontNames(); + if (fontNames.contains(name)) + { + retVal = name; + } + else + { + retVal = "SansSerif"; + } + return retVal; + } + + public static String[] getAvailableFontFamilyNames(Locale l) + { + Set<String> fontNames = getFontNames(); + int numNames = fontNames.size(); + String[] ret = fontNames.toArray(new String[numNames]); + return ret; + } + + private static synchronized Set<String> getFontNames() + { + if (availableFontNames == null) + { + HashSet<String> familyNames = new HashSet<String>(); + for (Object o : fontProperties.keySet()) + { + if (o instanceof String) + { + String key = (String) o; + int slashIndex = key.indexOf('/'); + String name = key.substring(0, slashIndex); + familyNames.add(name); + } + } + availableFontNames = familyNames; + } + return availableFontNames; + } + + /** + * Takes a font spec as returned by {@link #encodeFont(String, int)}, + * and returns the corresponding font file, or <code>null</code> if no such + * font mapping exists. + * + * @param fontSpec font name and style as returned by + * {@link #encodeFont(String, int)} + * + * @return filename of the corresponding font file + */ + private synchronized String mapFontToFilename(String fontSpec) + { + if (fontToFileMap == null) + { + fontToFileMap = new HashMap<String,Map<String,String>>(); + + // Initialize font spec to file mapping according to the + // font.properties. + for (Object o : fontProperties.keySet()) + { + if (o instanceof String) + { + String key = (String) o; + int slashIndex = key.indexOf('/'); + String name = key.substring(0, slashIndex); + String spec = key.substring(slashIndex + 1); + // Handle aliases in the 2nd pass below. + if (! spec.equals("a")) + { + Map<String,String> specToFileMap = fontToFileMap.get(name); + if (specToFileMap == null) + { + specToFileMap = new HashMap<String,String>(); + fontToFileMap.put(name, specToFileMap); + } + specToFileMap.put(spec, fontProperties.getProperty(key)); + } + } + } + // 2nd pass for handling aliases. + for (Object o : fontProperties.keySet()) + { + if (o instanceof String) + { + String key = (String) o; + int slashIndex = key.indexOf('/'); + String name = key.substring(0, slashIndex); + String spec = key.substring(slashIndex + 1); + // Handle aliases in the 2nd pass below. + if (spec.equals("a")) + { + String alias = fontProperties.getProperty(key); + Map<String,String> specToFileMap = fontToFileMap.get(alias); + fontToFileMap.put(name, specToFileMap); + } + } + } + } + // Look up font file. + int slashIndex = fontSpec.indexOf('/'); + String name = fontSpec.substring(0, slashIndex); + String spec = fontSpec.substring(slashIndex + 1); + return fontToFileMap.get(name).get(spec); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/AutoHinter.java b/libjava/classpath/gnu/java/awt/font/autofit/AutoHinter.java new file mode 100644 index 000000000..b0420ab7a --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/AutoHinter.java @@ -0,0 +1,83 @@ +/* AutoHinter.java -- The entry point into the hinter implementation. + 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.font.autofit; + +import gnu.java.awt.font.opentype.Hinter; +import gnu.java.awt.font.opentype.OpenTypeFont; +import gnu.java.awt.font.opentype.truetype.Fixed; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * The public interface to the automatic gridfitter. + */ +public class AutoHinter + implements Hinter +{ + Latin latinScript; + LatinMetrics metrics; + GlyphHints hints; + + HintScaler scaler = new HintScaler(); + public void init(OpenTypeFont font) + { + // TODO: Should support other scripts too. + latinScript = new Latin(); + metrics = new LatinMetrics(font); + latinScript.initMetrics(metrics, font); + scaler.face = font; + } + + public void applyHints(Zone outline) + { + if (hints == null) + hints = new GlyphHints(); + scaler.xScale = Fixed.valueOf16(outline.scaleX * 64); + scaler.yScale = Fixed.valueOf16(outline.scaleY * 64); + latinScript.scaleMetrics(metrics, scaler); + latinScript.applyHints(hints, outline, metrics); + } + + public void setFlags(int flags) + { + if (hints == null) + hints = new GlyphHints(); + hints.flags = flags; + } + +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java b/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java new file mode 100644 index 000000000..87f2abcc3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java @@ -0,0 +1,112 @@ +/* AxisHints.java -- Hints specific to an axis + 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.font.autofit; + +class AxisHints +{ + + Segment[] segments; + int majorDir; + int numSegments; + int numEdges; + Edge[] edges; + + AxisHints() + { + segments = new Segment[4]; + edges = new Edge[4]; + } + + Segment newSegment() + { + if (numSegments >= segments.length) + { + // Grow array. + int newMax = segments.length; + newMax += (newMax >> 2) + 4; // From FreeType. + Segment[] newSegs = new Segment[newMax]; + System.arraycopy(segments, 0, newSegs, 0, numSegments); + segments = newSegs; + } + Segment seg = new Segment(); + segments[numSegments] = seg; + numSegments++; + return seg; + } + + public Edge newEdge(int pos) + { + if (numEdges >= edges.length) + { + // Grow array. + int newMax = edges.length; + newMax += (newMax >> 2) + 4; // From FreeType. + Edge[] newEdges = new Edge[newMax]; + System.arraycopy(edges, 0, newEdges, 0, numEdges); + edges = newEdges; + } + int edgeIndex = numEdges; + Edge edge = edges[edgeIndex] = new Edge(); + while (edgeIndex > 0 && edges[edgeIndex - 1].fpos > pos) + { + edges[edgeIndex] = edges[edgeIndex - 1]; + edgeIndex--; + } + edges[edgeIndex] = edge; + numEdges++; + edge.fpos = pos; + + return edge; + + } + + int getEdgeIndex(Edge edge2) + { + int idx = -1; + for (int i = 0; i < numEdges; i++) + { + if (edges[i] == edge2) + { + idx = i; + break; + } + } + return idx; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Constants.java b/libjava/classpath/gnu/java/awt/font/autofit/Constants.java new file mode 100644 index 000000000..c5b90fa54 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Constants.java @@ -0,0 +1,86 @@ +/* Constants.java -- Some constants used in the autofitter + 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.font.autofit; + +/** + * Some constants used in the autofitter. + */ +interface Constants +{ + + /** + * The horizontal dimension. + */ + static final int DIMENSION_HORZ = 0; + + /** + * The vertical dimension. + */ + static final int DIMENSION_VERT = 1; + + /** + * The number of dimensions. + */ + static final int DIMENSION_MAX = 2; + + /** + * Indicates a vector with no specific direction. + */ + static final int DIR_NONE = 0; + + /** + * Right direction. + */ + static final int DIR_RIGHT = 1; + + /** + * Left direction. + */ + static final int DIR_LEFT = -1; + + /** + * Up direction. + */ + static final int DIR_UP = 2; + + /** + * Down direction. + */ + static final int DIR_DOWN = -2; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Edge.java b/libjava/classpath/gnu/java/awt/font/autofit/Edge.java new file mode 100644 index 000000000..6420fa1cb --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Edge.java @@ -0,0 +1,82 @@ +/* Edge.java -- An edge of segments + 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.font.autofit; + +import gnu.java.lang.CPStringBuilder; + +class Edge +{ + int fpos; + Segment first; + Segment last; + int opos; + Edge link; + Edge serif; + int flags; + int dir; + Width blueEdge; + int pos; + int scale; + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Edge] id"); + s.append(hashCode()); + s.append(", fpos: "); + s.append(fpos); + s.append(", opos: "); + s.append(opos); + s.append(", pos: "); + s.append(pos); + s.append(", dir: "); + s.append(dir); + s.append(", serif: "); + s.append(serif != null ? serif.hashCode() : "null"); + s.append(", link: "); + s.append(link != null ? link.hashCode() : "null"); + s.append(", flags: " + flags); + s.append(", blue: " + blueEdge); + s.append(", first: "); + s.append(first == null ? "null" : first.hashCode()); + s.append(", last: "); + s.append(last == null ? "null" : last.hashCode()); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java b/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java new file mode 100644 index 000000000..033d63fa4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java @@ -0,0 +1,640 @@ +/* GlyphHints.java -- Data and methods for actual hinting + 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.font.autofit; + +import gnu.java.awt.font.FontDelegate; +import gnu.java.awt.font.opentype.truetype.Fixed; +import gnu.java.awt.font.opentype.truetype.Point; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * The data and methods used for the actual hinting process. + */ +class GlyphHints + implements Constants +{ + + int xScale; + int xDelta; + int yScale; + int yDelta; + + AxisHints[] axis; + + Point[] points; + int numPoints; + int maxPoints; + + Point[] contours; + int numContours; + int maxContours; + + ScriptMetrics metrics; + + int flags; + + GlyphHints() + { + axis = new AxisHints[Constants.DIMENSION_MAX]; + axis[Constants.DIMENSION_VERT] = new AxisHints(); + axis[Constants.DIMENSION_HORZ] = new AxisHints(); + + xScale = Fixed.ONE; + yScale = Fixed.ONE; + } + + void rescale(ScriptMetrics m) + { + metrics = m; + // TODO: Copy scalerFlags. + } + + void reload(Zone outline) + { + numPoints = 0; + numContours = 0; + axis[0].numSegments = 0; + axis[0].numEdges = 0; + axis[1].numSegments = 0; + axis[1].numEdges = 0; + + // Create/reallocate the contours array. + int newMax = outline.getNumContours(); + if (newMax > maxContours || contours == null) + { + newMax = (newMax + 3) & ~3; // Taken from afhints.c . + Point[] newContours = new Point[newMax]; + if (contours != null) + { + System.arraycopy(contours, 0, newContours, 0, maxContours); + } + contours = newContours; + maxContours = newMax; + } + + // Create/reallocate the points array. + newMax = outline.getSize() + 2; + if (newMax > maxPoints || points == null) + { + newMax = (newMax + 2 + 7) & ~7; // Taken from afhints.c . + Point[] newPoints = new Point[newMax]; + if (points != null) + { + System.arraycopy(points, 0, newPoints, 0, maxPoints); + } + points = newPoints; + maxPoints = newMax; + } + + numPoints = outline.getSize() - 4; // 4 phantom points. + numContours = outline.getNumContours(); + + // Set major direction. We don't handle Type 1 fonts yet. + axis[DIMENSION_HORZ].majorDir = DIR_UP; + axis[DIMENSION_VERT].majorDir = DIR_LEFT; + + // TODO: Freetype seems to scale and translate the glyph at that point. + // I suppose that this is not really needed. + // The scales are scaling from font units to 1/64 device pixels. + xScale = Fixed.valueOf16(outline.scaleX * 64); + yScale = Fixed.valueOf16(outline.scaleY * 64); + + // FIXME: What is that xDelta and yDelta used for? + System.arraycopy(outline.getPoints(), 0, points, 0, numPoints); + + // Setup prev and next and contours array. + // TODO: Probably cache this. + contours = new Point[numContours]; + Point currentContour = points[0]; + for (int i = 0, cIndex = 0; i < numPoints; i++) + { + // Start new contour when the last point has been a contour end. + if (outline.isContourEnd(i)) + { + // Connect the contour end point to the start point. + points[i].setNext(currentContour); + currentContour.setPrev(points[i]); + contours[cIndex] = currentContour; + cIndex++; + currentContour = i < numPoints - 1 ? points[i + 1] : null; + } + else + { + // Connect the current and the previous point. + points[i].setNext(points[i + 1]); + points[i + 1].setPrev(points[i]); + } + } + // Compute directions of in and out vectors of all points as well + // as the weak point flag. + for (int i = 0; i < numPoints; i++) + { + // Compute in and out dir. + Point p = points[i]; + Point prev = p.getPrev(); + int inX = p.getOrigX() - prev.getOrigX(); + int inY = p.getOrigY() - prev.getOrigY(); + p.setInDir(Utils.computeDirection(inX, inY)); + Point next = p.getNext(); + int outX = next.getOrigX() - p.getOrigX(); + int outY = next.getOrigY() - p.getOrigY(); + p.setOutDir(Utils.computeDirection(outX, outY)); + + if (p.isControlPoint()) + { + setWeakPoint(p); + } + else if (p.getOutDir() == p.getInDir()) + { + if (p.getOutDir() != DIR_NONE) + setWeakPoint(p); + else + { + int angleIn = Utils.atan(inY, inX); + int angleOut = Utils.atan(outY, outX); + int delta = Utils.angleDiff(angleIn, angleOut); + if (delta < 2 && delta > -2) + setWeakPoint(p); + } + } + else if (p.getInDir() == - p.getOutDir()) + { + setWeakPoint(p); + } + } + computeInflectionPoints(); + } + + private void setWeakPoint(Point p) + { + p.setFlags((byte) (p.getFlags() | Point.FLAG_WEAK_INTERPOLATION)); + } + + /** + * Computes the inflection points for a glyph. + */ + private void computeInflectionPoints() + { + // Do each contour separately. + contours : for (int c = 0; c < contours.length; c++) + { + Point point = contours[c]; + Point first = point; + Point start = point; + Point end = point; + do + { + end = end.getNext(); + if (end == first) + continue contours; + } while (end.getOrigX() == first.getOrigX() + && end.getOrigY() == first.getOrigY()); + + // Extend segment start whenever possible. + Point before = start; + int angleIn; + int angleSeg = Utils.atan(end.getOrigX() - start.getOrigX(), + end.getOrigY() - start.getOrigY()); + do + { + do + { + start = before; + before = before.getPrev(); + if (before == first) + continue contours; + } while (before.getOrigX() == start.getOrigX() + && before.getOrigY() == start.getOrigY()); + angleIn = Utils.atan(start.getOrigX() - before.getOrigX(), + start.getOrigY() - before.getOrigY()); + } while (angleIn == angleSeg); + + first = start; + int diffIn = Utils.angleDiff(angleIn, angleSeg); + // Now, process all segments in the contour. + Point after; + boolean finished = false; + int angleOut, diffOut; + do + { + // First, extend the current segment's end whenever possible. + after = end; + do + { + do + { + end = after; + after = after.getNext(); + if (after == first) + finished = true; + } while (end.getOrigX() == after.getOrigX() + && end.getOrigY() == after.getOrigY()); + angleOut = Utils.atan(after.getOrigX() - end.getOrigX(), + after.getOrigY() - end.getOrigY()); + } while (angleOut == angleSeg); + diffOut = Utils.angleDiff(angleSeg, angleOut); + if ((diffIn ^ diffOut) < 0) + { + // diffIn and diffOut have different signs, we have + // inflection points here. + do + { + start.addFlags(Point.FLAG_INFLECTION); + start = start.getNext(); + } while (start != end); + start.addFlags(Point.FLAG_INFLECTION); + } + start = end; + end = after; + angleSeg = angleOut; + diffIn = diffOut; + } while (! finished); + } + } + + boolean doHorizontal() + { + return (flags & FontDelegate.FLAG_NO_HINT_HORIZONTAL) == 0; + } + + boolean doVertical() + { + return (flags & FontDelegate.FLAG_NO_HINT_VERTICAL) == 0; + } + + void alignWeakPoints(int dim) + { + short touchFlag; + Point point; + // PASS 1 : Move segments to edge positions. + if (dim == DIMENSION_HORZ) + { + touchFlag = Point.FLAG_DONE_X; + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setU(point.getX()); + point.setV(point.getScaledX()); + } + } + else + { + touchFlag = Point.FLAG_DONE_Y; + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setU(point.getY()); + point.setV(point.getScaledY()); + } + } + point = points[0]; + for (int c = 0; c < numContours; c++) + { + point = contours[c]; + int idx = getPointIndex(point); + Point endPoint = point.getPrev(); + int endIdx = getPointIndex(endPoint); + int firstIdx = idx; + while (idx <= endIdx + && (point.getFlags() & touchFlag) == 0) + { + idx++; + point = points[idx]; + } + if (idx <= endIdx) + { + int firstTouched = idx; + int curTouched = idx; + idx++; + point = points[idx]; + while (idx <= endIdx) + { + if ((point.getFlags() & touchFlag) != 0) + { + // We found two successive touch points. We interpolate + // all contour points between them. + iupInterp(curTouched + 1, idx - 1, curTouched, idx); + curTouched = idx; + } + idx++; + point = points[idx]; + } + if (curTouched == firstTouched) + { + // This is a special case: Only one point was touched in the + // contour. We thus simply shift the whole contour. + iupShift(firstIdx, endIdx, curTouched); + } + else + { + // Now interpolate after the last touched point to the end + // of the contour. + iupInterp(curTouched + 1, endIdx, curTouched, firstTouched); + // If the first contour point isn't touched, interpolate + // from the contour start to the first touched point. + if (firstTouched > 0) + { + iupInterp(firstIdx, firstTouched - 1, curTouched, + firstTouched); + } + } + } + } + // Now store the values back. + if (dim == DIMENSION_HORZ) + { + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setX(point.getU()); + } + } + else + { + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setY(point.getU()); + } + } + } + + private void iupShift(int p1, int p2, int ref) + { + int delta = points[ref].getU() - points[ref].getV(); + for (int p = p1; p < ref; p++) + { + points[p].setU(points[p].getV() + delta); + } + for (int p = ref + 1; p <= p2; p++) + { + points[p].setU(points[p].getV() + delta); + } + } + + private void iupInterp(int p1, int p2, int ref1, int ref2) + { + int v1 = points[ref1].getV(); + int v2 = points[ref2].getV(); + int d1 = points[ref1].getU() - v1; + int d2 = points[ref2].getU() - v2; + if (p1 > p2) + return; + if (v1 == v2) + { + for (int p = p1; p <= p2; p++) + { + int u = points[p].getV(); + if (u <= v1) + u += d1; + else + u += d2; + points[p].setU(u); + } + } + else if (v1 < v2) + { + for (int p = p1; p <= p2; p++) + { + int u = points[p].getV(); + if (u <= v1) + u += d1; + else if (u >= v2) + u += d2; + else + { + u = points[ref1].getU() + Utils.mulDiv(u - v1, + points[ref2].getU() + - points[ref1].getU(), + v2 - v1); + } + points[p].setU(u); + } + } + else + { + for (int p = p1; p <= p2; p++) + { + int u = points[p].getV(); + if (u <= v2) + u += d2; + else if (u >= v1) + u += d1; + else + { + u = points[ref1].getU() + Utils.mulDiv(u - v1, + points[ref2].getU() + - points[ref1].getU(), + v2 - v1); + } + points[p].setU(u); + } + } + } + + void alignStrongPoints(int dim) + { + AxisHints ax = axis[dim]; + Edge[] edges = ax.edges; + int numEdges = ax.numEdges; + short touchFlag; + if (dim == DIMENSION_HORZ) + touchFlag = Point.FLAG_DONE_X; + else + touchFlag = Point.FLAG_DONE_Y; + + if (numEdges > 0) + { + for (int p = 0; p < numPoints; p++) + { + Point point = points[p]; + if ((point.getFlags() & touchFlag) != 0) + continue; + // If this point is a candidate for weak interpolation, we + // interpolate it after all strong points have been processed. + if ((point.getFlags() & Point.FLAG_WEAK_INTERPOLATION) != 0 + && (point.getFlags() & Point.FLAG_INFLECTION) == 0) + continue; + + int u, ou, fu, delta; + if (dim == DIMENSION_VERT) + { + u = point.getOrigY(); + ou = point.getScaledY(); + } + else + { + u = point.getOrigX(); + ou = point.getScaledX(); + } + fu = u; + // Is the point before the first edge? + Edge edge = edges[0]; + // Inversed vertical dimension. + delta = edge.fpos - u; + if (delta >= 0) + { + u = edge.pos - (edge.opos - ou); + storePoint(point, u, dim, touchFlag); + } + else + { + // Is the point after the last edge? + edge = edges[numEdges - 1]; + delta = u - edge.fpos; + if (delta >= 0) + { + u = edge.pos + (ou - edge.opos); + storePoint(point, u, dim, touchFlag); + } + else + { + // Find enclosing edges. + int min = 0; + int max = numEdges; + int mid, fpos; + boolean found = false; + while (min < max) + { + mid = (max + min) / 2; + edge = edges[mid]; + fpos = edge.fpos; + if (u < fpos) + max = mid; + else if (u > fpos) + min = mid + 1; + else + { + // Directly on the edge. + u = edge.pos; + storePoint(point, u, dim, touchFlag); + found = true; + break; + } + } + if (! found) + { + Edge before = edges[min - 1]; + Edge after = edges[min]; + if (before.scale == 0) + { + before.scale = Fixed.div16(after.pos - before.pos, + after.fpos - before.fpos); + } + u = before.pos + Fixed.mul16(fu - before.fpos, + before.scale); + } + storePoint(point, u, dim, touchFlag); + } + } + } + } + } + + private void storePoint(Point p, int u, int dim, short touchFlag) + { + if (dim == DIMENSION_HORZ) + p.setX(u); + else + p.setY(u); + p.addFlags(touchFlag); + } + + void alignEdgePoints(int dim) + { + AxisHints ax = axis[dim]; + Edge[] edges = ax.edges; + int numEdges = ax.numEdges; + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + Segment seg = edge.first; + do + { + Point point = seg.first; + while (true) + { + if (dim == DIMENSION_HORZ) + { + point.setX(edge.pos); + point.addFlags(Point.FLAG_DONE_X); + } + else + { + point.setY(edge.pos); + point.addFlags(Point.FLAG_DONE_Y); + } + if (point == seg.last) + break; + point = point.getNext(); + } + seg = seg.edgeNext; + } while (seg != edge.first); + } + } + + private int getPointIndex(Point p) + { + int idx = -1; + for (int i = 0; i < numPoints; i++) + { + if (p == points[i]) + { + idx = i; + break; + } + } + return idx; + } + + public boolean doAlignEdgePoints() + { + return (flags & FontDelegate.FLAG_NO_HINT_EDGE_POINTS) == 0; + } + + public boolean doAlignStrongPoints() + { + return (flags & FontDelegate.FLAG_NO_HINT_STRONG_POINTS) == 0; + } + + public boolean doAlignWeakPoints() + { + return (flags & FontDelegate.FLAG_NO_HINT_WEAK_POINTS) == 0; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/HintScaler.java b/libjava/classpath/gnu/java/awt/font/autofit/HintScaler.java new file mode 100644 index 000000000..01276b4db --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/HintScaler.java @@ -0,0 +1,53 @@ +/* Scaler.java -- FIXME: briefly describe file purpose + 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.font.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; + +class HintScaler +{ + + int xScale; + int xDelta; + int yScale; + int yDelta; + OpenTypeFont face; + int renderMode; + +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Latin.java b/libjava/classpath/gnu/java/awt/font/autofit/Latin.java new file mode 100644 index 000000000..c132c2cdc --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Latin.java @@ -0,0 +1,1363 @@ +/* Latin.java -- Latin specific glyph handling + 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.font.autofit; + +import java.awt.geom.AffineTransform; +import java.util.HashSet; + +import gnu.java.awt.font.opentype.OpenTypeFont; +import gnu.java.awt.font.opentype.truetype.Fixed; +import gnu.java.awt.font.opentype.truetype.Point; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * Implements Latin specific glyph handling. + */ +class Latin + implements Script, Constants +{ + + static final int MAX_WIDTHS = 16; + + private final static int MAX_TEST_CHARS = 12; + + /** + * The types of the 6 blue zones. + */ + private static final int CAPITAL_TOP = 0; + private static final int CAPITAL_BOTTOM = 1; + private static final int SMALL_F_TOP = 2; + private static final int SMALL_TOP = 3; + private static final int SMALL_BOTTOM = 4; + private static final int SMALL_MINOR = 5; + static final int BLUE_MAX = 6; + + /** + * The test chars for the blue zones. + * + * @see #initBlues(LatinMetrics, OpenTypeFont) + */ + private static final String[] TEST_CHARS = + new String[]{"THEZOCQS", "HEZLOCUS", "fijkdbh", + "xzroesc", "xzroesc", "pqgjy"}; + + public void applyHints(GlyphHints hints, Zone outline, ScriptMetrics metrics) + { + hints.reload(outline); + hints.rescale(metrics); + if (hints.doHorizontal()) + { + detectFeatures(hints, DIMENSION_HORZ); + } + if (hints.doVertical()) + { + detectFeatures(hints, DIMENSION_VERT); + computeBlueEdges(hints, (LatinMetrics) metrics); + } + // Grid-fit the outline. + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + if (dim == DIMENSION_HORZ && hints.doHorizontal() + || dim == DIMENSION_VERT && hints.doVertical()) + { + hintEdges(hints, dim); + if (hints.doAlignEdgePoints()) + hints.alignEdgePoints(dim); + if (hints.doAlignStrongPoints()) + hints.alignStrongPoints(dim); + if (hints.doAlignWeakPoints()) + hints.alignWeakPoints(dim); + + } + } + // FreeType does a save call here. I guess that's not needed as we operate + // on the live glyph data anyway. + } + + private void hintEdges(GlyphHints hints, int dim) + { + AxisHints axis = hints.axis[dim]; + Edge[] edges = axis.edges; + int numEdges = axis.numEdges; + Edge anchor = null; + int hasSerifs = 0; + + // We begin by aligning all stems relative to the blue zone if + // needed -- that's only for horizontal edges. + if (dim == DIMENSION_VERT) + { + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0) + continue; + + Width blue = edge.blueEdge; + Edge edge1 = null; + Edge edge2 = edge.link; + if (blue != null) + { + edge1 = edge; + } + else if (edge2 != null && edge2.blueEdge != null) + { + blue = edge2.blueEdge; + edge1 = edge2; + edge2 = edge; + } + if (edge1 == null) + continue; + + edge1.pos = blue.fit; + edge1.flags |= Segment.FLAG_EDGE_DONE; + + if (edge2 != null && edge2.blueEdge == null) + { + alignLinkedEdge(hints, dim, edge1, edge2); + edge2.flags |= Segment.FLAG_EDGE_DONE; + } + if (anchor == null) + anchor = edge; + } + } + + // Now we will align all stem edges, trying to maintain the + // relative order of stems in the glyph. + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0) + continue; + Edge edge2 = edge.link; + if (edge2 == null) + { + hasSerifs++; + continue; + } + // Now align the stem. + // This should not happen, but it's better to be safe. + if (edge2.blueEdge != null || axis.getEdgeIndex(edge2) < e) + { + alignLinkedEdge(hints, dim, edge2, edge); + edge.flags |= Segment.FLAG_EDGE_DONE; + continue; + } + + if (anchor == null) + { + int orgLen = edge2.opos - edge.opos; + int curLen = computeStemWidth(hints, dim, orgLen, edge.flags, + edge2.flags); + int uOff, dOff, orgCenter, curPos1, error1, error2; + if (curLen <= 64) // < 1 Pixel. + { + uOff = 32; + dOff = 32; + } + else + { + uOff = 38; + dOff = 26; + } + if (curLen < 96) + { + orgCenter = edge.opos + (orgLen >> 1); + curPos1 = Utils.pixRound(orgCenter); + error1 = orgCenter - (curPos1 - uOff); + if (error1 < 0) + error1 = -error1; + error2 = orgCenter - (curPos1 + dOff); + if (error2 < 0) + error2 = -error2; + if (error1 < error2) + { + curPos1 -= uOff; + } + else + { + curPos1 += dOff; + } + edge.pos = curPos1 - curLen / 2; + edge2.pos = curPos1 + curLen / 2; + } + else + { + edge.pos = Utils.pixRound(edge.opos); + } + anchor = edge; + edge.flags |= Segment.FLAG_EDGE_DONE; + alignLinkedEdge(hints, dim, edge, edge2); + } + else + { + int aDiff = edge.opos - anchor.opos; + int orgPos = anchor.pos + aDiff; + int orgLen = edge2.opos - edge.opos; + int orgCenter = orgPos + (orgLen >> 1); + int curLen = computeStemWidth(hints, dim, orgLen, edge.flags, + edge2.flags); + //System.err.println("stem width: " + curLen); + if (curLen < 96) + { + int uOff, dOff; + int curPos1 = Utils.pixRound(orgCenter); + if (curLen <= 64) + { + uOff = 32; + dOff = 32; + } + else + { + uOff = 38; + dOff = 26; + } + int delta1 = orgCenter - (curPos1 - uOff); + if (delta1 < 0) + delta1 = -delta1; + int delta2 = orgCenter - (curPos1 + dOff); + if (delta2 < 0) + delta2 = -delta2; + if (delta1 < delta2) + { + curPos1 -= uOff; + } + else + { + curPos1 += dOff; + } + edge.pos = curPos1 - curLen / 2; + edge2.pos = curPos1 + curLen / 2; + } + else + { + orgPos = anchor.pos + (edge.opos - anchor.opos); + orgLen = edge2.opos - edge.opos; + orgCenter = orgPos + (orgLen >> 1); + curLen = computeStemWidth(hints, dim, orgLen, edge.flags, + edge2.flags); + int curPos1 = Utils.pixRound(orgPos); + int delta1 = curPos1 + (curLen >> 1) - orgCenter; + if (delta1 < 0) + delta1 = -delta1; + int curPos2 = Utils.pixRound(orgPos + orgLen) - curLen; + int delta2 = curPos2 + (curLen >> 1) - orgCenter; + if (delta2 < 0) + delta2 = -delta2; + edge.pos = (delta1 < delta2) ? curPos1 : curPos2; + edge2.pos = edge.pos + curLen; + } + edge.flags |= Segment.FLAG_EDGE_DONE; + edge2.flags |= Segment.FLAG_EDGE_DONE; + + if (e > 0 && edge.pos < edges[e - 1].pos) + { + edge.pos = edges[e - 1].pos; + } + } + } + // TODO: Implement the lowercase m symmetry thing. + + // Now we hint the remaining edges (serifs and singles) in order + // to complete our processing. + if (hasSerifs > 0 || anchor == null) + { + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0) + continue; + if (edge.serif != null) + { + alignSerifEdge(hints, edge.serif, edge); + } + else if (anchor == null) + { + edge.pos = Utils.pixRound(edge.opos); + anchor = edge; + } + else + { + edge.pos = anchor.pos + + Utils.pixRound(edge.opos - anchor.opos); + } + edge.flags |= Segment.FLAG_EDGE_DONE; + + if (e > 0 && edge.pos < edges[e - 1].pos) + { + edge.pos = edges[e - 1].pos; + } + if (e + 1 < numEdges + && (edges[e + 1].flags & Segment.FLAG_EDGE_DONE) != 0 + && edge.pos > edges[e + 1].pos) + { + edge.pos = edges[e + 1].pos; + } + } + } + + // Debug: print all hinted edges. + // System.err.println("hinted edges: " ); + // for (int i = 0; i < numEdges; i++) + // { + // System.err.println("edge#" + i + ": " + edges[i]); + // } + } + + private void alignSerifEdge(GlyphHints hints, Edge base, Edge serif) + { + serif.pos = base.pos + (serif.opos - base.opos); + } + + private int computeStemWidth(GlyphHints hints, int dim, int width, + int baseFlags, int stemFlags) + { + LatinMetrics metrics = (LatinMetrics) hints.metrics; + LatinAxis axis = metrics.axis[dim]; + int dist = width; + int sign = 0; + boolean vertical = dim == DIMENSION_VERT; + if (! doStemAdjust(hints)) + return width; + if (dist < 0) + { + dist = -width; + sign = 1; + } + if ((vertical && ! doVertSnap(hints)) || ! vertical && ! doHorzSnap(hints)) + { + // Smooth hinting process. Very lightly quantize the stem width. + // Leave the widths of serifs alone. + if ((stemFlags & Segment.FLAG_EDGE_SERIF) != 0 && vertical + && dist < 3 * 64) + { + return doneWidth(dist, sign); + } + else if ((baseFlags & Segment.FLAG_EDGE_ROUND) != 0) + { + if (dist < 80) + dist = 64; + } + else if (dist < 56) + { + dist = 56; + } + if (axis.widthCount > 0) + { + int delta; + if (axis.widthCount > 0) + { + delta = dist - axis.widths[0].cur; + if (delta < 0) + { + delta = -delta; + } + if (delta < 40) + { + dist = axis.widths[0].cur; + if (dist < 48) + dist = 48; + return doneWidth(dist, sign); + } + } + if (dist < 3 * 64) // < 3 pixels. + { + delta = dist & 63; + dist &= -64; + if (delta < 10) + dist += delta; + else if (delta < 32) + dist += 10; + else if (delta < 54) + dist += 54; + else + dist += delta; + + } + else + { + dist = (dist + 32) & ~63; + } + } + } + else + { + // Strong hinting process: Snap the stem width to integer pixels. + dist = snapWidth(axis.widths, axis.widthCount, dist); + if (vertical) + { + // In the case of vertical hinting, always round + // the stem heights to integer pixels. + if (dist >= 64) + dist = (dist + 16) & ~63; + else + dist = 64; + } + else + { + if (doMono(hints)) + { + // Monochrome horizontal hinting: Snap widths to integer pixels + // with a different threshold. + if (dist < 64) + dist = 64; + else + dist = (dist + 32) & ~63; + } + else + { + // For anti-aliased hinting, we adopt a more subtle + // approach: We strengthen small stems, round those stems + // whose size is between 1 and 2 pixels to an integer, + // otherwise nothing. + if (dist < 48) + dist = (dist + 64) >> 1; + else if (dist < 128) + dist = (dist + 22) & ~63; + else + // Round otherwise to prevent color fringes in LCD mode. + dist = (dist + 32) & ~63; + } + } + } + return doneWidth(dist, sign); + } + + private boolean doMono(GlyphHints hints) + { + return true; + } + + private int snapWidth(Width[] widths, int count, int width) + { + int best = 64 + 32 + 2; + int reference = width; + for (int n = 0; n < count; n++) + { + int w = widths[n].cur; + int dist = width - w; + if (dist < 0) + dist = -dist; + if (dist < best) + { + best = dist; + reference = w; + } + } + int scaled = Utils.pixRound(reference); + if (width >= reference) + { + if (width < scaled + 48) + width = reference; + } + else + { + if (width > scaled + 48) + width = reference; + } + return width; + } + + private int doneWidth(int w, int s) + { + if (s == 1) + w = -w; + return w; + } + + private boolean doVertSnap(GlyphHints hints) + { + // TODO Auto-generated method stub + return true; + } + + private boolean doHorzSnap(GlyphHints hints) + { + // TODO Auto-generated method stub + return true; + } + + private boolean doStemAdjust(GlyphHints hints) + { + // TODO Auto-generated method stub + return true; + } + + private void alignLinkedEdge(GlyphHints hints, int dim, Edge base, Edge stem) + { + int dist = stem.opos - base.opos; + int fitted = computeStemWidth(hints, dim, dist, base.flags, stem.flags); + stem.pos = base.pos + fitted; + } + + public void doneMetrics(ScriptMetrics metrics) + { + // TODO Auto-generated method stub + + } + + /** + * Initializes the <code>hints</code> object. + * + * @param hints the hints to initialize + * @param metrics the metrics to use + */ + public void initHints(GlyphHints hints, ScriptMetrics metrics) + { + hints.rescale(metrics); + LatinMetrics lm = (LatinMetrics) metrics; + hints.xScale = lm.axis[DIMENSION_HORZ].scale; + hints.xDelta = lm.axis[DIMENSION_HORZ].delta; + hints.yScale = lm.axis[DIMENSION_VERT].scale; + hints.yDelta = lm.axis[DIMENSION_VERT].delta; + // TODO: Set the scaler and other flags. + } + + /** + * Initializes the script metrics. + * + * @param metrics the script metrics to initialize + * @param face the font + */ + public void initMetrics(ScriptMetrics metrics, OpenTypeFont face) + { + assert metrics instanceof LatinMetrics; + LatinMetrics lm = (LatinMetrics) metrics; + lm.unitsPerEm = face.unitsPerEm; + + // TODO: Check for latin charmap. + + initWidths(lm, face, 'o'); + initBlues(lm, face); + } + + public void scaleMetrics(ScriptMetrics metrics, HintScaler scaler) + { + LatinMetrics lm = (LatinMetrics) metrics; + lm.scaler.renderMode = scaler.renderMode; + lm.scaler.face = scaler.face; + scaleMetricsDim(lm, scaler, DIMENSION_HORZ); + scaleMetricsDim(lm, scaler, DIMENSION_VERT); + } + + private void scaleMetricsDim(LatinMetrics lm, HintScaler scaler, int dim) + { + int scale; + int delta; + if (dim == DIMENSION_HORZ) + { + scale = scaler.xScale; + delta = scaler.xDelta; + } + else + { + scale = scaler.yScale; + delta = scaler.yDelta; + } + LatinAxis axis = lm.axis[dim]; + if (axis.orgScale == scale && axis.orgDelta == delta) + // No change, no need to adjust. + return; + axis.orgScale = scale; + axis.orgDelta = delta; + + // Correct X and Y scale to optimize the alignment of the top small + // letters to the pixel grid. + LatinAxis axis2 = lm.axis[DIMENSION_VERT]; + LatinBlue blue = null; +// for (int nn = 0; nn < axis2.blueCount; nn++) +// { +// if ((axis2.blues[nn].flags & LatinBlue.FLAG_ADJUSTMENT) != 0) +// { +// blue = axis2.blues[nn]; +// break; +// } +// } +// if (blue != null) +// { +// int scaled = Fixed.mul16(blue.shoot.org, scaler.yScale); +// int fitted = Utils.pixRound(scaled); +// if (scaled != fitted) +// { +// if (dim == DIMENSION_HORZ) +// { +// if (fitted < scaled) +// { +// scale -= scale / 50; +// } +// } +// else +// { +// scale = Utils.mulDiv(scale, fitted, scaled); +// } +// } +// } + axis.scale = scale; + axis.delta = delta; + if (dim == DIMENSION_HORZ) + { + lm.scaler.xScale = scale; + lm.scaler.xDelta = delta; + } + else + { + lm.scaler.yScale = scale; + lm.scaler.yDelta = delta; + } + // Scale the standard widths. + for (int nn = 0; nn < axis.widthCount; nn++) + { + Width w = axis.widths[nn]; + w.cur = Fixed.mul16(w.org, scale); + w.fit = w.cur; + } + // Scale blue zones. + if (dim == DIMENSION_VERT) + { + for (int nn = 0; nn < axis.blueCount; nn++) + { + blue = axis.blues[nn]; + blue.ref.cur = Fixed.mul16(blue.ref.org, scale) + delta; + blue.ref.fit = blue.ref.cur; + blue.shoot.cur = Fixed.mul16(blue.ref.org, scale) + delta; + blue.flags &= ~LatinBlue.FLAG_BLUE_ACTIVE; + // A blue zone is only active if it is less than 3/4 pixels tall. + int dist = Fixed.mul16(blue.ref.org - blue.shoot.org, scale); + if (dist <= 48 && dist >= -48) + { + int delta1 = blue.shoot.org - blue.ref.org; + int delta2 = delta1; + if (delta1 < 0) + delta2 = -delta2; + delta2 = Fixed.mul16(delta2, scale); + if (delta2 < 32) + delta2 = 0; + else if (delta2 < 64) + delta2 = 32 + (((delta2 - 32) + 16) & ~31); + else + delta2 = Utils.pixRound(delta2); + if (delta1 < 0) + delta2 = -delta2; + blue.ref.fit = Utils.pixRound(blue.ref.cur); + blue.shoot.fit = blue.ref.fit + delta2; + blue.flags |= LatinBlue.FLAG_BLUE_ACTIVE; + } + } + } + } + + /** + * Determines the standard stem widths. + * + * @param metrics the metrics to use + * @param face the font face + * @param ch the character that is used for getting the widths + */ + private void initWidths(LatinMetrics metrics, OpenTypeFont face, char ch) + { + GlyphHints hints = new GlyphHints(); + metrics.axis[DIMENSION_HORZ].widthCount = 0; + metrics.axis[DIMENSION_VERT].widthCount = 0; + int glyphIndex = face.getGlyph(ch); + Zone outline = face.getRawGlyphOutline(glyphIndex, IDENTITY); + LatinMetrics dummy = new LatinMetrics(); + HintScaler scaler = dummy.scaler; + dummy.unitsPerEm = metrics.unitsPerEm; + scaler.xScale = scaler.yScale = 10000; + scaler.xDelta = scaler.yDelta = 0; + scaler.face = face; + hints.rescale(dummy); + hints.reload(outline); + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + LatinAxis axis = metrics.axis[dim]; + AxisHints axHints = hints.axis[dim]; + int numWidths = 0; + computeSegments(hints, dim); + linkSegments(hints, dim); + Segment[] segs = axHints.segments; + HashSet<Segment> touched = new HashSet<Segment>(); + for (int i = 0; i < segs.length; i++) + { + Segment seg = segs[i]; + Segment link = seg.link; + if (link != null && link.link == seg && ! touched.contains(link)) + { + int dist = Math.abs(seg.pos - link.pos); + if (numWidths < MAX_WIDTHS) + axis.widths[numWidths++] = new Width(dist); + } + touched.add(seg); + } + Utils.sort(numWidths, axis.widths); + axis.widthCount = numWidths; + } + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + LatinAxis axis = metrics.axis[dim]; + int stdw = axis.widthCount > 0 ? axis.widths[0].org + : constant(metrics, 50); + axis.edgeDistanceTreshold= stdw / 5; + } + } + + void linkSegments(GlyphHints hints, int dim) + { + AxisHints axis = hints.axis[dim]; + Segment[] segments = axis.segments; + int numSegs = axis.numSegments; + int majorDir = axis.majorDir; + int lenThreshold = constant((LatinMetrics) hints.metrics, 8); + lenThreshold = Math.min(1, lenThreshold); + int lenScore = constant((LatinMetrics) hints.metrics, 3000); + for (int i1 = 0; i1 < numSegs; i1++) + { + Segment seg1 = segments[i1]; + // The fake segments are introduced to hint the metrics. + // Never link them to anything. + if (seg1.first == seg1.last || seg1.dir != majorDir) + continue; + for (int i2 = 0; i2 < numSegs; i2++) + { + Segment seg2 = segments[i2]; + if (seg2 != seg1 && seg1.dir + seg2.dir == 0) + { + int pos1 = seg1.pos; + int pos2 = seg2.pos; + // The vertical coords are swapped compared to how FT handles + // this. + int dist = dim == DIMENSION_VERT ? pos1 - pos2 : pos2 - pos1; + if (dist >= 0) + { + int min = seg1.minPos; + int max = seg1.maxPos; + int len, score; + if (min < seg2.minPos) + min = seg2.minPos; + if (max > seg2.maxPos) + max = seg2.maxPos; + len = max - min; + if (len > lenThreshold) + { + score = dist + lenScore / len; + if (score < seg1.score) + { + seg1.score = score; + seg1.link = seg2; + } + if (score < seg2.score) + { + seg2.score = score; + seg2.link = seg1; + } + } + } + } + } + } + for (int i1 = 0; i1 < numSegs; i1++) + { + Segment seg1 = segments[i1]; + Segment seg2 = seg1.link; + if (seg2 != null) + { + seg2.numLinked++; + if (seg2.link != seg1) + { + seg1.link = null; + seg1.serif = seg2.link; + } + } + // Uncomment to show all segments. + // System.err.println("segment#" + i1 + ": " + seg1); + } + } + + /** + * Initializes the blue zones of the font. + * + * @param metrics the metrics to use + * @param face the font face to analyze + */ + private void initBlues(LatinMetrics metrics, OpenTypeFont face) + { + int[] flats = new int[MAX_TEST_CHARS]; + int[] rounds = new int[MAX_TEST_CHARS]; + int numFlats; + int numRounds; + LatinBlue blue; + LatinAxis axis = metrics.axis[DIMENSION_VERT]; + // We compute the blues simply by loading each character in the test + // strings, then compute its topmost or bottommost points. + for (int bb = 0; bb < BLUE_MAX; bb++) + { + String p = TEST_CHARS[bb]; + int blueRef; + int blueShoot; + numFlats = 0; + numRounds = 0; + for (int i = 0; i < p.length(); i++) + { + // Load the character. + int glyphIndex = face.getGlyph(p.charAt(i)); + Zone glyph = + face.getRawGlyphOutline(glyphIndex, IDENTITY); + + // Now compute the min and max points. + int numPoints = glyph.getSize() - 4; // 4 phantom points. + Point[] points = glyph.getPoints(); + Point point = points[0]; + int extremum = 0; + int index = 1; + if (isTopBlue(bb)) + { + for (; index < numPoints; index++) + { + point = points[index]; + // We have the vertical direction swapped. The higher + // points have smaller (negative) Y. + if (point.getOrigY() < points[extremum].getOrigY()) + extremum = index; + } + } + else + { + for (; index < numPoints; index++) + { + point = points[index]; + // We have the vertical direction swapped. The higher + // points have smaller (negative) Y. + if (point.getOrigY() > points[extremum].getOrigY()) + extremum = index; + } + } + // Debug, prints out the maxima. + // System.err.println("extremum for " + bb + " / "+ p.charAt(i) + // + ": " + points[extremum]); + + // Now determine if the point is part of a straight or round + // segment. + boolean round; + int idx = extremum; + int first, last, prev, next, end; + int dist; + last = -1; + first = 0; + for (int n = 0; n < glyph.getNumContours(); n++) + { + end = glyph.getContourEnd(n); + // System.err.println("contour end for " + n + ": " + end); + if (end >= idx) + { + last = end; + break; + } + first = end + 1; + } + // Should never happen. + assert last >= 0; + + // Now look for the previous and next points that are not on the + // same Y coordinate. Threshold the 'closeness'. + prev = idx; + next = prev; + do + { + if (prev > first) + prev--; + else + prev = last; + dist = points[prev].getOrigY() - points[extremum].getOrigY(); + if (dist < -5 || dist > 5) + break; + } while (prev != idx); + do + { + if (next < last) + next++; + else + next = first; + dist = points[next].getOrigY() - points[extremum].getOrigY(); + if (dist < -5 || dist > 5) + break; + } while (next != idx); + round = points[prev].isControlPoint() + || points[next].isControlPoint(); + + if (round) + { + rounds[numRounds++] = points[extremum].getOrigY(); + // System.err.println("new round extremum: " + bb + ": " + // + points[extremum].getOrigY()); + } + else + { + flats[numFlats++] = points[extremum].getOrigY(); + // System.err.println("new flat extremum: " + bb + ": " + // + points[extremum].getOrigY()); + } + } + // We have computed the contents of the rounds and flats tables. + // Now determine the reference and overshoot position of the blues -- + // we simply take the median after a simple sort. + Utils.sort(numRounds, rounds); + Utils.sort(numFlats, flats); + blue = axis.blues[axis.blueCount] = new LatinBlue(); + axis.blueCount++; + if (numFlats == 0) + { + blue.ref = blue.shoot = new Width(rounds[numRounds / 2]); + } + else if (numRounds == 0) + { + blue.ref = blue.shoot = new Width(flats[numFlats / 2]); + } + else + { + blue.ref = new Width(flats[numFlats / 2]); + blue.shoot = new Width(rounds[numRounds / 2]); + } + // There are sometimes problems: if the overshoot position of top + // zones is under its reference position, or the opposite for bottom + // zones. We must check everything there and correct problems. + if (blue.shoot != blue.ref) + { + int ref = blue.ref.org; + int shoot = blue.shoot.org; + // Inversed vertical coordinates! + boolean overRef = shoot < ref; + if (isTopBlue(bb) ^ overRef) + { + blue.shoot = blue.ref = new Width((shoot + ref) / 2); + } + } + blue.flags = 0; + if (isTopBlue(bb)) + blue.flags |= LatinBlue.FLAG_TOP; + // The following flag is used later to adjust y and x scales in + // order to optimize the pixel grid alignment of the top small + // letters. + if (bb == SMALL_TOP) + { + blue.flags |= LatinBlue.FLAG_ADJUSTMENT; + } + // Debug: print out the blue zones. + // System.err.println("blue zone #" + bb + ": " + blue); + } + } + + private static final AffineTransform IDENTITY = new AffineTransform(); + + private int constant(LatinMetrics metrics, int c) + { + return c * (metrics.unitsPerEm / 2048); + } + + private void computeSegments(GlyphHints hints, int dim) + { + Point[] points = hints.points; + if (dim == DIMENSION_HORZ) + { + for (int i = 0; i < hints.numPoints; i++) + { + points[i].setU(points[i].getOrigX()); + points[i].setV(points[i].getOrigY()); + } + } + else + { + for (int i = 0; i < hints.numPoints; i++) + { + points[i].setU(points[i].getOrigY()); + points[i].setV(points[i].getOrigX()); + } + } + // Now look at each contour. + AxisHints axis = hints.axis[dim]; + int majorDir = Math.abs(axis.majorDir); + int segmentDir = majorDir; + Point[] contours = hints.contours; + int numContours = hints.numContours; + Segment segment = null; + for (int i = 0; i < numContours; i++) + { + int minPos = 32000; + int maxPos = -32000; + + Point point = contours[i]; + Point last = point.getPrev(); + if (point == last) // Skip singletons. + continue; + if (Math.abs(last.getOutDir()) == majorDir + && Math.abs(point.getOutDir()) == majorDir) + { + // We are already on an edge. Locate its start. + last = point; + while (true) + { + point = point.getPrev(); + if (Math.abs(point.getOutDir()) != majorDir) + { + point = point.getNext(); + break; + } + if (point == last) + break; + } + } + last = point; + boolean passed = false; + boolean onEdge = false; + while (true) + { + int u, v; + if (onEdge) + { + u = point.getU(); + if (u < minPos) + minPos = u; + if (u > maxPos) + maxPos = u; + if (point.getOutDir() != segmentDir || point == last) + { + // Leaving an edge. Record new segment. + segment.last = point; + // (minPos + maxPos) / 2. + segment.pos = (minPos + maxPos) >> 1; + if (segment.first.isControlPoint() + || point.isControlPoint()) + segment.flags |= Segment.FLAG_EDGE_ROUND; + minPos = maxPos = point.getV(); + v = segment.first.getV(); + if (v < minPos) + minPos = v; + if (v > maxPos) + maxPos = v; + segment.minPos = minPos; + segment.maxPos = maxPos; + onEdge = false; + segment = null; + } + } + if (point == last) + { + if (passed) + break; + passed = true; + } + if (! onEdge && Math.abs(point.getOutDir()) == majorDir) + { + // This is the start of a new segment. + segmentDir = point.getOutDir(); + segment = axis.newSegment(); + segment.dir = segmentDir; + segment.flags = Segment.FLAG_EDGE_NORMAL; + minPos = maxPos = point.getU(); + segment.first = point; + segment.last = point; + segment.contour = contours[i]; + segment.score = 32000; + segment.len = 0; + segment.link = null; + onEdge = true; + } + point = point.getNext(); + } + } + + } + + private boolean isTopBlue(int b) + { + return b == CAPITAL_TOP || b == SMALL_F_TOP || b == SMALL_TOP; + } + + private void detectFeatures(GlyphHints hints, int dim) + { + computeSegments(hints, dim); + linkSegments(hints, dim); + computeEdges(hints, dim); + } + + private void computeEdges(GlyphHints hints, int dim) + { + AxisHints axis = hints.axis[dim]; + LatinAxis laxis = ((LatinMetrics) hints.metrics).axis[dim]; + Segment[] segments = axis.segments; + int numSegments = axis.numSegments; + Segment seg; + int upDir; + int scale; + int edgeDistanceThreshold; + axis.numEdges = 0; + scale = dim == DIMENSION_HORZ ? hints.xScale : hints.yScale; + upDir = dim == DIMENSION_HORZ ? DIR_UP : DIR_RIGHT; + + // We will begin by generating a sorted table of edges for the + // current direction. To do so, we simply scan each segment and try + // to find an edge in our table that corresponds to its position. + // + // If no edge is found, we create one and insert a new edge in the + // sorted table. Otherwise, we simply add the segment to the egde's + // list which will be processed in the second step to compute the + // edge's properties. + // + // Note that the edge table is sorted along the segment/edge + // position. + + edgeDistanceThreshold = Fixed.mul16(laxis.edgeDistanceTreshold, scale); + if (edgeDistanceThreshold > 64 / 4) + edgeDistanceThreshold = 64 / 4; + edgeDistanceThreshold = Fixed.div16(edgeDistanceThreshold, scale); + for (int i = 0; i < numSegments; i++) + { + seg = segments[i]; + Edge found = null; + for (int ee = 0; ee < axis.numEdges; ee++) + { + Edge edge = axis.edges[ee]; + int dist = seg.pos - edge.fpos; + if (dist < 0) + dist = -dist; + if (dist < edgeDistanceThreshold) + { + found = edge; + break; + } + } + if (found == null) + { + // Insert new edge in the list and sort according to + // the position. + Edge edge = axis.newEdge(seg.pos); + edge.first = seg; + edge.last = seg; + edge.fpos = seg.pos; + edge.opos = edge.pos = Fixed.mul16(seg.pos, scale); + seg.edgeNext = seg; + seg.edge = edge; + } + else + { + seg.edgeNext = found.first; + found.last.edgeNext = seg; + found.last = seg; + seg.edge = found; + } + } + // Good. We will now compute each edge's properties according to + // segments found on its position. Basically these are: + // - Edge's main direction. + // - Stem edge, serif edge, or both (which defaults to stem edge). + // - Rounded edge, straight or both (which defaults to straight). + // - Link for edge. + + // Now, compute each edge properties. + for (int e = 0; e < axis.numEdges; e++) + { + Edge edge = axis.edges[e]; + // Does it contain round segments? + int isRound = 0; + // Does it contain straight segments? + int isStraight = 0; + // Number of upward segments. + int ups = 0; + // Number of downward segments. + int downs = 0; + + seg = edge.first; + do + { + // Check for roundness of segment. + if ((seg.flags & Segment.FLAG_EDGE_ROUND) != 0) + isRound++; + else + isStraight++; + + // Check for segment direction. + if (seg.dir == upDir) + ups += seg.maxPos - seg.minPos; + else + downs += seg.maxPos - seg.minPos; + + // Check for links. If seg.serif is set, then seg.link must + // be ignored. + boolean isSerif = seg.serif != null && seg.serif.edge != edge; + if (seg.link != null || isSerif) + { + Edge edge2 = edge.link; + Segment seg2 = seg.link; + if (isSerif) + { + seg2 = seg.serif; + edge2 = edge.serif; + } + if (edge2 != null) + { + int edgeDelta = edge.fpos - edge2.fpos; + if (edgeDelta < 0) + edgeDelta = -edgeDelta; + int segDelta = seg.pos - seg2.pos; + if (segDelta < 0) + segDelta = -segDelta; + if (segDelta < edgeDelta) + edge2 = seg2.edge; + } + else + { + edge2 = seg2.edge; + } + if (isSerif) + { + edge.serif = edge2; + edge2.flags |= Segment.FLAG_EDGE_SERIF; + } + else + { + edge.link = edge2; + } + } + seg = seg.edgeNext; + } while (seg != edge.first); + edge.flags = Segment.FLAG_EDGE_NORMAL; + if (isRound > 0 && isRound > isStraight) + edge.flags |= Segment.FLAG_EDGE_ROUND; + + // Set the edge's main direction. + edge.dir = DIR_NONE; + if (ups > downs) + edge.dir = upDir; + else if (ups < downs) + edge.dir = -upDir; + else if (ups == downs) + edge.dir = 0; + + // Gets rid of serif if link is set. This gets rid of many + // unpleasant artifacts. + if (edge.serif != null && edge.link != null) + { + edge.serif = null; + } + + // Debug: Print out all edges. + // System.err.println("edge# " + e + ": " + edge); + } + } + + private void computeBlueEdges(GlyphHints hints, LatinMetrics metrics) + { + AxisHints axis = hints.axis[DIMENSION_VERT]; + Edge[] edges = axis.edges; + int numEdges = axis.numEdges; + LatinAxis latin = metrics.axis[DIMENSION_VERT]; + int scale = latin.scale; + + // Compute which blue zones are active. I.e. have their scaled + // size < 3/4 pixels. + + // For each horizontal edge search the blue zone that is closest. + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + // System.err.println("checking edge: " + edge); + Width bestBlue = null; + int bestDist = Fixed.mul16(metrics.unitsPerEm / 40, scale); + + if (bestDist > 64 / 2) + bestDist = 64 / 2; + for (int bb = 0; bb < BLUE_MAX; bb++) + { + LatinBlue blue = latin.blues[bb]; + // System.err.println("checking blue: " + blue); + // Skip inactive blue zones, i.e. those that are too small. + if ((blue.flags & LatinBlue.FLAG_BLUE_ACTIVE) == 0) + continue; + // If it is a top zone, check for right edges. If it is a bottom + // zone, check for left edges. + boolean isTopBlue = (blue.flags & LatinBlue.FLAG_TOP) != 0; + boolean isMajorDir = edge.dir == axis.majorDir; + + // If it is a top zone, the edge must be against the major + // direction. If it is a bottom zone it must be in the major + // direction. + if (isTopBlue ^ isMajorDir) + { + int dist = edge.fpos - blue.ref.org; + if (dist < 0) + dist = -dist; + dist = Fixed.mul16(dist, scale); + if (dist < bestDist) + { + bestDist = dist; + bestBlue = blue.ref; + } + + // Now, compare it to the overshoot position if the edge is + // rounded, and if the edge is over the reference position of + // a top zone, or under the reference position of a bottom + // zone. + if ((edge.flags & Segment.FLAG_EDGE_ROUND) != 0 && dist != 0) + { + // Inversed vertical coordinates! + boolean isUnderRef = edge.fpos > blue.ref.org; + if (isTopBlue ^ isUnderRef) + { + blue = latin.blues[bb]; // Needed? + dist = edge.fpos - blue.shoot.org; + if (dist < 0) + dist = -dist; + dist = Fixed.mul16(dist, scale); + if (dist < bestDist) + { + bestDist = dist; + bestBlue = blue.shoot; + } + } + } + + } + } + if (bestBlue != null) + { + edge.blueEdge = bestBlue; + // Debug: Print out the blue edges. + // System.err.println("blue edge for: " + edge + ": " + bestBlue); + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java new file mode 100644 index 000000000..9237d0ee5 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java @@ -0,0 +1,62 @@ +/* LatinAxis.java -- Axis specific data + 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.font.autofit; + +/** + * Some axis specific data. + */ +class LatinAxis +{ + + int scale; + int delta; + + int widthCount; + Width[] widths; + int edgeDistanceTreshold; + LatinBlue[] blues; + int blueCount; + int orgDelta; + int orgScale; + LatinAxis() + { + widths = new Width[Latin.MAX_WIDTHS]; + blues = new LatinBlue[Latin.BLUE_MAX]; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinBlue.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinBlue.java new file mode 100644 index 000000000..2cf68b75c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinBlue.java @@ -0,0 +1,61 @@ +/* LatinBlue.java -- FIXME: briefly describe file purpose + 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.font.autofit; + +import gnu.java.lang.CPStringBuilder; + +public class LatinBlue +{ + static final int FLAG_BLUE_ACTIVE = 1 << 0; + static final int FLAG_TOP = 1 << 1; + static final int FLAG_ADJUSTMENT = 1 << 2; + Width ref; + Width shoot; + int flags; + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[BlueZone]"); + s.append(" ref: "); + s.append(ref.org); + s.append(", shoot: "); + s.append(shoot.org); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java new file mode 100644 index 000000000..33fc63ad4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java @@ -0,0 +1,66 @@ +/* LatinMetrics.java -- Latin specific metrics data + 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.font.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; + +/** + * Latin specific metrics data. + */ +class LatinMetrics + extends ScriptMetrics +{ + + LatinAxis[] axis; + + int unitsPerEm; + + LatinMetrics() + { + super(); + axis = new LatinAxis[Constants.DIMENSION_MAX]; + axis[Constants.DIMENSION_HORZ] = new LatinAxis(); + axis[Constants.DIMENSION_VERT] = new LatinAxis(); + } + LatinMetrics(OpenTypeFont face) + { + this(); + unitsPerEm = face.unitsPerEm; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Script.java b/libjava/classpath/gnu/java/awt/font/autofit/Script.java new file mode 100644 index 000000000..c223f0a26 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Script.java @@ -0,0 +1,62 @@ +/* Script.java -- Defines script specific interface to the autofitter + 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.font.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * Defines script specific methods for the auto fitter. + */ +interface Script +{ + + /** + * Initializes the metrics. + */ + void initMetrics(ScriptMetrics metrics, OpenTypeFont face); + + void scaleMetrics(ScriptMetrics metrics , HintScaler scaler); + + void doneMetrics(ScriptMetrics metrics); + + void initHints(GlyphHints hints, ScriptMetrics metrics); + + void applyHints(GlyphHints hints, Zone outline, ScriptMetrics metrics); +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java b/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java new file mode 100644 index 000000000..984a06dae --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java @@ -0,0 +1,53 @@ +/* ScriptMetrics.java -- Script specific metrics data + 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.font.autofit; + +/** + * Script specific metrics data. + */ +class ScriptMetrics +{ + + Script script; + HintScaler scaler; + ScriptMetrics() + { + scaler = new HintScaler(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Segment.java b/libjava/classpath/gnu/java/awt/font/autofit/Segment.java new file mode 100644 index 000000000..9f9da6792 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Segment.java @@ -0,0 +1,97 @@ +/* Segment.java -- FIXME: briefly describe file purpose + 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.font.autofit; + +import gnu.java.awt.font.opentype.truetype.Point; + +import gnu.java.lang.CPStringBuilder; + +class Segment +{ + + static final int FLAG_EDGE_NORMAL = 0; + static final int FLAG_EDGE_ROUND = 1; + static final int FLAG_EDGE_SERIF = 2; + static final int FLAG_EDGE_DONE = 4; + int dir; + int flags; + Segment link; + Segment serif; + int numLinked; + int pos; + Point first; + Point last; + Point contour; + int minPos; + int maxPos; + int score; + int len; + Segment edgeNext; + Edge edge; + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Segment] id: "); + s.append(hashCode()); + s.append(", len:"); + s.append(len); + s.append(", round: "); + s.append(((flags & FLAG_EDGE_ROUND) != 0)); + s.append(", dir: "); + s.append(dir); + s.append(", pos: "); + s.append(pos); + s.append(", minPos: "); + s.append(minPos); + s.append(", maxPos: "); + s.append(maxPos); + s.append(", first: "); + s.append(first); + s.append(", last: "); + s.append(last); + s.append(", contour: "); + s.append(contour); + s.append(", link: "); + s.append(link == null ? "null" : link.hashCode()); + s.append(", serif: "); + s.append(serif == null ? "null" : serif.hashCode()); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Utils.java b/libjava/classpath/gnu/java/awt/font/autofit/Utils.java new file mode 100644 index 000000000..ca45bb2e4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Utils.java @@ -0,0 +1,255 @@ +/* Utils.java -- A collection of utility functions for the autofitter + 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.font.autofit; + +import gnu.java.awt.font.opentype.truetype.Fixed; + +/** + * A collection of utility methods used all around the auto fitter. + */ +class Utils + implements Constants +{ + + private static final int ATAN_BITS = 8; + private static final byte[] ATAN = new byte[] + { + 0, 0, 1, 1, 1, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 5, + 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, + 10, 10, 11, 11, 11, 12, 12, 12, + 13, 13, 13, 14, 14, 14, 14, 15, + 15, 15, 16, 16, 16, 17, 17, 17, + 18, 18, 18, 18, 19, 19, 19, 20, + 20, 20, 21, 21, 21, 21, 22, 22, + 22, 23, 23, 23, 24, 24, 24, 24, + 25, 25, 25, 26, 26, 26, 26, 27, + 27, 27, 28, 28, 28, 28, 29, 29, + 29, 30, 30, 30, 30, 31, 31, 31, + 31, 32, 32, 32, 33, 33, 33, 33, + 34, 34, 34, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 37, 37, 37, 38, + 38, 38, 38, 39, 39, 39, 39, 40, + 40, 40, 40, 41, 41, 41, 41, 42, + 42, 42, 42, 42, 43, 43, 43, 43, + 44, 44, 44, 44, 45, 45, 45, 45, + 46, 46, 46, 46, 46, 47, 47, 47, + 47, 48, 48, 48, 48, 48, 49, 49, + 49, 49, 50, 50, 50, 50, 50, 51, + 51, 51, 51, 51, 52, 52, 52, 52, + 52, 53, 53, 53, 53, 53, 54, 54, + 54, 54, 54, 55, 55, 55, 55, 55, + 56, 56, 56, 56, 56, 57, 57, 57, + 57, 57, 57, 58, 58, 58, 58, 58, + 59, 59, 59, 59, 59, 59, 60, 60, + 60, 60, 60, 61, 61, 61, 61, 61, + 61, 62, 62, 62, 62, 62, 62, 63, + 63, 63, 63, 63, 63, 64, 64, 64 + }; + + private static final int ANGLE_PI = 256; + private static final int ANGLE_PI2 = ANGLE_PI / 2; + private static final int ANGLE_PI4 = ANGLE_PI / 4; + private static final int ANGLE_2PI = ANGLE_PI * 2; + + /** + * Computes the direction constant for the specified vector. The vector is + * given as differential value already. + * + * @param dx the x vector + * @param dy the y vector + * + * @return the direction of that vector, or DIR_NONE, if that vector is not + * approximating against one of the major axises + */ + static int computeDirection(int dx, int dy) + { + int dir = DIR_NONE; + if (dx < 0) + { + if (dy < 0) + { + if (-dx * 12 < -dy) + dir = DIR_UP; + else if (-dy * 12 < -dx) + dir = DIR_LEFT; + } + else // dy >= 0 . + { + if (-dx * 12 < dy) + dir = DIR_DOWN; + else if (dy * 12 < -dx) + dir = DIR_LEFT; + } + } + else // dx >= 0 . + { + if (dy < 0) + { + if (dx * 12 < -dy) + dir = DIR_UP; + else if (-dy * 12 < dx) + dir = DIR_RIGHT; + } + else // dy >= 0 . + { + if (dx * 12 < dy) + dir = DIR_DOWN; + else if (dy * 12 < dx) + dir = DIR_RIGHT; + } + } + return dir; + } + + public static int atan(int dx, int dy) + { + int angle; + // Trivial cases. + if (dy == 0) + { + angle = 0; + if (dx < 0) + angle = ANGLE_PI; + return angle; + } + else if (dx == 0) + { + angle = ANGLE_PI2; + if (dy < 0) + angle = - ANGLE_PI2; + return angle; + } + + + angle = 0; + if (dx < 0) + { + dx = -dx; + dy = -dy; + angle = ANGLE_PI; + } + if (dy < 0) + { + int tmp = dx; + dx = -dy; + dy = tmp; + angle -= ANGLE_PI2; + } + if (dx == 0 && dy == 0) + return 0; + + if (dx == dy) + angle += ANGLE_PI4; + else if (dx > dy) + { + angle += ATAN[Fixed.div(dy, dx) << (ATAN_BITS - 6)]; + } + else + { + angle += ANGLE_PI2 - ATAN[Fixed.div(dx, dy) << (ATAN_BITS - 6)]; + } + + if (angle > ANGLE_PI) + angle -= ANGLE_2PI; + return angle; + } + + public static int angleDiff(int ang1, int ang2) + { + int delta = ang2 - ang1; + delta %= ANGLE_2PI; + if (delta < 0) + delta += ANGLE_2PI; + if (delta > ANGLE_PI) + delta -= ANGLE_2PI; + return delta; + } + + static void sort(int num, int[] array) + { + int swap; + for (int i = 1; i < num; i++) + { + for (int j = i; j > 0; j--) + { + if (array[j] > array[j - 1]) + break; + swap = array[j]; + array[j] = array[j - 1]; + array[j - 1] = swap; + } + } + } + + static void sort(int num, Width[] array) + { + Width swap; + for (int i = 1; i < num; i++) + { + for (int j = 1; j > 0; j--) + { + if (array[j].org > array[j - 1].org) + break; + swap = array[j]; + array[j] = array[j - 1]; + array[j - 1] = swap; + } + } + } + + static int pixRound(int val) + { + return pixFloor(val + 32); + } + + static int pixFloor(int val) + { + return val & ~63; + } + + public static int mulDiv(int a, int b, int c) + { + long prod = a * b; + long div = (prod / c); + return (int) div; + } + +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Width.java b/libjava/classpath/gnu/java/awt/font/autofit/Width.java new file mode 100644 index 000000000..079f7b396 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Width.java @@ -0,0 +1,64 @@ +/* Width.java -- FIXME: briefly describe file purpose + 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.font.autofit; + +import gnu.java.lang.CPStringBuilder; + +public class Width +{ + int org; + int cur; + int fit; + Width(int dist) + { + org = dist; + } + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Width] org: "); + s.append(org); + s.append(", cur: "); + s.append(cur); + s.append(", fit: "); + s.append(fit); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java new file mode 100644 index 000000000..8529f7e47 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java @@ -0,0 +1,1027 @@ +/* CharGlyphMap.java -- Manages the 'cmap' table of TrueType fonts + 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.font.opentype; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.ShortBuffer; +import java.nio.IntBuffer; + + +/** + * A mapping from Unicode codepoints to glyphs. This mapping + * does not perform any re-ordering or decomposition, so it + * is not everything that is needed to support Unicode. + * + * <p>This class manages the <code>cmap</code> table of + * OpenType and TrueType fonts. + * + * @see <a href="http://partners.adobe.com/asn/tech/type/opentype/cmap.jsp"> + * the <code>cmap</code> part of Adobe’ OpenType Specification</a> + * + * @see <a href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html"> + * the <code>cmap</code> section of Apple’s TrueType Reference + * Manual</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class CharGlyphMap +{ + private static final int PLATFORM_UNICODE = 0; + private static final int PLATFORM_MACINTOSH = 1; + private static final int PLATFORM_MICROSOFT = 3; + + + /** + * Determines the glyph index for a given Unicode codepoint. Users + * should be aware that the character-to-glyph mapping not not + * everything that is needed for full Unicode support. For example, + * the <code>cmap</code> table is not able to synthesize accented + * glyphs from the canonical decomposition sequence, even if the + * font would contain a glyph for the composed form. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. Surrogates + * (U+D800 to U+DFFF) cannot be passed, they must be mapped to + * UCS-4 first. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public abstract int getGlyph(int ucs4); + + + /** + * Reads a CharGlyphMap from an OpenType or TrueType <code>cmap</code> + * table. The current implementation works as follows: + * + * <p><ol><li>If the font has a type 4 cmap for the Unicode platform + * (encoding 0, 1, 2, 3 or 4), or a type 4 cmap for the Microsoft + * platform (encodings 1 or 10), that table is used to map Unicode + * codepoints to glyphs. Most recent fonts, both for Macintosh and + * Windows, should provide such a table.</li> + * + * <li>Otherwise, if the font has any type 0 cmap for the Macintosh + * platform, a Unicode-to-glyph mapping is synthesized from certain + * type 0 cmaps. The current implementation collects mappings from + * Roman, Icelandic, Turkish, Croatian, Romanian, Eastern European, + * Cyrillic, Greek, Hebrew, Arabic and Farsi cmaps.</li>.</ol> + * + * @param buf a buffer whose position is right at the start + * of the entire <code>cmap</code> table, and whose limit + * is at its end. + * + * @return a concrete subclass of <code>CharGlyphMap</code> + * that performs the mapping. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/cmap.jsp" + * >the <code>cmap</code> part of Adobe’ OpenType Specification</a> + * + * @see <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html" + * >the <code>cmap</code> section of Apple’s TrueType Reference + * Manual</a> + */ + public static CharGlyphMap forTable(ByteBuffer buf) + { + boolean hasType0 = false; + int start4 = -1, platform4 = 0, encoding4 = 0; + int start12 = -1, platform12 = 0, encoding12 = 0; + int version; + int numTables; + int tableStart = buf.position(); + int limit = buf.limit(); + int format, platform, language, encoding, length, offset; + + version = buf.getChar(); + if (version != 0) + return null; + + numTables = buf.getChar(); + for (int i = 0; i < numTables; i++) + { + buf.limit(limit).position(tableStart + 4 + i * 8); + platform = buf.getChar(); + encoding = buf.getChar(); + offset = tableStart + buf.getInt(); + + buf.position(offset); + format = buf.getChar(); + + switch (format) + { + case 0: + hasType0 = true; + break; + + case 4: + length = buf.getChar(); + language = buf.getChar(); + if ((start4 == -1) + && Type4.isSupported(platform, language, encoding)) + { + start4 = offset; + platform4 = platform; + encoding4 = encoding; + } + break; + + case 12: + if ((start12 == -1) && Type12.isSupported(platform, encoding)) + { + start12 = offset; + platform12 = platform; + encoding12 = encoding; + } + break; + } + } + + + if (start12 >= 0) + { + try + { + buf.limit(limit).position(start12); + return new Type12(buf, platform12, encoding12); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + if (start4 >= 0) + { + try + { + buf.limit(limit).position(start4); + return Type4.readTable(buf, platform4, encoding4); + } + catch (Exception ex) + { + } + } + + if (hasType0) + { + try + { + buf.limit(limit).position(tableStart); + return new Type0(buf); + } + catch (Exception ex) + { + } + } + + return new Dummy(); + } + + + /** + * A dummy mapping that maps anything to the undefined glyph. + * Used if no other cmap is understood in a font. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Dummy + extends CharGlyphMap + { + public int getGlyph(int ucs4) + { + return 0; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 0 tables. These tables have serious limitations: Only the first + * 256 glyphs can be addressed, and the source of the mapping is not + * Unicode, but an encoding used on the Macintosh. + * + * <p>However, some fonts have only a Type 0 cmap. In this case, we + * process all the Type 0 tables we understand, and establish + * a reversed glyph-to-Unicode mapping. When a glyph is requested + * for a given Unicode character, we perform a linear search on the + * reversed table to find the glyph which maps to the requested + * character. While not blazingly fast, this gives a reasonable + * fallback for old fonts. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type0 + extends CharGlyphMap + { + /** + * An array whose <code>i</code>-th element indicates the + * Unicode code point of glyph <code>i</code> in the font. + */ + private char[] glyphToUCS2 = new char[256]; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Arabic encoding. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ARABIC.TXT" + * >the Unicode mapping table for the MacOS Arabic encoding</a> + */ + private static final String UPPER_ARABIC + = "\u007e\u0000\u00c4\u00a0\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u06ba\u00ab\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u2026\u00ee\u00ef\u00f1\u00f3\u00bb\u00f4\u00f6\u00f7" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u066a" + + "\u0026\u0027\u0028\u0029\u002a\u002b\u060c\u002d\u002e\u002f" + + "\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669" + + "\u003a\u061b\u003c\u003d\u003e\u061f\u274a\u0621\u0622\u0623" + + "\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d" + + "\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637" + + "\u0638\u0639\u063a\u005b\\\u005d\u005e\u005f\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u067e\u0679\u0686" + + "\u06d5\u06a4\u06af\u0688\u0691\u007b\u007c\u007d\u0698\u06d2"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS East European Roman encoding. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CENTEURO.TXT" + * >the Unicode mapping table for the MacOS Central European + * encoding</a> + */ + private static final String UPPER_EAST_EUROPEAN_ROMAN + = "\u007e\u0000\u00c4\u0100\u0101\u00c9\u0104\u00d6\u00dc\u00e1" + + "\u0105\u010c\u00e4\u010d\u0106\u0107\u00e9\u0179\u017a\u010e" + + "\u00ed\u010f\u0112\u0113\u0116\u00f3\u0117\u00f4\u00f6\u00f5" + + "\u00fa\u011a\u011b\u00fc\u2020\u00b0\u0118\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u0119\u00a8\u2260\u0123\u012e" + + "\u012f\u012a\u2264\u2265\u012b\u0136\u2202\u2211\u0142\u013b" + + "\u013c\u013d\u013e\u0139\u013a\u0145\u0146\u0143\u00ac\u221a" + + "\u0144\u0147\u2206\u00ab\u00bb\u2026\u00a0\u0148\u0150\u00d5" + + "\u0151\u014c\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u014d\u0154\u0155\u0158\u2039\u203a\u0159\u0156\u0157\u0160" + + "\u201a\u201e\u0161\u015a\u015b\u00c1\u0164\u0165\u00cd\u017d" + + "\u017e\u016a\u00d3\u00d4\u016b\u016e\u00da\u016f\u0170\u0171" + + "\u0172\u0173\u00dd\u00fd\u0137\u017b\u0141\u017c\u0122\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding for the Croatian language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CROATIAN.TXT" + * >the Unicode mapping table for the MacOS Croatian encoding</a> + */ + private static final String UPPER_CROATIAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u0160\u2122\u00b4\u00a8\u2260\u017d\u00d8" + + "\u221e\u00b1\u2264\u2265\u2206\u00b5\u2202\u2211\u220f\u0161" + + "\u222b\u00aa\u00ba\u03a9\u017e\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u0106\u00ab\u010c\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u0110\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\uf8ff\u00a9\u2044\u20ac\u2039\u203a\u00c6\u00bb\u2013\u00b7" + + "\u201a\u201e\u2030\u00c2\u0107\u00c1\u010d\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\u0111\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u03c0\u00cb\u02da\u00b8\u00ca\u00e6\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Cyrillic encoding. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CYRILLIC.TXT" + * >the Unicode mapping table for the MacOS Cyrillic encoding</a> + */ + private static final String UPPER_CYRILLIC + = "\u007e\u0000\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417" + + "\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421" + + "\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b" + + "\u042c\u042d\u042e\u042f\u2020\u00b0\u0490\u00a3\u00a7\u2022" + + "\u00b6\u0406\u00ae\u00a9\u2122\u0402\u0452\u2260\u0403\u0453" + + "\u221e\u00b1\u2264\u2265\u0456\u00b5\u0491\u0408\u0404\u0454" + + "\u0407\u0457\u0409\u0459\u040a\u045a\u0458\u0405\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u040b\u045b\u040c" + + "\u045c\u0455\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u201e" + + "\u040e\u045e\u040f\u045f\u2116\u0401\u0451\u044f\u0430\u0431" + + "\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b" + + "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445" + + "\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u20ac"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Arabic encoding with the Farsi language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/FARSI.TXT" + * >the Unicode mapping table for the MacOS Farsi encoding</a> + */ + private static final String UPPER_FARSI + = "\u007e\u0000\u00c4\u00a0\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u06ba\u00ab\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u2026\u00ee\u00ef\u00f1\u00f3\u00bb\u00f4\u00f6\u00f7" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u066a" + + "\u0026\u0027\u0028\u0029\u002a\u002b\u060c\u002d\u002e\u002f" + + "\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9" + + "\u003a\u061b\u003c\u003d\u003e\u061f\u274a\u0621\u0622\u0623" + + "\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d" + + "\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637" + + "\u0638\u0639\u063a\u005b\\\u005d\u005e\u005f\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u067e\u0679\u0686" + + "\u06d5\u06a4\u06af\u0688\u0691\u007b\u007c\u007d\u0698\u06d2"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Greek encoding. + * + * @see <a + * href="http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/GREEK.TXT" + * >the Unicode mapping table for the MacOS Greek encoding</a> + */ + private static final String UPPER_GREEK + = "\u007e\u0000\u00c4\u00b9\u00b2\u00c9\u00b3\u00d6\u00dc\u0385" + + "\u00e0\u00e2\u00e4\u0384\u00a8\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00a3\u2122\u00ee\u00ef\u2022\u00bd\u2030\u00f4\u00f6\u00a6" + + "\u20ac\u00f9\u00fb\u00fc\u2020\u0393\u0394\u0398\u039b\u039e" + + "\u03a0\u00df\u00ae\u00a9\u03a3\u03aa\u00a7\u2260\u00b0\u00b7" + + "\u0391\u00b1\u2264\u2265\u00a5\u0392\u0395\u0396\u0397\u0399" + + "\u039a\u039c\u03a6\u03ab\u03a8\u03a9\u03ac\u039d\u00ac\u039f" + + "\u03a1\u2248\u03a4\u00ab\u00bb\u2026\u00a0\u03a5\u03a7\u0386" + + "\u0388\u0153\u2013\u2015\u201c\u201d\u2018\u2019\u00f7\u0389" + + "\u038a\u038c\u038e\u03ad\u03ae\u03af\u03cc\u038f\u03cd\u03b1" + + "\u03b2\u03c8\u03b4\u03b5\u03c6\u03b3\u03b7\u03b9\u03be\u03ba" + + "\u03bb\u03bc\u03bd\u03bf\u03c0\u03ce\u03c1\u03c3\u03c4\u03b8" + + "\u03c9\u03c2\u03c7\u03c5\u03b6\u03ca\u03cb\u0390\u03b0\u00ad"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Hebrew encoding. + * + * <p>The codepoint 0x81 (HEBREW LIGATURE YIDDISH YOD YOD PATAH) + * has no composed Unicode equivalent, but is expressed as the + * sequence U+05F2 U+05B7 in Unicode. A similar situation exists + * with the codepoint 0xC0 (HEBREW LIGATURE LAMED HOLAM), which + * MacOS converts to U+F86A U+05DC U+05B9. To correctly deal + * with these sequences, we probably should synthesize a ligature + * table if a Hebrew font only provides a Type 0 CMAP. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/HEBREW.TXT" + * >the Unicode mapping table for the MacOS Hebrew encoding</a> + */ + private static final String UPPER_HEBREW + = "\u007e\u0000\u00c4\u0000\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u0025" + + "\u20aa\u0027\u0029\u0028\u002a\u002b\u002c\u002d\u002e\u002f" + + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039" + + "\u003a\u003b\u003c\u003d\u003e\u003f\u0000\u201e\uf89b\uf89c" + + "\uf89d\uf89e\u05bc\ufb4b\ufb35\u2026\u00a0\u05b8\u05b7\u05b5" + + "\u05b6\u05b4\u2013\u2014\u201c\u201d\u2018\u2019\ufb2a\ufb2b" + + "\u05bf\u05b0\u05b2\u05b1\u05bb\u05b9\u0000\u05b3\u05d0\u05d1" + + "\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db" + + "\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5" + + "\u05e6\u05e7\u05e8\u05e9\u05ea\u007d\u005d\u007b\u005b\u007c"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding with the Icelandic language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ICELAND.TXT" + * >the Unicode mapping table for the MacOS Icelandic encoding</a> + */ + private static final String UPPER_ICELANDIC + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u00dd\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u00d0\u00f0\u00de\u00fe\u00fd\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding for most languages. Exceptions include + * Croatian, Icelandic, Romanian, and Turkish. + * + * @see <a + * href="http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT" + * >the Unicode mapping table for the MacOS Roman encoding</a> + */ + private static final String UPPER_ROMAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u2039\u203a\ufb01\ufb02\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding with the Romanian language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMANIAN.TXT" + * >the Unicode mapping table for the MacOS Romanian encoding</a> + */ + private static final String UPPER_ROMANIAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u0102\u0218" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u0103\u0219\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u2039\u203a\u021a\u021b\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose <code>charAt(i)</code> is the Unicode character + * that corresponds to the codepoint <code>i + 127</code> in the + * MacOS Roman encoding with the Turkish language. + * + * @see <a href= + * "http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/TURKISH.TXT" + * >the Unicode mapping table for the MacOS Turkish encoding</a> + */ + private static final String UPPER_TURKISH + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u011e\u011f\u0130\u0131\u015e\u015f\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\uf8a0" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * Constructs a CharGlyphMap.Type0 from all type 0 cmaps provided + * by the font. The implementation is able to fuse multiple type + * 0 cmaps, such as the MacRoman, Turkish, Icelandic and Croatian + * encoding, into a single map from Unicode characters to glyph + * indices. + * + * @param buf a ByteBuffer whose position is right at the + * beginning of the entire cmap table of the font (<i>not</i> + * at some subtable). + */ + public Type0(ByteBuffer buf) + { + int numTables; + int tableStart = buf.position(); + int limit = buf.limit(); + + /* The CMAP version must be 0. */ + if (buf.getChar() != 0) + throw new IllegalStateException(); + + numTables = buf.getChar(); + for (int i = 0; i < numTables; i++) + { + buf.limit(limit).position(tableStart + 4 + i * 8); + int platform = buf.getChar(); + int encoding = buf.getChar(); + int offset = tableStart + buf.getInt(); + + buf.position(offset); + int format = buf.getChar(); + int length = buf.getChar(); + buf.limit(offset + length); + int language = buf.getChar(); + + if (format == 0) + readSingleTable(buf, platform, language, encoding); + } + } + + + /** + * Processes a CMAP Type 0 table whose platform, encoding and + * language are already known. + * + * @param buf the buffer to read the table from, positioned + * right after the language tag. + */ + private void readSingleTable(ByteBuffer buf, + int platform, int language, + int encoding) + { + String upper = getUpper129(platform, encoding, language); + if (upper == null) + return; + + /* Skip the MacOS codepoints [0 .. 31] because they do not + * correspond to any Unicode codepoint. + */ + buf.position(buf.position() + 32); + + /* Irrespective of script and language, the MacOS codepoints + * [32 .. 126] correspond to the same Unicode codepoint. + */ + for (int i = 32; i < 126; i++) + glyphToUCS2[buf.get() & 0xff] = (char) i; + + for (int i = 127; i < 256; i++) + glyphToUCS2[buf.get() & 0xff] = upper.charAt(i - 127); + + /* Glyph 0 is always the undefined character, which has + * no codepoint in Unicode. + */ + glyphToUCS2[0] = 0; + } + + + /** + * Determines the glyph index for a given Unicode codepoint. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + /* This linear search is not exactly super fast. However, + * only really ancient fonts have only a type 0 cmap, + * so it should not hurt in very many cases. If it shows + * to be a performance problem, one could do a binary search + * on a 256-entry table sorted by Unicode codepoint. The + * matching index of that table could then be used to look + * up the glyph ID at that position. + */ + for (int i = 0; i < 256; i++) + if (glyphToUCS2[i] == ucs4) + return i; + return 0; + } + + + /** + * Returns a String whose <code>charAt(i)</code> is the Unicode + * character that corresponds to the codepoint <code>i + + * 127</code> in the encoding specified by the platform, script + * and language tag of a Type 0 CMAP. + * + * @param language the language tag in the cmap subtable. For the + * Macintosh platform, this is 0 to indicate language-neutral + * encoding, or the MacOS language code <i>plus one.</i> The + * Apple documentation does not mention that one needs to be + * added, but the Adobe OpenType specification does. + * + * @return a String for mapping the top 129 characters to + * UCS-2. If <code>platform</code> is not <code>1</code> + * (indicating Macintosh), or if the combination of + * <code>script</code> and <code>language</code> is not + * recognized, <code>null</code> will be returned. + */ + private static String getUpper129(int platform, int script, int language) + { + if (platform != PLATFORM_MACINTOSH) + return null; + + switch (script) + { + case 0: /* smRoman */ + if (language == /* langIcelandic+1 */ 16) + return UPPER_ICELANDIC; + else if (language == /* langTurkish+1 */ 18) + return UPPER_TURKISH; + else if (language == /* langCroatian+1 */ 19) + return UPPER_CROATIAN; + else if (language == /* langRomanian+1 */ 38) + return UPPER_ROMANIAN; + else if (language == /* language-neutral */ 0) + return UPPER_ROMAN; + else + return null; + + case 4: /* smArabic */ + if (language == /* langFarsi+1 */ 32) + return UPPER_FARSI; + else + return UPPER_ARABIC; + + case 5: /* smHebrew */ + return UPPER_HEBREW; + + case 6: /* smGreek */ + return UPPER_GREEK; + + case 7: /* smCyrillic */ + return UPPER_CYRILLIC; + + case 29: /* smSlavic == smEastEurRoman */ + return UPPER_EAST_EUROPEAN_ROMAN; + } + + return null; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 4 tables. These tables are able to map two-byte encoded text + * to glyph IDs, such as Unicode Basic Multilingual Plane which + * contains U+0000 .. U+FFFE without surrogates. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type4 + extends CharGlyphMap + { + /** + * Determines whether this implementation supports a combination + * of platform, language and encoding is supported for a type 4 + * <code>cmap</code> table. + * + * <p>Currently, we support the following combinations: + * + * <ul><li>the Unicode platform in encodings 0, 1, 2, 3 and + * 4;</li> + * + * <li>the Microsoft platform in encodings 1 (Basic Multilingual + * Plane) and 10 (full Unicode).</li></ul> + * + * <p>Most recent Macintosh fonts provide a type 4 + * <code>cmap</code> for Unicode. Microsoft recommends providing a + * type 4 <code>cmap</code> for encoding 1 of the Microsoft + * platform. The implementation of GNU Classpath supports both + * variants. + * + * <p>Not supported are ShiftJIS, Big5, Wansung, Johab, and other + * non-Unicode encodings. Text can easily be converted to Unicode + * using the java.nio.charset package. + */ + static boolean isSupported(int platform, int language, int encoding) + { + switch (platform) + { + case PLATFORM_UNICODE: + return (encoding >= 0) && (encoding <= 4); + + case PLATFORM_MICROSOFT: + return (encoding == /* Basic Multilingual Plane */ 1) + || (encoding == /* Full Unicode */ 10); + } + + return false; + } + + + /** + * Processes a CMAP Type 4 table whose platform, encoding and + * language are already known. We understand the Unicode platform + * with encodings 0, 1, 2, 3 and 4, and the Microsoft platform + * with encodings 1 (Unicode BMP) and 10 (UCS-4). + * + * @param buf the buffer to read the table from, positioned at + * its beginning. + * + * @return a Type4 table, or <code>null</code> if the combination + * of platform and encoding is not understood. + */ + static Type4 readTable(ByteBuffer buf, + int platform, int encoding) + { + int tableStart = buf.position(); + char format = buf.getChar(); + int length = buf.getChar(); + int language = buf.getChar(); + + if ((format != 4) || !isSupported(platform, language, encoding)) + throw new IllegalArgumentException(); + + buf.limit(tableStart + length); + + int segCountX2 = buf.getChar(); + int segCount = segCountX2 / 2; + int searchRange = buf.getChar(); + int entrySelector = buf.getChar(); + int rangeShift = buf.getChar(); + + CharBuffer endCode, startCode, idRangeOffset_glyphID; + ShortBuffer idDelta; + + int pos = buf.position(); + endCode = buf.asCharBuffer(); + pos += segCountX2 + /* reservedPad */ 2; + + buf.position(pos); + startCode = buf.asCharBuffer(); + pos += segCountX2; + + buf.position(pos); + idDelta = buf.asShortBuffer(); + pos += segCountX2; + + buf.position(pos); + idRangeOffset_glyphID = buf.asCharBuffer(); + + endCode.limit(segCount); + startCode.limit(segCount); + idDelta.limit(segCount); + idRangeOffset_glyphID.limit((buf.limit() - pos) / 2); + + return new Type4(segCount, + endCode, startCode, idDelta, + idRangeOffset_glyphID); + } + + + private CharBuffer lastChar; + private CharBuffer firstChar; + private ShortBuffer idDelta; + private CharBuffer rangeID; + private int numSegments; + + private Type4(int numSegments, + CharBuffer lastChar, CharBuffer firstChar, + ShortBuffer idDelta, CharBuffer rangeID) + { + this.numSegments = numSegments; + this.lastChar = lastChar; + this.firstChar = firstChar; + this.idDelta = idDelta; + this.rangeID = rangeID; + } + + + /** + * Determines the glyph index for a given Unicode codepoint. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + char c, segStart; + int segment, idRangeOffset; + + if (ucs4 > 0xffff) + return 0; + + c = (char) ucs4; + segment = find(c); + segStart = firstChar.get(segment); + if ((c < segStart) || (c > lastChar.get(segment))) + return 0; + + /* + * System.out.println("seg " + segment + * + ", range=" + (int) rangeID[segment] + * + ", delta=" + delta[segment]); + */ + + idRangeOffset = rangeID.get(segment); + if (idRangeOffset == 0) + return (int) (char) (((int) c) + idDelta.get(segment)); + int result = rangeID.get((idRangeOffset >> 1) + + (c - segStart) + segment); + if (result == 0) + return 0; + return (int) (char) (result + idDelta.get(segment)); + } + + + private int find(char c) + { + int min, max, mid; + + min = 0; + max = numSegments - 1; + mid = max >> 1; + + while (min < max) + { + // System.out.println("(" + min + "," + max + ") " + mid); + char val = lastChar.get(mid); + if (val == c) + break; + else if (val < c) + min = mid + 1; + else if (val > c) + max = mid; + mid = (min + max) >> 1; + } + + return mid; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 12 tables. These tables are able to map four-byte encoded text + * to glyph IDs, such as Unicode UCS-4. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type12 + extends CharGlyphMap + { + int numGroups; + IntBuffer data; + + + /** + * Determines whether this implementation supports a combination + * of platform and encoding for a type 12 <code>cmap</code> table. + * + * <p>Currently, we support the following combinations: + * + * <ul><li>the Unicode platform in encodings 0, 1, 2, 3 and + * 4;</li> + * + * <li>the Microsoft platform in encodings 1 (Basic Multilingual + * Plane) and 10 (full Unicode).</li></ul> + */ + static boolean isSupported(int platform, int encoding) + { + switch (platform) + { + case PLATFORM_UNICODE: + return (encoding >= 0) && (encoding <= 4); + + case PLATFORM_MICROSOFT: + return (encoding == /* Basic Multilingual Plane */ 1) + || (encoding == /* Full Unicode */ 10); + } + + return false; + } + + + /** + * Constructs a <code>cmap</code> type 12 table whose platform and + * encoding are already known. We understand the Unicode platform + * with encodings 0, 1, 2, 3 and 4, and the Microsoft platform + * with encodings 1 (Unicode BMP) and 10 (UCS-4). + * + * @param buf the buffer to read the table from, positioned at + * its beginning. + */ + Type12(ByteBuffer buf, int platform, int encoding) + { + int tableStart = buf.position(); + int format = buf.getChar(); + if ((format != 12) || !isSupported(platform, encoding)) + throw new IllegalStateException(); + + buf.getChar(); // skip reserved field + buf.limit(tableStart + buf.getInt()); + int language = buf.getInt(); + numGroups = buf.getInt(); + data = buf.asIntBuffer(); + } + + + /** + * Determines the glyph index for a given Unicode codepoint. Users + * should be aware that the character-to-glyph mapping not not + * everything that is needed for full Unicode support. For example, + * the <code>cmap</code> table is not able to synthesize accented + * glyphs from the canonical decomposition sequence, even if the + * font would contain a glyph for the composed form. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. Surrogates + * (U+D800 to U+DFFF) cannot be passed, they must be mapped to + * UCS-4 first. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + int min, max, mid, startCharCode, endCharCode; + + min = 0; + max = numGroups - 1; + mid = max >> 1; + do + { + startCharCode = data.get(3 * mid); + endCharCode = data.get(3 * mid + 1); + + + /* + System.out.println("group " + mid + " (U+" + + Integer.toHexString(startCharCode) + + " .. U+" + Integer.toHexString(endCharCode) + + "): glyph " + (int) data.get(mid*3+2)); + */ + + if ((startCharCode <= ucs4) && (ucs4 <= endCharCode)) + return ucs4 + - startCharCode + + /* startGlyphID */ data.get(mid * 3 + 2); + + if (endCharCode < ucs4) + min = mid + 1; + else + max = mid; + mid = (min + max) >> 1; + } + while (min < max); + + startCharCode = data.get(3 * mid); + endCharCode = data.get(3 * mid + 1); + if ((startCharCode <= ucs4) && (ucs4 <= endCharCode)) + return ucs4 + - startCharCode + + /* startGlyphID */ data.get(mid * 3 + 2); + + return 0; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java b/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java new file mode 100644 index 000000000..72cecb542 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java @@ -0,0 +1,1135 @@ +/* GlyphNamer.java -- Provides glyph names. + 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.font.opentype; + +import gnu.java.lang.CPStringBuilder; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.CharBuffer; + + +/** + * Provides names for glyphs, which is useful when embedding fonts + * in PostScript or PDF documents. + * + * <p>If the font has a <code>Zapf</code> table, it is used to map + * glyph IDs back to a sequence of Unicode codepoints, which then + * makes it possible to look up or synthesize a PostScript glyph name + * according to Adobe’s conventions. This allows to extract the + * original text from the generated PDF or PostScript file, which is + * important for indexing, searching and extracting. + * + * <p>Otherwise, glyph names are taken from the <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM06/Chap6post.html" + * ><code>post</code> table</a>. All known formats (1, 2, 2.5, 3 and + * 4) are supported. + * + * <p><b>Open Tasks:</b> The code could be cleaner structured by + * having separate sub-classes for each variant of the POST table. + * Also, the implementation should not read in all glyph names if a + * font provides them in a POST table of type 2. It would be + * sufficient to just read in the offsets and delay the String + * fetching and conversion to the time when the glyph name is actually + * requested. + * + * <p><b>Lack of Thread Safety:</b> The GlyphNamer class is + * intentionally <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the GlyphNamer. + * It would thus be wasteful to acquire additional locks for the + * GlyphNamer. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphNamer +{ + /** + * The 'post' table of the font. + */ + private ByteBuffer postTable; + + + /** + * The 'Zapf' table of the font, or null if the font has no + * such table. + */ + private ByteBuffer zapfTable; + + + /** + * The offset of each glyph relative to the Zapf table, + * or null if the font does not have a Zapf table. + */ + private IntBuffer zapfOffsets; + + + /** + * The offset from the start of the Zapf table to the start + * of the extra info area. + */ + private int zapfExtraInfo; + + + /** + * The format of the post table, a Fixed 16.16 number. + */ + private int postFormat; + + + /** + * An array of glyph names. Used for table formats 1, 2, 2.5. + */ + private String[] glyphNames; + + + /** + * An array from glyph to character codes. Similar to the + * workings of a Zapf table, but maps to CID instead of + * Unicode. Used for table format 4. + */ + private CharBuffer glyphCharacterCodes; + + + /** + * The PostScript names of the 258 standard Macintosh glyphs. Note + * that some of these glyphs are not in the Adobe Standard Glyph + * List for New Fonts, namely .notdef, .null, nonmarkingreturn, + * nonbreakingspace, apple, onesuperior, twosuperior, and + * threesuperior. + */ + private static final String[] STANDARD_POSTSCRIPT_GLYPH_NAMES = + { + ".notdef", // glyph #0 + ".null", // glyph #1 + "nonmarkingreturn", // glyph #2 + "space", // glyph #3 + "exclam", // glyph #4 + "quotedbl", // glyph #5 + "numbersign", // glyph #6 + "dollar", // glyph #7 + "percent", // glyph #8 + "ampersand", // glyph #9 + "quotesingle", // glyph #10 + "parenleft", // glyph #11 + "parenright", // glyph #12 + "asterisk", // glyph #13 + "plus", // glyph #14 + "comma", // glyph #15 + "hyphen", // glyph #16 + "period", // glyph #17 + "slash", // glyph #18 + "zero", // glyph #19 + "one", // glyph #20 + "two", // glyph #21 + "three", // glyph #22 + "four", // glyph #23 + "five", // glyph #24 + "six", // glyph #25 + "seven", // glyph #26 + "eight", // glyph #27 + "nine", // glyph #28 + "colon", // glyph #29 + "semicolon", // glyph #30 + "less", // glyph #31 + "equal", // glyph #32 + "greater", // glyph #33 + "question", // glyph #34 + "at", // glyph #35 + "A", // glyph #36 + "B", // glyph #37 + "C", // glyph #38 + "D", // glyph #39 + "E", // glyph #40 + "F", // glyph #41 + "G", // glyph #42 + "H", // glyph #43 + "I", // glyph #44 + "J", // glyph #45 + "K", // glyph #46 + "L", // glyph #47 + "M", // glyph #48 + "N", // glyph #49 + "O", // glyph #50 + "P", // glyph #51 + "Q", // glyph #52 + "R", // glyph #53 + "S", // glyph #54 + "T", // glyph #55 + "U", // glyph #56 + "V", // glyph #57 + "W", // glyph #58 + "X", // glyph #59 + "Y", // glyph #60 + "Z", // glyph #61 + "bracketleft", // glyph #62 + "backslash", // glyph #63 + "bracketright", // glyph #64 + "asciicircum", // glyph #65 + "underscore", // glyph #66 + "grave", // glyph #67 + "a", // glyph #68 + "b", // glyph #69 + "c", // glyph #70 + "d", // glyph #71 + "e", // glyph #72 + "f", // glyph #73 + "g", // glyph #74 + "h", // glyph #75 + "i", // glyph #76 + "j", // glyph #77 + "k", // glyph #78 + "l", // glyph #79 + "m", // glyph #80 + "n", // glyph #81 + "o", // glyph #82 + "p", // glyph #83 + "q", // glyph #84 + "r", // glyph #85 + "s", // glyph #86 + "t", // glyph #87 + "u", // glyph #88 + "v", // glyph #89 + "w", // glyph #90 + "x", // glyph #91 + "y", // glyph #92 + "z", // glyph #93 + "braceleft", // glyph #94 + "bar", // glyph #95 + "braceright", // glyph #96 + "asciitilde", // glyph #97 + "Adieresis", // glyph #98 + "Aring", // glyph #99 + "Ccedilla", // glyph #100 + "Eacute", // glyph #101 + "Ntilde", // glyph #102 + "Odieresis", // glyph #103 + "Udieresis", // glyph #104 + "aacute", // glyph #105 + "agrave", // glyph #106 + "acircumflex", // glyph #107 + "adieresis", // glyph #108 + "atilde", // glyph #109 + "aring", // glyph #110 + "ccedilla", // glyph #111 + "eacute", // glyph #112 + "egrave", // glyph #113 + "ecircumflex", // glyph #114 + "edieresis", // glyph #115 + "iacute", // glyph #116 + "igrave", // glyph #117 + "icircumflex", // glyph #118 + "idieresis", // glyph #119 + "ntilde", // glyph #120 + "oacute", // glyph #121 + "ograve", // glyph #122 + "ocircumflex", // glyph #123 + "odieresis", // glyph #124 + "otilde", // glyph #125 + "uacute", // glyph #126 + "ugrave", // glyph #127 + "ucircumflex", // glyph #128 + "udieresis", // glyph #129 + "dagger", // glyph #130 + "degree", // glyph #131 + "cent", // glyph #132 + "sterling", // glyph #133 + "section", // glyph #134 + "bullet", // glyph #135 + "paragraph", // glyph #136 + "germandbls", // glyph #137 + "registered", // glyph #138 + "copyright", // glyph #139 + "trademark", // glyph #140 + "acute", // glyph #141 + "dieresis", // glyph #142 + "notequal", // glyph #143 + "AE", // glyph #144 + "Oslash", // glyph #145 + "infinity", // glyph #146 + "plusminus", // glyph #147 + "lessequal", // glyph #148 + "greaterequal", // glyph #149 + "yen", // glyph #150 + "mu", // glyph #151 + "partialdiff", // glyph #152 + "summation", // glyph #153 + "product", // glyph #154 + "pi", // glyph #155 + "integral", // glyph #156 + "ordfeminine", // glyph #157 + "ordmasculine", // glyph #158 + "Omega", // glyph #159 + "ae", // glyph #160 + "oslash", // glyph #161 + "questiondown", // glyph #162 + "exclamdown", // glyph #163 + "logicalnot", // glyph #164 + "radical", // glyph #165 + "florin", // glyph #166 + "approxequal", // glyph #167 + "Delta", // glyph #168 + "guillemotleft", // glyph #169 + "guillemotright", // glyph #170 + "ellipsis", // glyph #171 + "nonbreakingspace", // glyph #172 + "Agrave", // glyph #173 + "Atilde", // glyph #174 + "Otilde", // glyph #175 + "OE", // glyph #176 + "oe", // glyph #177 + "endash", // glyph #178 + "emdash", // glyph #179 + "quotedblleft", // glyph #180 + "quotedblright", // glyph #181 + "quoteleft", // glyph #182 + "quoteright", // glyph #183 + "divide", // glyph #184 + "lozenge", // glyph #185 + "ydieresis", // glyph #186 + "Ydieresis", // glyph #187 + "fraction", // glyph #188 + "currency", // glyph #189 + "guilsinglleft", // glyph #190 + "guilsinglright", // glyph #191 + "fi", // glyph #192 + "fl", // glyph #193 + "daggerdbl", // glyph #194 + "periodcentered", // glyph #195 + "quotesinglbase", // glyph #196 + "quotedblbase", // glyph #197 + "perthousand", // glyph #198 + "Acircumflex", // glyph #199 + "Ecircumflex", // glyph #200 + "Aacute", // glyph #201 + "Edieresis", // glyph #202 + "Egrave", // glyph #203 + "Iacute", // glyph #204 + "Icircumflex", // glyph #205 + "Idieresis", // glyph #206 + "Igrave", // glyph #207 + "Oacute", // glyph #208 + "Ocircumflex", // glyph #209 + "apple", // glyph #210 + "Ograve", // glyph #211 + "Uacute", // glyph #212 + "Ucircumflex", // glyph #213 + "Ugrave", // glyph #214 + "dotlessi", // glyph #215 + "circumflex", // glyph #216 + "tilde", // glyph #217 + "macron", // glyph #218 + "breve", // glyph #219 + "dotaccent", // glyph #220 + "ring", // glyph #221 + "cedilla", // glyph #222 + "hungarumlaut", // glyph #223 + "ogonek", // glyph #224 + "caron", // glyph #225 + "Lslash", // glyph #226 + "lslash", // glyph #227 + "Scaron", // glyph #228 + "scaron", // glyph #229 + "Zcaron", // glyph #230 + "zcaron", // glyph #231 + "brokenbar", // glyph #232 + "Eth", // glyph #233 + "eth", // glyph #234 + "Yacute", // glyph #235 + "yacute", // glyph #236 + "Thorn", // glyph #237 + "thorn", // glyph #238 + "minus", // glyph #239 + "multiply", // glyph #240 + "onesuperior", // glyph #241 + "twosuperior", // glyph #242 + "threesuperior", // glyph #243 + "onehalf", // glyph #244 + "onequarter", // glyph #245 + "threequarters", // glyph #246 + "franc", // glyph #247 + "Gbreve", // glyph #248 + "gbreve", // glyph #249 + "Idotaccent", // glyph #250 + "Scedilla", // glyph #251 + "scedilla", // glyph #252 + "Cacute", // glyph #253 + "cacute", // glyph #254 + "Ccaron", // glyph #255 + "ccaron", // glyph #256 + "dcroat" // glyph #257 + }; + + + private GlyphNamer(int numGlyphs, + ByteBuffer postTable, + ByteBuffer zapfTable) + { + this.postTable = postTable; + this.zapfTable = zapfTable; + + if ((zapfTable != null) && (zapfTable.getInt(0) == 0x00010000)) + { + readZapf(numGlyphs); + return; + } + + readPost(); + } + + + /** + * Sets up the information which allows to retrieve the information + * on demand. + * + * @param numGlyphs the number of glyphs in the font. This value + * comes from the <code>maxp</code> table. + */ + public static GlyphNamer forTables(int numGlyphs, + ByteBuffer postTable, + ByteBuffer zapfTable) + { + return new GlyphNamer(numGlyphs, postTable, zapfTable); + } + + + /** + * Retrieves or synthesizes a PostScript name for the glyph. + * Although the code is reasonably fast, it is recommended + * to cache the results in the printer driver. + * + * <p>If a font provides a 'Zapf' table, the reverse mapping + * from glyph to UTF-16 sequence is performed, and a glyph + * name is synthesized following the recommendations by Adobe. + * This allows to extract the original text from the generated + * PostScript or PDF, which is a requirement for indexing + * and searching. + * + * <p>If a font does not provide a 'Zapf' table, the glyph name + * is taken from the 'post' table. Note that some fonts have + * wrong data in their post data, in which case the resulting + * name will be garbage. Usually, this does not hurt, unless + * the user wants to extract text from the generated PostScript + * or PDF file. The GNU implementation understands all known + * formats of the post table (1, 2, 2.5, 3 and 4). + * + * @param glyph the index of the glyph whose name is to be + * retrieved. + * + * @return the glyph name, such as <code>A</code>, + * <code>gcircumflex</code>, <code>z_uni0302</code>, or + * <code>u11C42</code>.</li> + */ + String getGlyphName(int glyph) + { + if (zapfOffsets != null) + { + zapfTable.position(zapfOffsets.get(glyph) + 8); + int numChars = zapfTable.getChar(); + char[] chars = new char[numChars]; + for (int i = 0; i < numChars; i++) + chars[i] = zapfTable.getChar(); + return getGlyphName(chars); + } + + + /* Type 1, Type 2, Type 2.5 */ + if (glyphNames != null) + return glyphNames[glyph]; + + /* Type 4: Synthesized glyph name. */ + if (glyphCharacterCodes != null) + return "a" + glyphCharacterCodes.get(glyph); + + /* Type 3: Arbitrary, but unique name for the glyph. + * + * To find out what a good naming scheme would be, we have printed + * a document containing the character U+201C in the font + * "Hiragino Kaku Gothic Pro W3" (by Dainippon Screen Mfg. Co., + * Ltd.) on Apple MacOS X 10.1.5. This font has a type 3 'post' + * table, and its 'cmap' maps U+201C to glyph #108. The generated + * PostScript file defined a character whose name was "g108". + * + * Therefore, we use 'g' as name prefix. According to the + * TrueType/OpenType specification, it should not matter what + * prefix we use. On the other hand, it does not hurt either to be + * compatible with a good printer driver. + * + * Actually, that specific font also contains a 'Zapf' table, + * which allows to generate glyph names according to Adobe's + * conventions, so that extracting text from and searching in the + * generated PostScript or PDF becomes possible. While the Apple + * PostScript printer driver does not seem to use the 'Zapf' table + * for this purpose, we do. + */ + return "g" + glyph; + } + + + /** + * Sets up some buffers which allow to quickly read information from + * the Zapf table. + * + * @see <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM06/Chap6Zapf.html"> + * Apple’s documentation of the <code>Zapf</code> table</a> + */ + private void readZapf(int numGlyphs) + { + zapfExtraInfo = zapfTable.getInt(4); + zapfTable.position(8); + zapfOffsets = zapfTable.asIntBuffer(); + zapfOffsets.limit(numGlyphs); + } + + + /** + * Reads in the PostScript data from a <code>post</code> table of a + * TrueType or OpenType font. The implementation currently + * understands the table formats 1, 2, 2.5, 3, and 4. + */ + private void readPost() + { + int numGlyphs, nameIndex, maxNameIndex; + char[] nameIndices; + String[] names; + byte[] pascalName; + + postTable.position(0); + postFormat = postTable.getInt(); + switch (postFormat) + { + case 0x00010000: + glyphNames = STANDARD_POSTSCRIPT_GLYPH_NAMES; + return; + + case 0x00020000: + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphNames = new String[numGlyphs]; + pascalName = new byte[255]; + nameIndices = new char[numGlyphs]; + maxNameIndex = 0; + for (int i = 0; i < numGlyphs; i++) + maxNameIndex = Math.max(maxNameIndex, + nameIndices[i] = postTable.getChar()); + + names = new String[Math.max(maxNameIndex - 258 + 1, 0)]; + for (int i = 258; i <= maxNameIndex; i++) + { + int nameLen = (postTable.get() & 0xff); + postTable.get(pascalName, 0, nameLen); + names[i - 258] = new String(pascalName, 0, nameLen); + } + for (int i = 0; i < numGlyphs; i++) + { + nameIndex = nameIndices[i]; + if (nameIndex < 258) + glyphNames[i] = STANDARD_POSTSCRIPT_GLYPH_NAMES[nameIndex]; + else + glyphNames[i] = names[nameIndex - 258]; + } + return; + + case 0x00025000: // in case some font has a wrong representation of 2.5 + case 0x00028000: + /* Format 2.5 is a re-ordering of the standard names. It has + * been deprecated in February 2000, but might still occasionally + * float around. Since it can be supported with so little code, + * we do so. + */ + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphNames = new String[numGlyphs]; + for (int i = 0; i < numGlyphs; i++) + glyphNames[i] = STANDARD_POSTSCRIPT_GLYPH_NAMES[i + postTable.get()]; + return; + + case 0x00030000: + /* Format 3 leaves it to the printer driver to choose whatever + * name it wants to. + */ + return; + + case 0x00040000: + /* Format 4 is used by Apple for composite fonts that have + * synthetic glyph names. The name of a glyph is "a" plus + * the integer (in decimal notation) that follows the table + * after numGlyphs. + */ + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphCharacterCodes = postTable.asCharBuffer(); + glyphCharacterCodes.limit(numGlyphs); + return; + } + } + + + + /* For generating the following tables, a quick-and-dirty Python + * script was used. It is unlikely that we ever need to run it + * again, but for information and convenient access, it is included + * below. Initial '#' characters need to be removed from the generated + * strings, they are present so that the lines not break in the middle + * of Java escape sequences (no, this is not very clean). + * + * import string + * + * javaEscapes = {0x22:'\\"', 0x5c:'\\\\'} + * def escape(c): + * if javaEscapes.has_key(c): + * return javaEscapes[c] + * elif 0x20 <= c <= 0x7e: + * return chr(c) + * else: + * return '\\u%04x' % c + * + * def dump(name, s, stride): + * s = ('#' * stride) + s + * print " private static final String %s" % name + * for i in range(0, len(s), 60): + * print ' + "%s"' % s[i:i+60] + * + * glyphs = {} + * for line in open('aglfn13.txt', 'r').readlines(): + * if line[0] == '#': continue + * [ucs, glyphName, desc] = line.split(';') + * glyph = int('0x' + ucs, 0) + * assert (not glyphs.has_key(glyph)) or (glyphs[glyph] == glyphName) + * glyphs[glyph] = glyphName + * del glyphs[0] # arrowvertex + * k = glyphs.keys() + * k.sort() + * numGlyphs = len(k) + * names = '' + * pos = [] + * for glyph in k: + * pos.append(len(names) + 1) + * names = names + '/' + glyphs[glyph] + * dump('AGLFN_GLYPHS', string.join(map(escape, k), ''), 5) + * dump('AGLFN_NAME_OFFSET', string.join(map(escape, pos), ''), 4) + * dump('AGLFN_NAMES', names + '/', 0) + */ + + + /** + * A String that contains the Unicode codepoint for each glyph + * in the Adobe Glyph List. The characters are in sorted order. + * + * Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts</a> + */ + private static final String AGLFN_GLYPHS + = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU" + + "VWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u00a1\u00a2\u00a3" + + "\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ae" + + "\u00af\u00b0\u00b1\u00b4\u00b5\u00b6\u00b7\u00b8\u00ba\u00bb" + + "\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5" + + "\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf" + + "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9" + + "\u00da\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3" + + "\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed" + + "\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7" + + "\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff\u0100\u0101" + + "\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b" + + "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115" + + "\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u011e\u011f" + + "\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129" + + "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0132\u0133" + + "\u0134\u0135\u0136\u0137\u0138\u0139\u013a\u013b\u013c\u013d" + + "\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147" + + "\u0148\u0149\u014a\u014b\u014c\u014d\u014e\u014f\u0150\u0151" + + "\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b" + + "\u015c\u015d\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165" + + "\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d\u016e\u016f" + + "\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179" + + "\u017a\u017b\u017c\u017d\u017e\u017f\u0192\u01a0\u01a1\u01af" + + "\u01b0\u01e6\u01e7\u01fa\u01fb\u01fc\u01fd\u01fe\u01ff\u0218" + + "\u0219\u02bc\u02bd\u02c6\u02c7\u02d8\u02d9\u02da\u02db\u02dc" + + "\u02dd\u0300\u0301\u0303\u0309\u0323\u0384\u0385\u0386\u0387" + + "\u0388\u0389\u038a\u038c\u038e\u038f\u0390\u0391\u0392\u0393" + + "\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e" + + "\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03aa" + + "\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4" + + "\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bd\u03be\u03bf" + + "\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9" + + "\u03ca\u03cb\u03cc\u03cd\u03ce\u03d1\u03d2\u03d5\u03d6\u0401" + + "\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b" + + "\u040c\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416" + + "\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420" + + "\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a" + + "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434" + + "\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e" + + "\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448" + + "\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0451\u0452\u0453" + + "\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045e" + + "\u045f\u0462\u0463\u0472\u0473\u0474\u0475\u0490\u0491\u04d9" + + "\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9" + + "\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05d0" + + "\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da" + + "\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4" + + "\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u060c" + + "\u061b\u061f\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628" + + "\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632" + + "\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0660\u0661\u0662" + + "\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u066a\u066d\u0679" + + "\u067e\u0686\u0688\u0691\u0698\u06a4\u06af\u06ba\u06d2\u06d5" + + "\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u200c\u200d" + + "\u200e\u200f\u2012\u2013\u2014\u2015\u2017\u2018\u2019\u201a" + + "\u201b\u201c\u201d\u201e\u2020\u2021\u2022\u2024\u2025\u2026" + + "\u202c\u202d\u202e\u2030\u2032\u2033\u2039\u203a\u203c\u2044" + + "\u20a1\u20a3\u20a4\u20a7\u20aa\u20ab\u20ac\u2105\u2111\u2113" + + "\u2116\u2118\u211c\u211e\u2122\u2126\u212e\u2135\u2153\u2154" + + "\u215b\u215c\u215d\u215e\u2190\u2191\u2192\u2193\u2194\u2195" + + "\u21a8\u21b5\u21d0\u21d1\u21d2\u21d3\u21d4\u2200\u2202\u2203" + + "\u2205\u2206\u2207\u2208\u2209\u220b\u220f\u2211\u2212\u2217" + + "\u221a\u221d\u221e\u221f\u2220\u2227\u2228\u2229\u222a\u222b" + + "\u2234\u223c\u2245\u2248\u2260\u2261\u2264\u2265\u2282\u2283" + + "\u2284\u2286\u2287\u2295\u2297\u22a5\u22c5\u2302\u2310\u2320" + + "\u2321\u2329\u232a\u2500\u2502\u250c\u2510\u2514\u2518\u251c" + + "\u2524\u252c\u2534\u253c\u2550\u2551\u2552\u2553\u2554\u2555" + + "\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f" + + "\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569" + + "\u256a\u256b\u256c\u2580\u2584\u2588\u258c\u2590\u2591\u2592" + + "\u2593\u25a0\u25a1\u25aa\u25ab\u25ac\u25b2\u25ba\u25bc\u25c4" + + "\u25ca\u25cb\u25cf\u25d8\u25d9\u25e6\u263a\u263b\u263c\u2640" + + "\u2642\u2660\u2663\u2665\u2666\u266a\u266b"; + + + /** + * The offset of each glyph name in AGLFN_NAMES. + * + * Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts</a> + */ + private static final String AGLFN_NAME_OFFSET + = "\u0001\u0007\u000e\u0017\")1;GQ\\ejpw~\u0084\u0089\u008d" + + "\u0091\u0097\u009c\u00a1\u00a5\u00ab\u00b1\u00b6\u00bc\u00c6" + + "\u00cb\u00d1\u00d9\u00e2\u00e5\u00e7\u00e9\u00eb\u00ed\u00ef" + + "\u00f1\u00f3\u00f5\u00f7\u00f9\u00fb\u00fd\u00ff\u0101\u0103" + + "\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117" + + "\u0119\u0125\u012f\u013c\u0148\u0153\u0159\u015b\u015d\u015f" + + "\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173" + + "\u0175\u0177\u0179\u017b\u017d\u017f\u0181\u0183\u0185\u0187" + + "\u0189\u018b\u018d\u0197\u019b\u01a6\u01b1\u01bc\u01c1\u01ca" + + "\u01d3\u01d7\u01e1\u01e9\u01f2\u01fc\u0208\u0216\u0221\u022c" + + "\u0233\u023a\u0244\u024a\u024d\u0257\u0266\u026e\u027b\u028a" + + "\u0295\u029d\u02ab\u02b8\u02bf\u02c6\u02d2\u02d9\u02e3\u02e9" + + "\u02ec\u02f5\u02fc\u0303\u030f\u0319\u0320\u0327\u0333\u033d" + + "\u0341\u0348\u034f\u0356\u0362\u0369\u0373\u037c\u0383\u038a" + + "\u0391\u039d\u03a7\u03ae\u03b4\u03bf\u03c6\u03cd\u03d9\u03e0" + + "\u03ea\u03f0\u03f3\u03fc\u0403\u040a\u0416\u0420\u0427\u042e" + + "\u043a\u0444\u0448\u044f\u0456\u045d\u0469\u0470\u047a\u0481" + + "\u0488\u048f\u0496\u04a2\u04ac\u04b3\u04b9\u04c3\u04cb\u04d3" + + "\u04da\u04e1\u04e9\u04f1\u04f8\u04ff\u050b\u0517\u0522\u052d" + + "\u0534\u053b\u0542\u0549\u0550\u0557\u055f\u0567\u056e\u0575" + + "\u0580\u058b\u0593\u059b\u05a2\u05a9\u05b5\u05c1\u05c8\u05cf" + + "\u05da\u05e5\u05f2\u05ff\u060b\u0617\u061c\u0621\u0628\u062f" + + "\u0637\u063f\u0646\u064d\u0655\u065d\u0668\u0671\u0674\u0677" + + "\u0683\u068f\u069c\u06a9\u06b6\u06bd\u06c4\u06d1\u06de\u06e5" + + "\u06ec\u06f1\u06f6\u06fd\u0704\u070b\u0712\u071f\u072c\u0733" + + "\u073a\u0746\u074a\u074e\u0756\u075e\u0765\u076c\u077a\u0788" + + "\u078b\u078e\u0795\u079c\u07a9\u07b6\u07bd\u07c4\u07cb\u07d2" + + "\u07de\u07ea\u07f3\u07fc\u0803\u080a\u0817\u0824\u082b\u0832" + + "\u0837\u083c\u0843\u084a\u0852\u085a\u0861\u0868\u086e\u0874" + + "\u0882\u0890\u0898\u08a0\u08ac\u08b8\u08c4\u08d0\u08da\u08e1" + + "\u08e8\u08f3\u08fe\u0905\u090c\u0912\u0919\u091f\u0925\u092b" + + "\u0931\u0938\u093f\u094a\u0955\u095d\u0965\u0971\u097d\u098a" + + "\u0997\u09a1\u09ab\u09b6\u09bc\u09c2\u09cc\u09d1\u09d8\u09de" + + "\u09eb\u09f5\u09ff\u0a09\u0a17\u0a24\u0a2a\u0a38\u0a43\u0a4d" + + "\u0a5a\u0a63\u0a6d\u0a7a\u0a87\u0a92\u0aa4\u0aaa\u0aaf\u0ab5" + + "\u0abd\u0ac2\u0ac6\u0acc\u0ad1\u0ad7\u0ade\u0ae1\u0ae4\u0ae7" + + "\u0aef\u0af2\u0af6\u0afc\u0b00\u0b08\u0b0c\u0b10\u0b14\u0b21" + + "\u0b31\u0b3c\u0b49\u0b52\u0b5c\u0b71\u0b77\u0b7c\u0b82\u0b88" + + "\u0b90\u0b95\u0b99\u0b9f\u0ba4\u0baa\u0bb1\u0bb4\u0bb7\u0bbf" + + "\u0bc2\u0bc6\u0bcd\u0bd3\u0bd7\u0bdf\u0be3\u0be7\u0beb\u0bf1" + + "\u0bfe\u0c0e\u0c1b\u0c28\u0c33\u0c3a\u0c43\u0c48\u0c4f\u0c59" + + "\u0c63\u0c6d\u0c77\u0c81\u0c8b\u0c95\u0c9f\u0ca9\u0cb3\u0cbd" + + "\u0cc7\u0cd1\u0cdb\u0ce5\u0cef\u0cf9\u0d03\u0d0d\u0d17\u0d21" + + "\u0d2b\u0d35\u0d3f\u0d49\u0d53\u0d5d\u0d67\u0d71\u0d7b\u0d85" + + "\u0d8f\u0d99\u0da3\u0dad\u0db7\u0dc1\u0dcb\u0dd5\u0ddf\u0de9" + + "\u0df3\u0dfd\u0e07\u0e11\u0e1b\u0e25\u0e2f\u0e39\u0e43\u0e4d" + + "\u0e57\u0e61\u0e6b\u0e75\u0e7f\u0e89\u0e93\u0e9d\u0ea7\u0eb1" + + "\u0ebb\u0ec5\u0ecf\u0ed9\u0ee3\u0eed\u0ef7\u0f01\u0f0b\u0f15" + + "\u0f1f\u0f29\u0f33\u0f3d\u0f47\u0f51\u0f5b\u0f65\u0f6f\u0f79" + + "\u0f83\u0f8d\u0f97\u0fa1\u0fab\u0fb5\u0fbf\u0fc9\u0fd3\u0fdd" + + "\u0fe7\u0ff1\u0ffb\u1005\u100f\u1019\u1023\u102d\u1037\u1041" + + "\u104b\u1055\u105f\u1069\u1073\u107d\u1087\u1091\u109b\u10a5" + + "\u10af\u10b9\u10c3\u10cd\u10d7\u10e1\u10eb\u10f5\u10ff\u1109" + + "\u1113\u111d\u1127\u1131\u113b\u1145\u114f\u1159\u1163\u116d" + + "\u1177\u1181\u118b\u1195\u119f\u11a9\u11b3\u11bd\u11c7\u11d1" + + "\u11db\u11e5\u11ef\u11f9\u1203\u120d\u1217\u1221\u122b\u1235" + + "\u123f\u1249\u1253\u125d\u1267\u1271\u127b\u1285\u128f\u1299" + + "\u12a3\u12ad\u12b7\u12c1\u12cb\u12d5\u12df\u12e9\u12f3\u12fd" + + "\u1307\u1311\u131b\u1325\u132f\u1339\u1343\u134d\u1357\u1361" + + "\u136b\u1375\u137f\u1389\u1393\u139d\u13a7\u13b1\u13bb\u13c5" + + "\u13cf\u13d9\u13e3\u13ed\u13f7\u1401\u140b\u1415\u141f\u1429" + + "\u1433\u143d\u1447\u1451\u145b\u1465\u146f\u1479\u1483\u148d" + + "\u1497\u14a1\u14ab\u14b5\u14bf\u14c9\u14d3\u14dd\u14e7\u14f1" + + "\u14f8\u14ff\u1506\u150d\u1517\u1521\u1528\u152f\u1539\u1541" + + "\u1549\u1551\u155c\u1563\u156a\u1574\u1582\u158c\u1597\u15a6" + + "\u15b4\u15c1\u15cf\u15dc\u15e3\u15ed\u15f4\u1603\u1612\u161b" + + "\u1625\u162f\u1639\u1645\u164c\u1653\u1661\u1670\u167a\u1683" + + "\u1691\u1697\u169c\u16a3\u16ad\u16b2\u16b7\u16c1\u16ca\u16d4" + + "\u16de\u16ea\u16f3\u1700\u170a\u1710\u171a\u1720\u1729\u1733" + + "\u173d\u174a\u1756\u1763\u176d\u1775\u1780\u178a\u1794\u179e" + + "\u17ab\u17ba\u17c7\u17d2\u17e0\u17ed\u17fa\u1804\u1810\u181c" + + "\u1825\u182b\u1834\u183c\u1847\u1850\u1858\u1862\u1868\u1875" + + "\u187d\u188a\u1893\u189e\u18a4\u18af\u18b9\u18c6\u18cc\u18d5" + + "\u18df\u18e7\u18f1\u18fd\u1906\u1912\u191c\u1929\u1936\u1945" + + "\u194f\u195c\u196b\u1976\u1985\u1993\u199b\u19a1\u19af\u19ba" + + "\u19c5\u19cf\u19da\u19e3\u19ec\u19f5\u19fe\u1a07\u1a10\u1a19" + + "\u1a22\u1a2b\u1a34\u1a3d\u1a46\u1a4f\u1a58\u1a61\u1a6a\u1a73" + + "\u1a7c\u1a85\u1a8e\u1a97\u1aa0\u1aa9\u1ab2\u1abb\u1ac4\u1acd" + + "\u1ad6\u1adf\u1ae8\u1af1\u1afa\u1b03\u1b0c\u1b15\u1b1e\u1b27" + + "\u1b30\u1b39\u1b42\u1b4a\u1b52\u1b58\u1b60\u1b68\u1b70\u1b76" + + "\u1b7e\u1b88\u1b8f\u1b96\u1b9d\u1ba8\u1bb0\u1bb8\u1bc0\u1bc8" + + "\u1bd0\u1bd7\u1bde\u1be8\u1bf2\u1bfd\u1c07\u1c14\u1c18\u1c1f" + + "\u1c24\u1c2a\u1c2f\u1c35\u1c3d\u1c49"; + + + /** + * The name of each glyph in the Adobe Glyph List for New Fonts + * (AGLFN). The name of the n-th glyph starts at position + * AGLFN_NAME_OFFSET.charAt(n). It ends before the following + * slash (slashes cannot be part of a PostScript name, which + * is why we use it for separation). + * + * <p>Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts</a> + */ + private static final String AGLFN_NAMES + = "/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/q" + + "uotesingle/parenleft/parenright/asterisk/plus/comma/hyphen/p" + + "eriod/slash/zero/one/two/three/four/five/six/seven/eight/nin" + + "e/colon/semicolon/less/equal/greater/question/at/A/B/C/D/E/F" + + "/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backsla" + + "sh/bracketright/asciicircum/underscore/grave/a/b/c/d/e/f/g/h" + + "/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/bracerigh" + + "t/asciitilde/exclamdown/cent/sterling/currency/yen/brokenbar" + + "/section/dieresis/copyright/ordfeminine/guillemotleft/logica" + + "lnot/registered/macron/degree/plusminus/acute/mu/paragraph/p" + + "eriodcentered/cedilla/ordmasculine/guillemotright/onequarter" + + "/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumfle" + + "x/Atilde/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumfl" + + "ex/Edieresis/Igrave/Iacute/Icircumflex/Idieresis/Eth/Ntilde/" + + "Ograve/Oacute/Ocircumflex/Otilde/Odieresis/multiply/Oslash/U" + + "grave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls/a" + + "grave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/" + + "egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumfle" + + "x/idieresis/eth/ntilde/ograve/oacute/ocircumflex/otilde/odie" + + "resis/divide/oslash/ugrave/uacute/ucircumflex/udieresis/yacu" + + "te/thorn/ydieresis/Amacron/amacron/Abreve/abreve/Aogonek/aog" + + "onek/Cacute/cacute/Ccircumflex/ccircumflex/Cdotaccent/cdotac" + + "cent/Ccaron/ccaron/Dcaron/dcaron/Dcroat/dcroat/Emacron/emacr" + + "on/Ebreve/ebreve/Edotaccent/edotaccent/Eogonek/eogonek/Ecaro" + + "n/ecaron/Gcircumflex/gcircumflex/Gbreve/gbreve/Gdotaccent/gd" + + "otaccent/Gcommaaccent/gcommaaccent/Hcircumflex/hcircumflex/H" + + "bar/hbar/Itilde/itilde/Imacron/imacron/Ibreve/ibreve/Iogonek" + + "/iogonek/Idotaccent/dotlessi/IJ/ij/Jcircumflex/jcircumflex/K" + + "commaaccent/kcommaaccent/kgreenlandic/Lacute/lacute/Lcommaac" + + "cent/lcommaaccent/Lcaron/lcaron/Ldot/ldot/Lslash/lslash/Nacu" + + "te/nacute/Ncommaaccent/ncommaaccent/Ncaron/ncaron/napostroph" + + "e/Eng/eng/Omacron/omacron/Obreve/obreve/Ohungarumlaut/ohunga" + + "rumlaut/OE/oe/Racute/racute/Rcommaaccent/rcommaaccent/Rcaron" + + "/rcaron/Sacute/sacute/Scircumflex/scircumflex/Scedilla/scedi" + + "lla/Scaron/scaron/Tcommaaccent/tcommaaccent/Tcaron/tcaron/Tb" + + "ar/tbar/Utilde/utilde/Umacron/umacron/Ubreve/ubreve/Uring/ur" + + "ing/Uhungarumlaut/uhungarumlaut/Uogonek/uogonek/Wcircumflex/" + + "wcircumflex/Ycircumflex/ycircumflex/Ydieresis/Zacute/zacute/" + + "Zdotaccent/zdotaccent/Zcaron/zcaron/longs/florin/Ohorn/ohorn" + + "/Uhorn/uhorn/Gcaron/gcaron/Aringacute/aringacute/AEacute/aea" + + "cute/Oslashacute/oslashacute/Scommaaccent/scommaaccent/afii5" + + "7929/afii64937/circumflex/caron/breve/dotaccent/ring/ogonek/" + + "tilde/hungarumlaut/gravecomb/acutecomb/tildecomb/hookaboveco" + + "mb/dotbelowcomb/tonos/dieresistonos/Alphatonos/anoteleia/Eps" + + "ilontonos/Etatonos/Iotatonos/Omicrontonos/Upsilontonos/Omega" + + "tonos/iotadieresistonos/Alpha/Beta/Gamma/Epsilon/Zeta/Eta/Th" + + "eta/Iota/Kappa/Lambda/Mu/Nu/Xi/Omicron/Pi/Rho/Sigma/Tau/Upsi" + + "lon/Phi/Chi/Psi/Iotadieresis/Upsilondieresis/alphatonos/epsi" + + "lontonos/etatonos/iotatonos/upsilondieresistonos/alpha/beta/" + + "gamma/delta/epsilon/zeta/eta/theta/iota/kappa/lambda/nu/xi/o" + + "micron/pi/rho/sigma1/sigma/tau/upsilon/phi/chi/psi/omega/iot" + + "adieresis/upsilondieresis/omicrontonos/upsilontonos/omegaton" + + "os/theta1/Upsilon1/phi1/omega1/afii10023/afii10051/afii10052" + + "/afii10053/afii10054/afii10055/afii10056/afii10057/afii10058" + + "/afii10059/afii10060/afii10061/afii10062/afii10145/afii10017" + + "/afii10018/afii10019/afii10020/afii10021/afii10022/afii10024" + + "/afii10025/afii10026/afii10027/afii10028/afii10029/afii10030" + + "/afii10031/afii10032/afii10033/afii10034/afii10035/afii10036" + + "/afii10037/afii10038/afii10039/afii10040/afii10041/afii10042" + + "/afii10043/afii10044/afii10045/afii10046/afii10047/afii10048" + + "/afii10049/afii10065/afii10066/afii10067/afii10068/afii10069" + + "/afii10070/afii10072/afii10073/afii10074/afii10075/afii10076" + + "/afii10077/afii10078/afii10079/afii10080/afii10081/afii10082" + + "/afii10083/afii10084/afii10085/afii10086/afii10087/afii10088" + + "/afii10089/afii10090/afii10091/afii10092/afii10093/afii10094" + + "/afii10095/afii10096/afii10097/afii10071/afii10099/afii10100" + + "/afii10101/afii10102/afii10103/afii10104/afii10105/afii10106" + + "/afii10107/afii10108/afii10109/afii10110/afii10193/afii10146" + + "/afii10194/afii10147/afii10195/afii10148/afii10196/afii10050" + + "/afii10098/afii10846/afii57799/afii57801/afii57800/afii57802" + + "/afii57793/afii57794/afii57795/afii57798/afii57797/afii57806" + + "/afii57796/afii57807/afii57839/afii57645/afii57841/afii57842" + + "/afii57804/afii57803/afii57658/afii57664/afii57665/afii57666" + + "/afii57667/afii57668/afii57669/afii57670/afii57671/afii57672" + + "/afii57673/afii57674/afii57675/afii57676/afii57677/afii57678" + + "/afii57679/afii57680/afii57681/afii57682/afii57683/afii57684" + + "/afii57685/afii57686/afii57687/afii57688/afii57689/afii57690" + + "/afii57716/afii57717/afii57718/afii57388/afii57403/afii57407" + + "/afii57409/afii57410/afii57411/afii57412/afii57413/afii57414" + + "/afii57415/afii57416/afii57417/afii57418/afii57419/afii57420" + + "/afii57421/afii57422/afii57423/afii57424/afii57425/afii57426" + + "/afii57427/afii57428/afii57429/afii57430/afii57431/afii57432" + + "/afii57433/afii57434/afii57440/afii57441/afii57442/afii57443" + + "/afii57444/afii57445/afii57446/afii57470/afii57448/afii57449" + + "/afii57450/afii57451/afii57452/afii57453/afii57454/afii57455" + + "/afii57456/afii57457/afii57458/afii57392/afii57393/afii57394" + + "/afii57395/afii57396/afii57397/afii57398/afii57399/afii57400" + + "/afii57401/afii57381/afii63167/afii57511/afii57506/afii57507" + + "/afii57512/afii57513/afii57508/afii57505/afii57509/afii57514" + + "/afii57519/afii57534/Wgrave/wgrave/Wacute/wacute/Wdieresis/w" + + "dieresis/Ygrave/ygrave/afii61664/afii301/afii299/afii300/fig" + + "uredash/endash/emdash/afii00208/underscoredbl/quoteleft/quot" + + "eright/quotesinglbase/quotereversed/quotedblleft/quotedblrig" + + "ht/quotedblbase/dagger/daggerdbl/bullet/onedotenleader/twodo" + + "tenleader/ellipsis/afii61573/afii61574/afii61575/perthousand" + + "/minute/second/guilsinglleft/guilsinglright/exclamdbl/fracti" + + "on/colonmonetary/franc/lira/peseta/afii57636/dong/Euro/afii6" + + "1248/Ifraktur/afii61289/afii61352/weierstrass/Rfraktur/presc" + + "ription/trademark/Omega/estimated/aleph/onethird/twothirds/o" + + "neeighth/threeeighths/fiveeighths/seveneighths/arrowleft/arr" + + "owup/arrowright/arrowdown/arrowboth/arrowupdn/arrowupdnbse/c" + + "arriagereturn/arrowdblleft/arrowdblup/arrowdblright/arrowdbl" + + "down/arrowdblboth/universal/partialdiff/existential/emptyset" + + "/Delta/gradient/element/notelement/suchthat/product/summatio" + + "n/minus/asteriskmath/radical/proportional/infinity/orthogona" + + "l/angle/logicaland/logicalor/intersection/union/integral/the" + + "refore/similar/congruent/approxequal/notequal/equivalence/le" + + "ssequal/greaterequal/propersubset/propersuperset/notsubset/r" + + "eflexsubset/reflexsuperset/circleplus/circlemultiply/perpend" + + "icular/dotmath/house/revlogicalnot/integraltp/integralbt/ang" + + "leleft/angleright/SF100000/SF110000/SF010000/SF030000/SF0200" + + "00/SF040000/SF080000/SF090000/SF060000/SF070000/SF050000/SF4" + + "30000/SF240000/SF510000/SF520000/SF390000/SF220000/SF210000/" + + "SF250000/SF500000/SF490000/SF380000/SF280000/SF270000/SF2600" + + "00/SF360000/SF370000/SF420000/SF190000/SF200000/SF230000/SF4" + + "70000/SF480000/SF410000/SF450000/SF460000/SF400000/SF540000/" + + "SF530000/SF440000/upblock/dnblock/block/lfblock/rtblock/ltsh" + + "ade/shade/dkshade/filledbox/H22073/H18543/H18551/filledrect/" + + "triagup/triagrt/triagdn/triaglf/lozenge/circle/H18533/invbul" + + "let/invcircle/openbullet/smileface/invsmileface/sun/female/m" + + "ale/spade/club/heart/diamond/musicalnote/musicalnotedbl/"; + + + /** + * Determines the name of a glyph according to the Adobe Glyph List + * for New Fonts (AGLFN). Because all glyphs in AGLFN correspond to + * a precomposed Unicode codepoint, the mismatch between characters + * and glyphs is not an issue here. + * + * @param c the Unicode codepoint that corresponds to the glyph, for + * example <code>0x010a</code> for <code>LATIN CAPITAL LETTER C WITH + * DOT ABOVE</code>. + * + * @return the glyph name, for example <code>Cdotaccent</code>. If + * the glyph is not in the <i>Adobe Glyph List for New Fonts</i>, + * <code>null</code> is returned. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/aglfn13.txt" >Adobe + * Glyph List for New Fonts (AGLFN), version 1.1 of April 17, + * 2003</a> + * + * @see <a href= + * "http://partners.adobe.com/asn/developer/type/unicodegn.html#6" + * >Adobe’s guidelines related to Unicode</a> + */ + private static String getAGLFNName(char c) + { + int min, max, mid; + char midChar; + + /* Performs a binary search in the sorted array (actually, a + * String) of glyphs in the Adobe Glyph List for New Fonts. + * + * A good compiler might be able to optimize a call to charAt for + * a static final String, but this routine is probably not that + * critical to performance. + */ + min = 0; + max = AGLFN_GLYPHS.length() - 1; + mid = max >> 1; + midChar = AGLFN_GLYPHS.charAt(mid); + do + { + if (midChar == c) + break; + else if (midChar < c) + min = mid + 1; + else + max = mid; + mid = (min + max) >> 1; + midChar = AGLFN_GLYPHS.charAt(mid); + } + while (min < max); + + if (midChar != c) + return null; + + int pos = AGLFN_NAME_OFFSET.charAt(mid); + return AGLFN_NAMES.substring(pos, AGLFN_NAMES.indexOf('/', pos)); + } + + + /** + * Returns the PostScript name of a glyph, given the sequence of + * Unicode characters that is required to produce the glyph. The + * returned name follows Adobe’s glyph naming recommendations + * in order to allow searching and indexing of the produced + * PostScript and PDF. + * + * <p>Some examples: + * <ul><li><code>U+0041</code> gives <code>A</code>;</li> + * <li><code>U+011D</code> gives <code>gcircumflex</code>;</li> + * <li><code>U+007A U+0302</code> gives <code>z_uni0302</code>;</li> + * <li><code>U+D807 U+DC42</code> (an UTF-16 escape sequence) + * gives <code>u11C42</code>;</li> + * </ul>. + * + * <p>The routine does <i>not</i> bring sequences in any canonical + * form. Therefore, the result for <code>U+0067 U+0302</code> (the + * decomposition of <code>U+011D</code>) will be + * <code>g_uni0302</code>, not <code>gcircumflex</code>. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/unicodegn.jsp" >Unicode + * and Glyph Names</a> and <a href= + * "http://partners.adobe.com/asn/tech/type/glyphnamelimits.jsp" + * >Glyph Names and Current Implementations</a> + */ + private static String getGlyphName(char[] chars) + { + char c; + String name; + int numChars; + boolean hasSurrogates = false; + + if ((chars == null) || ((numChars = chars.length) == 0)) + return ".notdef"; + + /* The vast majority of cases will be just a single character. + * Therefore, we have a special code path for this case. + */ + if (numChars == 1) + { + c = chars[0]; + name = getAGLFNName(c); + if (name != null) + return name; + } + + CPStringBuilder buf = new CPStringBuilder(numChars * 8); + for (int i = 0; i < numChars; i++) + { + if (i > 0) + buf.append('_'); + c = chars[i]; + + /* handle surrogate pairs */ + if (c >> 10 == 0x36) // U+D800 .. U+DBFF: High surrogate + { + /* Adobe recommends using the 'u' prefix only for + * characters outside the Unicode Basic Multilingual Plane, + * because Acrobat 4 and 5 understand only the "uni" prefix. + * The 'u' prefix will be supported by Acrobat 6 and later. + * + * For further information, please refer to this page: + * http://partners.adobe.com/asn/tech/type/glyphnamelimits.jsp#3 + */ + int ucs4 = (((c & 0x3ff) << 10) | (chars[++i] & 0x3ff)) + 0x10000; + buf.append('u'); + buf.append(Integer.toHexString(ucs4).toUpperCase()); + } + else + { + /* Try the Adobe Glyph List. */ + name = getAGLFNName(c); + if (name != null) + buf.append(name); + else + { + char nibble; + buf.append("uni"); + nibble = (char) (((c >> 12) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 8) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 4) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 0) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + } + } + } + return buf.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/Hinter.java b/libjava/classpath/gnu/java/awt/font/opentype/Hinter.java new file mode 100644 index 000000000..9758a2896 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/Hinter.java @@ -0,0 +1,63 @@ +/* Hinter.java -- The interface to a hinting implementation + 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.font.opentype; + +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * The interface to a hinting implementation. + */ +public interface Hinter +{ + /** + * Initializes the hinter. + * + * @param face the font for which the hinter should be used + */ + void init(OpenTypeFont face); + + /** + * Hints the specified outline. + * + * @param outline the outline to hint + */ + void applyHints(Zone outline); + + void setFlags(int flags); +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java b/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java new file mode 100644 index 000000000..c0f3de80e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java @@ -0,0 +1,235 @@ +/* MacResourceFork.java -- Parses MacOS resource forks. + 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.font.opentype; + +import java.nio.ByteBuffer; + + +/** + * A class for accessing data that is stored in the resource fork of + * Macintosh files. Writing resource forks is currently not supported. + * + * <p>The gnu.java.awt.font package uses this class for accessing + * fonts in the MacOS X ".dfont" format, which is is a file in the + * format of a Macintosh resource fork, but stored in the normal data + * fork of the file. + * + * <p>The implementation has been designed to work efficiently with + * the virtual memory subsystem. It is recommended to pass an + * instance of {@link java.nio.MappedByteBuffer} to the constructor. + * + * <p>Thread Safety: All access is synchronized on the ByteBuffer + * that is passed to the constructor. + * + * @see <a href= + * "http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html" + * >Apple’ developer documentation about the Resource File + * Format</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class MacResourceFork +{ + int[] types; + Resource[][] resources; + ByteBuffer buf; + + public MacResourceFork(ByteBuffer buf) + { + int typeListOffset; + int refListOffset; + int nameListOffset; + int mapOffset, mapLen; + int dataOffset, dataLen; + int numTypes; + + synchronized (buf) + { + buf = buf.duplicate(); + this.buf = buf; + buf.position(0); + dataOffset = buf.getInt(); + mapOffset = buf.getInt(); + dataLen = buf.getInt(); + mapLen = buf.getInt(); + buf.position(mapOffset + 24); + refListOffset = mapOffset + buf.getChar(); + nameListOffset = mapOffset + buf.getChar(); + numTypes = buf.getChar() + 1; + types = new int[numTypes]; + resources = new Resource[numTypes][]; + + /* Parse resource type list. */ + typeListOffset = buf.position(); + for (int i = 0; i < numTypes; i++) + { + buf.position(typeListOffset + 8 * i); + int resType = buf.getInt(); + int numRes = buf.getChar() + 1; + + types[i] = resType; + resources[i] = new Resource[numRes]; + + buf.position(refListOffset + buf.getChar()); + for (int j = 0; j < numRes; j++) + { + short resID = buf.getShort(); + int resNameOffset = nameListOffset + buf.getChar(); + int resDataOffset = buf.getInt(); + byte resAttr = (byte) (resDataOffset >> 24); + resDataOffset = dataOffset + (resDataOffset & 0x00ffffff); + buf.getInt(); /* skip four reserved bytes */ + + Resource rsrc = new Resource(buf, resType, resID, resDataOffset, + resNameOffset); + resources[i][j] = rsrc; + } + } + } + } + + + public Resource[] getResources(int type) + { + synchronized (buf) + { + for (int i = 0; i < types.length; i++) + { + if (types[i] == type) + return resources[i]; + } + } + return null; + } + + + public Resource getResource(int type, short id) + { + Resource[] res; + + synchronized (buf) + { + for (int i = 0; i < types.length; i++) + { + if (types[i] != type) + continue; + + res = resources[i]; + for (int j = 0; j < res.length; j++) + if (res[j].getID() == id) + return res[j]; + } + } + + return null; + } + + + /** + * A single resource that is contained in a resource fork. + */ + public static final class Resource + { + int type; + short id; + byte attribute; + int nameOffset; + int dataOffset; + ByteBuffer buf; + + private Resource(ByteBuffer buf, + int type, short id, int dataOffset, int nameOffset) + { + this.buf = buf; + this.type = type; + this.id = id; + this.dataOffset = dataOffset; + this.nameOffset = nameOffset; + } + + + /** + * Returns the type of this resource. + * + * @return an <code>int</code> encoding a four-byte type tag, + * such as <code>0x464f4e54</code> for <code>'FONT'</code>. + */ + public int getType() + { + return type; + } + + + /** + * Returns the ID of this resource. + */ + public short getID() + { + return id; + } + + + /** + * Retrieves the content of the resource. Only one page of memory + * is touched, irrespective of the actual size of the resource. + */ + public ByteBuffer getContent() + { + synchronized (buf) + { + buf.limit(buf.capacity()); + int len = buf.getInt(dataOffset); + buf.position(dataOffset + 4).limit(dataOffset + 4 + len); + return buf.slice(); + } + } + + + /** + * Determines the length of the resource in bytes. + */ + public int getLength() + { + synchronized (buf) + { + return buf.getInt(dataOffset); + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java new file mode 100644 index 000000000..1f1d50ac1 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java @@ -0,0 +1,702 @@ +/* NameDecoder.java -- Decodes names of OpenType and TrueType fonts. + 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.font.opentype; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Locale; + + +/** + * A utility class that helps with decoding the names of OpenType + * and TrueType fonts. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class NameDecoder +{ + public static final int NAME_COPYRIGHT = 0; + + + /** + * Specifies the name of the family to which a font belongs, for + * example “Univers”. + */ + public static final int NAME_FAMILY = 1; + + + /** + * Specified the name of the font inside its family, for + * example “Light”. + */ + public static final int NAME_SUBFAMILY = 2; + + + public static final int NAME_UNIQUE = 3; + + + /** + * Specifies the full human-readable name of a font, for example + * “Univers Light” + */ + public static final int NAME_FULL = 4; + + + public static final int NAME_VERSION = 5; + + + /** + * Specifies the PostScript name of a font, for example + * “Univers-Light”. + */ + public static final int NAME_POSTSCRIPT = 6; + + + public static final int NAME_TRADEMARK = 7; + public static final int NAME_MANUFACTURER = 8; + public static final int NAME_DESIGNER = 9; + public static final int NAME_DESCRIPTION = 10; + public static final int NAME_VENDOR_URL = 11; + public static final int NAME_DESIGNER_URL = 12; + public static final int NAME_LICENSE = 13; + public static final int NAME_LICENSE_URL = 14; + public static final int NAME_PREFERRED_FAMILY = 16; + public static final int NAME_PREFERRED_SUBFAMILY = 17; + public static final int NAME_FULL_MACCOMPATIBLE = 18; + public static final int NAME_SAMPLE_TEXT = 19; + public static final int NAME_POSTSCRIPT_CID = 20; + + + private static final int PLATFORM_MACINTOSH = 1; + private static final int PLATFORM_MICROSOFT = 3; + + + public static String getName(ByteBuffer nameTable, + int name, Locale locale) + { + int numRecords; + int macLanguage, msLanguage; + int offset; + int namePlatform, nameEncoding, nameLanguage, nameID, nameLen; + int nameStart; + String result; + boolean match; + + if (nameTable == null) + return null; + + nameTable.position(0); + /* We understand only format 0 of the name table. */ + if (nameTable.getShort() != 0) + return null; + + macLanguage = getMacLanguageCode(locale); + msLanguage = getMicrosoftLanguageCode(locale); + numRecords = nameTable.getShort(); + offset = nameTable.getShort(); + + for (int i = 0; i < numRecords; i++) + { + 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) + { + case PLATFORM_MACINTOSH: + if ((nameLanguage == macLanguage) || (locale == null)) + match = true; + else + { + switch (macLanguage) + { + case 49: /* Azerbaijani/Cyrillic */ + match = (nameLanguage == /* Azerbaijani/Arabic */ 50) + || (nameLanguage == /* Azerbaijani/Roman */ 150); + break; + + case 57: /* Mongolian/Mongolian */ + match = (nameLanguage == /* Mongolian/Cyrillic */ 58); + break; + + case 83: /* Malay/Roman */ + match = (nameLanguage == /* Malay/Arabic */ 84); + break; + } + } + break; + + case PLATFORM_MICROSOFT: + if (((nameLanguage & 0xff) == msLanguage) || (locale == null)) + match = true; + break; + } + + + if (match) + { + result = decodeName(namePlatform, nameEncoding, nameLanguage, + nameTable, nameStart, nameLen); + if (result != null) + return result; + } + } + + return null; + } + + + /** + * The language codes used by the Macintosh operating system. MacOS + * defines numeric language identifiers in the range [0 .. 95] and + * [128 .. 150]. To map this numeric identifier into an ISO 639 + * language code, multiply it by two and take the substring at that + * position. + * + * <p>ISO 639 has revised the code for some languages, namely + * <code>he</code> for Hebrew (formerly <code>iw</code>), + * <code>yi</code> (formerly <code>ji</code>), and <code>id</code> + * for Indonesian (formerly <code>in</code>). In those cases, this + * table intentionally contains the older, obsolete code. The + * reason is that this is the code which + * java.util.Locale.getLanguage() is specified to return. The + * implementation of {@link #getMacLanguageCode} depends on this. + * + * @see <a href= + * "http://www.unicode.org/unicode/onlinedat/languages.html" + * >Language Codes: ISO 639, Microsoft and Macintosh</a> + */ + private static final String macLanguageCodes + // 0 1 2 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + = "enfrdeitnlsvesdaptnoiwjaarfielismttrhrzhurhithkoltplhuetlv " + + // 3 4 5 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "fofaruzhnlgdsqrocssksljisrmkbgukbeuzkkazazhykamokytgtkmnmnps" + + // 6 7 8 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "kukssdbonesamrbnasgupaormlkntatesimykmloviintlmsmsamti sosw" + + // 9 10 11 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "rwrn mgeo " + + // 12 13 14 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + " cyeucalaqugnayttugtsjwsuglafbriugdgvgatoelkl" + + // 15 + // 0 + + "az"; + + + /** + * The primary language IDs used by the Microsoft operating systems. + * + * <p>ISO 639 has revised the code for some languages, namely + * <code>he</code> for Hebrew (formerly <code>iw</code>), + * <code>yi</code> (formerly <code>ji</code>), and <code>id</code> + * for Indonesian (formerly <code>in</code>). In those cases, this + * table intentionally contains the older, obsolete code. The + * reason is that this is the code which + * java.util.Locale.getLanguage() is specified to return. The + * implementation of {@link #getMicrosoftLanguageCode} depends on + * this. + * + * @see <a href= + * "http://www.unicode.org/unicode/onlinedat/languages.html" + * >Language Codes: ISO 639, Microsoft and Macintosh</a> + */ + private static final String microsoftLanguageCodes + // 0 1 2 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + = " arbgcazhcsdadeelenesfifriwhuisitjakonlnoplptrmrorushsksqsv" + + // 3 4 5 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "thtrurinukbesletlvlttgfavihyazeu mk ts xhzuafkafohimt " + + // 6 7 8 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "gajimskkkyswtkuzttbnpaguortateknmlasmrsamnbocykmlomygl sd" + + // 9 10 11 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + " si iuam ksnefypstl ha yo omtign laso"; + + + /** + * Maps a Java Locale into a MacOS language code. + * + * <p>For languages that are written in several script systems, + * MacOS defines multiple language codes. Java Locales have a + * variant which could be used for that purpose, but a small + * test program revealed that with Sun's JDK 1.4.1_01, only two + * of 134 available Locales have a variant tag (namely no_NO_NY + * and th_TH_TH).</p> + * + * <p>The following cases are problematic: + * + * <ul> <li>Azerbaijani (az): The MacOS language code is 49 if + * Azerbaijani is written in the Cyrillic script; 50 if written in + * the Arabic script; 150 if written in the Roman script. This + * method will always return 49 for the Azerbaijani locale.</li> + * + * <li>Mongolian (mn): The MacOS language code is 57 if Mongolian is + * written in the Mongolian script; 58 if written in the Cyrillic + * script. This method will always return 57 for the Mongolian + * locale.</li> + * + * <li>Malay (ms): The MacOS language code is 83 if Malay is written + * in the Roman script; 84 if written in the Arabic script. This + * method will always return 83 for the Malay locale.</li> </ul> + * + * @return a MacOS language code, or -1 if there is no such code for + * <code>loc</code>’s language. + */ + private static int getMacLanguageCode(Locale loc) + { + int code; + + if (loc == null) + return -1; + + code = findLanguageCode(loc.getLanguage(), macLanguageCodes); + switch (code) + { + case 19: + /* Traditional Chinese (MacOS language #19) and and Simplified + * Chinese (MacOS language #33) both have "zh" as their ISO 639 + * code. + */ + if (loc.equals(Locale.SIMPLIFIED_CHINESE)) + code = 33; + break; + + // Other special cases would be 49, 57 and 83, but we do not + // know what do do about them. See the method documentation for + // details. + } + + return code; + } + + + /** + * Maps a Java Locale into a Microsoft language code. + */ + private static int getMicrosoftLanguageCode(Locale locale) + { + String isoCode; + int code; + + if (locale == null) + return -1; + + isoCode = locale.getLanguage(); + code = findLanguageCode(isoCode, microsoftLanguageCodes); + if (code == -1) + { + if (isoCode.equals("hr") || isoCode.equals("sr")) + { + /* Microsoft uses code 26 for "sh" (Serbo-Croatian), + * "hr" (Croatian) and "sr" (Serbian). Our table contains + * "sh". + */ + code = 26; + } + else if (isoCode.equals("gd")) + { + /* Microsoft uses code 60 for "gd" (Scottish Gaelic) and + * "ga" (Irish Gaelic). Out table contains "ga". + */ + code = 60; + } + } + return code; + } + + + private static int findLanguageCode(String lang, String langCodes) + { + int index; + if (lang == null) + return -1; + + if (lang.length() != 2) + return -1; + + index = 0; + do + { + index = langCodes.indexOf(lang, index); + + /* The index must be even to be considered a match. Otherwise, we + * could match with the second letter of one language and the + * first of antoher one. + */ + } + while (!((index < 0) || ((index & 1) == 0))); + if (index < 0) + return -1; + + index = index / 2; + return index; + } + + + private static String decodeName(int platform, int encoding, int language, + ByteBuffer buffer, int offset, int len) + { + 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 + { + buffer.position(offset); + buffer.get(byteBuf); + try + { + return new String(byteBuf, charsetName); + } + catch (UnsupportedEncodingException uex) + { + } + } + finally + { + buffer.position(oldPosition); + } + + return null; + } + + + /** + * Maps a MacOS language code into a Java Locale. + * + * @param macLanguageCode the MacOS language code for + * the language whose Java locale is to be retrieved. + * + * @return an suitable Locale, or <code>null</code> if + * the mapping cannot be performed. + */ + private static Locale getMacLocale(int macLanguageCode) + { + String isoCode; + + switch (macLanguageCode) + { + case 0: return Locale.ENGLISH; + case 1: return Locale.FRENCH; + case 2: return Locale.GERMAN; + case 3: return Locale.ITALIAN; + case 11: return Locale.JAPANESE; + case 23: return Locale.KOREAN; + case 19: return Locale.TRADITIONAL_CHINESE; + case 33: return Locale.SIMPLIFIED_CHINESE; + } + + if ((macLanguageCode < 0) || (macLanguageCode > 150)) + return null; + + isoCode = macLanguageCodes.substring(macLanguageCode << 1, + (macLanguageCode + 1) << 1); + if (isoCode.charAt(0) == ' ') + return null; + + return new Locale(isoCode); + } + + + + /** + * Maps a Windows LCID into a Java Locale. + * + * @param lcid the Windows language ID whose Java locale + * is to be retrieved. + * + * @return an suitable Locale, or <code>null</code> if + * the mapping cannot be performed. + */ + private static Locale getWindowsLocale(int lcid) + { + /* FIXME: This is grossly incomplete. */ + switch (lcid) + { + case 0x0407: return Locale.GERMAN; + case 0x0408: return new Locale("el", "GR"); + case 0x0409: return Locale.ENGLISH; + case 0x040b: return new Locale("fi"); + case 0x040c: return Locale.FRENCH; + case 0x0416: return new Locale("pt"); + case 0x0807: return new Locale("de", "CH"); + case 0x0809: return new Locale("en", "UK"); + case 0x080c: return new Locale("fr", "BE"); + case 0x0816: return new Locale("pt", "BR"); + case 0x0c07: return new Locale("de", "AT"); + case 0x0c09: return new Locale("en", "AU"); + case 0x0c0c: return new Locale("fr", "CA"); + case 0x1007: return new Locale("de", "LU"); + case 0x1009: return new Locale("en", "CA"); + case 0x100c: return new Locale("fr", "CH"); + case 0x1407: return new Locale("de", "LI"); + case 0x1409: return new Locale("en", "NZ"); + case 0x140c: return new Locale("fr", "LU"); + case 0x1809: return new Locale("en", "IE"); + + default: + return null; + } + } + + + /** + * Maps a Macintosh Script Manager code to the name of the + * corresponding Java Charset. + * + * @param macScript a MacOS ScriptCode, for example + * 6 for <code>smGreek</code>. + * + * @return a String that can be used to retrieve a Java + * CharsetDecorder, for example <code>MacGreek</code>, or + * <code>null</code> if <code>macScript</code> has an + * unsupported value. + */ + private static String getMacCharsetName(int macScript) + { + switch (macScript) + { + case 0: return "MacRoman"; + case 1: return "MacJapanese"; + case 2: return "MacKorean"; + case 3: return "MacTradChinese"; + case 4: return "MacArabic"; + case 5: return "MacHebrew"; + case 6: return "MacGreek"; + case 7: return "MacCyrillic"; + case 8: return "MacRSymbol"; + case 9: return "MacDevanagari"; + case 10: return "MacGurmukhi"; + case 11: return "MacGujarati"; + case 12: return "MacOriya"; + case 13: return "MacBengali"; + case 14: return "MacTamil"; + case 15: return "MacTelugu"; + case 16: return "MacKannada"; + case 17: return "MacMalayalam"; + case 18: return "MacSinhalese"; + case 19: return "MacBurmese"; + case 20: return "MacKhmer"; + case 21: return "MacThai"; + case 22: return "MacLao"; + case 23: return "MacGeorgian"; + case 24: return "MacArmenian"; + case 25: return "MacSimpChinese"; + case 26: return "MacTibetan"; + case 27: return "MacMongolian"; + case 28: return "MacEthiopic"; + case 29: return "MacCentralEurope"; + case 30: return "MacVietnamese"; + case 31: return "MacExtArabic"; + + default: return null; + } + } + + + /** + * Maps a Microsoft locale ID (LCID) to the name of the + * corresponding Java Charset. + * + * @param lcid the Microsoft locale ID. + * + * @return a String that can be used to retrieve a Java + * CharsetDecorder, for example <code>windows-1252</code>, or + * <code>null</code> if <code>lcid</code> has an unsupported value. + */ + private static String getMicrosoftCharsetName(int lcid) + { + int lang; + char codePage = '?'; + + /* Extract the language code from the LCID. */ + lang = lcid & 0x3ff; + + /* In the majority of cases, the language alone determines the + * codepage. + */ + if (lang < 100) + codePage = (" 612D022322225022EC2202201?002A462110777 68 ?2 1 " + + " 2 2 2112 ?1 1 2 2 ") + .charAt(lang); + + /* There are a few exceptions, however, where multiple code pages + * are used for the same language. */ + if (codePage == '?') + { + switch (lcid) + { + case 0x041a: // Croatian --> Windows-1250 (Central Europe) + case 0x081a: // Serbian (Latin) --> Windows-1250 (Central Europe) + codePage = '0'; + break; + + case 0x42c: // Azeri (Latin) --> Windows-1254 (Turkish) + case 0x443: // Uzbek (Latin) --> Windows-1254 (Turkish) + codePage = '4'; + break; + + case 0x82c: // Azeri (Cyrillic) --> Windows-1251 (Cyrillic) + case 0x843: // Uzbek (Cyrillic) --> Windows-1251 (Cyrillic) + case 0xc1a: // Serbian (Cyrillic) --> Windows-1251 (Cyrillic) + codePage = '1'; + break; + } + } + + switch (codePage) + { + case '0': return "windows-1250"; // Central Europe + case '1': return "windows-1251"; // Cyrillic + case '2': return "windows-1252"; // Latin 1 + case '3': return "windows-1253"; // Greek + case '4': return "windows-1254"; // Turkish + case '5': return "windows-1255"; // Hebrew + case '6': return "windows-1256"; // Arabic + case '7': return "windows-1257"; // Baltic + case '8': return "windows-1258"; // Vietnam + case 'A': return "windows-874"; // Thai + case 'B': return "windows-936"; // Simplified Chinese, GBK + case 'C': return "windows-949"; // Korean + case 'D': return "windows-950"; // Traditional Chinese, Big5 + case 'E': return "windows-932"; // Japanese Shift-JIS + default: return null; + } + } + + + /** + * Returns the Locale of an OpenType name. + * + * @param platform the OpenType platform ID. + * + * @param language the language tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS language code. + * + * @param encoding the encoding tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS script code. + */ + public static Locale getLocale(int platform, int language, int encoding) + { + switch (platform) + { + case 1: /* Apple Macintosh */ + return getMacLocale(language); + + case 3: /* Microsoft Windows */ + return getWindowsLocale(language); + + default: + return null; + } + } + + + /** + * Determines the name of the charset for an OpenType font name. + * + * @param platform the OpenType platform ID. + * + * @param language the language tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS language code. + * + * @param encoding the encoding tag of the OpenType name. If + * <code>platform</code> is 1, this is the MacOS script code. + * + * @return a charset name such as <code>"MacRoman"</code>, + * or <code>null</code> if the combination is not known. + */ + public static String getCharsetName(int platform, int language, int encoding) + { + switch (platform) + { + case 1: /* Apple Macintosh */ + return getMacCharsetName(encoding); + + case 3: /* Microsoft Windows */ + return getMicrosoftCharsetName(language); + + default: + return null; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java new file mode 100644 index 000000000..f46addc3c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java @@ -0,0 +1,882 @@ +/* OpenTypeFont.java -- Manages OpenType and TrueType fonts. + 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.font.opentype; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.OpenType; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.nio.ByteBuffer; +import java.text.CharacterIterator; +import java.util.Locale; + +import gnu.java.awt.font.FontDelegate; +import gnu.java.awt.font.GNUGlyphVector; +import gnu.java.awt.font.autofit.AutoHinter; +import gnu.java.awt.font.opentype.truetype.TrueTypeScaler; +import gnu.java.awt.font.opentype.truetype.Zone; + + +/** + * A font that takes its data from OpenType or TrueType font tables. + * + * <p>OpenType is an extension of the TrueType font format. In addition + * to tables for names, kerning or layout, it also stores the shapes + * of individual glyphs. Three formats are recognized for glyphs: + * Quadratic splines (classic TrueType), cubic splines (PostScript), + * and bitmaps. + * + * @see <a + * href="http://partners.adobe.com/asn/tech/type/opentype/">Adobe’s + * OpenType specification</a> + * + * @see <a + * href="http://developer.apple.com/fonts/TTRefMan/">Apple’s</code> + * TrueType specification</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class OpenTypeFont + implements FontDelegate +{ + static final int TAG_OTTO = 0x4f54544f; // 'OTTO' + static final int TAG_SFNT = 0x73666e74; // 'sfnt' + static final int TAG_TRUE = 0x74727565; // 'true' + static final int TAG_TTCF = 0x74746366; // 'ttcf' + static final int TAG_ZAPF = 0x5a617066; // 'Zapf' + + + /** + * A buffer containing the font data. Note that this may well be an + * instance of the subclass MappedByteBuffer, in which case the + * virtual memory subsystem can more efficiently handle requests for + * font data. This is especially recommended for large font files + * that contain many glyphs that are rarely accessed. + */ + ByteBuffer buf; + + + /** + * The number of glyphs in this font. + */ + final int numGlyphs; + + int[] tableTag, tableStart, tableLength; + + + /** + * The version of the font in 16.16 fixed-point encoding, for + * example 0x00010000 for version 1.0. There are also two special + * version IDs used by fonts for Apple Macintosh, namely 'true' + * (0x74727565) and 'typ1'. OpenType fonts sometimes have 'OTTO' as + * their version. + */ + private int version; + + + /** + * The number of font units per em. For fonts with TrueType + * outlines, this is usually a power of two (such as 2048). For + * OpenType fonts with PostScript outlines, other values are + * acceptable (such as 1000). + */ + public int unitsPerEm; + + + /** + * A factor to convert font units into ems. This value is <code>1 / + * unitsPerEm</code>. + */ + private float emsPerUnit; + + + /** + * The scaler to which the actual scaling work is delegated. + */ + private Scaler scaler; + + + /** + * A delegate object for mapping Unicode UCS-4 codepoints to glyph + * IDs. + */ + private CharGlyphMap cmap; + + + /** + * A delegate object for providing a name for each glyph. + */ + private GlyphNamer glyphNamer; + + private Hinter hinter; + + /** + * Constructs an OpenType or TrueType font. + * + * @param buf a buffer with the contents of the font file. It is + * recommended to use a <code>MappedByteBuffer</code> for very + * large font files. + * + * @param offsetTablePosition the position of the OpenType offset + * table in the font file. The offset table of most OpenType and + * TrueType fonts starts at position 0. However, so-called TrueType + * Collections support multiple OpenType fonts in a single file, + * which allows sharing some glyphs between fonts. If many glyphs + * are shared (for example all the Kanji glyphs between multiple + * Japanese fonts), the space savings can be considerable. In that + * case, the offset table of each individual font would start at its + * own position. + * + * @throws java.awt.FontFormatException if the font data is + * not in OpenType or TrueType format. + */ + OpenTypeFont(ByteBuffer buf, int offsetTablePosition) + throws FontFormatException + { + int numTables, searchRange, entrySelector, rangeShift; + + //buf = buf.duplicate(); + this.buf = buf; + buf.limit(buf.capacity()); + buf.position(offsetTablePosition); + + /* Check that the font data is in a supported format. */ + version = buf.getInt(); + switch (version) + { + case 0x00010000: // Microsoft TrueType + case OpenType.TAG_TYP1: // Adobe PostScript embeded in Apple SFNT ('typ1') + case TAG_SFNT: // Apple TrueType + case TAG_TRUE: // Apple TrueType + case TAG_OTTO: // OpenType + break; + + default: + throw new FontFormatException("not in OpenType or TrueType format"); + } + + numTables = buf.getShort(); + searchRange = buf.getShort(); + entrySelector = buf.getShort(); + rangeShift = buf.getShort(); + + tableTag = new int[numTables]; + tableStart = new int[numTables]; + tableLength = new int[numTables]; + int lastTag = 0; + for (int i = 0; i < numTables; i++) + { + tableTag[i] = buf.getInt(); + if (lastTag >= tableTag[i]) + throw new FontFormatException("unordered OpenType table"); + + buf.getInt(); // ignore checksum + tableStart[i] = buf.getInt(); + tableLength[i] = buf.getInt(); + + //System.out.println(tagToString(tableTag[i]) + ", " + tableLength[i]); + } + + ByteBuffer head = getFontTable(OpenType.TAG_HEAD); + if ((head.getInt(0) != 0x00010000) + || (head.getInt(12) != 0x5f0f3cf5)) + throw new FontFormatException("unsupported head version"); + + unitsPerEm = head.getChar(18); + emsPerUnit = 1.0f / (float) unitsPerEm; + + + ByteBuffer maxp = getFontTable(OpenType.TAG_MAXP); + int maxpVersion = maxp.getInt(0); + switch (maxpVersion) + { + case 0x00005000: /* version 0.5, with wrong fractional part */ + numGlyphs = maxp.getChar(4); + break; + + case 0x00010000: /* version 1.0 */ + numGlyphs = maxp.getChar(4); + scaler = new TrueTypeScaler(unitsPerEm, + getFontTable(OpenType.TAG_HHEA), + getFontTable(OpenType.TAG_HMTX), + getFontTable(OpenType.TAG_VHEA), + getFontTable(OpenType.TAG_VMTX), + maxp, + getFontTable(OpenType.TAG_CVT), + getFontTable(OpenType.TAG_FPGM), + /* loca format */ head.getShort(50), + getFontTable(OpenType.TAG_LOCA), + getFontTable(OpenType.TAG_GLYF), + getFontTable(OpenType.TAG_PREP)); + break; + + default: + throw new FontFormatException("unsupported maxp version"); + } + } + + + /** + * Determines the index of a table into the offset table. The + * result can be used to find the offset and length of a table, as + * in <code>tableStart[getTableIndex(TAG_NAME)]</code>. + * + * @param tag the table identifier, for instance + * <code>OpenType.TAG_NAME</code>. + * + * @return the index of that table into the offset table, or + * -1 if the font does not contain the table specified by + * <code>tag</code>. + */ + private int getTableIndex(int tag) + { + /* FIXME: Since the font specification requires tableTag[] to be + * ordered, one should do binary search here. + */ + for (int i = 0; i < tableTag.length; i++) + if (tableTag[i] == tag) + return i; + return -1; + } + + + + /** + * Returns the name of the family to which this font face belongs, + * for example <i>“Univers”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the family name. + */ + public synchronized String getFamilyName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_FAMILY, locale); + if (name == null) + name = getName(NameDecoder.NAME_FAMILY, Locale.ENGLISH); + if (name == null) + name = getName(NameDecoder.NAME_FAMILY, /* any language */ null); + if (name == null) + name = getName(NameDecoder.NAME_FULL, locale); + if (name == null) + name = getName(NameDecoder.NAME_FULL, /* any language */ null); + return name; + } + + + /** + * Returns the name of this font face inside the family, for example + * <i>“Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the name of the face inside its family. + */ + public synchronized String getSubFamilyName(Locale locale) + { + 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; + } + + if (name == null) + { + String lang = locale.getLanguage(); + if ("de".equals(lang)) + name = "Standard"; + else if ("fr".equals(lang)) + name = "Standard"; + else if ("it".equals(lang)) + name = "Normale"; + else if ("nl".equals(lang)) + name = "Normaal"; + else if ("fi".equals(lang)) + name = "Normaali"; + else if ("sv".equals(lang)) + name = "Normal"; + else + name = "Regular"; + } + + return name; + } + + + + /** + * Returns the full name of this font face, for example + * <i>“Univers Light”</i>. + * + * @param locale the locale for which to localize the name. + * + * @return the face name. + */ + public synchronized String getFullName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_FULL, locale); + if (name == null) + name = getName(NameDecoder.NAME_FULL, Locale.ENGLISH); + if (name == null) + name = getName(NameDecoder.NAME_FULL, /* any language */ null); + + return name; + } + + + /** + * Returns the PostScript name of this font face, for example + * <i>“Univers-Light”</i>. + * + * @return the PostScript name, or <code>null</code> if the font + * does not provide a PostScript name. + */ + public synchronized String getPostScriptName() + { + return getName(NameDecoder.NAME_POSTSCRIPT, /* any language */ null); + } + + + /** + * Returns the number of glyphs in this font face. + */ + public int getNumGlyphs() + { + /* No synchronization is needed because the number of glyphs is + * set in the constructor, and it cannot change during the + * lifetime of the object. + */ + return numGlyphs; + } + + + /** + * Returns the index of the glyph which gets displayed if the font + * cannot map a Unicode code point to a glyph. Many fonts show this + * glyph as an empty box. + */ + public int getMissingGlyphCode() + { + /* No synchronization is needed because the result is constant. */ + return 0; + } + + + /** + * The font’s name table, or <code>null</code> if this + * table has not yet been accessed. + */ + private ByteBuffer nameTable; + + + /** + * 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) + nameTable = getFontTable(OpenType.TAG_NAME); + return NameDecoder.getName(nameTable, name, locale); + } + + + /** + * Returns the version of the font. + * + * @see java.awt.font.OpenType#getVersion + * + * @return the version in 16.16 fixed-point encoding, for example + * 0x00010000 for version 1.0. + */ + public int getVersion() + { + /* No synchronization is needed because the version is set in the + * constructor, and it cannot change during the lifetime of the + * object. + */ + return version; + } + + + /** + * Creates a view buffer for an OpenType table. The caller can + * access the returned buffer without needing to synchronize access + * from multiple threads. + * + * @param tag the table identifier, for example + * <code>OpenType.GLYF</code>. + * + * @return a slice of the underlying buffer containing the table, or + * <code>null</code> if the font does not contain the requested + * table. + */ + public synchronized ByteBuffer getFontTable(int tag) + { + int index, start, len; + ByteBuffer result; + + index = getTableIndex(tag); + if (index < 0) + return null; + + start = tableStart[index]; + len = tableLength[index]; + buf.limit(start + len).position(start); + result = buf.slice(); + result.limit(len); + return result; + } + + + /** + * Returns the size of one of the tables in the font, + * or -1 if the table does not exist. + */ + public int getFontTableSize(int tag) + { + int index = getTableIndex(tag); + if (index == -1) + return index; + return tableLength[index]; + } + + + private CharGlyphMap getCharGlyphMap() + { + if (cmap != null) + return cmap; + + synchronized (this) + { + if (cmap == null) + { + int index = getTableIndex(OpenType.TAG_CMAP); + int start = tableStart[index]; + buf.limit(start + tableLength[index]).position(start); + cmap = CharGlyphMap.forTable(buf); + } + return cmap; + } + } + + + + /** + * Looks up a glyph in the font’s <code>cmap</code> tables, + * without performing any glyph substitution or reordering. Because + * of this limitation, this method cannot be used for script systems + * that need advanced glyph mapping, such as Arabic, Korean, or even + * Latin with exotic accents. + * + * <p>It is safe to call this method from any thread. + * + * @param ucs4 the Unicode codepoint in the 32-bit Unicode character + * set UCS-4. Because UTF-16 surrogates do not correspond to a single + * glyph, it does not make sense to pass them here. + * + * @return the glyph index, or zero if the font does not contain + * a glyph for the specified codepoint. + */ + public int getGlyph(int ucs4) + { + return getCharGlyphMap().getGlyph(ucs4); + } + + + /** + * Creates a GlyphVector by mapping each character in a + * CharacterIterator to the corresponding glyph. + * + * <p>The mapping takes only the font’s <code>cmap</code> + * tables into consideration. No other operations (such as glyph + * re-ordering, composition, or ligature substitution) are + * performed. This means that the resulting GlyphVector will not be + * correct for text in languages that have complex + * character-to-glyph mappings, such as Arabic, Hebrew, Hindi, or + * Thai. + * + * @param font the font object that the created GlyphVector + * will return when it gets asked for its font. This argument is + * needed because the public API works with java.awt.Font, + * not with some private delegate like OpenTypeFont. + * + * @param frc the font rendering parameters that are used for + * measuring glyphs. The exact placement of text slightly depends on + * device-specific characteristics, for instance the device + * resolution or anti-aliasing. For this reason, any measurements + * will only be accurate if the passed + * <code>FontRenderContext</code> correctly reflects the relevant + * parameters. Hence, <code>frc</code> should be obtained from the + * same <code>Graphics2D</code> that will be used for drawing, and + * any rendering hints should be set to the desired values before + * obtaining <code>frc</code>. + * + * @param ci a CharacterIterator for iterating over the + * characters to be displayed. + */ + public synchronized GlyphVector createGlyphVector(Font font, + FontRenderContext frc, + CharacterIterator ci) + { + // Initialize hinter if necessary. + checkHinter(FontDelegate.FLAG_FITTED); + + CharGlyphMap cmap; + int numGlyphs; + int[] glyphs; + int glyph; + int c; + + cmap = getCharGlyphMap(); + numGlyphs = ci.getEndIndex() - ci.getBeginIndex(); + glyphs = new int[numGlyphs]; + glyph = 0; + for (c = ci.first(); c != CharacterIterator.DONE; c = ci.next()) + { + /* handle surrogate pairs */ + if (c >> 10 == 0x36) // U+D800 .. U+DBFF: High surrogate + c = (((c & 0x3ff) << 10) | (ci.next() & 0x3ff)) + 0x10000; + glyphs[glyph] = cmap.getGlyph(c); + glyph += 1; + } + + /* If we had surrogates, the allocated array is too large. + * Because this will occur very rarely, it seems acceptable to + * re-allocate a shorter array and copy the contents around. + */ + if (glyph != numGlyphs) + { + int[] newGlyphs = new int[glyph]; + System.arraycopy(glyphs, 0, newGlyphs, 0, glyph); + glyphs = newGlyphs; + } + + return new GNUGlyphVector(this, font, frc, glyphs); + } + + /** + * Returns the glyph code for the specified character. + * + * @param c the character to map + * + * @return the glyph code + */ + public int getGlyphIndex(int c) + { + return getCharGlyphMap().getGlyph(c); + } + + /** + * Determines the advance width for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is possible + * that both values are non-zero, for example if + * <code>transform</code> is a rotation, or in the case of Urdu + * fonts. + */ + public synchronized void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance) + { + /* Delegate the measurement to the scaler. The synchronization is + * needed because the scaler is not synchronized. + */ + scaler.getAdvance(glyphIndex, pointSize, transform, + antialias, fractionalMetrics, horizontal, + advance); + } + + + /** + * Returns the shape of a glyph. + * + * @param glyph the glyph whose advance width is to be determined + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, this + * parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional + * metrics, <code>false</code> for rounding the result to a pixel + * boundary. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or <code>null</code> for bitmap fonts. + */ + public synchronized GeneralPath getGlyphOutline(int glyph, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + int flags) + { + /* The synchronization is needed because the scaler is not + * synchronized. + */ + checkHinter(flags); + return scaler.getOutline(glyph, pointSize, transform, + antialias, fractionalMetrics, hinter, flags); + } + + /** + * Fetches the raw glyph outline for the specified glyph index. This is used + * for the autofitter only ATM and is otherwise not usable for outside code. + * + * @param glyph the glyph index to fetch + * @param transform the transform to apply + * + * @return the raw outline of that glyph + */ + public synchronized Zone getRawGlyphOutline(int glyph, + AffineTransform transform) + { + return scaler.getRawOutline(glyph, transform); + } + + /** + * Returns a name for the specified glyph. This is useful for + * generating PostScript or PDF files that embed some glyphs of a + * font. + * + * <p><b>Names are not unique:</b> Under some rare circumstances, + * the same name can be returned for different glyphs. It is + * therefore recommended that printer drivers check whether the same + * name has already been returned for antoher glyph, and make the + * name unique by adding the string ".alt" followed by the glyph + * index.</p> + * + * <p>This situation would occur for an OpenType or TrueType font + * that has a <code>post</code> table of format 3 and provides a + * mapping from glyph IDs to Unicode sequences through a + * <code>Zapf</code> table. If the same sequence of Unicode + * codepoints leads to different glyphs (depending on contextual + * position, for example, or on typographic sophistication level), + * the same name would get synthesized for those glyphs. + * + * @param glyphIndex the glyph whose name the caller wants to + * retrieve. + */ + public synchronized String getGlyphName(int glyphIndex) + { + if (glyphNamer == null) + glyphNamer = GlyphNamer.forTables(numGlyphs, + getFontTable(OpenType.TAG_POST), + getFontTable(TAG_ZAPF)); + + return glyphNamer.getGlyphName(glyphIndex); + } + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public synchronized float getAscent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + return scaler.getAscent(pointSize, transform, + antialiased, fractionalMetrics, + horizontal); + } + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public synchronized float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + return scaler.getDescent(pointSize, transform, + antialiased, fractionalMetrics, + horizontal); + } + + + /** + * Converts a four-byte tag identifier into a String that can be + * displayed when debugging this class. + * + * @param tag the tag as an <code>int</code>. + * + * @return the tag in human-readable form, for example + * <code>name</code> or <code>glyf</code>. + */ + static String tagToString(int tag) + { + char[] c = new char[4]; + c[0] = (char) ((tag >> 24) & 0xff); + c[1] = (char) ((tag >> 16) & 0xff); + c[2] = (char) ((tag >> 8) & 0xff); + c[3] = (char) (tag & 0xff); + return new String(c); + } + + /** + * Checks if a hinter is installed and installs one when not. + */ + private void checkHinter(int flags) + { + // When another hinting impl gets added (maybe a true TrueType hinter) + // then add some options here. The Hinter interface might need to be + // tweaked. + if (hinter == null) + { + try + { + hinter = new AutoHinter(); + hinter.init(this); + } + catch (Exception ex) + { + // Protect from problems inside hinter. + hinter = null; + ex.printStackTrace(); + } + } + hinter.setFlags(flags); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java new file mode 100644 index 000000000..32c4828c9 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java @@ -0,0 +1,140 @@ +/* OpenTypeFontFactory.java -- Creates OpenType and TrueType fonts. + 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.font.opentype; + +import gnu.java.awt.font.FontDelegate; +import java.awt.FontFormatException; +import java.awt.font.OpenType; +import java.nio.ByteBuffer; + + +/** + * A factory for creating fonts that are stored in an + * <i>sfnt</i>-housed format, for example OpenType or TrueType. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class OpenTypeFontFactory +{ + /** + * The constructor is private so nobody can construct an instance + * of this class. + */ + private OpenTypeFontFactory() + { + } + + + /** + * Creates FontDelegate objects for the fonts in the specified + * buffer. The following font formats are currently recognized: + * + * <p><ul> + * <li>OpenType (*.otf);</li> + * <li>TrueType (*.ttf);</li> + * <li>TrueType Collections (*.ttc);</li> + * <li>Apple MacOS X data-fork font (*.dfont).</li></ul> + * + * <p>Some formats may contain more than a single font, for example + * *.ttc and *.dfont files. This is the reason why this function + * returns an array. + * + * <p>The implementation reads data from the buffer only when + * needed. Therefore, it greatly increases efficiency if + * <code>buf</code> has been obtained through mapping a file into + * the virtual address space. + * + * @throws FontFormatException if the font data is not in one of the + * known formats. + */ + public static FontDelegate[] createFonts(ByteBuffer buf) + throws FontFormatException + { + OpenTypeFont[] fonts; + int version; + + version = buf.getInt(0); + switch (version) + { + case 0x00010000: // Microsoft Windows TrueType + case OpenType.TAG_TYP1: // Apple MacOS PostScript ('typ1') + case OpenTypeFont.TAG_SFNT: // Apple MacOS TrueType ('sfnt') + case OpenTypeFont.TAG_TRUE: // Apple MacOS TrueType ('true') + case OpenTypeFont.TAG_OTTO: // OpenType + return new OpenTypeFont[] { new OpenTypeFont(buf, 0) }; + } + + + /* TrueType Collection, see "TrueType Collections" in + * http://partners.adobe.com/asn/tech/type/opentype/otff.html + */ + if (version == OpenTypeFont.TAG_TTCF) + { + // This code has never been tested. + fonts = new OpenTypeFont[buf.getInt(8)]; + for (int i = 0; i < fonts.length; i++) + fonts[i] = new OpenTypeFont(buf, buf.getInt(16 + 4 * i)); + return fonts; + } + + + /* The MacOS X .dfont format is a Macintosh resource fork in + * a normal file, contaning one or several 'sfnt' resources. + * Unfortunately, MacOS resource forks have no magic code + * that could be used for identification. Instead, we just try + * to extract at least one 'sfnt'. + */ + try + { + MacResourceFork fork = new MacResourceFork(buf); + MacResourceFork.Resource[] rsrc; + + rsrc = fork.getResources(OpenTypeFont.TAG_SFNT); + fonts = new OpenTypeFont[rsrc.length]; + for (int i = 0; i < fonts.length; i++) + fonts[i] = new OpenTypeFont(rsrc[i].getContent(), 0); + + return fonts; + } + catch (Exception ex) + { + } + + throw new FontFormatException("not in OpenType or TrueType format"); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java new file mode 100644 index 000000000..c7582b666 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java @@ -0,0 +1,205 @@ +/* Scaler.java -- Common superclass for font scalers. + 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.font.opentype; + +import gnu.java.awt.font.opentype.truetype.Zone; + +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; + + +/** + * An common superclass for all font scalers. The main task of font + * scaler is to retrieve a scaled and hinted outline for a glyph. + * + * <p>To make text more legible, high-quality fonts contain + * instructions (sometimes also called “hints”) for + * moving the scaled control points towards the coordinate grid of the + * display device. + * + * <p><b>Lack of Thread Safety:</b> Font scalers are intentionally + * <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font that uses this scaler already has obtained a lock before + * calling the scaler. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class Scaler +{ + /** + * Retrieves the scaled outline of a glyph, adjusting control points + * to the raster grid if necessary. + * + * @param glyph the glyph number whose outline is retrieved. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias whether or not the rasterizer will perform + * anti-aliasing on the returned path. + * + * @param fractionalMetrics <code>false</code> for adjusting glyph + * positions to the raster grid of device space. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or <code>null</code> for bitmap fonts. + */ + public abstract GeneralPath getOutline(int glyph, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + Hinter hinter, int type); + + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is well + * possible that both values are non-zero, for example for rotated + * text or for Urdu fonts. + */ + public abstract void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance); + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public abstract float getAscent(float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal); + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public abstract float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); + + /** + * Returns the raw outline data. This is used for the autofitter atm. + * + * @param glyph the glyph index + * @param transform the transform to apply + * + * @return the raw glyph outline + */ + public abstract Zone getRawOutline(int glyph, AffineTransform transform); +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java new file mode 100644 index 000000000..87dfebd41 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java @@ -0,0 +1,176 @@ +/* Fixed.java -- Fixed-point arithmetics for TrueType coordinates. + 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.font.opentype.truetype; + +import gnu.java.lang.CPStringBuilder; + +/** + * A utility class for fixed-point arithmetics, where numbers are + * represented with 26 dot 6 digits. This representation is used by + * TrueType coordinates. + * + * <p>A good compiler will inline calls of methods in this class. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class Fixed +{ + public static final int ONE = 1<<6; + + + /** + * The constructor is private so nobody can use it. + */ + private Fixed() + { + } + + + /** + * Multiplies two fixed-point numbers. + */ + public static int mul(int a, int b) + { + return (int) ((((long) a) * b) >> 6); + } + + public static int mul16(int a, int b) + { + return (int) ((((long) a) * b) >> 16); + } + + public static int div(int a, int b) + { + return (int) ((((long) a) << 6) / b); + } + + public static int div16(int a, int b) + { + return (int) ((((long) a) << 16) / b); + } + + public static int ceil(int a) + { + return (a + 63) & -64; + } + + + public static int floor(int a) + { + return a & -64; + } + + + /** + * Calculates the length of a fixed-point vector. + */ + public static int vectorLength(int x, int y) + { + int shift; + float fx, fy; + + if (x == 0) + return Math.abs(y); + else if (y == 0) + return Math.abs(x); + + /* Use the FPU. */ + fx = ((float) x) / 64.0f; + fy = ((float) y) / 64.0f; + return (int) (Math.sqrt(fx * fx + fy * fy) * 64.0); + } + + + public static int intValue(int f) + { + return f >> 6; + } + + + public static float floatValue(int f) + { + return ((float) f) / 64; + } + public static float floatValue16(int f) + { + return ((float) f) / 65536; + } + + public static double doubleValue(int f) + { + return ((double) f) / 64; + } + + + public static int valueOf(float f) + { + return (int) (f * 64); + } + + + public static int valueOf(double d) + { + return (int) (d * 64); + } + + public static int valueOf16(double d) + { + return (int) (d * (1 << 16)); + } + + /** + * Makes a string representation of a fixed-point number. + */ + public static String toString(int f) + { + return String.valueOf(floatValue(f)); + } + + + public static String toString(int x, int y) + { + CPStringBuilder sbuf = new CPStringBuilder(40); + sbuf.append('('); + sbuf.append(((float) x) / 64); + sbuf.append(", "); + sbuf.append(((float) y) / 64); + sbuf.append(')'); + return sbuf.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java new file mode 100644 index 000000000..8a3c56665 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java @@ -0,0 +1,447 @@ +/* GlyphLoader.java -- Helper for loading TrueType glyph outlines. + 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.font.opentype.truetype; + +import gnu.java.awt.font.opentype.Hinter; + +import java.awt.geom.AffineTransform; +import java.nio.ByteBuffer; + + +/** + * A class for loading scaled and hinted glyph outlines. + * + * <p><b>Lack of Thread Safety:</b> Glyph loaders are intentionally + * <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the GlyphLoader. It would thus be wasteful to + * acquire additional locks for the GlyphLoader. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphLoader +{ + /** + * A helper object for locating glyph data. GlyphLocator is an + * abstract superclass, and there is a concretization for each glyph + * location table ('loca') format. + */ + private final GlyphLocator glyphLocator; + + + /** + * A helper object for measuring the advance width and height of a + * glyph. + */ + private final GlyphMeasurer glyphMeasurer; + + + /** + * The virtual machine for executing TrueType bytecodes. + */ + private final VirtualMachine vm; + + + /** + * The number of font units in one em. A typical value is 2048, + * but this depends on the font. + */ + private final int unitsPerEm; + + private final int[] contourEndPoints; + private final byte[] pointFlags; + + + /** + * Constructs a GlyphLoader. + */ + GlyphLoader(GlyphLocator glyphLocator, VirtualMachine vm, + int unitsPerEm, int maxContours, int maxPoints, + GlyphMeasurer glyphMeasurer) + { + this.glyphLocator = glyphLocator; + this.glyphMeasurer = glyphMeasurer; + this.unitsPerEm = unitsPerEm; + this.vm = vm; + + contourEndPoints = new int[maxContours]; + pointFlags = new byte[maxPoints]; + } + + + /** + * @param glyphIndex the number of the glyph whose outlines are to be + * retrieved. + */ + public void loadGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + Zone glyphZone, Hinter hinter) + { + glyphZone.setNumPoints(4); + loadSubGlyph(glyphIndex, pointSize, transform, antialias, glyphZone, + 0, 0, hinter); + } + + public void loadGlyph(int glyphIndex, AffineTransform transform, + Zone glyphZone, Hinter hinter) + { + loadGlyph(glyphIndex, unitsPerEm, transform, false, glyphZone, hinter); + } + + private void loadSubGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + Zone glyphZone, + int preTranslateX, + int preTranslateY, + Hinter hinter) + { + ByteBuffer glyph; + int numContours; + int xMin, yMin, xMax, yMax; + byte flag; + + glyph = glyphLocator.getGlyphData(glyphIndex); + + if (glyph == null) + { + glyphZone.setNumPoints(4); + setPhantomPoints(glyphIndex, 0, glyphZone); + glyphZone.transform(pointSize, transform, unitsPerEm, + preTranslateX, preTranslateY); + return; + } + + numContours = glyph.getShort(); + xMin = glyph.getChar(); + yMin = glyph.getChar(); + xMax = glyph.getChar(); + yMax = glyph.getChar(); + + + if (numContours >= 0) + loadSimpleGlyph(glyphIndex, pointSize, transform, antialias, + numContours, glyph, glyphZone, + preTranslateX, preTranslateY, hinter); + else + loadCompoundGlyph(glyphIndex, pointSize, transform, antialias, + glyph, glyphZone, + preTranslateX, preTranslateY, hinter); + } + + + private void loadSimpleGlyph(int glyphIndex, + double pointSize, AffineTransform transform, + boolean antialias, + int numContours, ByteBuffer glyph, + Zone glyphZone, + int preTranslateX, int preTranslateY, + Hinter hinter) + { + int numPoints; + int posInstructions, numInstructions; + boolean execInstructions; + + execInstructions = vm.setup(pointSize, transform, antialias); + + /* Load the contour end points and determine the number of + * points. + */ + for (int i = 0; i < numContours; i++) + contourEndPoints[i] = glyph.getChar(); + if (numContours > 0) + numPoints = 1 + contourEndPoints[numContours - 1]; + else + numPoints = 0; + glyphZone.setNumPoints(numPoints + 4); + + numInstructions = glyph.getChar(); + posInstructions = glyph.position(); + glyph.position(posInstructions + numInstructions); + loadFlags(numPoints, glyph); + loadCoordinates(numPoints, glyph, glyphZone); + for (int i = 0; i < numContours; i++) + glyphZone.setContourEnd(contourEndPoints[i], true); + + setPhantomPoints(glyphIndex, numPoints, glyphZone); + glyphZone.transform(pointSize, transform, unitsPerEm, + preTranslateX, preTranslateY); + + if (execInstructions && hinter != null) + { + hinter.applyHints(glyphZone); + } + } + + + private static final short ARGS_ARE_WORDS = 1; + private static final short ARGS_ARE_XY_VALUES = 2; + private static final short ROUND_XY_TO_GRID = 4; + private static final short WE_HAVE_A_SCALE = 8; + private static final short MORE_COMPONENTS = 32; + private static final short WE_HAVE_AN_X_AND_Y_SCALE = 64; + private static final short WE_HAVE_A_TWO_BY_TWO = 128; + private static final short WE_HAVE_INSTRUCTIONS = 256; + private static final short USE_MY_METRICS = 512; + private static final short OVERLAP_COMPOUND = 1024; + private static final short SCALED_COMPONENT_OFFSET = 2048; + private static final short UNSCALED_COMPONENT_OFFSET = 4096; + + private void loadCompoundGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + ByteBuffer glyph, + Zone glyphZone, + int preTranslateX, int preTranslateY, + Hinter hinter) + { + short flags; + int subGlyphIndex; + int metricsGlyphIndex; + Zone subGlyphZone = new Zone(glyphZone.getCapacity()); + int arg1, arg2; + double a, b, c, d, e, f; + AffineTransform componentTransform = new AffineTransform(); + + /* By default, use the metrics of the compound glyph. The default + * is overridden if some component glyph has the USE_MY_METRICS + * flag set. + */ + metricsGlyphIndex = glyphIndex; + + do + { + flags = glyph.getShort(); + subGlyphIndex = glyph.getChar(); + + if ((flags & USE_MY_METRICS) != 0) + metricsGlyphIndex = subGlyphIndex; + + if ((flags & ARGS_ARE_WORDS) != 0) + { + arg1 = glyph.getShort(); + arg2 = glyph.getShort(); + } + else + { + arg1 = glyph.get(); + arg2 = glyph.get(); + } + + if ((flags & WE_HAVE_A_SCALE) != 0) + { + a = d = getDouble214(glyph); + b = c = 0.0; + } + else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) + { + a = getDouble214(glyph); + d = getDouble214(glyph); + b = c = 0.0; + } + else if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0) + { + a = getDouble214(glyph); + b = getDouble214(glyph); + c = getDouble214(glyph); + d = getDouble214(glyph); + } + else + { + a = d = 1.0; + b = c = 0.0; + } + + double m = Math.max(Math.abs(a), Math.abs(b)); + double n = Math.max(Math.abs(c), Math.abs(d)); + + /* The Apple TrueType specification actually says that m is + * multiplied by two if + * + * abs(abs(a) - abs(c)) <= 33/65536, + * + * but this is probably a typo. On 2003-07-23, Sascha Brawer + * wrote an e-mail message to applefonts@apple.com, asking + * whether this might possibly be an error in the specification. + */ + if (Math.abs(Math.abs(a) - Math.abs(b)) <= 33.0/65536.0) + m = m * 2; + + if (Math.abs(Math.abs(c) - Math.abs(d)) <= 33.0/65536.0) + n = n * 2; + + if ((flags & ARGS_ARE_XY_VALUES) != 0) + { + e = m * arg1; + f = n * arg2; + } + else + e = f = 0.0; + + componentTransform.setTransform(a, b, c, d, 0.0, 0.0); + + // System.out.println("componentTransform = " + componentTransform + // + ", e=" + e + ", f=" + f); + componentTransform.concatenate(transform); + + int pos = glyph.position(); + int lim = glyph.limit(); + + loadSubGlyph(subGlyphIndex, pointSize, componentTransform, + antialias, subGlyphZone, + Math.round((float) e + preTranslateX), + Math.round(-((float) f + preTranslateY)), hinter); + glyphZone.combineWithSubGlyph(subGlyphZone, 4); + glyph.limit(lim).position(pos); + } + while ((flags & MORE_COMPONENTS) != 0); + + setPhantomPoints(metricsGlyphIndex, glyphZone.getSize() - 4, glyphZone); + } + + + private double getDouble214(ByteBuffer buf) + { + return ((double) buf.getShort()) / (1 << 14); + } + + + /** + * Loads the per-point flags of a glyph into the + * <code>pointFlags</code> field. + */ + private void loadFlags(int numPoints, ByteBuffer glyph) + { + byte flag; + int numRepetitions; + + for (int i = 0; i < numPoints; i++) + { + pointFlags[i] = flag = glyph.get(); + if ((flag & 8) != 0) + { + numRepetitions = ((int) glyph.get()) & 0xff; + while (numRepetitions > 0) + { + pointFlags[++i] = flag; + --numRepetitions; + } + } + } + } + + + private void loadCoordinates(int numPoints, ByteBuffer glyph, + Zone glyphZone) + { + int x, y; + byte flag; + + x = 0; + for (int i = 0; i < numPoints; i++) + { + flag = pointFlags[i]; + if ((flag & 2) == 0) + { + if ((flag & 16) == 0) + x += glyph.getShort(); + } + else + { + if ((flag & 16) != 0) + x += (glyph.get() & 0xff); + else + x -= (glyph.get() & 0xff); + } + glyphZone.setOriginalX(i, x); + glyphZone.setOnCurve(i, (flag & 1) == 1); + } + + y = 0; + for (int i = 0; i < numPoints; i++) + { + flag = pointFlags[i]; + if ((flag & 4) == 0) + { + if ((flag & 32) == 0) + y += glyph.getShort(); + } + else + { + if ((flag & 32) != 0) + y += (glyph.get() & 0xff); + else + y -= (glyph.get() & 0xff); + } + glyphZone.setOriginalY(i, -y); + } + } + + + private void setPhantomPoints(int glyphIndex, int numPoints, + Zone glyphZone) + { + /* Phantom point 0: Character origin. */ + glyphZone.setOriginalX(numPoints, 0); + glyphZone.setOriginalY(numPoints, 0); + + /* Phantom point 1: Horizontal advance point. */ + glyphZone.setOriginalX(numPoints + 1, + glyphMeasurer.getAdvanceWidth(glyphIndex, true)); + glyphZone.setOriginalY(numPoints + 1, + glyphMeasurer.getAdvanceHeight(glyphIndex, true)); + + /* Phantom point 2: Vertical origin. */ + int vertX = glyphMeasurer.getAscent(/* vertical */ false); + int vertY = glyphMeasurer.getAscent(/* horizontal */ true); + glyphZone.setOriginalX(numPoints + 2, vertX); + glyphZone.setOriginalY(numPoints + 2, vertY); + + /* Phantom point 3: Vertical advance point. */ + glyphZone.setOriginalX(numPoints + 3, + vertX + glyphMeasurer.getAdvanceWidth(glyphIndex, false)); + glyphZone.setOriginalY(numPoints + 3, + vertY + glyphMeasurer.getAdvanceHeight(glyphIndex, false)); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java new file mode 100644 index 000000000..fc2256b15 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java @@ -0,0 +1,187 @@ +/* GlyphLocator.java -- Locates outlines of TrueType glyphs. + 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.font.opentype.truetype; + +import java.awt.FontFormatException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.IntBuffer; + + +/** + * Locates glyph outlines in a TrueType or OpenType <code>glyf</code> + * table. + * + * @see <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/loca.html" + * >Adobe’s specification of the OpenType ‘loca’ + * table</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +abstract class GlyphLocator +{ + /** + * The actual glyph data of the font, which is contained in the + * 'glyf' table. + */ + protected ByteBuffer glyfTable; + + + /** + * Creates a new GlyphLocator for a <code>loca</code> table. + * + * @param format the format of the <code>loca</code> table. The + * value must be 0 for two-byte offsets, or 1 for four-byte + * offsets. TrueType and OpenType fonts indicate the format in the + * <code>indexToLoc</code> field of the <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/head.html" + * >font header</a>. + * + * @param loca the <code>loca</code> table of the font, which + * contains the position of each glyph in the <code>glyf</code> + * table. + * + * @param glyf the <code>glyf</code> table of the font, which + * contains the outline data of each glyph. + * + * @throws FontFormatException if <code>format</code> is neither 0 + * nor 1. + */ + public static GlyphLocator forTable(int format, ByteBuffer loca, + ByteBuffer glyf) + throws FontFormatException + { + switch (format) + { + case 0: + return new GlyphLocator.TwoByte(loca, glyf); + + case 1: + return new GlyphLocator.FourByte(loca, glyf); + + default: + throw new FontFormatException("unsupported loca format"); + } + } + + + /** + * Locates the outline data for a glyph. + * + * <p>For efficiency, the glyph locator does not create a new buffer + * for each invocation. Instead, this method always returns the same + * buffer object. Therefore, the data of a glyph must have been read + * completely before another glyph of the same font gets requested + * through this method. + * + * @param glyph the number of the glyph whose outlines are to be + * retrieved. + * + * @return a buffer whose position is set to the first byte of glyph + * data, and whose limit is set to disallow accessing any data that + * does not belong to the glyph. If there is no outline data for the + * requested glyph, as would be the case for the space glyph, the + * result will be <code>null</code>. + */ + public abstract ByteBuffer getGlyphData(int glyph); + + + /** + * A GlyphLocator that locates glyphs using two-byte offsets, + * interpreting <code>loca</code> tables of format 0. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private final static class TwoByte + extends GlyphLocator + { + final CharBuffer indexToLoc; + + TwoByte(ByteBuffer loca, ByteBuffer glyf) + { + this.glyfTable = glyf; + indexToLoc = loca.asCharBuffer(); + } + + + public ByteBuffer getGlyphData(int glyph) + { + int offset, limit; + offset = ((int) indexToLoc.get(glyph)) << 1; + limit = ((int) indexToLoc.get(glyph + 1)) << 1; + if (offset >= limit) + return null; + + glyfTable.limit(limit).position(offset); + return glyfTable; + } + } + + + /** + * A GlyphLocator that locates glyphs using four-byte offsets, + * interpreting <code>loca</code> tables of format 1. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private final static class FourByte + extends GlyphLocator + { + final IntBuffer indexToLoc; + + FourByte(ByteBuffer loca, ByteBuffer glyf) + { + this.glyfTable = glyf; + indexToLoc = loca.asIntBuffer(); + } + + + public ByteBuffer getGlyphData(int glyph) + { + int offset, limit; + offset = indexToLoc.get(glyph); + limit = indexToLoc.get(glyph + 1); + if (offset >= limit) + return null; + + glyfTable.limit(limit).position(offset); + return glyfTable; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java new file mode 100644 index 000000000..452456d13 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java @@ -0,0 +1,228 @@ +/* GlyphMeasurer.java -- Helper for measuring TrueType glyphs. + 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.font.opentype.truetype; + +import java.awt.FontFormatException; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + + +/** + * A class for measuring TrueType and OpenType glyphs. + * + * <p><b>Lack of Thread Safety:</b> Glyph measurers are intentionally + * <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the GlyphMeasurer. It would thus be wasteful to + * acquire additional locks for the GlyphMeasurer. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphMeasurer +{ + /** + * A view buffer that allows accessing the contents of the + * font’s <code>hmtx</code> table as shorts. + */ + private final ShortBuffer horizontalGlyphMetrics; + + + /** + * A view buffer that allows accessing the contents of the + * font’s <code>vmtx</code> table as shorts. + */ + private final ShortBuffer verticalGlyphMetrics; + + + private final int numLongHorizontalMetricsEntries; + private final int numLongVerticalMetricsEntries; + + private final int horizontalAscent; + private final int verticalAscent; + + private final int horizontalDescent; + private final int verticalDescent; + + private final int horizontalLineGap; + + + /** + * Constructs a GlyphMeasurer from TrueType/OpenType font tables. + * + * @param hhea the <code>hhea</code> table, which contains + * information about horizontal metrics that is common to all + * glyphs. + * + * @param hmtx the <code>hmtx</code> table, which contains + * glyph-specific information about horizontal metrics. + * + * @param vhea the <code>vhea</code> table, which contains + * information about vertical metrics that is common to all + * glyphs. If a font does not provide such a table, pass + * <code>null</code>. + * + * @param vmtx the <code>vmtx</code> table, which contains + * glyph-specific information about vertical metrics. If a font + * does not provide such a table, pass <code>null</code>. + */ + GlyphMeasurer(ByteBuffer hhea, ByteBuffer hmtx, + ByteBuffer vhea, ByteBuffer vmtx) + throws FontFormatException + { + if ((hhea.getInt(0) != 0x00010000) || (hhea.getInt(30) != 0)) + throw new FontFormatException("unsupported hhea format"); + + horizontalAscent = hhea.getShort(4); + horizontalDescent = hhea.getShort(6); + horizontalLineGap = hhea.getShort(8); + + numLongHorizontalMetricsEntries = hhea.getChar(34); + horizontalGlyphMetrics = hmtx.asShortBuffer(); + + if (vhea != null) + { + verticalAscent = vhea.getShort(4); + verticalDescent = vhea.getShort(6); + numLongVerticalMetricsEntries = vhea.getChar(34); + verticalGlyphMetrics = vmtx.asShortBuffer(); + } + else + { + verticalAscent = /* advanceWidthMax */ hhea.getChar(10) / 2; + verticalDescent = -verticalAscent; + numLongVerticalMetricsEntries = 0; + verticalGlyphMetrics = null; + } + } + + + /** + * Returns the distance from the baseline to the highest ascender. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the maximal ascent, in font units. + */ + public int getAscent(boolean horizontal) + { + return horizontal ? horizontalAscent : verticalAscent; + } + + + /** + * Returns the distance from the baseline to the lowest descender. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the maximal descent, in font units. + */ + public int getDescent(boolean horizontal) + { + return horizontal ? horizontalDescent : verticalDescent; + } + + + /** + * Returns the typographic line gap. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the line gap, in font units. + */ + public int getLineGap(boolean horizontal) + { + return horizontalLineGap; + } + + + /** + * Determines the advance width of a glyph, without considering + * hinting. + * + * @param glyphIndex the index of the glyph whose advance width is + * to be determined. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the advance width, in font units. + */ + public int getAdvanceWidth(int glyphIndex, boolean horizontal) + { + if (!horizontal) + return 0; + + glyphIndex = Math.min(glyphIndex, + numLongHorizontalMetricsEntries - 1); + return horizontalGlyphMetrics.get(glyphIndex << 1); + } + + + /** + * Determines the advance width of a glyph, without considering + * hinting. + * + * @param glyphIndex the index of the glyph whose advance width is + * to be determined. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the advance width, in font units. + */ + public int getAdvanceHeight(int glyphIndex, boolean horizontal) + { + if (horizontal) + return 0; + + /* If a font does not provide vertical glyph metrics, advance + * by the height of one horizontal line. + */ + if (verticalGlyphMetrics == null) + return horizontalAscent - horizontalDescent + horizontalLineGap; + + glyphIndex = Math.min(glyphIndex, + numLongVerticalMetricsEntries - 1); + return verticalGlyphMetrics.get(glyphIndex << 1); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java new file mode 100644 index 000000000..438eb6558 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java @@ -0,0 +1,287 @@ +/* Point.java -- Holds information for one point on a glyph outline + 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.font.opentype.truetype; + +import gnu.java.lang.CPStringBuilder; + +/** + * Encapsulates information regarding one point on a glyph outline. + */ +public class Point +{ + public static final short FLAG_TOUCHED_X = 1; + public static final short FLAG_TOUCHED_Y = 2; + public static final short FLAG_ON_CURVE = 4; + public static final short FLAG_CONTOUR_END = 8; + public static final short FLAG_WEAK_INTERPOLATION = 16; + public static final short FLAG_INFLECTION = 32; + public static final short FLAG_DONE_X = 64; + public static final short FLAG_DONE_Y = 128; + + /** + * Right direction. + */ + public static final int DIR_RIGHT = 1; + + /** + * Left direction. + */ + public static final int DIR_LEFT = -1; + + /** + * Up direction. + */ + public static final int DIR_UP = 2; + + /** + * Down direction. + */ + public static final int DIR_DOWN = -2; + + /** + * The original x coordinate in font units. + */ + int origX; + + /** + * The original y coordinate in font units. + */ + int origY; + + /** + * The x coordinate scaled to the target. + */ + int scaledX; + + /** + * The y coordinate scaled to the target. + */ + int scaledY; + + /** + * The final hinted and scaled x coordinate. + */ + int x; + + /** + * The final hinted and scaled y coordinate. + */ + int y; + + int u; + int v; + + /** + * The glyph flags. + */ + short flags; + + /** + * The previous point in the contour. + */ + private Point prev; + + /** + * The next point in the contour. + */ + private Point next; + + /** + * The in-direction of the point, according to the DIR_* constants of this + * class. + */ + int inDir; + + /** + * The out-direction of the point, according to the DIR_* constants of this + * class. + */ + int outDir; + + public Point getNext() + { + return next; + } + + public void setNext(Point next) + { + this.next = next; + } + + public Point getPrev() + { + return prev; + } + + public void setPrev(Point prev) + { + this.prev = prev; + } + + public int getOrigX() + { + return origX; + } + + public void setOrigX(int origX) + { + this.origX = origX; + } + + public int getOrigY() + { + return origY; + } + + public void setOrigY(int origY) + { + this.origY = origY; + } + + public int getInDir() + { + return inDir; + } + + public void setInDir(int inDir) + { + this.inDir = inDir; + } + + public int getOutDir() + { + return outDir; + } + + public void setOutDir(int outDir) + { + this.outDir = outDir; + } + + public short getFlags() + { + return flags; + } + + public void setFlags(short flags) + { + this.flags = flags; + } + + public void addFlags(short flags) + { + this.flags |= flags; + } + + public boolean isControlPoint() + { + return (flags & FLAG_ON_CURVE) == 0; + } + + public int getU() + { + return u; + } + + public void setU(int u) + { + this.u = u; + } + + public int getV() + { + return v; + } + + public void setV(int v) + { + this.v = v; + } + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Point] origX: "); + s.append(origX); + s.append(", origY: "); + s.append(origY); + // TODO: Add more info when needed. + return s.toString(); + } + + public int getX() + { + return x; + } + + public void setX(int x) + { + this.x = x; + } + + public int getY() + { + return y; + } + + public void setY(int y) + { + this.y = y; + } + + public int getScaledX() + { + return scaledX; + } + + public void setScaledX(int scaledX) + { + this.scaledX = scaledX; + } + + public int getScaledY() + { + return scaledY; + } + + public void setScaledY(int scaledY) + { + this.scaledY = scaledY; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java new file mode 100644 index 000000000..1d5c53f9d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java @@ -0,0 +1,380 @@ +/* TrueTypeScaler.java -- Font scaler for TrueType outlines. + 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.font.opentype.truetype; + +import gnu.java.awt.font.opentype.Hinter; +import gnu.java.awt.font.opentype.Scaler; + +import java.awt.FontFormatException; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.nio.ByteBuffer; + + +/** + * A scaler for fonts whose outlines are described in the TrueType + * format. + * + * <p><b>Lack of Thread Safety:</b> Font scalers are intentionally + * <i>not</i> safe to access from multiple concurrent threads. + * Synchronization needs to be performed externally. Usually, the font + * that uses this scaler already has obtained a lock before calling + * the scaler. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class TrueTypeScaler + extends Scaler +{ + /** + * The TrueType or OpenType table that contains the glyph outlines. + */ + private ByteBuffer glyfTable; + + + /** + * A helper object for loading glyph outlines. + */ + private GlyphLoader glyphLoader; + + + /** + * A helper object for measuring the advance width and height of a + * glyph. + */ + private final GlyphMeasurer glyphMeasurer; + + private final Zone glyphZone; + + + /** + * The number of units per em. A typical value is 2048, but some + * font use other numbers as well. + */ + private int unitsPerEm; + + + /** + * Constructs a new TrueTypeScaler. + * + * @param unitsPerEm the number of font units per em. This value can + * be retrieved from the font’s <code>head</code> table. + * + * @param maxp the <code>maxp</code> table of the font, which + * contains various constants needed for setting up the virtual + * machine that interprets TrueType bytecodes. + * + * @param controlValueTable the <code>cvt</code> table of the font, + * which contains the initial values of the control value table. + * + * @param fpgm the <code>fpgm</code> table of the font, which + * contains a font program that is executed exactly once. The + * purpose of the font program is to define functions and to patch + * the interpreter. + * + * @param locaFormat the format of the <code>loca</code> table. The + * value must be 0 for two-byte offsets, or 1 for four-byte + * offsets. TrueType and OpenType fonts indicate the format in the + * <code>indexToLoc</code> field of the <a href= + * "http://partners.adobe.com/asn/tech/type/opentype/head.html" + * >font header</a>. + * + * @param loca the <code>loca</code> table of the font, which + * contains for each glyph the offset of its outline data + * in <code>glyf</code>. + * + * @param glyf the <code>glyf</code> table of the font, which + * contains the outline data for all glyphs in the font. + * + * @param preProgram the <code>prep</code> table of the font, which + * contains a program that is executed whenever the point size or + * the device transform have changed. This program is called + * pre-program because it gets executed before the instructions of + * the individual glyphs. If the font does not contain a + * pre-program, pass <code>null</code>. + * + * @throws FontFormatException if <code>format</code> is neither 0 + * nor 1. + */ + public TrueTypeScaler(int unitsPerEm, + ByteBuffer hhea, + ByteBuffer htmx, + ByteBuffer vhea, + ByteBuffer vtmx, + ByteBuffer maxp, + ByteBuffer controlValueTable, + ByteBuffer fpgm, + int locaFormat, ByteBuffer loca, + ByteBuffer glyf, + ByteBuffer preProgram) + throws FontFormatException + { + int maxContours, maxPoints; + VirtualMachine vm; + + maxContours = Math.max(/* maxContours */ (int) maxp.getChar(8), + /* maxCompositeContours */ (int) maxp.getChar(12)) + + /* fix for some broken fonts */ 8; + maxPoints = Math.max(/* maxPoints */ (int) maxp.getChar(6), + /* maxCompositePoints */ (int) maxp.getChar(10)) + + /* fix for some broken fonts */ 12; + + + glyphZone = new Zone(maxPoints + /* four phantom points */ 4); + this.glyfTable = glyf; + vm = new VirtualMachine(unitsPerEm, maxp, + controlValueTable, fpgm, + preProgram); + + GlyphLocator locator = GlyphLocator.forTable(locaFormat, loca, glyf); + glyphMeasurer = new GlyphMeasurer(hhea, htmx, vhea, vtmx); + glyphLoader = new GlyphLoader(locator, vm, unitsPerEm, + maxContours, maxPoints, + glyphMeasurer); + + this.unitsPerEm = unitsPerEm; + } + + + /** + * Retrieves the scaled outline of a glyph, adjusting control points + * to the raster grid if necessary. + * + * @param glyphIndex the glyph number whose outline is retrieved. + * + * @param pointSize the point size for the glyph. + * + * @param deviceTransform an affine transformation for the device. + * + * @param antialias whether or not the rasterizer will perform + * anti-aliasing on the returned path. + * + * @param fractionalMetrics <code>false</code> for adjusting glyph + * positions to the raster grid of device space. + */ + public GeneralPath getOutline(int glyphIndex, + float pointSize, + AffineTransform deviceTransform, + boolean antialias, + boolean fractionalMetrics, Hinter hinter, + int type) + { + glyphLoader.loadGlyph(glyphIndex, pointSize, deviceTransform, + antialias, glyphZone, hinter); + return glyphZone.getPath(type); + } + + public Zone getRawOutline(int glyphIndex, AffineTransform transform) + { + Zone zone = new Zone(glyphZone.getCapacity()); + glyphLoader.loadGlyph(glyphIndex, transform, zone, null); + return zone; + } + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width and height is to + * be determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @param advance a point whose <code>x</code> and <code>y</code> + * fields will hold the advance in each direction. It is possible + * that both values are non-zero, for example if + * <code>transform</code> is a rotation, or in the case of Urdu + * fonts. + */ + public void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance) + { + double x, y; + double scaleFactor = (double) pointSize / unitsPerEm; + + /* FIXME: Should grid-fit if needed. Also, use cache if present + * in the font. + */ + advance.setLocation( + scaleFactor * glyphMeasurer.getAdvanceWidth(glyphIndex, horizontal), + scaleFactor * glyphMeasurer.getAdvanceHeight(glyphIndex, horizontal)); + + transform.transform(advance, advance); + } + + + /** + * Scales a value from font units to pixels, given the point size + * and the transform. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. + * + * @param fractionalMetrics <code>true</code> for fractional + * metrics, <code>false</code> for rounding the result to a pixel + * boundary. + * + * @param horizontal <code>true</code> if the <code>funits</code> + * value is along the x axis, <code>false</code> if it is along the + * y axis. + */ + private float scaleFromFUnits(int funits, + float pointSize, + AffineTransform transform, + boolean fractionalMetrics, + boolean horizontal) + { + double s; + + s = (double) pointSize / unitsPerEm; + if (transform != null) + s *= horizontal ? transform.getScaleY() : transform.getScaleX(); + s *= funits; + if (!fractionalMetrics) + s = Math.round(s); + return (float) s; + } + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public float getAscent(float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal) + { + /* Note that the ascent is orthogonal to the direction of line + * layout: If the line direction is horizontal, the measurement of + * ascent is along the vertical axis, and vice versa. + */ + return scaleFromFUnits(glyphMeasurer.getAscent(horizontal), + pointSize, + transform, + fractionalMetrics, + /* reverse */ !horizontal); + } + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased <code>true</code> for anti-aliased rendering, + * <code>false</code> for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics <code>true</code> for fractional metrics, + * <code>false</code> for rounding the result to a pixel boundary. + * + * @param horizontal <code>true</code> for horizontal line layout, + * <code>false</code> for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + /* Note that the descent is orthogonal to the direction of line + * layout: If the line direction is horizontal, the measurement of + * descent is along the vertical axis, and vice versa. + */ + return scaleFromFUnits(glyphMeasurer.getDescent(horizontal), + pointSize, + transform, + fractionalMetrics, + /* reverse */ !horizontal); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java new file mode 100644 index 000000000..512c39ffd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java @@ -0,0 +1,1815 @@ +/* VirtualMachine.java -- Virtual machine for TrueType bytecodes. + 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.font.opentype.truetype; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.FontFormatException; +import java.awt.geom.AffineTransform; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + + +/** + * A virtual machine for interpreting TrueType bytecodes. + * + * <p><b>Lack of Thread Safety:</b> The virtual machine is + * intentionally <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the VM. It would be wasteful to acquire + * additional locks for the VM. + * + * <p><b>Implementation Status:</b> The current implementation can + * execute pre-programs of fonts, but it does not yet actually move + * any points. Control flow and arithmeti instructions are + * implemented, but most geometric instructions are not working + * yet. So, the VirtualMachine class is currently a no-op. However, + * not very much is missing. You are more than welcome to complete the + * implementation. + * + * <p><b>Patents:</b> Apple Computer holds three United States Patents + * for the mathematical algorithms that are used by TrueType + * instructions. The monopoly granted by these patents will expire in + * October 2009. Before the expiration date, a license must be + * obtained from Apple Computer to use the patented technology inside + * the United States. For other countries, different dates might + * apply, or no license might be needed. + * + * <p>The default build of this class does not use the patented + * algorithms. If you have obtained a license from Apple, or if the + * patent protection has expired, or if no license is required for + * your contry, you can set a flag in the source file which will + * enable the use of the patented mathematical algorithms.</p> + * + * <p>The relevant patents are listed subsequently.</p> + * + * <p><ol><li>United States Patent 5155805, <i>Method and Apparatus + * for Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 13, + * 1992.</li> + * + * <li>United States Patent 5159668, <i>Method and Apparatus for + * Manipulating Outlines in Improving Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 27, + * 1992.</li> + * + * <li>United States Patent 5325479, <i>Method and Apparatus for + * Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 28, 1989. Date of patent: June 28, 1994 + * (with a statement that “[t]he portion of the term of this + * patent subsequent to Oct. 13, 2009 has been + * disclaimed”).</li></ol> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +class VirtualMachine +{ + /** + * Indicates whether or not to perform hinting operations that are + * protected by a number of US patents, two of which will expire on + * October 13, 2009, and one of which will expire on October 27, + * 2009. + */ + private final static boolean PATENTED_HINTING = false; + + + /** + * Indicates whether the execution of the Virtual Machine is traced + * to System.out. + */ + private final static boolean TRACE_EXECUTION = false; + + + /** + * The value 1 in 2-dot-14 fixed notation. + */ + private static final short ONE_214 = 0x4000; // 1 << 14 + + + /** + * The storage area of the virtual machine. + */ + private final int[] storage; + + + /** + * The stack. The stack grows from bottom to top, so + * <code>sp[0]</code> gets used before <code>sp[1]</code>. + */ + private int[] stack; + + + /** + * The maximum number of stack elements. + */ + private final int maxStackElements; + + + /** + * The current stack pointer of the virtual machine. + */ + private int sp; + + + /** + * fdefBuffer[i] is the buffer that contains the TrueType + * instructions of function #i. Most of the time, functions are + * defined in the font program, but a font may also re-define + * functions in its CVT program. + */ + private ByteBuffer[] fdefBuffer; + + + /** + * fdefEntryPoint[i] is the position in fdefBuffer[i] where the + * first TrueType instruction after the FDEF is located. + */ + private int[] fdefEntryPoint; + + + /** + * The original Control Value Table, sometimes abbreviated as CVT. + * The table contains signed 16-bit FUnits. Some fonts have no CVT, + * in which case the field will be <code>null</code>. + */ + private ShortBuffer controlValueTable; + + + /** + * The scaled values inside the control value table. + */ + private int[] cvt; + + + /** + * A value that is used by rounding operations to compensate for dot + * gain. + */ + private int engineCompensation = 0; + + + /** + * The contents of the font’s <code>fpgm</code> table, or + * <code>null</code> after the font program has been executed once. + */ + private ByteBuffer fontProgram; + + + /** + * The <code>prep</code> table of the font, which contains a program + * that is executed whenever the point size or the device transform + * have changed. This program is called pre-program because it gets + * executed before the instructions of the individual glyphs. If + * the font does not contain a pre-program, the value of this field + * is <code>null</code>. + */ + private ByteBuffer preProgram; + + + /** + * The number of points in the Twilight Zone. + */ + private int numTwilightPoints; + + + /** + * The current point size of the scaled font. The value is in Fixed + * 26.6 notation. + */ + private int pointSize; // 26.6 + + private AffineTransform deviceTransform; + + private int scaleX, scaleY, shearX, shearY; // 26.6 + + + /** + * Indicates whether or not scan-line conversion will use + * anti-aliasing (with gray levels). Font programs can ask for this + * value with the <code>GETINFO</code> instruction, and some + * programs may behave differently according to this setting. + */ + private boolean antialiased; + + + /* Graphics State. FIXME: Move this to its own class? Some + * documentation would not hurt, either. + */ + private int cvtCutIn; // 26.6 + private int deltaBase; // uint32 + private int deltaShift; // uint32 + private short freeX; // 2.14 + private short freeY; // 2.14 + private int loop; // int + private int minimumDistance; // 26.6 + private short projX; // 2.14 + private short projY; // 2.14 + private short dualX; // 2.14 + private short dualY; // 2.14 + private int rp0, rp1, rp2; // point numbers + private boolean scanControl; + private int scanType; + private int singleWidthValue; // 26.6 + private Zone zp0, zp1, zp2; + + private Zone twilightZone; + private Zone glyphZone; + + + /** + * Indicates whether or not the instructions that are associated + * with individual glyphs shall be executed. Set as a side effect + * of executing the pre-program when the point size, device + * transform or some other relevant parameter have changed. + */ + private boolean executeGlyphInstructions; + + + /** + * Indicates whether to ignore any modifications to the control + * value table that the font’s pre-program might have + * performed. Set as a side effect of executing the pre-program + * when the point size, device transform or some other relevant + * parameter have changed. + */ + private boolean ignoreCVTProgram; + + + /** + * The length of the space between rounded values. A value + * of zero means that rounding has been switched off. + */ + private int roundPeriod; // 26.6 + + + /** + * The offset of the rounded values from multiples of + * <code>roundPeriod</code>. + */ + private int roundPhase; // 26.6 + + + private int roundThreshold; // 26.6 + + + /** + * A cache for the number of pixels per EM. The value is a normal + * integer, not a fixed point notation. + * + * @see #getPixelsPerEM() + */ + private int cachedPixelsPerEM; + + + /** + * The number of font units per EM. + */ + private int unitsPerEm; + + + /** + * Constructs a new Virtual Machine for executing TrueType + * instructions. + * + * @param unitsPerEm the number of font units in one typographic + * em. + * + * @param preProgram the <code>prep</code> table of the font, which + * contains a program that is executed whenever the point size or + * the device transform have changed. This program is called + * pre-program because it gets executed before the instructions of + * the individual glyphs. If the font does not contain a + * pre-program, pass <code>null</code>. + */ + VirtualMachine(int unitsPerEm, + ByteBuffer maxp, + ByteBuffer controlValueTable, + ByteBuffer fontProgram, + ByteBuffer preProgram) + throws FontFormatException + { + int maxStorage, numFunctionDefs, maxInstructionDefs; + + if (maxp.getInt(0) != 0x00010000) + throw new FontFormatException("unsupported maxp version"); + + this.unitsPerEm = unitsPerEm; + maxStorage = maxp.getChar(18); + + /* FreeType says that there exist some broken fonts (like + * "Keystrokes MT") that contain function defs, but have a zero + * value in their maxp table. + */ + numFunctionDefs = maxp.getChar(20); + if (numFunctionDefs == 0) + numFunctionDefs = 64; + fdefBuffer = new ByteBuffer[numFunctionDefs]; + fdefEntryPoint = new int[numFunctionDefs]; + + /* Read the contents of the Control Value Table. */ + if (controlValueTable != null) + this.controlValueTable = controlValueTable.asShortBuffer(); + + maxInstructionDefs = maxp.getChar(22); + maxStackElements = maxp.getChar(24); + storage = new int[maxStorage]; + this.fontProgram = fontProgram; + this.preProgram = preProgram; + numTwilightPoints = maxp.getChar(16); + } + + + /** + * Sets the graphics state to default values. + */ + private void resetGraphicsState() + { + /* The freedom, projection and dual vector default to the x axis. */ + freeX = projX = dualX = ONE_214; + freeY = projY = dualX = 0; + cachedPixelsPerEM = 0; + + cvtCutIn = 68; // 17/16 in 26.6 notation + deltaBase = 9; + deltaShift = 3; + loop = 1; + minimumDistance = Fixed.ONE; + singleWidthValue = 0; + rp0 = rp1 = rp2 = 0; + scanControl = false; + scanType = 2; + zp0 = zp1 = zp2 = getZone(1); + + setRoundingMode(Fixed.ONE, 0x48); // round to grid + } + + + /** + * Reloads the control value table and scales each entry from font + * units to pixel values. + */ + private void reloadControlValueTable() + { + /* Some TrueType fonts have no control value table. */ + if (controlValueTable == null) + return; + + /* Read in the Control Value Table. */ + if (cvt == null) + cvt = new int[controlValueTable.capacity()]; + + /* Scale the entries. */ + for (int i = 0; i < cvt.length; i++) + cvt[i] = funitsToPixels(controlValueTable.get(i)); + } + + + /** + * Scales a value from font unites to pixels. + * + * @return the scaled value. + */ + private int funitsToPixels(int funits) + { + return (int) (((long) funits * scaleY + (unitsPerEm>>1)) + / unitsPerEm); + } + + + /** + * Sets up the virtual machine for the specified parameters. If + * there is no change to the last set-up, the method will quickly + * return. Otherwise, the font’s pre-program will be + * executed. + * + * @param pointSize the point size of the scaled font. + * + * @param deviceTransform an affine transformation which gets + * applied in addition to scaling by <code>pointSize</code>. Font + * programs can separately inquire about the point size. For this + * reason, it is not recommended to pre-multiply the point size to + * the device transformation. + * + * @param antialiased <code>true</code> if the scan-line conversion + * algorithm will use gray levels to give a smoother appearance, + * <code>false</code> otherwise. Font programs can ask for this + * value with the <code>GETINFO</code> instruction, and some + * programs may behave differently according to this setting. + */ + public boolean setup(double pointSize, + AffineTransform deviceTransform, + boolean antialiased) + { + boolean changeCTM; + int pointSize_Fixed; + + if (stack == null) + stack = new int[maxStackElements]; + + if (twilightZone == null) + twilightZone = new Zone(numTwilightPoints); + + /* If the font program has not yet been executed, do so. */ + if (fontProgram != null) + { + resetGraphicsState(); + sp = -1; + execute(fontProgram, 0); + fontProgram = null; // prevent further execution + } + + /* Determine whether the transformation matrix has changed. */ + pointSize_Fixed = Fixed.valueOf(pointSize); + changeCTM = ((pointSize_Fixed != this.pointSize) + || !deviceTransform.equals(this.deviceTransform) + || (antialiased != this.antialiased)); + + if (changeCTM) + { + this.pointSize = pointSize_Fixed; + this.deviceTransform = deviceTransform; + this.antialiased = antialiased; + scaleX = (int) (deviceTransform.getScaleX() * pointSize * 64); + scaleY = (int) (deviceTransform.getScaleY() * pointSize * 64); + shearX = (int) (deviceTransform.getShearX() * pointSize * 64); + shearY = (int) (deviceTransform.getShearY() * pointSize * 64); + + resetGraphicsState(); + reloadControlValueTable(); + executeGlyphInstructions = true; + ignoreCVTProgram = false; + + if (preProgram != null) + { + sp = -1; + execute(preProgram, 0); + if (ignoreCVTProgram) + reloadControlValueTable(); + } + } + + return executeGlyphInstructions; + } + + + /** + * Executes a stream of TrueType instructions. + */ + private void execute(ByteBuffer instructions, int pos) + { + instructions.position(pos); + + // FIXME: SECURITY: Possible denial-of-service attack + // via instructions that have an endless loop. + while (instructions.hasRemaining() + && executeInstruction(instructions)) + ; + } + + + /** + * Writes a textual description of the current TrueType instruction, + * including the top stack elements, to <code>System.out</code>. + * This is useful for debugging. + * + * @param inst the instruction stream, positioned at the current + * instruction. + */ + private void dumpInstruction(ByteBuffer inst) + { + CPStringBuilder sbuf = new CPStringBuilder(40); + int pc = inst.position(); + int bcode = inst.get(pc) & 0xff; + int count; + int delta; + + char pcPrefix = 'c'; + for (int i = 0; i < fdefBuffer.length; i++) + { + if (fdefBuffer[i] == inst) + { + pcPrefix = 'f'; + break; + } + } + sbuf.append(pcPrefix); + + + sbuf.append(getHex((short) inst.position())); + sbuf.append(": "); + sbuf.append(getHex((byte) bcode)); + sbuf.append(" "); + sbuf.append(INST_NAME[bcode]); + + if (bcode == 0x40) // NPUSHB + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(" "); + sbuf.append('$'); + sbuf.append(getHex(inst.get(pc + 2 + i))); + } + } + if (bcode == 0x41) // NPUSHW + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(' '); + sbuf.append('$'); + sbuf.append(getHex(inst.getShort(pc + 2 + 2*i))); + } + } + else + { + count = getInstructionLength(bcode) - 1; + for (int i = 0; i < count; i++) + { + sbuf.append(" $"); + sbuf.append(getHex(inst.get(pc + 1 + i))); + } + } + + while (sbuf.length() < 30) + sbuf.append(' '); + sbuf.append('|'); + sbuf.append(sp + 1); + sbuf.append("| "); + for (int i = sp; i >= Math.max(0, sp - 5); i = i - 1) + { + if (i < sp) + sbuf.append(" "); + if ((stack[i] >> 16) != 0) + sbuf.append(getHex((short) (stack[i] >> 16))); + sbuf.append(getHex((short) stack[i])); + } + System.out.println(sbuf); + } + + + private static char getNibble(int i, int rightShift) + { + i = (i >> rightShift) & 15; + if (i < 10) + return (char) (i + '0'); + else + return (char) (i + 'a' - 10); + } + + + private static String getHex(byte b) + { + char[] a = new char[2]; + a[0] = getNibble(b, 4); + a[1] = getNibble(b, 0); + return new String(a); + } + + + private static String getHex(short b) + { + char[] a = new char[4]; + a[0] = getNibble(b, 12); + a[1] = getNibble(b, 8); + a[2] = getNibble(b, 4); + a[3] = getNibble(b, 0); + return new String(a); + } + + + /** + * Skips any instructions until the specified opcode has been + * encoutered. + * + * @param inst the current instruction stream. After the call, + * the position of <code>inst</code> is right after the first + * occurence of <code>opcode</code>. + * + * @param opcode1 the opcode for which to look. + * + * @param opcode2 another opcode for which to look. Pass -1 + * if only <code>opcode1</code> would terminate skipping. + * + * @param illegalCode1 an opcode that must not be encountered + * while skipping. Pass -1 if any opcode is acceptable. + * + * @param illegalCode2 another opcode that must not be encountered + * while skipping. Pass -1 to perform no check. + * + * @param handleNestedIfClauses <code>true</code> to handle + * nested <code>IF [ELSE] EIF</code> clauses, <code>false</code> + * to ignore them. From the TrueType specification document, + * one would think that nested if clauses would not be valid, + * but they do appear in some fonts. + * + * @throws IllegalStateException if <code>illegalCode1</code> or + * <code>illegalCode2</code> has been encountered while skipping. + */ + private static void skipAfter(ByteBuffer inst, + int opcode1, int opcode2, + int illegalCode1, int illegalCode2, + boolean handleNestedIfClauses) + { + int pos = inst.position(); + int curOpcode; + int instLen; + int nestingLevel = 0; // increased inside IF [ELSE] EIF sequences + + while (true) + { + curOpcode = inst.get(pos) & 0xff; + instLen = getInstructionLength(curOpcode); + + if (false && TRACE_EXECUTION) + { + for (int i = 0; i < nestingLevel; i++) + System.out.print("--"); + System.out.print("--" + pos + "-" + INST_NAME[curOpcode]); + if (nestingLevel > 0) + System.out.print(", ifNestingLevel=" + nestingLevel); + System.out.println(); + } + + if (curOpcode == 0x40) // NPUSHB + pos += 1 + (inst.get(pos + 1) & 0xff); + else if (curOpcode == 0x41) // NPUSHW + pos += 1 + 2 * (inst.get(pos + 1) & 0xff); + else + pos += instLen; + + if ((nestingLevel == 0) + && ((curOpcode == opcode1) || (curOpcode == opcode2))) + break; + + if (handleNestedIfClauses) + { + if (curOpcode == /* IF */ 0x58) + ++nestingLevel; + else if (curOpcode == /* EIF */ 0x59) + --nestingLevel; + } + + if ((nestingLevel < 0) + || (curOpcode == illegalCode1) + || (curOpcode == illegalCode2)) + throw new IllegalStateException(); + } + + inst.position(pos); + } + + + /** + * Returns the number of bytes that a TrueType instruction occupies. + * + * @param opcode the instruction. + * + * @return the number of bytes occupied by the instructions and its + * operands. For <code>NPUSHB</code> and <code>NPUSHW</code>, where + * the instruction length depends on the first operand byte, the + * result is -1. + */ + private static int getInstructionLength(int opcode) + { + /* NPUSHB, NPUSHW --> see following byte */ + if ((opcode == 0x40) || (opcode == 0x41)) + return -1; + + /* PUSHB[0] .. PUSHB[7] --> 2, 3, 4, 5, 6, 7, 8, 9 */ + if ((opcode >= 0xb0) && (opcode <= 0xb7)) + return opcode - 0xae; + + /* PUSHW[0] .. PUSHW[7] --> 3, 5, 6, 7, 11, 13, 15, 17*/ + if ((opcode >= 0xb8) && (opcode <= 0xbf)) + return 1 + ((opcode - 0xb7) << 1); + + return 1; + } + + + /** + * Executes a single TrueType instruction. This is the core + * routine of the Virtual Machine. + * + * @return <code>true</code> if another instruction shall be + * executed in the same call frame; <code>false</code> if the + * current call frame shall be popped. + */ + private boolean executeInstruction(ByteBuffer inst) + { + if (TRACE_EXECUTION) + dumpInstruction(inst); + + int i, count, e1, e2, e3, e4, x, y; + int bcode = inst.get() & 0xff; + + switch (bcode) + { + case 0x00: // SVTCA[0], Set freedom and proj. Vectors To Coord. Axis [y] + setFreedomVector((short) 0, ONE_214); + setProjectionVector((short) 0, ONE_214); + break; + + case 0x01: // SVTCA[1], Set freedom and proj. Vectors To Coord. Axis [x] + setFreedomVector(ONE_214, (short) 0); + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x02: // SPVTCA[0], Set Projection Vector To Coordinate Axis [y] + setProjectionVector((short) 0, ONE_214); + break; + + case 0x03: // SPVTCA[1], Set Projection Vector To Coordinate Axis [x] + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x0c: // GPV, Get Projection Vector + stack[++sp] = projX; + stack[++sp] = projY; + break; + + case 0x0d: // GPV, Get Freedom Vector + stack[++sp] = freeX; + stack[++sp] = freeY; + break; + + case 0x0F: // ISECT, move point p to the InterSECTION of two lines + sp -= 4; + handleISECT(stack[sp], stack[sp+1], stack[sp+2], + stack[sp+3], stack[sp+4]); + break; + + case 0x10: // SRP0, Set Reference Point 0 + rp0 = stack[sp--]; + break; + + case 0x11: // SRP1, Set Reference Point 1 + rp1 = stack[sp--]; + break; + + case 0x12: // SRP2, Set Reference Point 2 + rp2 = stack[sp--]; + break; + + case 0x13: // SZP0, Set Zone Pointer 0 + zp0 = getZone(stack[sp--]); + break; + + case 0x14: // SZP1, Set Zone Pointer 1 + zp1 = getZone(stack[sp--]); + break; + + case 0x15: // SZP2, Set Zone Pointer 2 + zp2 = getZone(stack[sp--]); + break; + + case 0x16: // SZPS, Set Zone PointerS + zp0 = zp1 = zp2 = getZone(stack[sp--]); + break; + + case 0x17: // SLOOP, Set LOOP variable + loop = stack[sp--]; + break; + + case 0x18: // RTG, Round To Grid + setRoundingMode(Fixed.ONE, 0x48); + break; + + case 0x19: // RTHG, Round To Half Grid + setRoundingMode(Fixed.ONE, 0x68); + break; + + case 0x1a: // SMD, Set Minimum Distance + minimumDistance = stack[sp--]; + break; + + case 0x1B: // ELSE, ELSE clause + skipAfter(inst, + /* look for: EIF, -- */ 0x59, -1, + /* illegal: --, -- */ -1, -1, + /* handle nested if clauses */ true); + break; + + case 0x1C: // JMPR, JuMP Relative + inst.position(inst.position() - 1 + stack[sp--]); + break; + + case 0x1D: // SCVTCI, Set Control Value Table Cut-In + cvtCutIn = stack[sp--]; + break; + + case 0x1F: // SSW, Set Single Width + singleWidthValue = stack[sp--]; + break; + + case 0x20: // DUP, DUPlicate top stack element + e1 = stack[sp]; + stack[++sp] = e1; + break; + + case 0x21: // POP, POP top stack element + sp--; + break; + + case 0x22: // CLEAR, CLEAR the stack + sp = -1; + break; + + case 0x23: // SWAP, SWAP the top two elements on the stack + e1 = stack[sp--]; + e2 = stack[sp]; + stack[sp] = e1; + stack[++sp] = e2; + break; + + case 0x24: // DEPTH, DEPTH of the stack + stack[++sp] = sp + 1; + break; + + case 0x25: // CINDEX, Copy the INDEXed element to the top of the stack + stack[sp] = stack[sp - stack[sp]]; + break; + + case 0x26: // MINDEX, Move the INDEXed element to the top of the stack + i = stack[sp]; + e1 = stack[sp - i]; + System.arraycopy(/* src */ stack, /* srcPos */ sp - i + 1, + /* dest */ stack, /* destPos*/ sp - i, + /* length */ i - 1); + --sp; + stack[sp] = e1; + break; + + case 0x2a: // LOOPCALL, LOOP and CALL function + i = stack[sp--]; + count = stack[sp--]; + e1 = inst.position(); + e2 = sp; + for (int j = 0; j < count; j++) + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2B: // CALL, CALL function + i = stack[sp--]; + e1 = inst.position(); + e2 = sp; + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2C: // FDEF, Function DEFinition + i = stack[sp--]; + fdefBuffer[i] = inst; + fdefEntryPoint[i] = inst.position(); + skipAfter(inst, + /* look for: ENDF */ 0x2d, + /* look for: --- */ -1, + /* illegal: IDEF */ 0x89, + /* illegal: FDEF */ 0x2c, + /* do not handle nested if clauses */ false); + break; + + case 0x2D: // ENDF, END Function definition + /* Pop the current stack frame. */ + return false; + + case 0x2e: // MDAP[0], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ false); + break; + + case 0x2f: // MDAP[1], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ true); + break; + + case 0x39: // IP, Interpolate Point by the last relative stretch + handleIP(); + break; + + case 0x3d: // RTDG, Round To Double Grid + setRoundingMode(Fixed.ONE, 0x08); + roundThreshold = roundThreshold / 64; // period/128 + break; + + case 0x3e: // MIAP[0], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ false); + break; + + case 0x3f: // MIAP[1], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ true); + break; + + case 0x40: // NPUSHB + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0x41: // NPUSHW + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + case 0x42: // WS, Write Store + e1 = stack[sp--]; i = stack[sp--]; + storage[i] = e1; + break; + + case 0x43: // RS, Read Store + stack[sp] = storage[stack[sp]]; + break; + + case 0x44: // WCVTP, Write Control Value Table in Pixel units + e1 = stack[sp--]; + i = stack[sp--]; + if (i < cvt.length) + cvt[i] = e1; + break; + + case 0x45: // RCVT, Read Control Value Table entry + if (stack[sp] < cvt.length) + stack[sp] = cvt[stack[sp]]; + else + stack[sp] = 0; + break; + + case 0x46: // GC[0], Get Coordinate projected onto the projection vector + stack[sp] = getProjection(zp2, stack[sp]); + break; + + case 0x47: // GC[1], Get Coordinate projected onto the projection vector + stack[sp] = getOriginalProjection(zp2, stack[sp]); + break; + + case 0x4B: // MPPEM, Measure Pixels Per EM + stack[++sp] = getPixelsPerEM(); + break; + + case 0x4c: // MPS, Measure Point Size + /* FreeType2 returns pixels per em here, because they think that + * the point size would be irrelevant in a given font program. + * This is extremely surprising, because the appearance of good + * fonts _should_ change with point size. For example, a good + * font should be wider at small point sizes, and the holes + * inside glyphs ("Punzen" in German, I do not know the correct + * English expression) should be larger. Note that this change + * of appearance is dependent on point size, _not_ the + * resolution of the display device. + */ + stack[++sp] = pointSize; + break; + + case 0x4f: // DEBUG, DEBUG call + sp--; + break; + + case 0x50: // LT, Less Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] < e1) ? 1 : 0; + break; + + case 0x51: // LTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] <= e1) ? 1 : 0; + break; + + case 0x52: // GT, Greater Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] > e1) ? 1 : 0; + break; + + case 0x53: // GTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] >= e1) ? 1 : 0; + break; + + case 0x54: // EQ, EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] == e1) ? 1 : 0; + break; + + case 0x55: // NEQ, Not EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] != e1) ? 1 : 0; + break; + + case 0x58: // IF, IF test + if (stack[sp--] == 0) + skipAfter(inst, + /* look for: ELSE */ 0x1B, + /* look for: EIF */ 0x59, + /* illegal: -- */ -1, + /* illegal: -- */ -1, + /* handle nested if clauses */ true); + break; + + case 0x59: // EIF, End IF + // Do nothing. + break; + + case 0x5A: // AND + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) && (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5B: // OR + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) || (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5C: // NOT + stack[sp] = (stack[sp] != 0) ? 0 : 1; + break; + + case 0x5e: // SDB, Set Delta Base in the graphics state + deltaBase = stack[sp--]; + break; + + case 0x5f: // SDS, Set Delta Shift in the graphics state + deltaShift = stack[sp--]; + break; + + case 0x60: // ADD + e1 = stack[sp--]; + stack[sp] += e1; + break; + + case 0x61: // SUB, SUBtract + e1 = stack[sp--]; + stack[sp] -= e1; + break; + + case 0x62: // DIV, DIVide + e1 = stack[sp--]; + stack[sp] = Fixed.div(e1, stack[sp]); + break; + + case 0x63: // MUL, MULtiply + e1 = stack[sp--]; + stack[sp] = Fixed.mul(e1, stack[sp]); + break; + + case 0x64: // ABS, ABSolute value + stack[sp] = Math.abs(stack[sp]); + break; + + case 0x65: // NEG, NEGate + stack[sp] = -stack[sp]; + break; + + case 0x66: // FLOOR + stack[sp] = Fixed.floor(stack[sp]); + break; + + case 0x67: // CEILING + stack[sp] = Fixed.ceil(stack[sp]); + break; + + case 0x68: // ROUND[0] -- round grey distance + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x69: // ROUND[1] -- round black distance + stack[sp] = round(stack[sp], -engineCompensation); + break; + + case 0x6a: // ROUND[2] -- round white distance + stack[sp] = round(stack[sp], engineCompensation); + break; + + case 0x6b: // ROUND[3] -- round distance (not yet defined) + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x6c: // NROUND[0] -- compensate grey distance + stack[sp] = nround(stack[sp], 0); + break; + + case 0x6d: // NROUND[1] -- compensate black distance + stack[sp] = nround(stack[sp], -engineCompensation); + break; + + case 0x6e: // NROUND[2] -- compensate white distance + stack[sp] = nround(stack[sp], engineCompensation); + break; + + case 0x6f: // NROUND[3] -- compensate distance (not yet defined) + stack[sp] = nround(stack[sp], 0); + break; + + case 0x70: // WCVTF, Write Control Value Table in Funits + e1 = stack[sp--]; + cvt[stack[sp--]] = e1 * getPixelsPerEM(); + break; + + case 0x73: // DELTAC1, DELTA exception C1 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 0); + break; + + case 0x74: // DELTAC2, DELTA exception C2 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 16); + break; + + case 0x75: // DELTAC3, DELTA exception C3 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 32); + break; + + case 0x76: // SROUND, Super ROUND + setRoundingMode(Fixed.ONE, stack[sp--]); + break; + + case 0x77: // S45ROUND, Super ROUND 45 degrees + setRoundingMode(/* sqrt(2)/2 */ 0x2d, stack[sp--]); + break; + + case 0x78: // JROT, Jump Relative On True + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 != 0) + inst.position(i); + break; + + case 0x79: // JROF, Jump Relative On False + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 == 0) + inst.position(i); + break; + + case 0x7a: // ROFF, Round OFF + roundPeriod = 0; + break; + + case 0x7c: // RUTG, Round Up To Grid + setRoundingMode(Fixed.ONE, 0x40); + break; + + case 0x7d: // RDTG, Round Down To Grid + setRoundingMode(Fixed.ONE, 0x40); + roundThreshold = 0; + break; + + case 0x7e: // SANGW, Set ANGle Weight (no-op according to TrueType spec) + case 0x7f: // AA, Adjust Angle (no-op according to TrueType spec) + sp--; + break; + + case 0x85: // SCANCTRL, SCAN conversion ConTRoL + e1 = stack[sp--]; + int ppemThreshold = e1 & 255; + scanControl = false; + boolean ppemCondition = (ppemThreshold == 255) + || ((ppemThreshold != 0) && (getPixelsPerEM() > ppemThreshold)); + if (((e1 & (1<<8)) != 0) && ppemCondition) + scanControl = true; + if (((e1 & (1<<9)) != 0) && isRotated()) + scanControl = true; + if (((e1 & (1<<10)) != 0) && isStretched()) + scanControl = true; + if (((e1 & (1<<11)) != 0) && !ppemCondition) + scanControl = false; + if (((e1 & (1<<12)) != 0) && !isRotated()) + scanControl = false; + if (((e1 & (1<<13)) != 0) && !isStretched()) + scanControl = false; + break; + + case 0x88: // GETINFO, GET INFOrmation + e1 = 0; + if ((stack[sp] & 1) != 0) // ask for rasterizer version + e1 |= 35; // "Microsoft Rasterizer version 1.7" (grayscale-capable) + if (((stack[sp] & 2) != 0) && isRotated()) + e1 |= 1 << 8; // bit 8: glyph has been rotated + if (((stack[sp] & 4) != 0) && isStretched()) + e1 |= 1 << 9; // bit 9: glyph has been stretched + if (((stack[sp] & 32) != 0) && antialiased) + e1 |= 1 << 12; // bit 12: antialiasing is active + stack[sp] = e1; + break; + + case 0x8a: // ROLL, ROLL the top three stack elements + e1 = stack[sp - 2]; + stack[sp - 2] = stack[sp - 1]; + stack[sp - 1] = stack[sp]; + stack[sp] = e1; + break; + + case 0x8b: // MAX, MAXimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.max(e1, stack[sp]); + break; + + case 0x8c: // MIN, MINimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.min(e1, stack[sp]); + break; + + case 0x8d: // SCANTYPE + scanType = stack[sp--]; + break; + + case 0x8e: // INSTCTRL, INSTRuction execution ConTRoL + e1 = stack[sp--]; // selector + e2 = stack[sp--]; // value + switch (e1) + { + case 1: + executeGlyphInstructions = (e2 == 0); + break; + + case 2: + ignoreCVTProgram = (e2 != 0); + break; + } + break; + + case 0xb0: // PUSHB[0] + case 0xb1: // PUSHB[1] + case 0xb2: // PUSHB[2] + case 0xb3: // PUSHB[3] + case 0xb4: // PUSHB[4] + case 0xb5: // PUSHB[5] + case 0xb6: // PUSHB[6] + case 0xb7: // PUSHB[7] + count = bcode - 0xb0 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0xb8: // PUSHW[0] + case 0xb9: // PUSHW[1] + case 0xba: // PUSHW[2] + case 0xbb: // PUSHW[3] + case 0xbc: // PUSHW[4] + case 0xbd: // PUSHW[5] + case 0xbe: // PUSHW[6] + case 0xbf: // PUSHW[7] + count = bcode - 0xb8 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + // MIRPxxxx, Move Indirect Relative Point + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + e1 = stack[sp--]; + handleMIRP(bcode, /* point */ e1, /* cvtIndex */ stack[sp--]); + break; + + default: + throw new IllegalStateException(); + } + + return true; + } + + + /** + * Sets the rounding mode. + * + * @param period the grid period in fixed-point notation, such as + * {@link Fixed#ONE} for the <code>SROUND</code> instruction or + * <code>sqrt(2)/2</code> for the <code>S45ROUND</code> instruction. + * + * @param mode a byte whose bits are set according to the TrueType + * specification for SROUND and S45ROUND parameters. + */ + private void setRoundingMode(int period, int mode) + { + /* Set the period. */ + switch ((mode & 0xc0) >> 6) + { + case 0: + roundPeriod = period / 2; + break; + + case 2: + roundPeriod = period * 2; + break; + + default: + roundPeriod = period; + break; + } + + /* Set the phase. */ + switch ((mode & 0x30) >> 4) + { + case 0: + roundPhase = 0; + break; + + case 1: + roundPhase = roundPeriod >> 2; // period/4 + break; + + case 2: + roundPhase = roundPeriod >> 1; // period/2 + break; + + case 3: + roundPhase = (roundPeriod >> 1) + (roundPeriod >> 2); // period * 3/4 + break; + } + + /* Set the threshold. */ + int threshold = mode & 0x0f; + if (threshold == 0) + roundThreshold = roundPeriod - Fixed.ONE; + else + roundThreshold = ((threshold - 4) * roundPeriod) / 8; + } + + + + /** + * Implements the DELTAC instructions. These instructions check + * whether the current number of pixels per em is contained in an + * exception table. If it is, a delta value is determined, and the + * specified entry in the Control Value Table is modified according + * to the delta. + * + * @param pairs the delta table. Because the delta table is on + * the stack, callers usually just want to pass the stack array. + * + * @param offset the offset of the first pair in <code>pairs</code>. + * + * @param numPairs the number of pairs. + * + * @param base 0 for <code>DELTAC1</code>, 16 for <code>DELTAC2</code>, + * or 32 for <code>DELTAC2</code>. + * + * @see <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC1" + * >Apple’s documentation for <code>DELTAC1</code></a>, <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC2" + * ><code>DELTAC2</code></a>, and <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC3" + * ><code>DELTAC3</code></a> + */ + private void deltaC(int[] pairs, int offset, int numPairs, int base) + { + int arg, relativePpem; + int ppemTrigger = getPixelsPerEM() - (deltaBase + base); + int delta, cvtIndex, rightShift; + for (int i = 0; i < numPairs; i++) + { + arg = pairs[offset + 2 * i]; + relativePpem = (arg >> 4) & 15; + if (relativePpem == ppemTrigger) + { + delta = (arg & 15) - 8; + if (delta >= 0) + ++delta; + + rightShift = deltaShift - 6; + if (rightShift > 0) + delta = delta >> rightShift; + else if (rightShift < 0) + delta = delta << (-rightShift); + cvt[pairs[offset + 2 * i + 1]] += delta; + + break; + } + } + } + + + private Zone getZone(int zoneNumber) + { + return (zoneNumber == 0) ? twilightZone : glyphZone; + } + + + /** + * Projects the specified vector along the current projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getProjection(int x, int y) + { + return (int) (((((long) x) * projX + ((long) y) * projY)) >> 14); + } + + + /** + * Projects the specified vector along the current dual projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getDualProjection(int x, int y) + { + return (int) (((((long) x) * dualX + ((long) y) * dualY)) >> 14); + } + + + private int getProjection(Zone zone, int point) + { + return getProjection(zone.getX(point), zone.getY(point)); + } + + + private int getOriginalProjection(Zone zone, int point) + { + return getDualProjection(zone.getOriginalX(point), + zone.getOriginalY(point)); + } + + + private void handleISECT(int a0, int a1, int b0, int b1, int p) + { + System.out.println("FIXME: Unimplemented ISECT " + p); + } + + + private static int muldiv(int a, int b, int c) + { + int s; + s = a; a = Math.abs(a); + s ^= b; b = Math.abs(b); + s ^= c; c = Math.abs(c); + a = (int) ((((long) a) * b + (c>>1)) / c); + return (s < 0) ? -a : a; + } + + + private int getFreeDotProj() + { + int result; + + result = ((((int) projX) * freeX) << 2) + + ((((int) projY) * freeY) << 2); + + /* FIXME: This seems somewhat bogus. Need to contact the + * developers of FreeType. + */ + if (Math.abs(result) < 0x4000000) + result = 0x40000000; + return result; + } + + + private void movePoint(Zone zone, int point, int distance) + { + int freeDotProj = getFreeDotProj(); + int c; + + if (freeX != 0) + { + c = zone.getX(point); + c += muldiv(distance, freeX << 16, freeDotProj); + zone.setX(point, c, /* touch */ true); + } + + if (freeY != 0) + { + c = zone.getY(point); + c += muldiv(distance, freeY << 16, freeDotProj); + zone.setY(point, c, /* touch */ true); + } + + if (TRACE_EXECUTION) + { + System.out.println("point[" + point + "] moved to " + + Fixed.toString(zone.getX(point), + zone.getY(point))); + dumpVectors(); + } + } + + private void dumpVectors() + { + System.out.println(" proj=" + Fixed.toString(projX>>8, projY>>8) + + ", free=" + Fixed.toString(freeX>>8, freeY>>8)); + } + + + private void handleIP() + { + // Implementation taken from FreeType. + int p, org_a, org_b, org_x, cur_a, cur_b, cur_x, distance; + int freeDotProj; + + org_a = getOriginalProjection(zp0, rp1); + cur_a = getProjection(zp0, rp1); + + org_b = getOriginalProjection(zp1, rp2); + cur_b = getProjection(zp1, rp2); + + while (--loop >= 0) + { + p = stack[sp--]; + org_x = getOriginalProjection(zp2, p); + cur_x = getProjection(zp2, p); + + if (((org_a <= org_b) && (org_x <= org_a)) + || ((org_a > org_b) && (org_x >= org_a))) + distance = (cur_a - org_a) + (org_x - cur_x); + else if (((org_a <= org_b) && (org_x >= org_b)) + || ((org_a > org_b) && (org_x < org_b))) + distance = (cur_b - org_b) + (org_x - cur_x); + else + distance = muldiv(cur_b - cur_a, org_x - org_a, org_b - org_a) + + (cur_a - cur_x); + movePoint(zp2, p, distance); + } + loop = 1; + } + + + private void handleMDAP(int point, boolean round) + { + System.out.println("FIXME: Unimplemented MDAP: point " + + point + "/" + zp0); + } + + + private void handleMIAP(int cvtIndex, int point, boolean round) + { + int previousPos, pos; + + previousPos = getProjection(zp0, point); + pos = cvt[cvtIndex]; + + if (round) + { + if (Math.abs(pos - previousPos) > cvtCutIn) + pos = previousPos; + pos = round(pos, /* no engine compensation */ 0); + } + movePoint(zp0, point, pos - previousPos); + rp0 = rp1 = point; + } + + + private void handleMIRP(int bcode, int point, int cvtIndex) + { + System.out.println("FIXME: Unimplemented mirp " + point + ", " + cvtIndex); + } + + + + private int round(int distance, int compensation) + { + int result; + + if (roundPeriod == 0) + return nround(distance, compensation); + + if (distance >= 0) + { + result = distance + compensation - roundPhase + roundThreshold; + result &= -roundPeriod; // truncate to the next lowest periodic value + return Math.max(result, 0) + roundPhase; + } + else + { + result = compensation - roundPhase + roundThreshold - distance; + result &= -roundPeriod; + return Math.max(-result, 0) - roundPhase; + } + } + + + private static int nround(int distance, int compensation) + { + if (distance >= 0) + return Math.max(distance + compensation, 0); + else + return Math.min(distance - compensation, 0); + } + + + /** + * Determines whether the current glyph is rotated. + * + * @return <code>false</code> if the shearing factors for the + * <i>x</i> and <i>y</i> axes are zero; <code>true</code> if they + * are non-zero. + */ + private boolean isRotated() + { + return (shearX != 0) || (shearY != 0); + } + + + /** + * Determines whether the current glyph is stretched. + * + * @return <code>false</code> if the scaling factors for the + * <i>x</i> and <i>y</i> axes are are equal; <code>true</code> if + * they differ. + */ + private boolean isStretched() + { + return scaleX != scaleY; + } + + + /** + * Returns how many pixels there are per EM, in direction of the + * current projection vector. The result is a normal integer, + * not a Fixed. + */ + private int getPixelsPerEM() + { + if (cachedPixelsPerEM == 0) + { + cachedPixelsPerEM = Fixed.intValue(Fixed.vectorLength( + applyCTM_x(projX >> 8, projY >> 8), + applyCTM_y(projX >> 8, projY >> 8))); + } + + return cachedPixelsPerEM; + } + + + private void setProjectionVector(short x, short y) + { + if (PATENTED_HINTING) + { + if ((x != projX) || (y != projY)) + cachedPixelsPerEM = 0; + + projX = x; + projY = y; + } + } + + + private void setFreedomVector(short x, short y) + { + if (PATENTED_HINTING) + { + freeX = x; + freeY = y; + } + } + + + private void setDualVector(short x, short y) + { + if (PATENTED_HINTING) + { + dualX = x; + dualY = y; + } + } + + + private int applyCTM_x(int x, int y) + { + return (int) (((long) scaleX * x + (long) shearX * y) >> 6); + } + + private int applyCTM_y(int x, int y) + { + return (int) (((long) shearY * x + (long) scaleY * y) >> 6); + } + + + private static final String[] INST_NAME = + { + /* 00 */ "SVTCA[0]", "SVTCA[1]", "SPVTCA[0]", "SPVTCA[1]", + /* 04 */ "INST_04", "INST_05", "INST_06", "INST_07", + /* 08 */ "INST_08", "INST_09", "INST_0A", "INST_0B", + /* 0c */ "GPV", "GFV", "INST_0E", "ISECT", + /* 10 */ "SRP0", "SRP1", "SRP2", "SZP0", + /* 14 */ "SZP1", "SZP2", "SZPS", "SLOOP", + /* 18 */ "RTG", "RTHG", "SMD", "ELSE", + /* 1c */ "JMPR", "SCVTCI", "INST_1E", "SSW", + /* 20 */ "DUP", "POP", "CLEAR", "SWAP", + /* 24 */ "DEPTH", "CINDEX", "MINDEX", "INST_27", + /* 28 */ "INST_28", "INST_29", "LOOPCALL", "CALL", + /* 2c */ "FDEF", "ENDF", "MDAP[0]", "MDAP[1]", + /* 30 */ "IUP[0]", "IUP[1]", "SHP[0]", "SHP[1]", + /* 34 */ "INST_34", "INST_35", "INST_36", "INST_37", + /* 38 */ "INST_38", "IP", "INST_3A", "INST_3B", + /* 3c */ "INST_3C", "RTDG", "MIAP[0]", "MIAP[1]", + /* 40 */ "NPUSHB", "NPUSHW", "WS", "RS", + /* 44 */ "WCVTP", "RCVT", "GC[0]", "GC[1]", + /* 48 */ "INST_48", "INST_49", "INST_4A", "MPPEM", + /* 4c */ "MPS", "FLIPON", "FLIPOFF", "DEBUG", + /* 50 */ "LT", "LTEQ", "GT", "GTEQ", + /* 54 */ "EQ", "NEQ", "INST_56", "INST_57", + /* 58 */ "IF", "EIF", "AND", "OR", + /* 5c */ "NOT", "INST_5D", "SDB", "SDS", + /* 60 */ "ADD", "SUB", "DIV", "MUL", + /* 64 */ "ABS", "NEG", "FLOOR", "CEILING", + /* 68 */ "ROUND[0]", "ROUND[1]", "ROUND[2]", "ROUND[3]", + /* 6c */ "NROUND[0]", "NROUND[1]", "NROUND[2]", "NROUND[3]", + /* 70 */ "WCVTF", "INST_71", "INST_72", "DELTAC1", + /* 74 */ "DELTAC2", "DELTAC3", "SROUND", "S45ROUND", + /* 78 */ "JROT", "JROF", "ROFF", "INST_7B", + /* 7c */ "RUTG", "RDTG", "SANGW", "AA", + /* 80 */ "FLIPPT", "FLIPRGON", "FLIPRGOFF", "INST_83", + /* 84 */ "INST_84", "SCANCTRL", "INST_86", "INST_87", + /* 88 */ "GETINFO", "INST_89", "ROLL", "MAX", + /* 8c */ "MIN", "SCANTYPE", "INSTCTRL", "INST_8F", + /* 90 */ "INST_90", "INST_91", "INST_92", "INST_93", + /* 94 */ "INST_94", "INST_95", "INST_96", "INST_97", + /* 98 */ "INST_98", "INST_99", "INST_9A", "INST_9B", + /* 9c */ "INST_9C", "INST_9D", "INST_9E", "INST_9F", + /* a0 */ "INST_A0", "INST_A1", "INST_A2", "INST_A3", + /* a4 */ "INST_A4", "INST_A5", "INST_A6", "INST_A7", + /* a8 */ "INST_A8", "INST_A9", "INST_AA", "INST_AB", + /* ac */ "INST_AC", "INST_AD", "INST_AE", "INST_AF", + /* b0 */ "PUSHB[0]", "PUSHB[1]", "PUSHB[2]", "PUSHB[3]", + /* b4 */ "PUSHB[4]", "PUSHB[5]", "PUSHB[6]", "PUSHB[7]", + /* b8 */ "PUSHW[0]", "PUSHW[1]", "PUSHW[2]", "PUSHW[3]", + /* bc */ "PUSHW[4]", "PUSHW[5]", "PUSHW[6]", "PUSHW[7]", + /* c0 */ "INST_C0", "INST_C1", "INST_C2", "INST_C3", + /* c4 */ "INST_C4", "INST_C5", "INST_C6", "INST_C7", + /* c8 */ "INST_C8", "INST_C9", "INST_CA", "INST_CB", + /* cc */ "INST_CC", "INST_CD", "INST_CE", "INST_CF", + /* d0 */ "INST_D0", "INST_D1", "INST_D2", "INST_D3", + /* d4 */ "INST_D4", "INST_D5", "INST_D6", "INST_D7", + /* d8 */ "INST_D8", "INST_D9", "INST_DA", "INST_DB", + /* dc */ "INST_DC", "INST_DD", "INST_DE", "INST_DF", + /* e0 */ "MIRP00000", "MIRP00001", "MIRP00010", "MIRP00011", + /* e4 */ "MIRP00100", "MIRP00101", "MIRP00110", "MIRP00111", + /* e8 */ "MIRP01000", "MIRP01001", "MIRP01010", "MIRP01011", + /* ec */ "MIRP01100", "MIRP01101", "MIRP01110", "MIRP01111", + /* f0 */ "MIRP10000", "MIRP10001", "MIRP10010", "MIRP10011", + /* f4 */ "MIRP10100", "MIRP10101", "MIRP10110", "MIRP10111", + /* f8 */ "MIRP11000", "MIRP11001", "MIRP11010", "MIRP11011", + /* fc */ "MIRP11100", "MIRP11101", "MIRP11110", "MIRP11111" + }; +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java new file mode 100644 index 000000000..b0559e0e3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java @@ -0,0 +1,291 @@ +/* Zone.java -- A collection of points with some additional information. + 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.font.opentype.truetype; + +import gnu.java.awt.font.FontDelegate; + +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; + + +/** + * A collection of points with some additional information. + */ +public final class Zone +{ + private Point[] points; + private int numPoints; + + public double scaleX, scaleY, shearX, shearY; + + public Zone(int maxNumPoints) + { + points = new Point[maxNumPoints]; + } + + public int getCapacity() + { + return points.length; + } + + + public int getSize() + { + return numPoints; + } + + + public int getX(int point) + { + return getX(point, FontDelegate.FLAG_FITTED); + } + + public int getX(int point, int flags) + { + int x; + if ((flags & FontDelegate.FLAG_FITTED) != 0) + x = points[point].x; + else + x = points[point].scaledX; + return x; + } + + + public void setX(int point, int value, boolean touch) + { + points[point].scaledX = value; + points[point].x = value; + if (touch) + points[point].flags |= Point.FLAG_TOUCHED_X; + } + + + public void setY(int point, int value, boolean touch) + { + points[point].scaledY = value; + points[point].y = value; + if (touch) + points[point].flags |= Point.FLAG_TOUCHED_Y; + } + + public int getY(int point) + { + return getY(point, FontDelegate.FLAG_FITTED); + } + + public int getY(int point, int flags) + { + int y; + if ((flags & FontDelegate.FLAG_FITTED) != 0) + y = points[point].y; + else + y = points[point].scaledY; + return y; + } + + + public int getOriginalX(int point) + { + return points[point].origX; + } + + + public int getOriginalY(int point) + { + return points[point].origY; + } + + + public void setOriginalX(int point, int x) + { + points[point].origX = x; + } + + public void setOriginalY(int point, int y) + { + points[point].origY = y; + } + + public void setNumPoints(int numPoints) + { + for (int i = 0; i < numPoints; i++) + points[i] = new Point(); + this.numPoints = numPoints; + } + + + public boolean isOnCurve(int point) + { + return (points[point].flags & Point.FLAG_ON_CURVE) != 0; + } + + + public void setOnCurve(int point, boolean onCurve) + { + if (onCurve) + points[point].flags |= Point.FLAG_ON_CURVE; + else + points[point].flags &= ~Point.FLAG_ON_CURVE; + } + + + public boolean isContourEnd(int point) + { + return (points[point].flags & Point.FLAG_CONTOUR_END) != 0; + } + + + public void setContourEnd(int point, boolean segEnd) + { + if (segEnd) + points[point].flags |= Point.FLAG_CONTOUR_END; + else + points[point].flags &= ~Point.FLAG_CONTOUR_END; + } + + + + + void transform(double pointSize, AffineTransform deviceTransform, + int unitsPerEm, int preTranslateX, int preTranslateY) + { + double factor; + + factor = pointSize / (double) unitsPerEm; + scaleX = deviceTransform.getScaleX() * factor; + scaleY = deviceTransform.getScaleY() * factor; + shearX = deviceTransform.getShearX() * factor; + shearY = deviceTransform.getShearY() * factor; + + for (int i = 0; i < numPoints; i++) + { + int x = points[i].origX + preTranslateX; + int y = points[i].origY + preTranslateY; + + points[i].scaledX = points[i].x = Fixed.valueOf(scaleX * x + + shearX * y); + points[i].scaledY = points[i].y = Fixed.valueOf(shearY * x + + scaleY * y); + } + } + + + + void combineWithSubGlyph(Zone zone, int numPhantomPoints) + { + int offset = this.numPoints - numPhantomPoints; + int count = zone.numPoints; + System.arraycopy(zone.points, 0, this.points, offset, count); + this.numPoints += count - numPhantomPoints; + } + + + private void dump() + { + for (int i = 0; i < numPoints; i++) + { + System.out.print(" " + i + ": "); + System.out.print(Fixed.toString(points[i].scaledX, points[i].scaledY)); + System.out.print(' '); + System.out.print(Fixed.toString(points[i].origX, points[i].origY)); + System.out.print(' '); + if (isOnCurve(i)) + System.out.print('.'); + else + System.out.print('c'); + if (isContourEnd(i)) + System.out.print('E'); + System.out.println(); + if (isContourEnd(i)) + System.out.println(); + } + } + + + public PathIterator getPathIterator(int type) + { + return new ZonePathIterator(this, type); + } + + + public GeneralPath getPath(int type) + { + GeneralPath p = new GeneralPath(GeneralPath.WIND_NON_ZERO, numPoints); + p.append(getPathIterator(type), /* connect */ false); + return p; + } + + /** + * Returns the number of contours in this outline. + * + * @return the number of contours in this outline + */ + public int getNumContours() + { + int num = 0; + for (int i = 0; i < numPoints; i++) + { + if (isContourEnd(i)) + num++; + } + return num; + } + + public int getContourEnd(int n) + { + int idx = -1; + int num = 0; + for (int i = 0; i < numPoints; i++) + { + if (isContourEnd(i)) + { + idx = i; + if (num == n) + break; + num++; + } + } + return idx; + } + + public Point[] getPoints() + { + return points; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java new file mode 100644 index 000000000..f4534f36f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java @@ -0,0 +1,393 @@ +/* ZonePathIterator.java -- A PathIterator over glyph zones. + 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.font.opentype.truetype; + +import java.awt.geom.PathIterator; + + +/** + * A PathIterator that enumerates the non-phantom points in a zone. + * + * <p><b>Lack of thread safety:</b> Instances of this class are + * <i>not</i> safe to access from multiple concurrent threads. + * + * @see Zone + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class ZonePathIterator + implements PathIterator +{ + /** + * If <code>state</code> has this value, <code>currentSegment</code> + * will emit a <code>SEG_LINETO</code> or <code>SEG_QUADTO</code> segment + * to the current point. For a discussion of subtleties of on-curve + * and off-curve points, please refer to the documentation for + * {@link #getSegment}. + */ + private static final int EMIT_SEGMENT = 0; + + + /** + * If <code>state</code> has this value, <code>currentSegment</code> + * will emit a <code>SEG_CLOSE</code> in order to close the sub-path + * for the current contour. + */ + private static final int EMIT_CLOSE = 1; + + + /** + * If <code>state</code> has this value, <code>currentSegment</code> + * will emit a <code>SEG_MOVETO</code> segment to the first point in + * the current contour. If the first point is off-curve, a suitable + * on-curve point is calculated. + * + * @see #getStartSegment + */ + private static final int EMIT_MOVETO = 2; + + + /** + * The state of the iterator, which is one of + * <code>EMIT_SEGMENT</code>, <code>EMIT_CLOSE</code>, or + * <code>EMIT_MOVETO</code>. + */ + private int state; + + + + /** + * The zone whose segments are enumerated by this iterator. + */ + private Zone zone; + + + /** + * The total number of points in the zone, not including the four + * phantom points at its end. + */ + private int numPoints; + + + /** + * The number of the current point. + */ + private int curPoint; + + + /** + * The number of the first point in the current contour. + */ + private int contourStart; + + + private int type; + + /** + * Constructs a ZonePathIterator for the specified zone. + * + * @param zone the zone whose segments will be enumerated + * by this iterator. + */ + ZonePathIterator(Zone zone, int t) + { + this.zone = zone; + type = t; + numPoints = zone.getSize() - /* four phantom points */ 4; + + // The first segment that needs to be emitted is a SEG_MOVETO. + state = EMIT_MOVETO; + } + + + /** + * Returns the winding rule. TrueType glyphs always use the non-zero + * winding rule, so this method will always return {@link + * PathIterator#WIND_NON_ZERO}. + */ + public int getWindingRule() + { + return PathIterator.WIND_NON_ZERO; + } + + + + public boolean isDone() + { + return (state != EMIT_CLOSE) && (curPoint >= numPoints); + } + + + public void next() + { + boolean onCurve; + + /* If the current point is the end of a segment, and no SEG_CLOSE + * has been emitted yet, this will be the next segment. + */ + if (zone.isContourEnd(curPoint) && (state != EMIT_CLOSE)) + { + state = EMIT_CLOSE; + return; + } + + /* If the previously emitted segment was a SEG_CLOSE, we are now + * at the beginning of a new contour. + */ + if (state == EMIT_CLOSE) + { + contourStart = ++curPoint; + state = EMIT_MOVETO; + return; + } + + onCurve = zone.isOnCurve(curPoint); + + /* If the last segment was a moveto, and the current point + * (which is the first point in the contour) is off-curve, + * we need to emit a quadto segment for the first point. + */ + if ((state == EMIT_MOVETO) && !onCurve) + { + state = EMIT_SEGMENT; + return; + } + + + curPoint++; + + /* If the last point has been off-curve, and the now current + * point is on-curve, the last segment was a quadto that + * had the now current point at its end. In this case, we can + * skip a segment. + */ + if (!onCurve && zone.isOnCurve(curPoint)) + { + /* But if the skipped point is the end of a contour, we must not + * skip the SEG_CLOSE. An example where this matters is the 'o' + * glyph in the Helvetica font face that comes with MacOS X + * 10.1.5. + */ + if (zone.isContourEnd(curPoint)) + { + state = EMIT_CLOSE; + return; + } + + curPoint++; + } + + state = EMIT_SEGMENT; + } + + + /** + * Determines the successor of the current point in the current + * contour. The successor of the last point in a contour is the + * start of that contour. + * + * @return the number of the point that follows the current point in + * the same contour. + */ + private int getSuccessor(int p) + { + if (zone.isContourEnd(p)) + return contourStart; + else + return p + 1; + } + + + + /** + * Retrieves the current path segment using single-precision + * coordinate values. + */ + public int currentSegment(float[] coords) + { + switch (state) + { + case EMIT_CLOSE: + return PathIterator.SEG_CLOSE; + + case EMIT_MOVETO: + return getStartSegment(curPoint, coords); + + default: + return getSegment(curPoint, coords); + } + } + + + /** + * A helper array that is used by {@link + * #currentSegment(double[])}. + */ + float[] floats; + + + /** + * Retrieves the current path segment using double-precision + * coordinate values. + */ + public int currentSegment(double[] coords) + { + if (floats == null) + floats = new float[6]; + int result; + + result = currentSegment(floats); + for (int i = 0; i < 6; i++) + coords[i] = floats[i]; + return result; + } + + + /** + * Returns the segment for the specified point. + * + * <p><img src="doc-files/ZonePathIterator-1.png" width="426" + * height="194" alt="An example curve" /></p> + * + * <p>If <code>cur</code> is an on-curve point, the returned segment + * is a straight line to <code>cur</code>. In the illustration, this + * would be the case for <code>cur = 4</code>.</p> + * + * <p>If <code>cur</code> is an off-curve point, and + * <code>cur</code>’s successor <code>succ</code> is also + * off-curve, the returned segment is a quadratic Bézier + * spline whose control point is <code>cur</code>, and whose end + * point is located at the middle of the line connecting + * <code>cur</code> and <code>succ</code>. In the illustration, + * this would be the case for <code>cur = 5</code>.</p> + * + * <p>If <code>cur</code> is an off-curve point, and + * <code>cur</code>’s successor <code>succ</code> is + * on-curve, the returned segment is a quadratic Bézier + * spline whose control point is <code>cur</code>, and whose end + * point is <code>succ</code>. In the illustration, this would + * be the case for <code>cur = 6</code>.</p> + * + * @return either <code>PathIterator.SEG_LINETO</code> or + * <code>PathIterator.SEG_QUADTO</code>. + */ + private int getSegment(int cur, float[] coords) + { + int curX, curY; + int succ, succX, succY; + + curX = zone.getX(cur, type); + curY = zone.getY(cur, type); + coords[0] = Fixed.floatValue(curX); + coords[1] = Fixed.floatValue(curY); + + if (zone.isOnCurve(cur)) + return PathIterator.SEG_LINETO; + + succ = getSuccessor(cur); + succX = zone.getX(succ, type); + succY = zone.getY(succ, type); + + if (zone.isOnCurve(succ)) + { + coords[2] = Fixed.floatValue(succX); + coords[3] = Fixed.floatValue(succY); + } + else + { + coords[2] = Fixed.floatValue((curX + succX) / 2); + coords[3] = Fixed.floatValue((curY + succY) / 2); + } + return PathIterator.SEG_QUADTO; + } + + + /** + * Returns the start segment for the contour which starts + * at the specified point. + * + * <p>If the contour starts with an on-curve point, the returned + * segment is a <code>SEG_MOVETO</code> to that point.</p> + * + * <p>If the contour starts with an off-curve point, and the contour + * ends with an on-curve point, the returned segment is a + * <code>SEG_MOVETO</code> to the end point.</p> + * + * <p>If the contour starts with an off-curve point, and the contour + * also ends with an off-curve point, the returned segment is a + * <code>SEG_MOVETO</code> to the location at the middle between the + * start and end points of the contour.</p> + * + * @return <code>PathIterator.SEG_MOVETO</code>. + */ + private int getStartSegment(int contourStart, float[] coords) + { + int x, y; + + if (zone.isOnCurve(contourStart)) + { + x = zone.getX(contourStart, type); + y = zone.getY(contourStart, type); + } + else + { + /* Find the last point of the current contour. */ + int contourEnd = contourStart; + while (!zone.isContourEnd(contourEnd)) + ++contourEnd; + + if (zone.isOnCurve(contourEnd)) + { + /* An example is the 'o' glyph of the Helvetica which comes + * with Apple MacOS X 10.1.5. + */ + x = zone.getX(contourEnd, type); + y = zone.getY(contourEnd, type); + } + else + { + x = (zone.getX(contourStart, type) + zone.getX(contourEnd, type)) / 2; + y = (zone.getY(contourStart, type) + zone.getY(contourEnd, type)) / 2; + } + } + + coords[0] = Fixed.floatValue(x); + coords[1] = Fixed.floatValue(y); + return PathIterator.SEG_MOVETO; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia Binary files differnew file mode 100644 index 000000000..b715ea02c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png Binary files differnew file mode 100644 index 000000000..81d09d839 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png diff --git a/libjava/classpath/gnu/java/awt/image/AsyncImage.java b/libjava/classpath/gnu/java/awt/image/AsyncImage.java new file mode 100644 index 000000000..4fa33740e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/image/AsyncImage.java @@ -0,0 +1,300 @@ +/* AsyncImage.java -- Loads images asynchronously + Copyright (C) 2008 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.image; + + +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.util.HashSet; +import java.util.Iterator; + +/** + * Supports asynchronous loading of images. + */ +public class AsyncImage + extends Image +{ + + /** + * The image source for AsyncImages. + */ + private class AsyncImageSource + implements ImageProducer + { + /** + * The real image source, if already present, or <code>null</code> + * otherwise. + */ + private ImageProducer realSource; + + public void addConsumer(ImageConsumer ic) + { + startProduction(ic); + } + + public boolean isConsumer(ImageConsumer ic) + { + return false; + } + + public void removeConsumer(ImageConsumer ic) + { + // Nothing to do here. + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + startProduction(ic); + } + + public void startProduction(ImageConsumer ic) + { + ImageProducer ip = getRealSource(); + if (ip == null) + { + ic.setDimensions(1, 1); + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } + else + { + ip.startProduction(ic); + } + } + + /** + * Returns the real image source, if already present. Otherwise, this + * returns <code>null</code>. + * + * @return the real image source, or <code>null</code> if not present + */ + private ImageProducer getRealSource() + { + synchronized (AsyncImage.this) + { + ImageProducer source = realSource; + if (source == null) + { + Image ri = realImage; + if (ri != null) + { + realSource = source = ri.getSource(); + } + } + return source; + } + } + } + + /** + * The real image. This is null as long as the image is not complete. + */ + private volatile Image realImage; + + /** + * The image observers. + * + * This is package private to avoid accessor methods. + */ + HashSet<ImageObserver> observers; + + private volatile boolean complete = false; + + /** + * Creates a new AsyncImage. + */ + AsyncImage() + { + observers = new HashSet<ImageObserver>(); + } + + public void flush() + { + // Nothing to do here. + } + + public Graphics getGraphics() + { + Image r = realImage; + Graphics g = null; + if (r != null) + g = r.getGraphics(); // Should we return some dummy graphics instead? + return g; + } + + public boolean isComplete() { + return complete; + } + + public int getHeight(ImageObserver observer) + { + addObserver(observer); + int height = -1; + waitForImage(observer); + Image r = realImage; + if (r != null) + height = r.getHeight(observer); + return height; + } + + public Object getProperty(String name, ImageObserver observer) + { + addObserver(observer); + Image r = realImage; + Object prop = null; + if (r != null) + prop = r.getProperty(name, observer); + return prop; + } + + public ImageProducer getSource() + { + return new AsyncImageSource(); + } + + public int getWidth(ImageObserver observer) + { + addObserver(observer); + int width = -1; + waitForImage(observer); + Image r = realImage; + if (r != null) + width = r.getWidth(observer); + return width; + } + + public void addObserver(ImageObserver obs) + { + if (obs != null) + { + synchronized (this) + { + // This field gets null when image loading is complete and we don't + // need to store any more observers. + HashSet<ImageObserver> observs = observers; + if (observs != null) + { + observs.add(obs); + } + } + } + } + + public boolean prepareImage(int w, int h, ImageObserver obs) + { + addObserver(obs); + return realImage != null; + } + + public int checkImage(int w, int h, ImageObserver obs) + { + addObserver(obs); + int flags = 0; + if (realImage != null) + flags = ImageObserver.ALLBITS | ImageObserver.WIDTH + | ImageObserver.HEIGHT | ImageObserver.PROPERTIES; + return flags; + } + + public Image getRealImage() + { + return realImage; + } + + public void setRealImage(Image im) + { + realImage = im; + int status = ImageObserver.HEIGHT | ImageObserver.WIDTH; + notifyObservers(status, 0, 0, im.getWidth(null), im.getHeight(null)); + } + + public void notifyObservers(int status, int x, int y, int w, int h) + { + synchronized (this) + { + HashSet observs = observers; + if (observs != null) + { + Iterator i = observs.iterator(); + while (i.hasNext()) + { + ImageObserver obs = (ImageObserver) i.next(); + boolean complete = obs.imageUpdate(this, status, x, y, realImage.getWidth(obs), realImage.getHeight(obs)); + if (complete) // Remove completed observers. + i.remove(); + } + } + if ((status & ImageObserver.ALLBITS) != 0) + { + complete = true; + notifyAll(); + } + } + } + + /** + * Waits for the image to be loaded completely, if the image observer + * is <code>null</code>. Otherwise this is not necessary, because the + * image observer can be notified about later completion. + * + * @param observer the image observer + */ + public void waitForImage(ImageObserver observer) + { + if (!complete && observer == null) + { + synchronized (this) + { + while (! complete) + { + try + { + wait(); + } + catch (InterruptedException ex) + { + Thread.currentThread().interrupt(); + } + } + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/image/ImageConverter.java b/libjava/classpath/gnu/java/awt/image/ImageConverter.java new file mode 100644 index 000000000..f9c6268c3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/image/ImageConverter.java @@ -0,0 +1,528 @@ +/* ImageConverter.java -- Loads images asynchronously + Copyright (C) 2008 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.image; + +import gnu.java.awt.image.AsyncImage; + +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.util.Hashtable; + +/** + * Convert an Image to a BufferedImage. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class ImageConverter implements ImageConsumer +{ + + public static final String IMAGE_TRANSPARENCY_PROPERTY = + "gnu.awt.image.transparency"; + + public static final String IMAGE_PROPERTIES_PROPERTY = + "gnu.awt.image.properties"; + + private AsyncImage image; + private BufferedImage bImage; + private Hashtable imageProperties; + private int width, height; + private ColorModel colorModel; + private ColorModel targetColorModel; + + public ImageConverter() + { + width = 0; + height = 0; + image = new AsyncImage(); + } + + public void setDimensions(int w, int h) + { + width = w; + height = h; + } + + public void setProperties(Hashtable props) + { + // Ignore for now. + } + + public void setColorModel(ColorModel model) + { + colorModel = model; + } + + public void setHints(int flags) + { + // Ignore for now. + } + + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int offset, int scansize) + { + model = setupColorModel(model); + + if (bImage == null) + { + createImage(); + } + + Integer t = (Integer) imageProperties.get("gnu.awt.image.transparency"); + int transparency = t.intValue(); + + if(targetColorModel.equals(model)) + { + transparency = transferPixels(x, y, w, h, model, pixels, offset, + scansize, transparency); + } + else if (model instanceof IndexColorModel + && targetColorModel.equals(ColorModel.getRGBdefault())) + { + transparency = convertIndexColorModelToSRGB(x, y, w, h, + (IndexColorModel) model, + pixels, offset, scansize, + transparency); + } + else + { + transparency = convertPixels(x, y, w, h, model, pixels, offset, + scansize, transparency); + } + + imageProperties.put("gnu.awt.image.transparency", + Integer.valueOf(transparency)); + } + + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int offset, int scansize) + { + model = setupColorModel(model); + if (bImage == null) + { + createImage(); + } + + Integer t = (Integer) imageProperties.get(IMAGE_TRANSPARENCY_PROPERTY); + int transparency= t.intValue(); + + if (targetColorModel.equals(model)) + { + transparency = transferPixels(x, y, w, h, model, pixels, offset, + scansize, transparency); + } + else if (model instanceof IndexColorModel + && targetColorModel.equals(ColorModel.getRGBdefault())) + { + transparency = convertIndexColorModelToSRGB(x, y, w, h, + (IndexColorModel) model, + pixels, offset, scansize, + transparency); + } + else + { + transparency = convertPixels(x, y, w, h, model, pixels, offset, + scansize, transparency); + } + + imageProperties.put(IMAGE_TRANSPARENCY_PROPERTY, + Integer.valueOf(transparency)); + + } + + /** + * Initialize the color model for this setPixels run: <br/> + * 1. if no color model was given use the hinted color model <br/> + * 2. if no color model was given and non was hinted use the default sRGB color model. <br/> + * Also:<br/> + * If no target color model was set use the color model of the given pixels. + * @param model + * @return + */ + private ColorModel setupColorModel(ColorModel model) + { + // If the given color model is null use the previously hinted color model. + if (model == null) + model = colorModel; + + // If no color model was given or hinted use default sRGB. + if (model == null) + model = ColorModel.getRGBdefault(); + + // If no specific color model was requested for the target use the current + // pixels model. + if (targetColorModel == null) + targetColorModel = model; + targetColorModel = ColorModel.getRGBdefault(); + return model; + } + + /** + * Creates the image instance into which the pixel data is converted. + */ + private void createImage() + { + if (imageProperties == null) + { + imageProperties = new Hashtable(); + } + + imageProperties.put(IMAGE_TRANSPARENCY_PROPERTY, + Integer.valueOf(Transparency.OPAQUE)); + imageProperties.put(IMAGE_PROPERTIES_PROPERTY, imageProperties); + + // For the sRGB case let the GraphicsEnvironment create an image for us. + if (ColorModel.getRGBdefault().equals(targetColorModel)) + { + bImage = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration() + .createCompatibleImage(width, height, Transparency.TRANSLUCENT); + } + else + { + WritableRaster raster = + targetColorModel.createCompatibleWritableRaster(width, height); + bImage = new BufferedImage(targetColorModel, raster, false, + imageProperties); + } + image.setRealImage(bImage); + return; + } + + /** + * Transfers pixels into a raster of the same color model. + * + * @param x the X coordinate of the source pixel rectangle + * @param y the Y coordinate of the source pixel rectangle + * @param w the width of the source pixel rectangle + * @param h the height of the source pixel rectangle + * @param model the color model of the source pixels + * @param pixels the pixel data + * @param offset the offset in the pixel array + * @param scansize the scanline size + * @param transparency the assumed transparency + * + * @return the determined transparency + */ + private int transferPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int offset, int scansize, + int transparency) + { + // If we have the same color model, then we can simply drop + // the pixel value into the target raster. + bImage.getRaster().setDataElements(x, y, w, h, pixels); + + for (int yy = 0; yy < h; yy++) + { + for (int xx = 0; xx < w; xx++) + { + int pixel = 0xFF & pixels[yy * scansize + xx + offset]; + int alpha = model.getAlpha(pixel); + transparency = updateTransparency(alpha, transparency); + } + } + return transparency; + } + + /** + * Transfers pixels into a raster of the same color model. + * + * @param x the X coordinate of the source pixel rectangle + * @param y the Y coordinate of the source pixel rectangle + * @param w the width of the source pixel rectangle + * @param h the height of the source pixel rectangle + * @param model the color model of the source pixels + * @param pixels the pixel data + * @param offset the offset in the pixel array + * @param scansize the scanline size + * @param transparency the assumed transparency + * + * @return the determined transparency + */ + private int transferPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int offset, int scansize, + int transparency) + { + // If we have the same color model, then we can simply drop + // the pixel value into the target raster. + bImage.getRaster().setDataElements(x, y, w, h, pixels); + + for (int yy = 0; yy < h; yy++) + { + for (int xx = 0; xx < w; xx++) + { + int pixel = pixels[yy * scansize + xx + offset]; + int alpha = model.getAlpha(pixel); + transparency = updateTransparency(alpha, transparency); + } + } + return transparency; + } + + /** + * Converts pixel from one color model to another, and stores them in the + * target image. + * + * @param x the X coordinate of the source pixel rectangle + * @param y the Y coordinate of the source pixel rectangle + * @param w the width of the source pixel rectangle + * @param h the height of the source pixel rectangle + * @param model the color model of the source pixels + * @param pixels the pixel data + * @param offset the offset in the pixel array + * @param scansize the scanline size + * @param transparency the assumed transparency + * + * @return the determined transparency + */ + private int convertPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int offset, int scansize, + int transparency) + { + // If the color models are not the same, we must convert the + // pixel values from one model to the other. + Object dataEl = null; + // Convert pixels to the destination color model. + for (int yy = 0; yy < h; yy++) + { + for (int xx = 0; xx < w; xx++) + { + int pixel = 0xFF & pixels[yy * scansize + xx + offset]; + int rgb = model.getRGB(pixel); + int alpha = model.getAlpha(pixel); + transparency = updateTransparency(alpha, transparency); + dataEl = targetColorModel.getDataElements(rgb, dataEl); + bImage.getRaster().setDataElements(x + xx, y + yy, dataEl); + } + } + return transparency; + } + + /** + * Converts pixel from one color model to another, and stores them in the + * target image. + * + * @param x the X coordinate of the source pixel rectangle + * @param y the Y coordinate of the source pixel rectangle + * @param w the width of the source pixel rectangle + * @param h the height of the source pixel rectangle + * @param model the color model of the source pixels + * @param pixels the pixel data + * @param offset the offset in the pixel array + * @param scansize the scanline size + * @param transparency the assumed transparency + * + * @return the determined transparency + */ + private int convertPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int offset, int scansize, + int transparency) + { + // If the color models are not the same, we must convert the + // pixel values from one model to the other. + Object dataEl = null; + // Convert pixels to the destination color model. + for (int yy = 0; yy < h; yy++) + { + for (int xx = 0; xx < w; xx++) + { + int pixel = pixels[yy * scansize + xx + offset]; + int rgb = model.getRGB(pixel); + int alpha = model.getAlpha(pixel); + transparency = updateTransparency(alpha, transparency); + dataEl = targetColorModel.getDataElements(rgb, dataEl); + bImage.getRaster().setDataElements(x + xx, y + yy, dataEl); + } + } + return transparency; + } + + /** + * Converts pixels from an index color model to the target image. + * + * @param x the X coordinate of the source pixel rectangle + * @param y the Y coordinate of the source pixel rectangle + * @param w the width of the source pixel rectangle + * @param h the height of the source pixel rectangle + * @param model the color model of the source pixels + * @param pixels the pixel data + * @param offset the offset in the pixel array + * @param scansize the scanline size + * @param transparency the assumed transparency + * + * @return the determined transparency + */ + private int convertIndexColorModelToSRGB(int x, int y, int w, int h, + IndexColorModel model, + byte[] pixels, int offset, + int scansize, int transparency) + { + + int mapSize = model.getMapSize(); + int[] colorMap = new int[mapSize]; + for(int i=0; i < mapSize; i++) + { + colorMap[i] = model.getRGB(i); + } + + WritableRaster raster = bImage.getRaster(); + SinglePixelPackedSampleModel sampleMode = + (SinglePixelPackedSampleModel) raster.getSampleModel(); + DataBuffer dataBuffer = (DataBuffer) raster.getDataBuffer(); + + int rasterOffset = sampleMode.getOffset(x,y)+dataBuffer.getOffset(); + int rasterScanline = sampleMode.getScanlineStride(); + + for (int yy = 0; yy < h; yy++) + { + int xoffset = offset; + for (int xx = 0; xx < w; xx++) + { + int argb = colorMap[(pixels[xoffset++] & 0xFF)]; + dataBuffer.setElem(rasterOffset+xx, argb); + int alpha = (argb >>> 24); + transparency = updateTransparency(alpha, transparency); + } + offset += scansize; + rasterOffset += rasterScanline; + } + + return transparency; + } + + /** + * Converts pixels from an index color model to the target image. + * + * @param x the X coordinate of the source pixel rectangle + * @param y the Y coordinate of the source pixel rectangle + * @param w the width of the source pixel rectangle + * @param h the height of the source pixel rectangle + * @param model the color model of the source pixels + * @param pixels the pixel data + * @param offset the offset in the pixel array + * @param scansize the scanline size + * @param transparency the assumed transparency + * + * @return the determined transparency + */ + private int convertIndexColorModelToSRGB(int x, int y, int w, int h, + IndexColorModel model, int[] pixels, + int offset, int scansize, + int transparency) + { + int mapSize = model.getMapSize(); + int[] colorMap = new int[mapSize]; + for(int i=0; i < mapSize; i++) + { + colorMap[i] = model.getRGB(i); + } + + WritableRaster raster = bImage.getRaster(); + SinglePixelPackedSampleModel sampleMode = + (SinglePixelPackedSampleModel) raster.getSampleModel(); + DataBuffer dataBuffer = (DataBuffer)raster.getDataBuffer(); + + int rasterOffset = sampleMode.getOffset(x, y) + dataBuffer.getOffset(); + int rasterScanline = sampleMode.getScanlineStride(); + + for (int yy = 0; yy < h; yy++) + { + int xoffset = offset; + for (int xx = 0; xx < w; xx++) + { + int argb = colorMap[pixels[xoffset++]]; + dataBuffer.setElem(rasterOffset + xx, argb); + int alpha = (argb >>> 24); + transparency = updateTransparency(alpha, transparency); + } + offset += scansize; + rasterOffset += rasterScanline; + } + + return transparency; + } + + /** + * Updates the transparency information according to the alpha pixel value. + * + * @param alpha the alpha pixel value + * @param transparency the old transparency + * + * @return the updated transparency + */ + private int updateTransparency(int alpha, int transparency) + { + if (alpha != 0xFF) + { + if (alpha == 0x00 && transparency <= Transparency.BITMASK) + { + transparency = Transparency.BITMASK; + } + else if (transparency < Transparency.TRANSLUCENT) + { + transparency = Transparency.TRANSLUCENT; + } + } + return transparency; + } + + public void imageComplete(int status) + { + image.notifyObservers(ImageObserver.ALLBITS, 0, 0, width, height); + } + + public void setTargetColorModel(ColorModel model) + { + targetColorModel = model; + } + + public Image getImage() + { + return image; + } +} diff --git a/libjava/classpath/gnu/java/awt/image/ImageDecoder.java b/libjava/classpath/gnu/java/awt/image/ImageDecoder.java new file mode 100644 index 000000000..a572ea33b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/image/ImageDecoder.java @@ -0,0 +1,188 @@ +/* ImageDecoder.java -- + Copyright (C) 1999, 2000, 2004 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.image; + +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.io.ByteArrayInputStream; +import java.io.DataInput; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Vector; + +public abstract class ImageDecoder implements ImageProducer +{ + Vector consumers = new Vector (); + String filename; + URL url; + byte[] data; + int offset; + int length; + InputStream input; + DataInput datainput; + + static + { + // FIXME: there was some broken code here that looked like + // it wanted to rely on this property. I don't have any idea + // what it was intended to do. + // String endian = System.getProperties ().getProperty ("gnu.cpu.endian"); + } + + public ImageDecoder (String filename) + { + this.filename = filename; + } + + public ImageDecoder (URL url) + { + this.url = url; + } + + public ImageDecoder (InputStream is) + { + this.input = is; + } + + public ImageDecoder (DataInput datainput) + { + this.datainput = datainput; + } + + public ImageDecoder (byte[] imagedata, int imageoffset, int imagelength) + { + data = imagedata; + offset = imageoffset; + length = imagelength; + } + + public void addConsumer (ImageConsumer ic) + { + consumers.addElement (ic); + } + + public boolean isConsumer (ImageConsumer ic) + { + return consumers.contains (ic); + } + + public void removeConsumer (ImageConsumer ic) + { + consumers.removeElement (ic); + } + + public void startProduction (ImageConsumer ic) + { + if (!isConsumer(ic)) + addConsumer(ic); + + Vector list = (Vector) consumers.clone (); + try + { + // Create the input stream here rather than in the + // ImageDecoder constructors so that exceptions cause + // imageComplete to be called with an appropriate error + // status. + if (input == null) + { + try + { + if (url != null) + input = url.openStream(); + else if (datainput != null) + input = new DataInputStreamWrapper(datainput); + else + { + if (filename != null) + input = new FileInputStream (filename); + else + input = new ByteArrayInputStream (data, offset, length); + } + produce (list, input); + } + finally + { + input = null; + } + } + else + { + produce (list, input); + } + } + catch (Exception e) + { + for (int i = 0; i < list.size (); i++) + { + ImageConsumer ic2 = (ImageConsumer) list.elementAt (i); + ic2.imageComplete (ImageConsumer.IMAGEERROR); + } + } + } + + public void requestTopDownLeftRightResend (ImageConsumer ic) + { + } + + public abstract void produce (Vector v, InputStream is) throws IOException; + + private static class DataInputStreamWrapper extends InputStream + { + private final DataInput datainput; + + DataInputStreamWrapper(DataInput datainput) + { + this.datainput = datainput; + } + + public int read() throws IOException + { + try + { + return datainput.readByte() & 0xFF; + } + catch (EOFException eofe) + { + return -1; + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/image/XBMDecoder.java b/libjava/classpath/gnu/java/awt/image/XBMDecoder.java new file mode 100644 index 000000000..35a3568b8 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/image/XBMDecoder.java @@ -0,0 +1,155 @@ +/* XBMDecoder.java -- Decodes X-bitmaps + Copyright (C) 1999, 2004 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.image; + +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.StringTokenizer; +import java.util.Vector; + +public class XBMDecoder extends ImageDecoder +{ + BufferedReader reader; + static final ColorModel cm = ColorModel.getRGBdefault (); + static final int black = 0xff000000; + static final int transparent = 0x00000000; + static final int masktable[] = { 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 }; + + public XBMDecoder (String filename) + { + super (filename); + } + + public XBMDecoder (URL url) + { + super (url); + } + + public void produce (Vector v, InputStream is) throws IOException + { + reader = new BufferedReader (new InputStreamReader (is)); + int width = -1, height = -1; + + for (int i = 0; i < 2; i++) + { + String line = reader.readLine (); + StringTokenizer st = new StringTokenizer (line); + + st.nextToken (); // #define + st.nextToken (); // name_[width|height] + if (i == 0) + width = Integer.parseInt (st.nextToken (), 10); + else + height = Integer.parseInt (st.nextToken (), 10); + } + + for (int i = 0; i < v.size (); i++) + { + ImageConsumer ic = (ImageConsumer) v.elementAt (i); + + ic.setDimensions (width, height); + ic.setColorModel (cm); + ic.setHints (ImageConsumer.COMPLETESCANLINES + | ImageConsumer.SINGLEFRAME + | ImageConsumer.SINGLEPASS + | ImageConsumer.TOPDOWNLEFTRIGHT); + } + + /* skip to the byte array */ + while (reader.read () != '{') { } + + /* loop through each scanline */ + for (int line = 0; line < height; line++) + { + int scanline[] = getScanline (reader, width); + + for (int i = 0; i < v.size (); i++) + { + ImageConsumer ic = (ImageConsumer) v.elementAt (i); + ic.setPixels (0, 0 + line, width, 1, cm, scanline, 0, width); + } + } + + /* tell each ImageConsumer that we're finished */ + for (int i = 0; i < v.size (); i++) + { + ImageConsumer ic = (ImageConsumer) v.elementAt (i); + ic.imageComplete (ImageConsumer.STATICIMAGEDONE); + } + } + + public static int[] getScanline (Reader in, int len) throws IOException + { + char byteStr[] = new char[2]; + int scanline[] = new int[len]; + int x = 0; + + while (x < len) + { + int ch = in.read (); + if (ch == '0') + { + in.read (); // 'x' + + byteStr[0] = (char) in.read (); + byteStr[1] = (char) in.read (); + + int byteVal = Integer.parseInt (new String (byteStr), 16); + + for (int i = 0; i < 8; i++, x++) + { + if (x == len) // condition occurs if bitmap is padded + return scanline; + + scanline[x] = ((byteVal & masktable[i]) != 0) ? + black : transparent; + } + } + } + + return scanline; + } +} diff --git a/libjava/classpath/gnu/java/awt/image/package.html b/libjava/classpath/gnu/java/awt/image/package.html new file mode 100644 index 000000000..8823367ea --- /dev/null +++ b/libjava/classpath/gnu/java/awt/image/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.awt.image package. + Copyright (C) 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.awt.image</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java new file mode 100644 index 000000000..1334866f2 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java @@ -0,0 +1,2094 @@ +/* AbstractGraphics2D.java -- Abstract Graphics2D implementation + 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 gnu.java.util.LRUCache; + +import java.awt.AWTError; +import java.awt.AlphaComposite; +import java.awt.AWTPermission; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.Toolkit; +import java.awt.RenderingHints.Key; +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.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.FilteredImageSource; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.ReplicateScaleFilter; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * 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>Alternativly the backend can provide a method for filling Shapes by + * overriding the protected method fillShape(). This can be accomplished + * by a polygon filling function of the backend. Keep in mind though that + * Shapes can be quite complex (i.e. non-convex and containing holes, etc) + * which is not supported by all polygon fillers. Also it must be noted + * that fillShape() is expected to handle painting and compositing as well as + * clipping and transformation. If your backend can't support this natively, + * then you can fallback to the implementation in this class. You'll need + * to provide a writable Raster then, see above.</p> + * <p>Another alternative is to implement fillScanline() which only requires + * the backend to be able to draw horizontal lines in device space, + * which is usually very cheap. + * The implementation should still handle painting and compositing, + * but no more clipping and transformation is required by the backend.</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> + * <li>Provide an accelerated implementation for fillShape(). For instance, + * OpenGL can fill shapes very efficiently. There are some considerations + * to be made though, see above for details.</li> + * </ol> + * </p> + * + * @author Roman Kennke (kennke@aicas.com) + */ +public abstract class AbstractGraphics2D + extends Graphics2D + implements Cloneable, Pixelizer +{ + /** + * Caches scaled versions of an image. + * + * @see #drawImage(Image, int, int, int, int, ImageObserver) + */ + protected static final WeakHashMap<Image, HashMap<Dimension,Image>> imageCache = + new WeakHashMap<Image, HashMap<Dimension, Image>>(); + + /** + * Wether we use anti aliasing for rendering text by default or not. + */ + private static final boolean DEFAULT_TEXT_AA = + Boolean.getBoolean("gnu.java2d.default_text_aa"); + + /** + * The default font to use on the graphics object. + */ + private static final Font FONT = new Font("SansSerif", Font.PLAIN, 12); + + /** + * The size of the LRU cache used for caching GlyphVectors. + */ + private static final int GV_CACHE_SIZE = 50; + + /** + * Caches certain shapes to avoid massive creation of such Shapes in + * the various draw* and fill* methods. + */ + private static final ShapeCache shapeCache = new ShapeCache(); + + /** + * A pool of scanline converters. It is important to reuse scanline + * converters because they keep their datastructures in place. We pool them + * for use in multiple threads. + */ + private static final LinkedList<ScanlineConverter> scanlineConverters = + new LinkedList<ScanlineConverter>(); + + /** + * Caches glyph vectors for better drawing performance. + */ + private static final Map<TextCacheKey,GlyphVector> gvCache = + Collections.synchronizedMap(new LRUCache<TextCacheKey,GlyphVector>(GV_CACHE_SIZE)); + + /** + * This key is used to search in the gvCache without allocating a new + * key each time. + */ + private static final TextCacheKey searchTextKey = new TextCacheKey(); + + /** + * The transformation for this Graphics2D instance + */ + protected AffineTransform transform; + + /** + * The foreground. + */ + private Paint paint; + + /** + * The paint context during rendering. + */ + private PaintContext paintContext = null; + + /** + * The background. + */ + private Color background = Color.WHITE; + + /** + * Foreground color, as set by setColor. + */ + private Color foreground = Color.BLACK; + private boolean isForegroundColorNull = true; + + /** + * The current font. + */ + private Font font; + + /** + * The current composite setting. + */ + private Composite composite; + + /** + * The current stroke setting. + */ + private Stroke stroke; + + /** + * The current clip. This clip is in user coordinate space. + */ + private Shape clip; + + /** + * The rendering hints. + */ + private RenderingHints renderingHints; + + /** + * The raster of the destination surface. This is where the painting is + * performed. + */ + private WritableRaster destinationRaster; + + /** + * 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. + * - The paint must be a solid color. + * - The composite must be an AlphaComposite.SrcOver. + * - The clip must be a Rectangle. + * - The stroke must be a plain BasicStroke(). + * + * These conditions represent the standard settings of a new + * AbstractGraphics2D object and will be the most commonly used setting + * in Swing rendering and should therefore be optimized as much as possible. + */ + private boolean isOptimized = true; + + private static final BasicStroke STANDARD_STROKE = new BasicStroke(); + + private static final HashMap<Key, Object> STANDARD_HINTS; + static + { + + HashMap<Key, Object> hints = new HashMap<Key, Object>(); + hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + hints.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_DEFAULT); + + STANDARD_HINTS = hints; + } + + /** + * Creates a new AbstractGraphics2D instance. + */ + protected AbstractGraphics2D() + { + transform = new AffineTransform(); + background = Color.WHITE; + composite = AlphaComposite.SrcOver; + stroke = STANDARD_STROKE; + renderingHints = new RenderingHints(STANDARD_HINTS); + } + + /** + * Draws the specified shape. The shape is passed through the current stroke + * and is then forwarded to {@link #fillShape}. + * + * @param shape the shape to draw + */ + public void draw(Shape shape) + { + // Stroke the shape. + Shape strokedShape = stroke.createStrokedShape(shape); + // Fill the stroked shape. + fillShape(strokedShape, false); + } + + + /** + * 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) + { + 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) + { + 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) + { + 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) + { + 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); + } + + /** + * Draws the specified string at the specified location. + * + * @param text the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(String text, int x, int y) + { + GlyphVector gv; + synchronized (searchTextKey) + { + TextCacheKey tck = searchTextKey; + FontRenderContext frc = getFontRenderContext(); + tck.setString(text); + tck.setFont(font); + tck.setFontRenderContext(frc); + if (gvCache.containsKey(tck)) + { + gv = gvCache.get(tck); + } + else + { + gv = font.createGlyphVector(frc, text.toCharArray()); + gvCache.put(new TextCacheKey(text, font, frc), gv); + } + } + drawGlyphVector(gv, x, y); + } + + /** + * Draws the specified string at the specified location. + * + * @param text the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(String text, float x, float y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray()); + drawGlyphVector(gv, x, y); + } + + /** + * Draws the specified string (as AttributedCharacterIterator) at the + * specified location. + * + * @param iterator the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(AttributedCharacterIterator iterator, int x, int y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, iterator); + drawGlyphVector(gv, x, y); + } + + /** + * Draws the specified string (as AttributedCharacterIterator) at the + * specified location. + * + * @param iterator the string to draw + * @param x the x location, relative to the bounding rectangle of the text + * @param y the y location, relative to the bounding rectangle of the text + */ + public void drawString(AttributedCharacterIterator iterator, float x, float y) + { + FontRenderContext ctx = getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(ctx, iterator); + drawGlyphVector(gv, x, y); + } + + /** + * Fills the specified shape with the current foreground. + * + * @param shape the shape to fill + */ + public void fill(Shape shape) + { + fillShape(shape, false); + } + + public boolean hit(Rectangle rect, Shape text, boolean onStroke) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Sets the composite. + * + * @param comp the composite to set + */ + public void setComposite(Composite comp) + { + if (! (comp instanceof AlphaComposite)) + { + // FIXME: this check is only required "if this Graphics2D + // context is drawing to a Component on the display screen". + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new AWTPermission("readDisplayPixels")); + } + + composite = comp; + if (! (comp.equals(AlphaComposite.SrcOver))) + isOptimized = false; + else + updateOptimization(); + } + + /** + * Sets the current foreground. + * + * @param p the foreground to set. + */ + public void setPaint(Paint p) + { + if (p != null) + { + paint = p; + + if (! (paint instanceof Color)) + { + isOptimized = false; + } + else + { + this.foreground = (Color) paint; + isForegroundColorNull = false; + updateOptimization(); + } + } + else + { + this.foreground = Color.BLACK; + isForegroundColorNull = true; + } + + // free resources if needed, then put the paint context to null + if (this.paintContext != null) + this.paintContext.dispose(); + + this.paintContext = null; + } + + /** + * Sets the stroke for this graphics object. + * + * @param s the stroke to set + */ + public void setStroke(Stroke s) + { + stroke = s; + if (! stroke.equals(new BasicStroke())) + isOptimized = false; + else + updateOptimization(); + } + + /** + * Sets the specified rendering hint. + * + * @param hintKey the key of the rendering hint + * @param hintValue the value + */ + public void setRenderingHint(Key hintKey, Object hintValue) + { + renderingHints.put(hintKey, hintValue); + } + + /** + * Returns the rendering hint for the specified key. + * + * @param hintKey the rendering hint key + * + * @return the rendering hint for the specified key + */ + public Object getRenderingHint(Key hintKey) + { + return renderingHints.get(hintKey); + } + + /** + * Sets the specified rendering hints. + * + * @param hints the rendering hints to set + */ + public void setRenderingHints(Map hints) + { + renderingHints.clear(); + renderingHints.putAll(hints); + } + + /** + * Adds the specified rendering hints. + * + * @param hints the rendering hints to add + */ + public void addRenderingHints(Map hints) + { + renderingHints.putAll(hints); + } + + /** + * Returns the current rendering hints. + * + * @return the current rendering hints + */ + public RenderingHints getRenderingHints() + { + return (RenderingHints) renderingHints.clone(); + } + + /** + * Translates the coordinate system by (x, y). + * + * @param x the translation X coordinate + * @param y the translation Y coordinate + */ + public void translate(int x, int y) + { + transform.translate(x, y); + + // Update the clip. We special-case rectangular clips here, because they + // are so common (e.g. in Swing). + if (clip != null) + { + if (clip instanceof Rectangle) + { + Rectangle r = (Rectangle) clip; + r.x -= x; + r.y -= y; + setClip(r); + } + else + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.translate(-x, -y); + updateClip(clipTransform); + } + } + } + + /** + * Translates the coordinate system by (tx, ty). + * + * @param tx the translation X coordinate + * @param ty the translation Y coordinate + */ + public void translate(double tx, double ty) + { + transform.translate(tx, ty); + + // Update the clip. We special-case rectangular clips here, because they + // are so common (e.g. in Swing). + if (clip != null) + { + if (clip instanceof Rectangle) + { + Rectangle r = (Rectangle) clip; + r.x -= tx; + r.y -= ty; + } + else + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.translate(-tx, -ty); + updateClip(clipTransform); + } + } + } + + /** + * Rotates the coordinate system by <code>theta</code> degrees. + * + * @param theta the angle be which to rotate the coordinate system + */ + public void rotate(double theta) + { + transform.rotate(theta); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.rotate(-theta); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Rotates the coordinate system by <code>theta</code> around the point + * (x, y). + * + * @param theta the angle by which to rotate the coordinate system + * @param x the point around which to rotate, X coordinate + * @param y the point around which to rotate, Y coordinate + */ + public void rotate(double theta, double x, double y) + { + transform.rotate(theta, x, y); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.rotate(-theta, x, y); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Scales the coordinate system by the factors <code>scaleX</code> and + * <code>scaleY</code>. + * + * @param scaleX the factor by which to scale the X axis + * @param scaleY the factor by which to scale the Y axis + */ + public void scale(double scaleX, double scaleY) + { + transform.scale(scaleX, scaleY); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.scale(1 / scaleX, 1 / scaleY); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Shears the coordinate system by <code>shearX</code> and + * <code>shearY</code>. + * + * @param shearX the X shearing + * @param shearY the Y shearing + */ + public void shear(double shearX, double shearY) + { + transform.shear(shearX, shearY); + if (clip != null) + { + AffineTransform clipTransform = new AffineTransform(); + clipTransform.shear(-shearX, -shearY); + updateClip(clipTransform); + } + updateOptimization(); + } + + /** + * Transforms the coordinate system using the specified transform + * <code>t</code>. + * + * @param t the transform + */ + public void transform(AffineTransform t) + { + transform.concatenate(t); + try + { + AffineTransform clipTransform = t.createInverse(); + updateClip(clipTransform); + } + catch (NoninvertibleTransformException ex) + { + // TODO: How can we deal properly with this? + ex.printStackTrace(); + } + updateOptimization(); + } + + /** + * Sets the transformation for this Graphics object. + * + * @param t the transformation to set + */ + public void setTransform(AffineTransform t) + { + // Transform clip into target space using the old transform. + updateClip(transform); + transform.setTransform(t); + // Transform the clip back into user space using the inverse new transform. + try + { + updateClip(transform.createInverse()); + } + catch (NoninvertibleTransformException ex) + { + // TODO: How can we deal properly with this? + ex.printStackTrace(); + } + updateOptimization(); + } + + /** + * Returns the transformation of this coordinate system. + * + * @return the transformation of this coordinate system + */ + public AffineTransform getTransform() + { + return (AffineTransform) transform.clone(); + } + + /** + * Returns the current foreground. + * + * @return the current foreground + */ + public Paint getPaint() + { + return paint; + } + + + /** + * Returns the current composite. + * + * @return the current composite + */ + public Composite getComposite() + { + return composite; + } + + /** + * Sets the current background. + * + * @param color the background to set. + */ + public void setBackground(Color color) + { + background = color; + } + + /** + * Returns the current background. + * + * @return the current background + */ + public Color getBackground() + { + return background; + } + + /** + * Returns the current stroke. + * + * @return the current stroke + */ + public Stroke getStroke() + { + return stroke; + } + + /** + * Intersects the clip of this graphics object with the specified clip. + * + * @param s the clip with which the current clip should be intersected + */ + public void clip(Shape s) + { + // Initialize clip if not already present. + if (clip == null) + setClip(s); + + // This is so common, let's optimize this. + else if (clip instanceof Rectangle && s instanceof Rectangle) + { + Rectangle clipRect = (Rectangle) clip; + Rectangle r = (Rectangle) s; + computeIntersection(r.x, r.y, r.width, r.height, 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; + isOptimized = false; + // Call setClip so that subclasses get notified. + setClip(clip); + } + } + + public FontRenderContext getFontRenderContext() + { + // Protect our own transform from beeing modified. + AffineTransform tf = new AffineTransform(transform); + // TODO: Determine antialias and fractionalmetrics parameters correctly. + return new FontRenderContext(tf, false, true); + } + + /** + * Draws the specified glyph vector at the specified location. + * + * @param gv the glyph vector to draw + * @param x the location, x coordinate + * @param y the location, y coordinate + */ + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + translate(x, y); + fillShape(gv.getOutline(), true); + translate(-x, -y); + } + + /** + * Creates a copy of this graphics object. + * + * @return a copy of this graphics object + */ + public Graphics create() + { + AbstractGraphics2D copy = (AbstractGraphics2D) clone(); + return copy; + } + + /** + * Creates and returns a copy of this Graphics object. This should + * be overridden by subclasses if additional state must be handled when + * cloning. This is called by {@link #create()}. + * + * @return a copy of this Graphics object + */ + protected Object clone() + { + try + { + AbstractGraphics2D copy = (AbstractGraphics2D) super.clone(); + // Copy the clip. If it's a Rectangle, preserve that for optimization. + if (clip instanceof Rectangle) + copy.clip = new Rectangle((Rectangle) clip); + else if (clip != null) + copy.clip = new GeneralPath(clip); + else + copy.clip = null; + + copy.renderingHints = new RenderingHints(null); + copy.renderingHints.putAll(renderingHints); + copy.transform = new AffineTransform(transform); + // The remaining state is inmmutable and doesn't need to be copied. + return copy; + } + catch (CloneNotSupportedException ex) + { + AWTError err = new AWTError("Unexpected exception while cloning"); + err.initCause(ex); + throw err; + } + } + + /** + * Returns the current foreground. + */ + public Color getColor() + { + if (isForegroundColorNull) + return null; + + return this.foreground; + } + + /** + * Sets the current foreground. + * + * @param color the foreground to set + */ + public void setColor(Color color) + { + this.setPaint(color); + } + + public void setPaintMode() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public void setXORMode(Color color) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Returns the current font. + * + * @return the current font + */ + public Font getFont() + { + return font; + } + + /** + * Sets the font on this graphics object. When <code>f == null</code>, the + * current setting is not changed. + * + * @param f the font to set + */ + public void setFont(Font f) + { + if (f != null) + font = f; + } + + /** + * Returns the font metrics for the specified font. + * + * @param font the font for which to fetch the font metrics + * + * @return the font metrics for the specified font + */ + public FontMetrics getFontMetrics(Font font) + { + return Toolkit.getDefaultToolkit().getFontMetrics(font); + } + + /** + * Returns the bounds of the current clip. + * + * @return the bounds of the current clip + */ + public Rectangle getClipBounds() + { + Rectangle b = null; + if (clip != null) + b = clip.getBounds(); + return b; + } + + /** + * Intersects the current clipping region with the specified rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public void clipRect(int x, int y, int width, int height) + { + clip(new Rectangle(x, y, width, height)); + } + + /** + * Sets the clip to the specified rectangle. + * + * @param x the x coordinate of the clip rectangle + * @param y the y coordinate of the clip rectangle + * @param width the width of the clip rectangle + * @param height the height of the clip rectangle + */ + public void setClip(int x, int y, int width, int height) + { + setClip(new Rectangle(x, y, width, height)); + } + + /** + * Returns the current clip. + * + * @return the current clip + */ + public Shape getClip() + { + return clip; + } + + /** + * Sets the current clipping area to <code>clip</code>. + * + * @param c the clip to set + */ + public void setClip(Shape c) + { + clip = c; + if (! (clip instanceof Rectangle)) + isOptimized = false; + else + updateOptimization(); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) + { + if (isOptimized) + rawCopyArea(x, y, width, height, dx, dy); + else + copyAreaImpl(x, y, width, height, dx, dy); + } + + /** + * Draws a line from (x1, y1) to (x2, y2). + * + * This implementation transforms the coordinates and forwards the call to + * {@link #rawDrawLine}. + */ + public void drawLine(int x1, int y1, int x2, int y2) + { + if (isOptimized) + { + int tx = (int) transform.getTranslateX(); + int ty = (int) transform.getTranslateY(); + rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty); + } + else + { + ShapeCache sc = shapeCache; + if (sc.line == null) + sc.line = new Line2D.Float(); + sc.line.setLine(x1, y1, x2, y2); + draw(sc.line); + } + } + + public void drawRect(int x, int y, int w, int h) + { + if (isOptimized) + { + int tx = (int) transform.getTranslateX(); + int ty = (int) transform.getTranslateY(); + rawDrawRect(x + tx, y + ty, w, h); + } + else + { + ShapeCache sc = shapeCache; + if (sc.rect == null) + sc.rect = new Rectangle(); + sc.rect.setBounds(x, y, w, h); + draw(sc.rect); + } + } + + /** + * Fills a rectangle with the current paint. + * + * @param x the upper left corner, X coordinate + * @param y the upper left corner, Y coordinate + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public void fillRect(int x, int y, int width, int height) + { + if (isOptimized) + { + rawFillRect(x + (int) transform.getTranslateX(), + y + (int) transform.getTranslateY(), width, height); + } + else + { + ShapeCache sc = shapeCache; + if (sc.rect == null) + sc.rect = new Rectangle(); + sc.rect.setBounds(x, y, width, height); + fill(sc.rect); + } + } + + /** + * Fills a rectangle with the current background color. + * + * This implementation temporarily sets the foreground color to the + * background and forwards the call to {@link #fillRect(int, int, int, int)}. + * + * @param x the upper left corner, X coordinate + * @param y the upper left corner, Y coordinate + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public void clearRect(int x, int y, int width, int height) + { + if (isOptimized) + rawClearRect(x, y, width, height); + else + { + Paint savedForeground = getPaint(); + setPaint(getBackground()); + fillRect(x, y, width, height); + setPaint(savedForeground); + } + } + + /** + * Draws a rounded rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * @param arcWidth the width of the arcs + * @param arcHeight the height of the arcs + */ + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) + { + ShapeCache sc = shapeCache; + if (sc.roundRect == null) + sc.roundRect = new RoundRectangle2D.Float(); + sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight); + draw(sc.roundRect); + } + + /** + * Fills a rounded rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * @param arcWidth the width of the arcs + * @param arcHeight the height of the arcs + */ + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight) + { + ShapeCache sc = shapeCache; + if (sc.roundRect == null) + sc.roundRect = new RoundRectangle2D.Float(); + sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight); + fill(sc.roundRect); + } + + /** + * Draws the outline of an oval. + * + * @param x the upper left corner of the bounding rectangle of the ellipse + * @param y the upper left corner of the bounding rectangle of the ellipse + * @param width the width of the ellipse + * @param height the height of the ellipse + */ + public void drawOval(int x, int y, int width, int height) + { + ShapeCache sc = shapeCache; + if (sc.ellipse == null) + sc.ellipse = new Ellipse2D.Float(); + sc.ellipse.setFrame(x, y, width, height); + draw(sc.ellipse); + } + + /** + * Fills an oval. + * + * @param x the upper left corner of the bounding rectangle of the ellipse + * @param y the upper left corner of the bounding rectangle of the ellipse + * @param width the width of the ellipse + * @param height the height of the ellipse + */ + public void fillOval(int x, int y, int width, int height) + { + ShapeCache sc = shapeCache; + if (sc.ellipse == null) + sc.ellipse = new Ellipse2D.Float(); + sc.ellipse.setFrame(x, y, width, height); + fill(sc.ellipse); + } + + /** + * Draws an arc. + */ + public void drawArc(int x, int y, int width, int height, int arcStart, + int arcAngle) + { + ShapeCache sc = shapeCache; + if (sc.arc == null) + sc.arc = new Arc2D.Float(); + sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.OPEN); + draw(sc.arc); + } + + /** + * Fills an arc. + */ + public void fillArc(int x, int y, int width, int height, int arcStart, + int arcAngle) + { + ShapeCache sc = shapeCache; + if (sc.arc == null) + sc.arc = new Arc2D.Float(); + sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.PIE); + draw(sc.arc); + } + + public void drawPolyline(int[] xPoints, int[] yPoints, int npoints) + { + ShapeCache sc = shapeCache; + if (sc.polyline == null) + sc.polyline = new GeneralPath(); + GeneralPath p = sc.polyline; + p.reset(); + if (npoints > 0) + p.moveTo(xPoints[0], yPoints[0]); + for (int i = 1; i < npoints; i++) + p.lineTo(xPoints[i], yPoints[i]); + fill(p); + } + + /** + * Draws the outline of a polygon. + */ + public void drawPolygon(int[] xPoints, int[] yPoints, int npoints) + { + ShapeCache sc = shapeCache; + if (sc.polygon == null) + sc.polygon = new Polygon(); + sc.polygon.reset(); + sc.polygon.xpoints = xPoints; + sc.polygon.ypoints = yPoints; + sc.polygon.npoints = npoints; + draw(sc.polygon); + } + + /** + * Fills the outline of a polygon. + */ + public void fillPolygon(int[] xPoints, int[] yPoints, int npoints) + { + ShapeCache sc = shapeCache; + if (sc.polygon == null) + sc.polygon = new Polygon(); + sc.polygon.reset(); + sc.polygon.xpoints = xPoints; + sc.polygon.ypoints = yPoints; + sc.polygon.npoints = npoints; + fill(sc.polygon); + } + + /** + * 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) + { + boolean ret; + if (isOptimized) + { + ret = rawDrawImage(image, x + (int) transform.getTranslateX(), + y + (int) transform.getTranslateY(), 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) + { + AffineTransform t = new AffineTransform(); + int imWidth = image.getWidth(observer); + int imHeight = image.getHeight(observer); + if (imWidth == width && imHeight == height) + { + // No need to scale, fall back to non-scaling loops. + return drawImage(image, x, y, observer); + } + else + { + Image scaled = prepareImage(image, width, height); + // Ideally, this should notify the observer about the scaling progress. + return drawImage(scaled, x, y, 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) + { + 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) + { + 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) + { + 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: Do something with bgcolor. + return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } + + /** + * Disposes this graphics object. + */ + public void dispose() + { + // Nothing special to do here. + } + + /** + * Fills the specified shape. Override this if your backend can efficiently + * fill shapes. This is possible on many systems via a polygon fill + * method or something similar. But keep in mind that Shapes can be quite + * complex (non-convex, with holes etc), which is not necessarily supported + * by all polygon fillers. Also note that you must perform clipping + * before filling the shape. + * + * @param s the shape to fill + * @param isFont <code>true</code> if the shape is a font outline + */ + protected void fillShape(Shape s, boolean isFont) + { + // Determine if we need to antialias stuff. + boolean antialias = false; + if (isFont) + { + Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING); + // We default to antialiasing for text rendering. + antialias = v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON + || (v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT + && DEFAULT_TEXT_AA); + } + else + { + Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING); + antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON); + } + ScanlineConverter sc = getScanlineConverter(); + int resolution = 0; + int yRes = 0; + if (antialias) + { + // Adjust resolution according to rendering hints. + resolution = 2; + yRes = 4; + } + sc.renderShape(this, s, clip, transform, resolution, yRes, renderingHints); + freeScanlineConverter(sc); + } + + /** + * Returns the color model of this Graphics object. + * + * @return the color model of this Graphics object + */ + protected abstract ColorModel getColorModel(); + + /** + * Returns the bounds of the target. + * + * @return the bounds of the target + */ + protected abstract Rectangle getDeviceBounds(); + + /** + * 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 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 rawDrawLine(int x0, int y0, int x1, int y1) + { + ShapeCache sc = shapeCache; + if (sc.line == null) + sc.line = new Line2D.Float(); + sc.line.setLine(x0, y0, x1, y1); + draw(sc.line); + } + + protected void rawDrawRect(int x, int y, int w, int h) + { + ShapeCache sc = shapeCache; + if (sc.rect == null) + sc.rect = new Rectangle(); + sc.rect.setBounds(x, y, w, h); + draw(sc.rect); + } + + /** + * 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. + * + * @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 void rawClearRect(int x, int y, int w, int h) + { + Paint savedForeground = getPaint(); + setPaint(getBackground()); + rawFillRect(x, y, w, h); + setPaint(savedForeground); + } + + /** + * Fills a rectangle in optimization mode. The implementation should respect + * the clip but can assume that it is a rectangle. + * + * @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 void rawFillRect(int x, int y, int w, int h) + { + ShapeCache sc = shapeCache; + if (sc.rect == null) + sc.rect = new Rectangle(); + sc.rect.setBounds(x, y, w, h); + fill(sc.rect); + } + + /** + * Draws an image in optimization mode. The implementation should respect + * the clip but can assume that it is a rectangle. + * + * @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 boolean rawDrawImage(Image image, int x, int y, ImageObserver obs) + { + AffineTransform t = new AffineTransform(); + t.translate(x, y); + return drawImage(image, t, obs); + } + + /** + * 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 rawCopyArea(int x, int y, int w, int h, int dx, int dy) + { + 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."); + } + + /** + * Paints a scanline between x0 and x1. Override this when your backend + * can efficiently draw/fill horizontal lines. + * + * @param x0 the left offset + * @param x1 the right offset + * @param y the scanline + */ + public void renderScanline(int y, ScanlineCoverage c) + { + PaintContext pCtx = getPaintContext(); + + int x0 = c.getMinX(); + int x1 = c.getMaxX(); + Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1); + + // Do the anti aliasing thing. + float coverageAlpha = 0; + float maxCoverage = c.getMaxCoverage(); + ColorModel cm = pCtx.getColorModel(); + DataBuffer db = paintRaster.getDataBuffer(); + Point loc = new Point(paintRaster.getMinX(), paintRaster.getMinY()); + SampleModel sm = paintRaster.getSampleModel(); + WritableRaster writeRaster = Raster.createWritableRaster(sm, db, loc); + WritableRaster alphaRaster = cm.getAlphaRaster(writeRaster); + int pixel; + ScanlineCoverage.Iterator iter = c.iterate(); + while (iter.hasNext()) + { + ScanlineCoverage.Range range = iter.next(); + coverageAlpha = range.getCoverage() / maxCoverage; + if (coverageAlpha < 1.0) + { + for (int x = range.getXPos(); x < range.getXPosEnd(); x++) + { + pixel = alphaRaster.getSample(x, y, 0); + pixel = (int) (pixel * coverageAlpha); + alphaRaster.setSample(x, y, 0, pixel); + } + } + } + ColorModel paintColorModel = pCtx.getColorModel(); + CompositeContext cCtx = composite.createContext(paintColorModel, + getColorModel(), + renderingHints); + WritableRaster raster = getDestinationRaster(); + WritableRaster targetChild = raster.createWritableTranslatedChild(-x0, -y); + + cCtx.compose(paintRaster, targetChild, targetChild); + updateRaster(raster, x0, y, x1 - x0, 1); + cCtx.dispose(); + } + + + /** + * Initializes this graphics object. This must be called by subclasses in + * order to correctly initialize the state of this object. + */ + protected void init() + { + setPaint(Color.BLACK); + setFont(FONT); + isOptimized = true; + } + + /** + * Returns a WritableRaster that is used by this class to perform the + * rendering in. It is not necessary that the target surface immediately + * reflects changes in the raster. Updates to the raster are notified via + * {@link #updateRaster}. + * + * @return the destination raster + */ + protected WritableRaster getDestinationRaster() + { + // TODO: Ideally we would fetch the xdrawable's surface pixels for + // initialization of the raster. + Rectangle db = getDeviceBounds(); + if (destinationRaster == null) + { + int[] bandMasks = new int[]{ 0xFF0000, 0xFF00, 0xFF }; + destinationRaster = Raster.createPackedRaster(DataBuffer.TYPE_INT, + db.width, db.height, + bandMasks, null); + // Initialize raster with white. + int x0 = destinationRaster.getMinX(); + int x1 = destinationRaster.getWidth() + x0; + int y0 = destinationRaster.getMinY(); + int y1 = destinationRaster.getHeight() + y0; + int numBands = destinationRaster.getNumBands(); + for (int y = y0; y < y1; y++) + { + for (int x = x0; x < x1; x++) + { + for (int b = 0; b < numBands; b++) + destinationRaster.setSample(x, y, b, 255); + } + } + } + return destinationRaster; + } + + /** + * Notifies the backend that the raster has changed in the specified + * rectangular area. The raster that is provided in this method is always + * the same as the one returned in {@link #getDestinationRaster}. + * Backends that reflect changes to this raster directly don't need to do + * anything here. + * + * @param raster the updated raster, identical to the raster returned + * by {@link #getDestinationRaster()} + * @param x the upper left corner of the updated region, X coordinate + * @param y the upper lef corner of the updated region, Y coordinate + * @param w the width of the updated region + * @param h the height of the updated region + */ + protected void updateRaster(Raster raster, int x, int y, int w, int h) + { + // Nothing to do here. Backends that need to update their surface + // to reflect the change should override this method. + } + + // Some helper methods. + + /** + * Helper method to check and update the optimization conditions. + */ + private void updateOptimization() + { + int transformType = transform.getType(); + boolean optimizedTransform = false; + if (transformType == AffineTransform.TYPE_IDENTITY + || transformType == AffineTransform.TYPE_TRANSLATION) + optimizedTransform = true; + + boolean optimizedClip = (clip == null || clip instanceof Rectangle); + isOptimized = optimizedClip + && optimizedTransform && paint instanceof Color + && composite == AlphaComposite.SrcOver + && stroke.equals(new BasicStroke()); + } + + /** + * Calculates the intersection of two rectangles. The result is stored + * in <code>rect</code>. This is basically the same + * like {@link Rectangle#intersection(Rectangle)}, only that it does not + * create new Rectangle instances. The tradeoff is that you loose any data in + * <code>rect</code>. + * + * @param x upper-left x coodinate of first rectangle + * @param y upper-left y coodinate of first rectangle + * @param w width of first rectangle + * @param h height of first rectangle + * @param rect a Rectangle object of the second rectangle + * + * @throws NullPointerException if rect is null + * + * @return a rectangle corresponding to the intersection of the + * two rectangles. An empty rectangle is returned if the rectangles + * do not overlap + */ + private static Rectangle computeIntersection(int x, int y, int w, int h, + Rectangle rect) + { + int x2 = rect.x; + int y2 = rect.y; + int w2 = rect.width; + int h2 = rect.height; + + int dx = (x > x2) ? x : x2; + int dy = (y > y2) ? y : y2; + int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx); + int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy); + + if (dw >= 0 && dh >= 0) + rect.setBounds(dx, dy, dw, dh); + else + rect.setBounds(0, 0, 0, 0); + + return rect; + } + + /** + * Helper method to transform the clip. This is called by the various + * transformation-manipulation methods to update the clip (which is in + * userspace) accordingly. + * + * The transform usually is the inverse transform that was applied to the + * graphics object. + * + * @param t the transform to apply to the clip + */ + private void updateClip(AffineTransform t) + { + if (! (clip instanceof GeneralPath)) + clip = new GeneralPath(clip); + + GeneralPath p = (GeneralPath) clip; + p.transform(t); + } + + /** + * Returns a free scanline converter from the pool. + * + * @return a scanline converter + */ + private ScanlineConverter getScanlineConverter() + { + synchronized (scanlineConverters) + { + ScanlineConverter sc; + if (scanlineConverters.size() > 0) + { + sc = scanlineConverters.removeFirst(); + } + else + { + sc = new ScanlineConverter(); + } + return sc; + } + } + + /** + * Puts a scanline converter back in the pool. + * + * @param sc + */ + private void freeScanlineConverter(ScanlineConverter sc) + { + synchronized (scanlineConverters) + { + scanlineConverters.addLast(sc); + } + } + + private PaintContext getPaintContext() + { + if (this.paintContext == null) + { + this.paintContext = + this.foreground.createContext(getColorModel(), + getDeviceBounds(), + getClipBounds(), + getTransform(), + getRenderingHints()); + } + + return this.paintContext; + } + + /** + * Scales an image to the specified width and height. This should also + * be used to implement + * {@link Toolkit#prepareImage(Image, int, int, ImageObserver)}. + * This uses {@link Toolkit#createImage(ImageProducer)} to create the actual + * image. + * + * @param image the image to prepare + * @param w the width + * @param h the height + * + * @return the scaled image + */ + public static Image prepareImage(Image image, int w, int h) + { + // Try to find cached scaled image. + HashMap<Dimension,Image> scaledTable = imageCache.get(image); + Dimension size = new Dimension(w, h); + Image scaled = null; + if (scaledTable != null) + { + scaled = scaledTable.get(size); + } + if (scaled == null) + { + // No cached scaled image. Start scaling image now. + ImageProducer source = image.getSource(); + ReplicateScaleFilter scaler = new ReplicateScaleFilter(w, h); + FilteredImageSource filteredSource = + new FilteredImageSource(source, scaler); + // Ideally, this should asynchronously scale the image. + Image scaledImage = + Toolkit.getDefaultToolkit().createImage(filteredSource); + scaled = scaledImage; + // Put scaled image in cache. + if (scaledTable == null) + { + scaledTable = new HashMap<Dimension,Image>(); + imageCache.put(image, scaledTable); + } + scaledTable.put(size, scaledImage); + } + return scaled; + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java b/libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java new file mode 100644 index 000000000..efe1966e3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java @@ -0,0 +1,197 @@ +/* ActiveEdges.java -- A collection of active edges for scanline conversion + 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 gnu.java.lang.CPStringBuilder; + +/** + * A collection of active edges for scanline conversion. + */ +final class ActiveEdges +{ + + /** + * The active edges. This can contain null values at arbirary locations. + * The method #sort() packs this together. + */ + private PolyEdge[] activeEdges; + + /** + * The actual number of active edges. The array can be bigger than this + * number. + */ + private int numActiveEdges; + + /** + * Creates a new ActiveEdges object. + */ + ActiveEdges() + { + activeEdges = new PolyEdge[8]; + numActiveEdges = 0; + } + + /** + * Clears out all active edges. This is cheap as it simply resets the + * counter to 0. It does not release all references to PolyEdge instances. + */ + void clear() + { + numActiveEdges = 0; + } + + /** + * Adds the specified edge to the list of active edges. This does not yet + * sort the edges and therefore does destroy any order of the list. + * + * @param edge the edge to add + */ + void add(PolyEdge edge) + { + // Grow array when necessary. + int oldSize = activeEdges.length; + if (numActiveEdges >= oldSize) + { + int newSize = oldSize + oldSize / 4 + 1; + PolyEdge[] newEdges = new PolyEdge[newSize]; + System.arraycopy(activeEdges, 0, newEdges, 0, oldSize); + activeEdges = newEdges; + } + activeEdges[numActiveEdges] = edge; + numActiveEdges++; + } + + /** + * Intersects all active edges, sorts them according to their intersection + * points and packs the array to remove unneeded edges. This does also + * remove any edges that do not intersect the scanline (i.e. they end above + * of the scanline). + * + * @param y the scanline height + */ + void intersectSortAndPack(int n, int y) + { + // Intersect and pack in one go. + int last = 0; + PolyEdge tmp; + for (int i = 0; i < numActiveEdges; i++) + { + PolyEdge edge = activeEdges[i]; + // Clear out edge that ends above the scanline. + if (edge != null && edge.y1 >= y) + { + assert edge.y1 >= y && edge.y0 <= y : "edge must cross scanline"; + edge.intersect(n, y); + activeEdges[last] = edge; + last++; + + // Bubble up the added edge. + for (int j = last - 1; j > 0; j--) + { + if (activeEdges[j].xIntersection + < activeEdges[j - 1].xIntersection) + { + tmp = activeEdges[j]; + activeEdges[j] = activeEdges[j - 1]; + activeEdges[j - 1] = tmp; + } + else + { + // The beginning of the list is already sorted. + break; + } + } + } + } + numActiveEdges = last; + + } + + /** + * Returns the number of active edges. This is only reliable after a + * call to {@link #intersectSortAndPack(int, int)}. + * + * @return the number of active edges + */ + int getNumActiveEdges() + { + return numActiveEdges; + } + + /** + * Returns the active edge at the position <code>i</code>. + * + * @param i the index + * + * @return the active edge at the specified index + */ + PolyEdge getActiveEdge(int i) + { + return activeEdges[i]; + } + + /** + * Removes all edges that end above the specified height. + * + * @param y the cut-off height + */ + void remove(int y) + { + for (int i = 0; i < numActiveEdges; i++) + { + PolyEdge edge = activeEdges[i]; + if (edge != null && edge.y1 < y) + { + activeEdges[i] = null; + } + } + } + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[ActiveEdges] "); + for (int i = 0; i < numActiveEdges; i++) + { + s.append(activeEdges[i]); + s.append(','); + } + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java b/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java new file mode 100644 index 000000000..f1f082abe --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/AlphaCompositeContext.java @@ -0,0 +1,316 @@ +/* AlphaCompositeContext.java -- CompositeContext impl for AlphaComposite + 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.AlphaComposite; +import java.awt.CompositeContext; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * A CompositeContext implementation for {@link AlphaComposite}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class AlphaCompositeContext + implements CompositeContext +{ + + /** + * The Composite object for which we perform compositing. + */ + private AlphaComposite composite; + + /** + * The source color model. + */ + private ColorModel srcColorModel; + + /** + * The destination color model. + */ + private ColorModel dstColorModel; + + /** + * The blending factor for the source. + */ + private float fs; + + /** + * The blending factor for the destination. + */ + private float fd; + + /** + * Creates a new AlphaCompositeContext. + * + * @param aComp the AlphaComposite object + * @param srcCM the source color model + * @param dstCM the destination color model + */ + public AlphaCompositeContext(AlphaComposite aComp, ColorModel srcCM, + ColorModel dstCM) + { + composite = aComp; + srcColorModel = srcCM; + dstColorModel = dstCM; + + + // Determine the blending factors according to the rule in the + // AlphaComposite. For some rules the factors must be determined + // dynamically because they depend on the actual pixel value. + switch (composite.getRule()) + { + case AlphaComposite.CLEAR: + fs = 0.F; + fd= 0.F; + break; + case AlphaComposite.DST: + fs = 0.F; + fd= 1.F; + break; + case AlphaComposite.DST_ATOP: + fs = 1.F; // Determined later as 1 - alpha_dst; + fd = 1.F; // Determined later as alpha_src; + break; + case AlphaComposite.DST_IN: + fs = 0.F; + fd = 0.F; // Determined later as alpha_src; + break; + case AlphaComposite.DST_OUT: + fs = 0.F; + fd = 0.F; // Determined later as 1 - alpha_src; + break; + case AlphaComposite.DST_OVER: + fs = 1.F; // Determined later as 1 - alpha_dst. + fd= 1.F; + break; + case AlphaComposite.SRC: + fs = 1.F; + fd= 0.F; + break; + case AlphaComposite.SRC_ATOP: + fs = 1.F; // Determined later as alpha_dst; + fd = 1.F; // Determined later as 1 - alpha_src; + break; + case AlphaComposite.SRC_IN: + fs = 0.F; // Determined later as alpha_dst; + fd = 0.F; + break; + case AlphaComposite.SRC_OUT: + fs = 0.F; // Determined later as 1 - alpha_dst; + fd = 0.F; + break; + case AlphaComposite.SRC_OVER: + fs = 1.F; + fd= 1.F; // Determined later as 1 - alpha_src. + break; + case AlphaComposite.XOR: + fs = 1.F; // Determined later as 1 - alpha_dst. + fd= 1.F; // Determined later as 1 - alpha_src. + break; + default: + throw new AWTError("Illegal AlphaComposite rule"); + } + + } + + /** + * Releases all resources held by this composite object. + */ + public void dispose() + { + // Nothing to do here yet. + } + + /** + * Performs compositing according to the rules specified in the + * AlphaComposite from the constructor. + */ + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) + { + + // TODO: This implementation is very general and highly inefficient. There + // are two possible ways to optimize this: + // 1. Special cased implementations for common ColorModels and transfer + // types. + // 2. Native implementation. + + int x0 = src.getMinX(); + int y0 = src.getMinY(); + int width = src.getWidth(); + int height = src.getHeight(); + int x1 = x0 + width; + int y1 = y0 + height; + + Object srcPixel = null; + Object dstPixel = null; + + // Prepare the array that holds the color and alpha components of the + // source pixels. + float[] srcComponents; + int srcComponentsLength = srcColorModel.getNumComponents(); + if (! srcColorModel.hasAlpha()) + srcComponentsLength += 1; + srcComponents = new float[srcComponentsLength]; + + // Prepare the array that holds the color and alpha components of the + // destination pixels. + float[] dstComponents; + int dstComponentsLength = dstColorModel.getNumComponents(); + if (! dstColorModel.hasAlpha()) + dstComponentsLength += 1; + dstComponents = new float[dstComponentsLength]; + + if (srcComponentsLength != dstComponentsLength) + throw new AWTError("The color models of the source and destination have" + + "incompatible number of color components"); + + int srcTransferType = srcColorModel.getTransferType(); + int dstTransferType = dstColorModel.getTransferType(); + + for (int y = y0; y < y1; y++) + { + for (int x = x0; x < x1; x++) + { + // Fetch source pixel. + srcPixel = src.getDataElements(x, y, (int[]) srcPixel); + // Fetch destination pixel. + dstPixel = dstIn.getDataElements(x, y, dstPixel); + // Get normalized components. This is the only type that is + // guaranteed to be supported by all ColorModels. + srcComponents = + srcColorModel.getNormalizedComponents(srcPixel, srcComponents, 0); + if (! srcColorModel.hasAlpha()) + srcComponents[srcComponentsLength - 1] = 1.0F; + dstComponents = + dstColorModel.getNormalizedComponents(dstPixel, dstComponents, 0); + if (! dstColorModel.hasAlpha()) + dstComponents[dstComponentsLength - 1] = 1.0F; + + // Prepare the input. + float compositeAlpha = composite.getAlpha(); + srcComponents[srcComponentsLength - 1] *= compositeAlpha; + if (srcColorModel.isAlphaPremultiplied()) + { + for (int i = srcComponentsLength - 2; i >= 0; i--) + srcComponents[i] *= compositeAlpha; + } + else + { + for (int i = srcComponentsLength - 2; i >= 0; i--) + srcComponents[i] *= srcComponents[srcComponentsLength - 1]; + } + if (! dstColorModel.isAlphaPremultiplied()) + { + for (int i = dstComponentsLength - 2; i >= 0; i--) + dstComponents[i] *= dstComponents[dstComponents.length - 1]; + } + + // Determine the blending factors according to the rule in the + // AlphaComposite. For some rules the factors must be determined + // dynamically because they depend on the actual pixel value. + float srcAlpha = srcComponents[srcComponentsLength - 1]; + float dstAlpha = dstComponents[dstComponentsLength - 1]; + switch (composite.getRule()) + { + case AlphaComposite.DST_ATOP: + fs = 1.F - dstAlpha; + fd = srcAlpha; + break; + case AlphaComposite.DST_IN: + fd = srcAlpha; + break; + case AlphaComposite.DST_OUT: + fd = 1.F - srcAlpha; + break; + case AlphaComposite.DST_OVER: + fs = 1.F - dstAlpha; + break; + case AlphaComposite.SRC_ATOP: + fs = srcAlpha; + fd = 1.F - srcAlpha; + break; + case AlphaComposite.SRC_IN: + fs = dstAlpha; + break; + case AlphaComposite.SRC_OUT: + fs = 1.F - dstAlpha; + break; + case AlphaComposite.SRC_OVER: + fd= 1.F - srcAlpha; + break; + case AlphaComposite.XOR: + fs = 1.F - dstAlpha; + fd= 1.F - srcAlpha; + break; + default: + // For the other cases the factors have already been determined + // in the constructor. + } + + // Apply the blending equation to the pixels. + for (int i = 0; i < srcComponentsLength; i++) + { + dstComponents[i] = srcComponents[i] * fs + + dstComponents[i] * fd; + } + + // Convert the result back when the destination is not + // alpha-premultiplied. + dstAlpha = dstComponents[dstComponentsLength - 1]; + if (!dstColorModel.isAlphaPremultiplied() && dstAlpha != 0.F) + { + for (int i = 0; i < dstComponentsLength - 1; i++) + { + dstComponents[i] = dstComponents[i] / dstAlpha; + } + } + + // Store the result in the destination raster. + dstPixel = dstColorModel.getDataElements(dstComponents, 0, + dstPixel); + dstOut.setDataElements(x, y, dstPixel); + } // End X loop. + } // End Y loop. + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/CubicSegment.java b/libjava/classpath/gnu/java/awt/java2d/CubicSegment.java new file mode 100644 index 000000000..8197c94c5 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/CubicSegment.java @@ -0,0 +1,184 @@ +/* CubicSegment.java -- Cubic segment used for BasicStroke + 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.geom.CubicCurve2D; +import java.awt.geom.Point2D; + +/** + * Cubic Bezier curve segment + */ +public class CubicSegment extends Segment +{ + public Point2D cp1; // control points + public Point2D cp2; // control points + + /** + * Constructor - takes coordinates of the starting point, + * first control point, second control point and end point, + * respecively. + */ + public CubicSegment(double x1, double y1, double c1x, double c1y, + double c2x, double c2y, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp1 = new Point2D.Double(c1x, c1y); + cp2 = new Point2D.Double(c2x, c2y); + } + + public CubicSegment(Point2D p1, Point2D cp1, Point2D cp2, Point2D p2) + { + super(); + P1 = p1; + P2 = p2; + this.cp1 = cp1; + this.cp2 = cp2; + } + + /** + * Clones this segment + */ + public Object clone() + { + CubicSegment segment = null; + + try + { + segment = (CubicSegment) super.clone(); + + segment.P1 = (Point2D) P1.clone(); + segment.P2 = (Point2D) P2.clone(); + segment.cp1 = (Point2D) cp1.clone(); + segment.cp2 = (Point2D) cp2.clone(); + } + catch (CloneNotSupportedException cnse) + { + InternalError ie = new InternalError(); + ie.initCause(cnse); + throw ie; + } + + return segment; + } + + /** + * Get the "top" and "bottom" segments of this segment. First array element is + * p0 + normal, second is p0 - normal. + */ + public Segment[] getDisplacedSegments(double radius) + { + // It is, apparently, impossible to derive a curve parallel to a bezier + // curve (unless it's a straight line), so we have no choice but to + // approximate the displaced segments. Similar to FlattenPathIterator. + + Segment segmentTop = null; + Segment segmentBottom = null; + this.radius = radius; + + CubicCurve2D[] curves = new CubicCurve2D[10]; + curves[0] = new CubicCurve2D.Double(P1.getX(), P1.getY(), cp1.getX(), + cp1.getY(), cp2.getX(), cp2.getY(), + P2.getX(), P2.getY()); + int numCurves = 1; + + // Hard-coded a recursion limit of 10 and flatness of 1... should we make + // this an option somewhere? + while (numCurves > 0) + { + // The curve is flat enough, or we've reached our recursion limit, + // so take the current start/end points and add it as a line segment + // to our final approximated curves + if (curves[numCurves - 1].getFlatnessSq() <= (radius / 3) || numCurves == 10) + { + Segment[] displaced = new LineSegment( + curves[numCurves - 1].getP1(), + curves[numCurves - 1].getP2()).getDisplacedSegments(radius); + if (segmentTop == null) + { + segmentTop = displaced[0]; + segmentBottom = displaced[1]; + } + else + { + segmentTop.add(displaced[0]); + segmentBottom.add(displaced[1]); + } + numCurves--; + } + + // Otherwise, subdivide again and continue + else + { + CubicCurve2D left = new CubicCurve2D.Double(); + CubicCurve2D right = new CubicCurve2D.Double(); + curves[numCurves - 1].subdivide(left, right); + curves[numCurves - 1] = right; + curves[numCurves] = left; + curves[numCurves - 1] = right; + curves[numCurves] = left; + numCurves++; + } + } + + return new Segment[] { segmentTop, segmentBottom }; + } + + public void reverse() + { + Point2D temp = P1; + P1 = P2; + P2 = temp; + temp = cp1; + cp1 = cp2; + cp2 = temp; + } + + public double[] cp1() + { + return new double[]{cp1.getX(), cp1.getY()}; + } + + public double[] cp2() + { + return new double[]{cp2.getX(), cp2.getY()}; + } +} // class CubicSegment 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 000000000..7e5fb5638 --- /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/LineSegment.java b/libjava/classpath/gnu/java/awt/java2d/LineSegment.java new file mode 100644 index 000000000..904037989 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/LineSegment.java @@ -0,0 +1,118 @@ +/* LineSegment.java -- Line segment used for BasicStroke + 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.geom.Point2D; + +public class LineSegment extends Segment +{ + public LineSegment(double x1, double y1, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + } + + public LineSegment(Point2D p1, Point2D p2) + { + super(); + P1 = (Point2D) p1.clone(); + P2 = (Point2D) p2.clone(); + } + + /** + * Clones this segment + */ + public Object clone() + { + LineSegment segment = null; + + try + { + segment = (LineSegment) super.clone(); + segment.P1 = (Point2D) P1.clone(); + segment.P2 = (Point2D) P2.clone(); + } + catch (CloneNotSupportedException cnse) + { + InternalError ie = new InternalError(); + ie.initCause(cnse); + throw ie; + } + + return segment; + } + + /** + * Get the "top" and "bottom" segments of this segment. + * First array element is p0 + normal, second is p0 - normal. + */ + public Segment[] getDisplacedSegments(double radius) + { + this.radius = radius; + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = P2.getX(); + double y1 = P2.getY(); + double[] p = normal(x0, y0, x1, y1); + Segment s1 = (new LineSegment(x0 + p[0], y0 + p[1], + x1 + p[0], y1 + p[1] )); + Segment s2 = (new LineSegment(x0 - p[0], y0 - p[1], + x1 - p[0], y1 - p[1] )); + return new Segment[]{s1, s2}; + } + + public void reverse() + { + Point2D p = P1; + P1 = P2; + P2 = p; + } + + public double[] cp1() + { + return new double[]{P2.getX(), P2.getY()}; + } + + public double[] cp2() + { + return new double[]{P1.getX(), P1.getY()}; + } +} // class LineSegment diff --git a/libjava/classpath/gnu/java/awt/java2d/PixelCoverage.java b/libjava/classpath/gnu/java/awt/java2d/PixelCoverage.java new file mode 100644 index 000000000..356021b3d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/PixelCoverage.java @@ -0,0 +1,132 @@ +package gnu.java.awt.java2d; + +/** + * Stores and handles the pixel converage for a scanline. The pixel coverage + * is stored as sorted list of buckets, each of which holds information about + * the coverage for the X and Y axis. This is utilized to compute the actual + * coverage for each pixel on the scanline and finding chunks of pixels with + * equal coverage. + */ +final class PixelCoverage +{ + + /** + * One bucket in the list. + */ + private static final class Bucket + { + /** + * The X coordinate on the scanline to which this bucket belongs. + */ + int xPos; + + /** + * The X coverage. + */ + int xCov; + + /** + * The Y coverage. + */ + int yCov; + + /** + * Implements a linked list. This points to the next element of the list. + */ + Bucket next; + + /** + * Implements a linked list. This points to the previous element of the + * list. + */ + Bucket prev; + } + + /** + * The head of the sorted list of buckets. + */ + private Bucket head; + + /** + * The current bucket. We make use of the fact that the scanline converter + * always scans the scanline (and thus this list) from left to right to + * quickly find buckets or insertion points. + */ + private Bucket current; + + /** + * The bucket after the last valid bucket. Unused buckets are not thrown + * away and garbage collected. Instead, we keep them at the tail of the list + * and reuse them when necessary. + */ + private Bucket last; + + /** + * Indicates the the next scan of the scanline begins and that the next + * request will be at the beginning of this list. This makes searching and + * sorting of this list very quick. + */ + void rewind() + { + current = head; + } + + /** + * Clears the list. This does not throw away the old buckets but only + * resets the end-pointer of the list to the first element. All buckets are + * then unused and are reused when the list is filled again. + */ + void clear() + { + last = head; + } + + /** + * This adds the specified x and y coverage to the pixel at the specified + * X position. + * + * @param x the X position + * @param xc the x coverage + * @param yc the y coverage + */ + void add(int x, int xc, int yc) + { + Bucket bucket = findOrInsert(x); + bucket.xCov += xc; + bucket.yCov += yc; + } + + /** + * Finds the bucket in the list with the specified X coordinate. + * If no such bucket is found, then a new one is fetched (either a cached + * bucket from the end of the list or a newly allocated one) inserted at the + * correct position and returned. + * + * @param x the X coordinate + * + * @return a bucket to hold the coverage data + */ + private Bucket findOrInsert(int x) + { + // First search for a matching bucket. + if (head == null) + { + // Special case: the list is still empty. + head = new Bucket(); + current = head; + return head; + } + + // This performs a linear search, starting from the current bucket. + // This is reasonably efficient because access to this list is always done + // in a linear fashion and we are not more then 1 or 2 buckets away from + // the one we're looking for. + Bucket match = current; + while (match != null && match.xPos != x) + { + + } + + return match; + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/Pixelizer.java b/libjava/classpath/gnu/java/awt/java2d/Pixelizer.java new file mode 100644 index 000000000..43e53bf63 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/Pixelizer.java @@ -0,0 +1,56 @@ +/* Pixelizer.java -- Interface for the target of the rasterizer + Copyright (C) 2007 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; + +/** + * A pixelizer is responsible for actually manipulating the pixel of a drawing + * surface after the scanline conversion process. It receives coverage + * information for a scanline and adjusts the surface pixels accordingly. + */ +public interface Pixelizer +{ + + /** + * Renders the pixel for one scanline at the Y location <code>y</code> + * and using the coverage information in <code>sc</code>. + * + * @param y the scanline Y coordinate + * @param sc the coverage information + */ + void renderScanline(int y, ScanlineCoverage sc); +} diff --git a/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java new file mode 100644 index 000000000..eb0cc7f8f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java @@ -0,0 +1,171 @@ +/* PolyEdge.java -- An edge in a polygon, used for polygon filling + 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 gnu.java.math.Fixed; + +/** + * An edge in a polygon. + * + * @author Roman Kennke (kennke@aicas.com) + */ +final class PolyEdge + implements Comparable +{ + + /** + * The start and end coordinates of the edge. y0 is always smaller or equal + * than y1. + * + * These values are stored as fixed-point decimals. + */ + public int x0, y0, x1, y1; + + /** + * The slope of the edge. This is dx / dy. + * + * This is a fixed point decimal. + */ + private int slope; + + /** + * The intersection of this edge with the current scanline. + * + * This is a fixed point decimal. + */ + int xIntersection; + + /** + * Indicates whether this edge is from the clip or from the target shape. + */ + boolean isClip; + + /** + * Implements a linked list for the edge pool. + */ + PolyEdge poolNext; + + /** + * Implements a linked list for the scanline edge lists. + */ + PolyEdge scanlineNext; + + /** + * Create an uninitialized edge. + */ + PolyEdge() + { + // Nothing to do here. + } + + /** + * Creates a new PolyEdge with the specified coordinates. + * + * @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 + */ + PolyEdge(int n, int x0, int y0, int x1, int y1, boolean clip) + { + init(n, x0, y0, x1, y1, clip); + } + + /** + * (Re-) Initializes this edge. + * + * @param x0 + * @param y0 + * @param x1 + * @param y1 + */ + void init(int n, int x0, int y0, int x1, int y1, boolean clip) + { + isClip = clip; + if (y0 < y1) + { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + } + else + { + this.x0 = x1; + this.y0 = y1; + this.x1 = x0; + this.y1 = y0; + } + slope = Fixed.div(n, this.x1 - this.x0, this.y1 - this.y0); + } + + /** + * Sorts PolyEdges by the x coordinate from the minimum x value. + */ + public int compareTo(Object o) + { + PolyEdge other = (PolyEdge) o; + int comp = 0; + if (x0 < other.x0) + comp = -1; + else if (x0 > other.x0) + comp = 1; + return comp; + } + + /** + * Intersects this edge with the scanline at height y. The result is + * stored in {@link #xIntersection}. + * + * @param y the scanline + */ + void intersect(int n, int y) + { + int dy = y - y0; + int dx = Fixed.mul(n, slope, dy); + xIntersection = x0 + dx; + } + + public String toString() + { + return "Edge: " + x0 + ", " + y0 + ", " + x1 + ", " + y1 + ", slope: " + + slope + ", xIntersection: " + xIntersection + + ", isClip: " + isClip; + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/PolyEdgeComparator.java b/libjava/classpath/gnu/java/awt/java2d/PolyEdgeComparator.java new file mode 100644 index 000000000..6706f2294 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/PolyEdgeComparator.java @@ -0,0 +1,70 @@ +/* PolyEdgeComparator.java -- Sorts PolyEdges by their current intersection + points + 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.util.Comparator; + +/** + * Sorts {@link PolyEdge}s by their current intersection points. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class PolyEdgeComparator + implements Comparator +{ + + /** + * The current scanline. + */ + int y; + + public int compare(Object o1, Object o2) + { + PolyEdge edge1 = (PolyEdge) o1; + PolyEdge edge2 = (PolyEdge) o2; + int comp = 0; + if (edge1.xIntersection < edge2.xIntersection) + comp = -1; + else if (edge1.xIntersection > edge2.xIntersection) + comp = 1; + return comp; + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java b/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java new file mode 100644 index 000000000..b8d0ff52e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/QuadSegment.java @@ -0,0 +1,260 @@ +/* QuadSegment.java -- QuadCurve segment used for BasicStroke + 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.geom.Point2D; +import java.awt.geom.QuadCurve2D; + +/** + * Quadratic Bezier curve segment + * + * Note: Most peers don't support quadratics directly, so it might make + * sense to represent them as cubics internally and just be done with it. + * I think we should be peer-agnostic, however, and stay faithful to the + * input geometry types as far as possible. + */ +public class QuadSegment extends Segment +{ + public Point2D cp; // control point + + /** + * Constructor, takes the coordinates of the start, control, + * and end point, respectively. + */ + public QuadSegment(double x1, double y1, double cx, double cy, double x2, + double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp = new Point2D.Double(cx, cy); + } + + public QuadSegment(Point2D p1, Point2D cp, Point2D p2) + { + super(); + P1 = p1; + P2 = p2; + this.cp = cp; + } + + public QuadSegment(QuadCurve2D curve) + { + super(); + P1 = curve.getP1(); + P2 = curve.getP2(); + this.cp = curve.getCtrlPt(); + } + + /** + * Clones this segment + */ + public Object clone() + { + QuadSegment segment = null; + + try + { + segment = (QuadSegment) super.clone(); + + segment.P1 = (Point2D) P1.clone(); + segment.P2 = (Point2D) P2.clone(); + segment.cp = (Point2D) cp.clone(); + } + catch (CloneNotSupportedException cnse) + { + InternalError ie = new InternalError(); + ie.initCause(cnse); + throw ie; + } + + return segment; + } + + /** + * Get the "top" and "bottom" segments of a given segment. + * First array element is p0 + normal, second is p0 - normal. + */ + public Segment[] getDisplacedSegments(double radius) + { + this.radius = radius; + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + QuadCurve2D left = new QuadCurve2D.Double(); + QuadCurve2D right = new QuadCurve2D.Double(); + QuadCurve2D orig = new QuadCurve2D.Double(x0, y0, x1, y1, x2, y2); + orig.subdivide(left, right); + + QuadSegment s1 = offsetSubdivided(left, true); + QuadSegment s2 = offsetSubdivided(left, false); + + s1.add( offsetSubdivided(right, true) ); + s2.add( offsetSubdivided(right, false) ); + + return new Segment[]{s1, s2}; + } + + private QuadSegment offsetSubdivided(QuadCurve2D curve, boolean plus) + { + double[] n1 = normal(curve.getX1(), curve.getY1(), + curve.getCtrlX(), curve.getCtrlY()); + double[] n2 = normal(curve.getCtrlX(), curve.getCtrlY(), + curve.getX2(), curve.getY2()); + + Point2D cp; + QuadSegment s; + if(!plus) + { + n1[0] = -n1[0]; + n1[1] = -n1[1]; + n2[0] = -n2[0]; + n2[1] = -n2[1]; + } + + // Handle special cases where the control point is equal to an end point + // or end points are equal (ie, straight lines) + if (curve.getP1().equals(curve.getCtrlPt())) + { + cp = curve.getCtrlPt(); + cp.setLocation(cp.getX() + n2[0], cp.getY() + n2[1]); + n1[0] = n2[0]; + n1[1] = n2[1]; + } + else if (curve.getP2().equals(curve.getCtrlPt())) + { + cp = curve.getCtrlPt(); + cp.setLocation(cp.getX() + n1[0], cp.getY() + n1[1]); + n2[0] = n1[0]; + n2[1] = n1[1]; + } + else if (curve.getP1().equals(curve.getP2())) + { + cp = curve.getCtrlPt(); + + double deltaX = curve.getX1() - curve.getCtrlX(); + double deltaY = curve.getY1() - curve.getCtrlY(); + double length = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)); + double ratio = radius / length; + deltaX *= ratio; + deltaY *= ratio; + + if (plus) + cp.setLocation(cp.getX() + deltaX, cp.getY() + deltaY); + else + cp.setLocation(cp.getX() - deltaX, cp.getY() - deltaY); + } + else if (n1[0] == n2[0] && n1[1] == n2[1]) + { + cp = curve.getCtrlPt(); + cp.setLocation(cp.getX() + n1[0], cp.getY() + n1[1]); + } + else + { + cp = lineIntersection(curve.getX1() + n1[0], + curve.getY1() + n1[1], + curve.getCtrlX() + n1[0], + curve.getCtrlY() + n1[1], + curve.getCtrlX() + n2[0], + curve.getCtrlY() + n2[1], + curve.getX2() + n2[0], + curve.getY2() + n2[1], true); + } + + s = new QuadSegment(curve.getX1() + n1[0], curve.getY1() + n1[1], + cp.getX(), cp.getY(), + curve.getX2() + n2[0], curve.getY2() + n2[1]); + + return s; + } + + private Point2D lineIntersection(double X1, double Y1, + double X2, double Y2, + double X3, double Y3, + double X4, double Y4, + boolean infinite) + { + double x1 = X1; + double y1 = Y1; + double rx = X2 - x1; + double ry = Y2 - y1; + + double x2 = X3; + double y2 = Y3; + double sx = X4 - x2; + double sy = Y4 - y2; + + double determinant = sx * ry - sy * rx; + double nom = (sx * (y2 - y1) + sy * (x1 - x2)); + + // lines can be considered parallel. + if (Math.abs(determinant) < 1E-6) + return null; + + nom = nom / determinant; + + // check if lines are within the bounds + if(!infinite && (nom > 1.0 || nom < 0.0)) + return null; + + return new Point2D.Double(x1 + nom * rx, y1 + nom * ry); + } + + public void reverse() + { + Point2D p = P1; + P1 = P2; + P2 = p; + } + + public double[] cp1() + { + return new double[]{cp.getX(), cp.getY()}; + } + + public double[] cp2() + { + return new double[]{cp.getX(), cp.getY()}; + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java b/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java new file mode 100644 index 000000000..193ea4c7c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/RasterGraphics.java @@ -0,0 +1,118 @@ +/* RasterGraphics.java -- A Graphics2D impl for Rasters + 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.GraphicsConfiguration; +import java.awt.Rectangle; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; + +/** + * A Graphics2D implementation that operates on Raster objects. This is + * primarily used for BufferedImages, but can theoretically be used on + * arbitrary WritableRasters. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class RasterGraphics + extends AbstractGraphics2D +{ + + /** + * The raster on which we operate. + */ + private WritableRaster raster; + + /** + * The color model of this Graphics instance. + */ + private ColorModel colorModel; + + public RasterGraphics(WritableRaster r, ColorModel cm) + { + super(); + raster = r; + colorModel = cm; + init(); + } + + @Override + public void renderScanline(int y, ScanlineCoverage c) + { + if (y >= getDeviceBounds().width) + return; + + super.renderScanline(y, c); + } + + /** + * Returns the color model of this Graphics object. + * + * @return the color model of this Graphics object + */ + protected ColorModel getColorModel() + { + return colorModel; + } + + /** + * Returns a WritableRaster that is used by this class to perform the + * rendering in. It is not necessary that the target surface immediately + * reflects changes in the raster. Updates to the raster are notified via + * {@link AbstractGraphics2D#updateRaster}. + * + * @return the destination raster + */ + protected WritableRaster getDestinationRaster() + { + return raster; + } + + public GraphicsConfiguration getDeviceConfiguration() + { + // TODO Auto-generated method stub + return null; + } + + @Override + protected Rectangle getDeviceBounds() + { + return this.raster.getBounds(); + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/Scanline.java b/libjava/classpath/gnu/java/awt/java2d/Scanline.java new file mode 100644 index 000000000..24c3d34d5 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/Scanline.java @@ -0,0 +1,91 @@ +/* Scanline.java -- A scanline for the scanline converter + 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; + +/** + * Represents a scanline in the {@link ScanlineConverter}. This is basically + * a sorted list of {@link PolyEdge}s that is made for maximum reuse. + */ +class Scanline +{ + + /** + * The actual edges array. The fields can be null. + */ + private PolyEdge edges; + + /** + * Clears this scanline. This only resets the number of edges to 0. The + * actual PolyEdge objects are preserved for possible later reuse. + */ + void clear() + { + edges = null; + } + + /** + * Create a new Scanline. + */ + Scanline() + { + // Nothing to do. + } + + /** + * Inserts an edge into this scanline. This is performed in a sorted fashion, + * and so that it reuses as much existing resources as possible. + */ + void addEdge(PolyEdge edge) + { + + // Allocate PolyEdge when necessary or reuse an old one. + edge.scanlineNext = edges; + edges = edge; + } + + /** + * Returns the edges queue. + * + * @return the edges queue + */ + PolyEdge getEdges() + { + return edges; + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java b/libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java new file mode 100644 index 000000000..2c3481b6c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java @@ -0,0 +1,451 @@ +/* ScanlineConverter.java -- Rasterizes Shapes + Copyright (C) 2006, 2007 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 gnu.java.math.Fixed; + +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; + +/** + * Rasterizes {@link Shape} objects on an AbstractGraphics2D. + */ +public final class ScanlineConverter +{ + + /** + * The number of digits to use for fixed point arithmetics. + */ + private static int FIXED_DIGITS = 6; + + /** + * The fixed point constant for the number one. + */ + private static int ONE = Fixed.fixedValue(FIXED_DIGITS, 1); + + /** + * The actual number of scanlines. + */ + private int numScanlines; + + /** + * The number of scanlines. This can contain more elements than we have + * scanlines. The real number of scanlines is stored in + * {@link #numScanlines}. This can also contain null values for empty + * scanlines. + */ + private Scanline[] scanlines; + + /** + * The upper bounds which correspond to the index 0 in the scanline array. + * + * This is a fixed point value. + */ + private int upperBounds; + + /** + * The resolution of the scanline converter. + * + * This is a fixed point value. + */ + private int resolution; + + /** + * The number of significant bits for the 'Y' resolution. + */ + private int yResolution; + + /** + * One half step according to the resolution. This is stored to avoid + * unnecessary operations during rendering. + */ + private int halfStep; + + /** + * This is used in {@link #addShape(PathIterator, boolean)} to + * receive the coordinates of the path. + */ + private float[] coords; + + /** + * The active edges. + */ + private ActiveEdges activeEdges; + + private PolyEdge edgePool; + private PolyEdge edgePoolLast; + + private int minY; + private int maxY; + private int minX; + private int maxX; + + /** + * Holds and manages information about the pixel coverage. + */ + private ScanlineCoverage scanlineCoverage; + + /** + * Create a new ScanlineConverter. + */ + ScanlineConverter() + { + scanlines = new Scanline[10]; + coords = new float[6]; + activeEdges = new ActiveEdges(); + edgePool = new PolyEdge(); + edgePoolLast = edgePool; + scanlineCoverage = new ScanlineCoverage(); + } + + /** + * Renders the specified shape using the specified clip and transform. + * + * @param p the pixelizer that receives the coverage information + * @param shape the shape to render + * @param clip the clip + * @param trans the transform + */ + public void renderShape(Pixelizer p, Shape shape, Shape clip, + AffineTransform trans, int res, int yRes, + RenderingHints hints) + { + // TODO: Do something useful with the rendering hints. Like, adjusting + // the resolution. + + // Prepare resolution and upper bounds. + clear(); + setResolution(res, yRes); + + boolean haveClip = clip != null; + + // Add shapes. + float flatness = Fixed.floatValue(FIXED_DIGITS, resolution / 2); + PathIterator path = shape.getPathIterator(trans, flatness); + addShape(path, false); + if (haveClip) + { + path= clip.getPathIterator(trans, flatness); + addShape(path, true); + } + + setUpperBounds(minY); + + PolyEdge edge = edgePool; + while (edge != edgePoolLast) + { + addEdge(edge); + edge = edge.poolNext; + } + + int y = upperBounds; + int index; + activeEdges.clear(); + // The render loop... + Scanline scanline = null; + int lastRealY = Fixed.intValue(FIXED_DIGITS, y); + while (y <= maxY) + { + // First we put together our list of active edges. + index = scanlineIndex(y); + // If we go outside the scanline array we still need to render the + // remaining edges until they end. + scanline = index < scanlines.length ? scanlines[index] : null; + if (scanline != null) + { + edge = scanline.getEdges(); + while (edge != null) + { + activeEdges.add(edge); + edge = edge.scanlineNext; + } + } + + // Then we intersect all active edges with the current scanline + // and sort them according to their intersection points. + activeEdges.intersectSortAndPack(FIXED_DIGITS, y + halfStep); + + // Ok, now we can perform the actual scanlining. + int realY = Fixed.intValue(FIXED_DIGITS, y + resolution); + boolean push = lastRealY != realY; + + doScanline(p, y, push, haveClip); + + // Remove obsolete active edges. + //activeEdges.remove(y + halfStep); + // Go on with the next line... + y += resolution; + lastRealY = realY; + + } + } + + /** + * Clears all scanlines. + */ + private void clear() + { + // Reset edge pool. + edgePoolLast = edgePool; + + // Reset scanlines. + for (int i = scanlines.length - 1; i >= 0 ; i--) + { + Scanline sl = scanlines[i]; + if (sl != null) + sl.clear(); + } + + // Reset scanline coverage. + scanlineCoverage.clear(); + + // Reset bounds. + minY = Integer.MAX_VALUE; + maxY = Integer.MIN_VALUE; + minX = Integer.MAX_VALUE; + maxX = Integer.MIN_VALUE; + } + + /** + * Performs the scanlining on the current set of active edges. + * + * @param p the pixelizer to receive the pixel coverage data + * @param y the Y coordinate + * @param push true when the scanline is ready to be pushed to the + * pixelizer + * @param haveClip true when there's a clip, false otherwise + */ + private void doScanline(Pixelizer p, int y, boolean push, + boolean haveClip) + { + // First, rewind the scanline coverage. + scanlineCoverage.rewind(); + + // We begin outside the clip and outside the shape. We only draw when + // we are inside the clip AND inside the shape. + boolean inClip = ! haveClip; + boolean inShape = false; + PolyEdge lastEdge = null; + int numEdges = activeEdges.getNumActiveEdges(); + for (int i = 0; i < numEdges; i++) + { + PolyEdge edge = activeEdges.getActiveEdge(i); + if (inClip && inShape) + { + assert lastEdge != null; + int x0 = lastEdge.xIntersection; + int x1 = edge.xIntersection; + assert x0 <= x1; + + int pix0 = Fixed.intValue(FIXED_DIGITS, x0); + int pix1 = Fixed.intValue(FIXED_DIGITS, x1); + int frac0 = ONE - Fixed.trunc(FIXED_DIGITS, x0); + int frac1 = ONE - Fixed.trunc(FIXED_DIGITS, x1); + // Only keep the first 4 digits after the point. + frac0 = frac0 >> (FIXED_DIGITS - yResolution); + frac1 = frac1 >> (FIXED_DIGITS - yResolution); + scanlineCoverage.add(pix0, 1 * (1 << yResolution), frac0); + scanlineCoverage.add(pix1, -1 * (1 << yResolution), -frac1); + } + if (edge.isClip) + inClip = ! inClip; + else + inShape = ! inShape; + + lastEdge = edge; + } + + // Push out the whole scanline to the pixelizer. + if (push && ! scanlineCoverage.isEmpty()) + { + p.renderScanline(Fixed.intValue(FIXED_DIGITS, y), scanlineCoverage); + scanlineCoverage.clear(); + } + } + + + /** + * Sets the resolution. A value of 0 rasterizes the shape normally without + * anti-aliasing. Greater values renders with a resolution of 2 ^ res. + * + * @param res the resolution + */ + private void setResolution(int res, int yRes) + { + int scanlinesPerPixel = 1 << res; + int one = Fixed.fixedValue(FIXED_DIGITS, 1); + resolution = one / (scanlinesPerPixel); + halfStep = resolution / 2; + + scanlineCoverage.setMaxCoverage(scanlinesPerPixel << yResolution); + + yResolution = yRes; + } + + /** + * Sets the vertical bounds of that shape that is beeing rendered. + * + * @param y0 the upper bounds + */ + private void setUpperBounds(int y0) + { + upperBounds = fit(y0); + } + + /** + * Add a shape to the scanline converter. + * + * @param path + * @param clip + */ + private void addShape(PathIterator path, boolean clip) + { + int startX = 0; + int startY = 0; + int lastX = 0; + int lastY = 0; + while (! path.isDone()) + { + int type = path.currentSegment(coords); + switch (type) + { + case PathIterator.SEG_MOVETO: + startX = lastX = Fixed.fixedValue(FIXED_DIGITS, coords[0]); + startY = lastY = Fixed.fixedValue(FIXED_DIGITS, coords[1]); + minY = Math.min(startY, minY); + maxY = Math.max(startY, maxY); + minX = Math.min(startX, minX); + maxX = Math.max(startX, maxX); + break; + case PathIterator.SEG_LINETO: + int x = Fixed.fixedValue(FIXED_DIGITS, coords[0]); + int y = Fixed.fixedValue(FIXED_DIGITS, coords[1]); + edgePoolAdd(lastX, lastY, x, y, clip); + lastX = x; + lastY = y; + minY = Math.min(lastY, minY); + maxY = Math.max(lastY, maxY); + minX = Math.min(lastX, minX); + maxX = Math.max(lastX, maxX); + break; + case PathIterator.SEG_CLOSE: + edgePoolAdd(lastX, lastY, startX, startY, clip); + lastX = startX; + lastY = startY; + break; + case PathIterator.SEG_CUBICTO: + case PathIterator.SEG_QUADTO: + default: + assert false; + } + path.next(); + } + } + + /** + * Adds an edge into the scanline array. + */ + private void addEdge(PolyEdge edge) + { + // Determine index. + int upper = Math.min(edge.y0, edge.y1); + // Fit to raster. + int index = scanlineIndex(upper); + // Grow array when necessary. + if (index >= scanlines.length) + { + int oldSize = scanlines.length; + int newSize = Math.max(oldSize + oldSize / 2 + 1, index + 10); + Scanline[] newScanlines = new Scanline[newSize]; + System.arraycopy(scanlines, 0, newScanlines, 0, oldSize); + scanlines = newScanlines; + } + + // Add edge. + if (scanlines[index] == null) + { + scanlines[index] = new Scanline(); + } + scanlines[index].addEdge(edge); + } + + /** + * Fits an Y coordinate to the grid. + * + * @param y the Y coordinate to fit + * + * @return the fitted Y coordinate + */ + private int fit(int y) + { + int val1 = Fixed.div(FIXED_DIGITS, y, resolution); + int rounded = Fixed.round(FIXED_DIGITS, val1); + return Fixed.mul(FIXED_DIGITS, rounded, resolution); + } + + /** + * Calculates the scanline index for the specified y coordinate. + * + * @param y the y coordinate as fixed point value + * + * @return the scanline index + */ + private int scanlineIndex(int y) + { + int fitted = fit(y); + // Cleverly skip the fixed point conversions here. + return (fitted - upperBounds)/ resolution; + } + + private void edgePoolAdd(int x0, int y0, int x1, int y1, boolean clip) + { + // Don't need no horizontal edges. + if (y0 != y1) + { + edgePoolLast.init(FIXED_DIGITS, x0, y0, x1, y1, clip); + if (edgePoolLast.poolNext == null) + { + edgePoolLast.poolNext = new PolyEdge(); + } + edgePoolLast = edgePoolLast.poolNext; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/java2d/ScanlineCoverage.java b/libjava/classpath/gnu/java/awt/java2d/ScanlineCoverage.java new file mode 100644 index 000000000..9ffb63e1d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/ScanlineCoverage.java @@ -0,0 +1,630 @@ +/* ScanlineCoverage.java -- Manages coverage information for a scanline + Copyright (C) 2007 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; + +/** + * Stores and handles the pixel converage for a scanline. The pixel coverage + * is stored as sorted list of {@linke Covergage} entries, each of which holds + * information about the coverage for the X and Y axis. This is utilized to + * compute the actual coverage for each pixel on the scanline and finding + * chunks of pixels with equal coverage quickly. + */ +public final class ScanlineCoverage +{ + + /** + * Iterates over the coverage list and calculates the actual coverage + * ranges on a scanline. + */ + public final class Iterator + { + /** + * This instance is reused in the iteration. + */ + private Range range; + + /** + * The pointer to the current item in the iteration. + */ + private Coverage currentItem; + + /** + * The current coverage value. + */ + private int currentCoverage; + + /** + * True when the current pixel coverage has already been handled, false + * otherwise. + */ + private boolean handledPixelCoverage; + + /** + * Creates a new CoverageIterator. + */ + Iterator() + { + range = new Range(); + } + + /** + * Returns the next coverage range on the scanline. The returned object + * will always be the same object, but with different values. Keep that + * in mind when dealing with this object. + * + * @return the next coverage range on the scanline + */ + public Range next() + { + // TODO: Lump together the single-pixel coverage and the + // between-pixel coverage when the pixel coverage delta is 0. + if (handledPixelCoverage == false) + { + // Handle single pixel coverage. + range.setXPos(currentItem.xPos); + range.setLength(1); + range.setCoverage(currentCoverage + currentItem.pixelCoverage); + handledPixelCoverage = true; + } + else + { + // Handle pixel span coverage. + currentCoverage += currentItem.covDelta; + range.setCoverage(currentCoverage); + range.setXPos(currentItem.xPos + 1); + currentItem = currentItem.next; + range.setLength(currentItem.xPos - range.xPos); + handledPixelCoverage = false; + } + return range; + } + + /** + * Returns {@ true} when there are more coverage ranges to iterate, + * {@ false} otherwise. + * + * @return {@ true} when there are more coverage ranges to iterate, + * {@ false} otherwise + */ + public boolean hasNext() + { + boolean hasNext; + if (currentItem != null && handledPixelCoverage == false) + { + // We have at least one more coverage item when there's a pixel + // coverage piece left. + hasNext = true; + } + else if (currentItem == null || currentItem.next == null + || currentItem.next == last) + { + hasNext = false; + } + else + { + hasNext = true; + } + return hasNext; + } + + /** + * Resets this iterator to the start of the list. + */ + void reset() + { + currentItem = head; + currentCoverage = 0; + handledPixelCoverage = false; + } + } + + /** + * A data object that carries information about pixel coverage on a scanline. + * The data consists of a starting X position on the scanline, the + * length of the range in pixels and the actual coverage value. + **/ + public static final class Range + { + /** + * The X position on the scanline, in pixels. + */ + private int xPos; + + /** + * The length of the range, in pixels. + */ + private int length; + + /** + * The actual coverage. The relation depends on + * {@link ScanlineCoverage#maxCoverage}. + */ + private int coverage; + + /** + * Creates a new CoverageRange object. + */ + Range() + { + // Nothing to do. The values get initialized in the corresponding + // setters. + } + + /** + * Sets the X start position (left) on the scanline. This value is + * considered to be in pixels and device space. + * + * @param x the x position + */ + void setXPos(int x) + { + xPos = x; + } + + /** + * Returns the X start position (left) on the scanline. This value + * is considered to be in pixels and device space. + * + * @return the X position on the scanline + */ + public int getXPos() + { + return xPos; + } + + /** + * Sets the length of the pixel range. This is in pixel units. + * + * @param l the length of the range + */ + void setLength(int l) + { + length = l; + } + + /** + * Returns the length of the range in pixel units. + * + * @return the length of the range in pixel units + */ + public int getLength() + { + return length; + } + + /** + * Returns the first X position after the range. + * + * @return the first X position after the range + */ + public int getXPosEnd() + { + return xPos + length; + } + + /** + * Sets the coverage of the pixel range. The relation of that value + * depends on {@link ScanlineCoverage#maxCoverage}. + * + * @param cov the coverage value for the pixel range + */ + void setCoverage(int cov) + { + coverage = cov; + } + + /** + * Returns the coverage of the pixel range. The relation of this value + * depends on {@link ScanlineCoverage#getMaxCoverage()}. + * + * @return the coverage of the pixel range + */ + public int getCoverage() + { + return coverage; + } + + /** + * Returns a string representation. + */ + public String toString() + { + return "Coverage range: xPos=" + xPos + ", length=" + length + + ", coverage: " + coverage; + } + } + + /** + * One bucket in the list. + */ + private static final class Coverage + { + /** + * The X coordinate on the scanline to which this bucket belongs. + */ + int xPos; + + /** + * The coverage delta from the pixel at xPos to xPos + 1. + */ + int covDelta; + + /** + * The delta for the pixel at xPos. This is added to the pixel at xPos, + * but not to the following pixel. + */ + int pixelCoverage; + + /** + * Implements a linked list. This points to the next element of the list. + */ + Coverage next; + + /** + * Returns the X coordinate for this entry. + * + * @return the X coordinate for this entry + */ + public int getXPos() + { + return xPos; + } + + /** + * Returns the coverage delta for this entry. + * + * @return the coverage delta for this entry + */ + public int getCoverageDelta() + { + return covDelta; + } + + /** + * Returns a string representation. + * + * @return a string representation + */ + public String toString() + { + return "Coverage: xPos: " + xPos + ", covDelta: " + covDelta; + } + + /** + * Returns a string representation of this entry and all the following + * in the linked list. + * + * @return a string representation of this entry and all the following + * in the linked list + */ + public String list() + { + String str = toString(); + if (next != null) + str = str + " --> " + next.list(); + return str; + } + } + + /** + * The head of the sorted list of buckets. + */ + private Coverage head; + + /** + * The current bucket. We make use of the fact that the scanline converter + * always scans the scanline (and thus this list) from left to right to + * quickly find buckets or insertion points. + */ + private Coverage current; + + /** + * The item that is before current in the list. + */ + private Coverage currentPrev; + + /** + * The bucket after the last valid bucket. Unused buckets are not thrown + * away and garbage collected. Instead, we keep them at the tail of the list + * and reuse them when necessary. + */ + private Coverage last; + + /** + * The last valid entry. + */ + private Coverage lastPrev; + + /** + * The minimum X coordinate of this scanline. + */ + private int minX; + + /** + * The maximum X coordinate of this scanline. + */ + private int maxX; + + /** + * The maximum coverage value. + */ + private int maxCoverage; + + /** + * The iterator over the ranges of this scanline. + */ + private Iterator iterator; + + /** + * Creates a new ScanlineCoverage instance. + */ + public ScanlineCoverage() + { + iterator = new Iterator(); + } + + /** + * Indicates the the next scan of the scanline begins and that the next + * request will be at the beginning of this list. This makes searching and + * sorting of this list very quick. + */ + public void rewind() + { + current = head; + currentPrev = null; + } + + /** + * Clears the list. This does not throw away the old buckets but only + * resets the end-pointer of the list to the first element. All buckets are + * then unused and are reused when the list is filled again. + */ + public void clear() + { + last = head; + lastPrev = null; + current = head; + currentPrev = null; + minX = Integer.MAX_VALUE; + maxX = Integer.MIN_VALUE; + } + + /** + * This adds the specified coverage to the pixel at the specified + * X position. + * + * @param x the X position + * @param xc the x coverage + * @param yc the y coverage + */ + public void add(int x, int xc, int yc) + { + Coverage bucket = findOrInsert(x); + bucket.covDelta += xc; + bucket.pixelCoverage += yc; + minX = Math.min(minX, x); + maxX = Math.max(maxX, x); + } + + /** + * Returns the maximum coverage value for the scanline. + * + * @return the maximum coverage value for the scanline + */ + public int getMaxCoverage() + { + return maxCoverage; + } + + /** + * Sets the maximum coverage value for the scanline. + * + * @param maxCov the maximum coverage value for the scanline + */ + void setMaxCoverage(int maxCov) + { + maxCoverage = maxCov; + } + + /** + * Returns the maximum X coordinate of the current scanline. + * + * @return the maximum X coordinate of the current scanline + */ + public int getMaxX() + { + return maxX; + } + + /** + * Returns the minimum X coordinate of the current scanline. + * + * @return the minimum X coordinate of the current scanline + */ + public int getMinX() + { + return minX; + } + + /** + * Finds the bucket in the list with the specified X coordinate. + * If no such bucket is found, then a new one is fetched (either a cached + * bucket from the end of the list or a newly allocated one) inserted at the + * correct position and returned. + * + * @param x the X coordinate + * + * @return a bucket to hold the coverage data + */ + private Coverage findOrInsert(int x) + { + // First search for a matching bucket. + if (head == null) + { + // Special case: the list is still empty. + // Testpoint 1. + head = new Coverage(); + head.xPos = x; + current = head; + currentPrev = null; + return head; + } + + // This performs a linear search, starting from the current bucket. + // This is reasonably efficient because access to this list is always done + // in a linear fashion and we are usually not more then 1 or 2 buckets away + // from the one we're looking for. + Coverage match = current; + Coverage prev = currentPrev; + while (match != last && match.xPos < x) + { + prev = match; + match = match.next; + } + + // At this point we have either found an entry with xPos >= x, or reached + // the end of the list (match == last || match == null). + if (match == null) + { + // End of the list. No cached items to reuse. + // Testpoint 2. + match = new Coverage(); + match.xPos = x; + if (prev != null) + prev.next = match; + current = match; + currentPrev = prev; + return match; + } + else if (match == last) + { + // End of the list. Reuse this item. Expand list. + // Testpoint 3. + last = match.next; + lastPrev = match; + match.xPos = x; + match.covDelta = 0; + match.pixelCoverage = 0; + // Keep link to last element or null, indicating the end of the list. + current = match; + currentPrev = prev; + return match; + } + + if (x == match.xPos) + { + // Special case: We have another coverage entry at the same location + // as an already existing entry. Return this. + // Testpoint 4. + current = match; + currentPrev = prev; + return match; + } + else // x <= match.xPos + { + assert (x <= match.xPos); + assert (prev == null ||x > prev.xPos); + + // Create new entry, or reuse existing one. + Coverage cov; + if (last != null) + { + // Testpoint 5. + cov = last; + last = cov.next; + lastPrev.next = last; + } + else + { + // Testpoint 6. + cov = new Coverage(); + } + + cov.xPos = x; + cov.covDelta = 0; + cov.pixelCoverage = 0; + + // Insert this item in the list. + if (prev != null) + { + // Testpoint 5 & 6. + prev.next = cov; + cov.next = match; + current = cov; + currentPrev = prev; + } + else + { + // Testpoint 7. + assert (match == head); + // Insert at head. + head = cov; + head.next = match; + current = head; + currentPrev = null; + } + return cov; + } + } + + /** + * (Re-)Starts iterating the coverage values for the scanline. + * Use the returned iterator to get the consecutive coverage ranges. + * + * @return the iterator + */ + public Iterator iterate() + { + iterator.reset(); + return iterator; + } + + /** + * Returns {@ true} if this object has no entries for the current scanline, + * {@ false} otherwise. + * + * @return {@ true} if this object has no entries for the current scanline, + * {@ false} otherwise + */ + public boolean isEmpty() + { + return head == null || head == last + || head.next == null || head.next == last; + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/Segment.java b/libjava/classpath/gnu/java/awt/java2d/Segment.java new file mode 100644 index 000000000..1d9a57001 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/Segment.java @@ -0,0 +1,158 @@ +/* Segment.java -- Abstract segment used for BasicStroke + 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.geom.Point2D; + +public abstract class Segment implements Cloneable +{ + // Start and end points of THIS segment + public Point2D P1; + public Point2D P2; + + // Segments can be linked together internally as a linked list + public Segment first; + public Segment next; + public Segment last; + + // Half the stroke width + protected double radius; + + /** + * Create a new, empty segment + */ + public Segment() + { + P1 = P2 = null; + first = this; + next = null; + last = this; + } + + /** + * Add a segment to the polygon + * @param newsegment segment to add + */ + public void add(Segment newsegment) + { + newsegment.first = first; + last.next = newsegment; + last = last.next.last; + } + + /** + * Reverses the orientation of the whole polygon + */ + public void reverseAll() + { + reverse(); + first = last; + Segment v = next; + Segment former = this; + next = null; + + while (v != null) + { + v.reverse(); + v.last = this; + Segment oldnext = v.next; + v.next = former; + + former = v; + v = oldnext; // move to the next in list + } + } + + public String toString() + { + return "Segment:"+P1+", "+P2; + } + + /** + * Get the normal vector to the slope of the line. + * @return vector of length radius, normal to the (x0,y0)-(x1,y1) vector) + */ + protected double[] normal(double x0, double y0, double x1, double y1) + { + double dx = (x1 - x0); + double dy = (y1 - y0); + if( dy == 0 ) + { + dy = radius * ((dx > 0)?1:-1); + dx = 0; + } + else if( dx == 0 ) + { + dx = radius * ((dy > 0)?-1:1); + dy = 0; + } + else + { + double N = Math.sqrt(dx * dx + dy * dy); + double odx = dx; + dx = -radius * dy / N; + dy = radius * odx / N; + } + return new double[]{ dx, dy }; + } + + /** + * Reverse the current segment + */ + public abstract void reverse(); + + /** + * Get the "top" and "bottom" segments of a segment. + * First array element is p0 + normal, second is p0 - normal. + */ + public abstract Segment[] getDisplacedSegments(double radius); + + /** + * Returns the coordinates of the first control point, or the start point + * for a line segment. + */ + public abstract double[] cp1(); + + /** + * Returns the coordinates of the second control point, or the end point + * for a line segment. + */ + public abstract double[] cp2(); + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/ShapeCache.java b/libjava/classpath/gnu/java/awt/java2d/ShapeCache.java new file mode 100644 index 000000000..89a9ac4ab --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/ShapeCache.java @@ -0,0 +1,90 @@ +/* ShapeCache.java -- Caches certain Shapes for reuse in AbstractGraphics2D + 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.Polygon; +import java.awt.Rectangle; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.RoundRectangle2D; + +/** + * Caches certain Shape objects for reuse in AbstractGraphics2D. This avoids + * massive creation of such objects. + */ +public class ShapeCache +{ + + /** + * A cached Line2D. + */ + public Line2D line; + + /** + * A cached Rectangle. + */ + public Rectangle rect; + + /** + * A cached RoundRectangle2D. + */ + public RoundRectangle2D roundRect; + + /** + * A cached Ellipse2D. + */ + public Ellipse2D ellipse; + + /** + * A cached Arc2D. + */ + public Arc2D arc; + + /** + * A cached Polygon. + */ + public Polygon polygon; + + /** + * A cached polyline. + */ + public GeneralPath polyline; +} diff --git a/libjava/classpath/gnu/java/awt/java2d/ShapeWrapper.java b/libjava/classpath/gnu/java/awt/java2d/ShapeWrapper.java new file mode 100644 index 000000000..f4e77f450 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/ShapeWrapper.java @@ -0,0 +1,119 @@ +/* ShapeWrapper.java -- Protects shapes by wrapping them + Copyright (C) 2007 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.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * Protects any other shape from beeing modified by wrapping it. + */ +public class ShapeWrapper + implements Shape +{ + + /** + * The shape to be protected. + */ + private Shape shape; + + /** + * Creates a new ShapeWrapper. + * + * @param other the shape to be protected + */ + public ShapeWrapper(Shape other) + { + shape = other; + } + + public boolean contains(double x, double y) + { + return shape.contains(x, y); + } + + public boolean contains(Point2D p) + { + return shape.contains(p); + } + + public boolean contains(double x, double y, double w, double h) + { + return shape.contains(x, y, w, h); + } + + public boolean contains(Rectangle2D r) + { + return shape.contains(r); + } + + public Rectangle getBounds() + { + return shape.getBounds(); + } + + public Rectangle2D getBounds2D() + { + return shape.getBounds2D(); + } + + public PathIterator getPathIterator(AffineTransform transform) + { + return shape.getPathIterator(transform); + } + + public PathIterator getPathIterator(AffineTransform transform, double flatness) + { + return shape.getPathIterator(transform, flatness); + } + + public boolean intersects(double x, double y, double w, double h) + { + return shape.intersects(x, y, w, h); + } + + public boolean intersects(Rectangle2D r) + { + return shape.intersects(r); + } + +} diff --git a/libjava/classpath/gnu/java/awt/java2d/TextCacheKey.java b/libjava/classpath/gnu/java/awt/java2d/TextCacheKey.java new file mode 100644 index 000000000..0a60c6226 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/TextCacheKey.java @@ -0,0 +1,153 @@ +/* TextCacheKey.java -- Key to use for caching texts with their rendered layout + Copyright (C) 2007 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.Font; +import java.awt.font.FontRenderContext; + +/** + * A key object to be used when caching pre-rendered text. + */ +public class TextCacheKey +{ + + /** + * The actual string. + */ + private String string; + + /** + * The font render context. + */ + private FontRenderContext fontRenderContext; + + /** + * The font. + */ + private Font font; + + /** + * Creates a new TextCacheKey. + * + * This is intended to be used as search key. It is important to initialize + * the values using the setter methods before using this key, otherwise + * it will throw NPEs. + */ + public TextCacheKey() + { + // No-arg constructor. + } + + /** + * Creates a new TextCacheKey with initial values. + * + * @param s the string + * @param f the font + * @param frc the font render context + */ + public TextCacheKey(String s, Font f, FontRenderContext frc) + { + string = s; + font = f; + fontRenderContext = frc; + } + + /** + * Re-sets the string. This is intented to be used in search keys only. + * + * @param s the string to set + */ + public void setString(String s) + { + string = s; + } + + /** + * Sets the font render context. + * This is intented to be used in search keys only. + * + * @param frc the new font render context + */ + public void setFontRenderContext(FontRenderContext frc) + { + fontRenderContext = frc; + } + + /** + * Sets the font. + * This is intented to be used in search keys only. + * + * @param f the font to set + */ + public void setFont(Font f) + { + font = f; + } + + /** + * Determines if two objects are equal. + * + * @see Object#equals(Object) + */ + public boolean equals(Object o) + { + boolean eq; + if (o instanceof TextCacheKey) + { + TextCacheKey other = (TextCacheKey) o; + eq = other.string.equals(string) + && other.font.equals(font) + && other.fontRenderContext.equals(fontRenderContext); + } + else + { + eq = false; + } + return eq; + } + + /** + * Computes a hashcode for this key. + * + * @see Object#hashCode() + */ + public int hashCode() + { + return string.hashCode() ^ font.hashCode() ^ fontRenderContext.hashCode(); + } +} 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 000000000..2523d2311 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/java2d/TexturePaintContext.java @@ -0,0 +1,211 @@ +/* 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.getMinY()); + 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; + + // Handle possible negative values (replicating above the top-left) + if (dx < 0) + dx += width; + if (dy < 0) + dy += height; + + // 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/package.html b/libjava/classpath/gnu/java/awt/package.html new file mode 100644 index 000000000..166168510 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.awt package. + Copyright (C) 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.awt</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/awt/peer/ClasspathDesktopPeer.java b/libjava/classpath/gnu/java/awt/peer/ClasspathDesktopPeer.java new file mode 100644 index 000000000..fd4f498aa --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/ClasspathDesktopPeer.java @@ -0,0 +1,301 @@ +/* ClasspathDesktopPeer.java -- Offers a concrete implementation for DesktopPeer + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.java.awt.peer; + +import java.awt.AWTPermission; +import java.awt.Desktop.Action; +import java.awt.peer.DesktopPeer; + +import java.io.File; +import java.io.IOException; + +import java.net.URI; + +import java.util.prefs.Preferences; + +/** + * Offers a common implementation for the Desktop peers, that enables + * access to default system application within java processes. + * + * @author Mario Torre <neugens@limasoftware.net> + */ +public class ClasspathDesktopPeer + implements DesktopPeer +{ + /** This is the fallback browser, if no desktop was detected. */ + protected static final String _DEFAULT_BROWSER = "firefox"; + + /** gnu.java.awt.peer.Desktop.html.command */ + protected static final String _BROWSE = "html"; + + /** gnu.java.awt.peer.Desktop.mail.command */ + protected static final String _MAIL = "mail"; + + /** gnu.java.awt.peer.Desktop.edit.command */ + protected static final String _EDIT = "edit"; + + /** gnu.java.awt.peer.Desktop.print.command */ + protected static final String _PRINT = "print"; + + /** gnu.java.awt.peer.Desktop.open.command */ + protected static final String _OPEN = "open"; + + /** */ + protected static final KDEDesktopPeer kde = new KDEDesktopPeer(); + + /** */ + protected static final GnomeDesktopPeer gnome = new GnomeDesktopPeer(); + + /** */ + protected static final ClasspathDesktopPeer classpath = + new ClasspathDesktopPeer(); + + /** + * Preference subsystem. Packagers and users can override the default + * behaviour of this class via preferences and system properties. + */ + protected Preferences prefs = + Preferences.userNodeForPackage(ClasspathDesktopPeer.class).node("Desktop"); + + /** + * @param target + */ + protected ClasspathDesktopPeer() + { + /* nothing to do */ + } + + public boolean isSupported(Action action) + { + String check = null; + + switch(action) + { + case BROWSE: + check = _BROWSE; + break; + + case MAIL: + check = _MAIL; + break; + + case EDIT: + check = _EDIT; + break; + + case PRINT: + check = _PRINT; + break; + + case OPEN: default: + check = _OPEN; + break; + } + + return this.supportCommand(check); + } + + public void browse(URI url) throws IOException + { + checkPermissions(); + + String browser = getCommand(_BROWSE); + + if (browser == null) + throw new UnsupportedOperationException(); + + browser = browser + " " + url.toString(); + + Runtime.getRuntime().exec(browser); + } + + public void edit(File file) throws IOException + { + checkPermissions(file, false); + + String edit = getCommand(_EDIT); + + if (edit == null) + throw new UnsupportedOperationException(); + + edit = edit + " " + file.getAbsolutePath(); + Runtime.getRuntime().exec(edit); + } + + public void mail(URI mailtoURL) throws IOException + { + checkPermissions(); + + String scheme = mailtoURL.getScheme(); + if (scheme == null || !scheme.equalsIgnoreCase("mailto")) + throw new IllegalArgumentException("URI Scheme not of type mailto"); + + String mail = getCommand(_MAIL); + + if (mail == null) + throw new UnsupportedOperationException(); + + mail = mail + " " + mailtoURL.toString(); + + Runtime.getRuntime().exec(mail); + } + + public void mail() throws IOException + { + checkPermissions(); + + String mail = getCommand(_MAIL); + + if (mail == null) + throw new UnsupportedOperationException(); + + Runtime.getRuntime().exec(mail); + } + + public void open(File file) throws IOException + { + checkPermissions(file, true); + + String open = getCommand(_OPEN); + + if (open == null) + throw new UnsupportedOperationException(); + + open = open + " " + file.getAbsolutePath(); + Runtime.getRuntime().exec(open); + } + + public void print(File file) throws IOException + { + checkPrintPermissions(file); + + String print = getCommand(_PRINT); + + if (print == null) + throw new UnsupportedOperationException(); + + print = print + " " + file.getAbsolutePath(); + Runtime.getRuntime().exec(print); + } + + protected String getCommand(String action) + { + // check if a system property exist + String command = + System.getProperty("gnu.java.awt.peer.Desktop." + action + ".command"); + + // otherwise, get it from preferences, if any + if (command == null) + { + command = prefs.node(action).get("command", null); + } + + return command; + } + + /** + * Note: Checks for AWTPermission("showWindowWithoutWarningBanner") only. + */ + protected void checkPermissions() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new AWTPermission("showWindowWithoutWarningBanner")); + } + } + + /** + * Calls checkPermissions() and checks for SecurityManager.checkRead() + * and, if readOnly is false, for SecurityManager.checkWrite() + */ + protected void checkPermissions(File file, boolean readOnly) + { + checkPermissions(); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(file.toString()); + if (!readOnly) sm.checkWrite(file.toString()); + } + } + + /** + * Calls checkPermissions(file, true) and checks for + * SecurityManager.checkPrintJobAccess() + */ + protected void checkPrintPermissions(File file) + { + checkPermissions(file, true); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPrintJobAccess(); + } + } + + /** + * @param check + * @return + */ + protected boolean supportCommand(String check) + { + return ((this.getCommand(check) != null) ? true : false); + } + + /** + * @return + */ + public static DesktopPeer getDesktop() + { + // check if we are under Gnome or KDE or anything else + String desktopSession = System.getenv("GNOME_DESKTOP_SESSION_ID"); + if (desktopSession == null) + { + desktopSession = System.getenv("KDE_FULL_SESSION"); + if (desktopSession != null) + return kde; + } + else + { + return gnome; + } + + // revert to this class for default values + return classpath; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/ClasspathFontPeer.java b/libjava/classpath/gnu/java/awt/peer/ClasspathFontPeer.java new file mode 100644 index 000000000..96677a4af --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/ClasspathFontPeer.java @@ -0,0 +1,865 @@ +/* ClasspathFontPeer.java -- Font peer used by GNU Classpath. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.peer; + +import gnu.java.awt.ClasspathToolkit; + +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.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.font.TransformAttribute; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.peer.FontPeer; +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; + +/** + * A peer for fonts that are used inside Classpath. The purpose of + * this interface is to abstract from platform-specific font handling + * in the Classpath implementation of java.awt.Font and related + * classes. + * + * <p><b>State kept by the peer:</b> a peer is generated for each Font + * object in the default implementation. If you wish to share peers between + * fonts, you will need to subclass both ClasspathFontPeer and + * {@link ClasspathToolKit}.</p> + * + * <p><b>Thread Safety:</b> Methods of this interface may be called + * from arbitrary threads at any time. Implementations of the + * <code>ClasspathFontPeer</code> interface are required to perform + * the necessary synchronization.</p> + * + * @see java.awt.Font#getPeer + * @see java.awt.Toolkit#getFontPeer + * + * @author Sascha Brawer (brawer@dandelis.ch) + * @author Graydon Hoare (graydon@redhat.com) + */ +public abstract class ClasspathFontPeer + implements FontPeer +{ + + /*************************************************************************/ + + /* + * Instance Variables + */ + + /** + * The 3 names of this font. all fonts have 3 names, some of which + * may be equal: + * + * logical -- name the font was constructed from + * family -- a designer or brand name (Helvetica) + * face -- specific instance of a design (Helvetica Regular) + * + * @see isLogicalFontName + */ + + protected String logicalName; + protected String familyName; + protected String faceName; + + /** + * The font style, which is a combination (by OR-ing) of the font style + * constants PLAIN, BOLD and ITALIC, in this class. + */ + protected int style; + + /** + * The font point size. A point is 1/72 of an inch. + */ + protected float size; + + /** + * The affine transformation the font is currently subject to. + */ + protected AffineTransform transform; + + static class LRUCache<K,V> extends LinkedHashMap<K,V> + { + int max_entries; + public LRUCache(int max) + { + super(max, 0.75f, true); + max_entries = max; + } + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > max_entries; + } + } + + private static LRUCache<AffineTransform,TransformAttribute> transCache = + new LRUCache<AffineTransform,TransformAttribute>(50); + + protected static ClasspathToolkit tk() + { + return (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); + } + + /* + * Confusingly, a Logical Font is a concept unrelated to + * a Font's Logical Name. + * + * A Logical Font is one of 6 built-in, abstract font types + * which must be supported by any java environment: SansSerif, + * Serif, Monospaced, Dialog, and DialogInput. + * + * A Font's Logical Name is the name the font was constructed + * from. This might be the name of a Logical Font, or it might + * be the name of a Font Face. + */ + + protected static boolean isLogicalFontName(String name) + { + String uname = name.toUpperCase (); + return (uname.equals ("SANSSERIF") || + uname.equals ("SERIF") || + uname.equals ("MONOSPACED") || + uname.equals ("DIALOG") || + uname.equals ("DIALOGINPUT") || + uname.equals ("DEFAULT")); + } + + protected static String logicalFontNameToFaceName (String name) + { + String uname = name.toUpperCase (); + if (uname.equals("SANSSERIF")) + return "Helvetica"; + else if (uname.equals ("SERIF")) + return "Times"; + else if (uname.equals ("MONOSPACED")) + return "Courier"; + else if (uname.equals ("DIALOG")) + return "Helvetica"; + else if (uname.equals ("DIALOGINPUT")) + return "Helvetica"; + else if (uname.equals ("DEFAULT")) + return "Dialog.plain"; + else + return "Helvetica"; + } + + protected static String faceNameToFamilyName (String name) + { + return name; + } + + public static void copyStyleToAttrs (int style, Map attrs) + { + if ((style & Font.BOLD) == Font.BOLD) + attrs.put (TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); + else + attrs.put (TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR); + + if ((style & Font.ITALIC) == Font.ITALIC) + attrs.put (TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); + else + attrs.put (TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR); + } + + protected static void copyFamilyToAttrs (String fam, Map attrs) + { + if (fam != null) + attrs.put (TextAttribute.FAMILY, fam); + } + + public static void copySizeToAttrs (float size, Map attrs) + { + attrs.put (TextAttribute.SIZE, new Float (size)); + } + + protected static void copyTransformToAttrs (AffineTransform trans, Map attrs) + { + if (trans != null) + { + TransformAttribute ta; + synchronized(transCache) + { + ta = transCache.get(trans); + if (ta == null) + { + ta = new TransformAttribute(trans); + transCache.put(trans, ta); + } + } + attrs.put(TextAttribute.TRANSFORM, ta); + } + } + + + protected void setStandardAttributes (String name, String family, int style, + float size, AffineTransform trans) + { + this.logicalName = name; + + if (isLogicalFontName (name)) + this.faceName = logicalFontNameToFaceName (name); + else + this.faceName = name; + + if (family != null) + this.familyName = family; + else + this.familyName = faceNameToFamilyName (faceName); + + this.style = style; + this.size = size; + this.transform = trans; + } + + + protected void setStandardAttributes (String name, Map attribs) + { + String family = this.familyName; + AffineTransform trans = this.transform; + float size = this.size; + int style = this.style; + + if (attribs.containsKey (TextAttribute.FAMILY)) + family = (String) attribs.get (TextAttribute.FAMILY); + + if (name == null) + name = "Default"; + + if (attribs.containsKey (TextAttribute.WEIGHT)) + { + Float weight = (Float) attribs.get (TextAttribute.WEIGHT); + if (weight.floatValue () >= TextAttribute.WEIGHT_BOLD.floatValue ()) + style += Font.BOLD; + } + + if (attribs.containsKey (TextAttribute.POSTURE)) + { + Float posture = (Float) attribs.get (TextAttribute.POSTURE); + if (posture.floatValue () >= TextAttribute.POSTURE_OBLIQUE.floatValue ()) + style += Font.ITALIC; + } + + if (attribs.containsKey (TextAttribute.SIZE)) + { + Float sz = (Float) attribs.get (TextAttribute.SIZE); + size = sz.floatValue (); + + // Pango doesn't accept 0 as a font size. + if (size < 1) + size = 1; + } + else + size = 12; + + if (attribs.containsKey (TextAttribute.TRANSFORM)) + { + TransformAttribute ta = (TransformAttribute) + attribs.get(TextAttribute.TRANSFORM); + trans = ta.getTransform (); + } + + setStandardAttributes (name, family, style, size, trans); + } + + protected void getStandardAttributes (Map attrs) + { + copyFamilyToAttrs (this.familyName, attrs); + copySizeToAttrs (this.size, attrs); + copyStyleToAttrs (this.style, attrs); + copyTransformToAttrs (this.transform, attrs); + } + + + /* Begin public API */ + + public ClasspathFontPeer (String name, Map attrs) + { + setStandardAttributes (name, attrs); + } + + public ClasspathFontPeer (String name, int style, int size) + { + setStandardAttributes (name, (String)null, style, + (float)size, (AffineTransform)null); + } + + /** + * Implementation of {@link Font#getName} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public String getName (Font font) + { + return logicalName; + } + + /** + * Implementation of {@link Font#getFamily()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public String getFamily (Font font) + { + return familyName; + } + + /** + * Implementation of {@link Font#getFamily(Locale)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public String getFamily (Font font, Locale lc) + { + return familyName; + } + + /** + * Implementation of {@link Font#getFontName()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public String getFontName (Font font) + { + return faceName; + } + + /** + * Implementation of {@link Font#getFontName(Locale)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public String getFontName (Font font, Locale lc) + { + return faceName; + } + + /** + * Implementation of {@link Font#getSize} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public float getSize (Font font) + { + return size; + } + + /** + * Implementation of {@link Font#isPlain} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public boolean isPlain (Font font) + { + return style == Font.PLAIN; + } + + /** + * Implementation of {@link Font#isBold} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public boolean isBold (Font font) + { + return ((style & Font.BOLD) == Font.BOLD); + } + + /** + * Implementation of {@link Font#isItalic} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public boolean isItalic (Font font) + { + return ((style & Font.ITALIC) == Font.ITALIC); + } + + /** + * Implementation of {@link Font#deriveFont(int, float)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public Font deriveFont (Font font, int style, float size) + { + Map attrs = new HashMap (); + getStandardAttributes (attrs); + copyStyleToAttrs (style, attrs); + copySizeToAttrs (size, attrs); + return tk().getFont (logicalName, attrs); + } + + /** + * Implementation of {@link Font#deriveFont(float)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public Font deriveFont (Font font, float size) + { + Map attrs = new HashMap (); + getStandardAttributes (attrs); + copySizeToAttrs (size, attrs); + return tk().getFont (logicalName, attrs); + } + + /** + * Implementation of {@link Font#deriveFont(int)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public Font deriveFont (Font font, int style) + { + Map attrs = new HashMap (); + getStandardAttributes (attrs); + copyStyleToAttrs (style, attrs); + return tk().getFont (logicalName, attrs); + } + + /** + * Implementation of {@link Font#deriveFont(int, AffineTransform)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public Font deriveFont (Font font, int style, AffineTransform t) + { + Map attrs = new HashMap (); + getStandardAttributes (attrs); + copyStyleToAttrs (style, attrs); + copyTransformToAttrs (t, attrs); + return tk().getFont (logicalName, attrs); + } + + /** + * Implementation of {@link Font#deriveFont(AffineTransform)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public Font deriveFont (Font font, AffineTransform t) + { + Map attrs = new HashMap (); + getStandardAttributes (attrs); + copyTransformToAttrs (t, attrs); + return tk().getFont (logicalName, attrs); + } + + /** + * Implementation of {@link Font#deriveFont(Map)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public Font deriveFont (Font font, Map attrs) + { + return tk().getFont (logicalName, attrs); + } + + /** + * Implementation of {@link Font#getAttributes()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public Map getAttributes (Font font) + { + HashMap h = new HashMap (); + getStandardAttributes (h); + return h; + } + + /** + * Implementation of {@link Font#getAvailableAttributes()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public AttributedCharacterIterator.Attribute[] getAvailableAttributes(Font font) + { + AttributedCharacterIterator.Attribute a[] = + new AttributedCharacterIterator.Attribute[5]; + a[0] = TextAttribute.FAMILY; + a[1] = TextAttribute.SIZE; + a[2] = TextAttribute.POSTURE; + a[3] = TextAttribute.WEIGHT; + a[4] = TextAttribute.TRANSFORM; + return a; + } + + /** + * Implementation of {@link Font#getTransform()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public AffineTransform getTransform (Font font) + { + if (transform == null) + transform = new AffineTransform (); + return transform; + } + + /** + * Implementation of {@link Font#isTransformed()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public boolean isTransformed (Font font) + { + return ! transform.isIdentity (); + } + + /** + * Implementation of {@link Font#getItalicAngle()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public float getItalicAngle (Font font) + { + if ((style & Font.ITALIC) == Font.ITALIC) + return TextAttribute.POSTURE_OBLIQUE.floatValue (); + else + return TextAttribute.POSTURE_REGULAR.floatValue (); + } + + + /** + * Implementation of {@link Font#getStyle()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public int getStyle (Font font) + { + return style; + } + + + + + /* Remaining methods are abstract */ + + /** + * Implementation of {@link Font#canDisplay(char)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract boolean canDisplay (Font font, int c); + + /** + * Implementation of {@link Font#canDisplay(String)}, + * {@link Font#canDisplay(char [], int, int)}, and + * {@link Font#canDisplay(CharacterIterator, int, int)}. + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract int canDisplayUpTo (Font font, CharacterIterator i, int start, int limit); + + + /** + * Returns the name of this font face inside the family, for example + * <i>“Light”</i>. + * + * <p>This method is currently not used by {@link Font}. However, + * this name would be needed by any serious desktop publishing + * application. + * + * @param font the font whose sub-family name is requested. + * + * @param locale the locale for which to localize the name. If + * <code>locale</code> is <code>null</code>, the returned name is + * localized to the user’s default locale. + * + * @return the name of the face inside its family, or + * <code>null</code> if the font does not provide a sub-family name. + */ + + public abstract String getSubFamilyName (Font font, Locale locale); + + + /** + * Implementation of {@link Font#getPSName()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract String getPostScriptName (Font font); + + + /** + * Implementation of {@link Font#getNumGlyphs()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract int getNumGlyphs (Font font); + + + /** + * Implementation of {@link Font#getMissingGlyphCode()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract int getMissingGlyphCode (Font font); + + + /** + * Implementation of {@link Font#getBaselineFor(char)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract byte getBaselineFor (Font font, char c); + + + /** + * Returns a name for the specified glyph. This is useful for + * generating PostScript or PDF files that embed some glyphs of a + * font. If the implementation follows glyph naming conventions + * specified by Adobe, search engines can extract the original text + * from the generated PostScript and PDF files. + * + * <p>This method is currently not used by GNU Classpath. However, + * it would be very useful for someone wishing to write a good + * PostScript or PDF stream provider for the + * <code>javax.print</code> package. + * + * <p><b>Names are not unique:</b> Under some rare circumstances, + * the same name can be returned for different glyphs. It is + * therefore recommended that printer drivers check whether the same + * name has already been returned for antoher glyph, and make the + * name unique by adding the string ".alt" followed by the glyph + * index.</p> + * + * <p>This situation would occur for an OpenType or TrueType font + * that has a <code>post</code> table of format 3 and provides a + * mapping from glyph IDs to Unicode sequences through a + * <code>Zapf</code> table. If the same sequence of Unicode + * codepoints leads to different glyphs (depending on contextual + * position, for example, or on typographic sophistication level), + * the same name would get synthesized for those glyphs. To avoid + * this, the font peer would have to go through the names of all + * glyphs, which would make this operation very inefficient with + * large fonts. + * + * @param font the font containing the glyph whose name is + * requested. + * + * @param glyphIndex the glyph whose name the caller wants to + * retrieve. + * + * @return the glyph name, or <code>null</code> if a font does not + * provide glyph names. + */ + + public abstract String getGlyphName (Font font, int glyphIndex); + + + /** + * Implementation of {@link + * Font#createGlyphVector(FontRenderContext, String)}, {@link + * Font#createGlyphVector(FontRenderContext, char[])}, and {@link + * Font#createGlyphVector(FontRenderContext, CharacterIterator)}. + * + * @param font the font object that the created GlyphVector will return + * when it gets asked for its font. This argument is needed because the + * public API of {@link GlyphVector} works with {@link java.awt.Font}, + * not with font peers. + */ + + public abstract GlyphVector createGlyphVector (Font font, + FontRenderContext frc, + CharacterIterator ci); + + + /** + * Implementation of {@link Font#createGlyphVector(FontRenderContext, + * int[])}. + * + * @param font the font object that the created GlyphVector will return + * when it gets asked for its font. This argument is needed because the + * public API of {@link GlyphVector} works with {@link java.awt.Font}, + * not with font peers. + */ + + public abstract GlyphVector createGlyphVector (Font font, + FontRenderContext ctx, + int[] glyphCodes); + + + /** + * Implementation of {@link Font#layoutGlyphVector(FontRenderContext, + * char[], int, int, int)}. + * + * @param font the font object that the created GlyphVector will return + * when it gets asked for its font. This argument is needed because the + * public API of {@link GlyphVector} works with {@link java.awt.Font}, + * not with font peers. + */ + + public abstract GlyphVector layoutGlyphVector (Font font, + FontRenderContext frc, + char[] chars, int start, + int limit, int flags); + + + /** + * Implementation of {@link Font#getFontMetrics()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract FontMetrics getFontMetrics (Font font); + + + /** + * Implementation of {@link Font#hasUniformLineMetrics()} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract boolean hasUniformLineMetrics (Font font); + + + /** + * Implementation of {@link Font#getLineMetrics(CharacterIterator, int, + * int, FontRenderContext)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract LineMetrics getLineMetrics (Font font, + CharacterIterator ci, + int begin, int limit, + FontRenderContext rc); + + /** + * Implementation of {@link Font#getMaxCharBounds(FontRenderContext)} + * + * @param font the font this peer is being called from. This may be + * useful if you are sharing peers between Font objects. Otherwise it may + * be ignored. + */ + + public abstract Rectangle2D getMaxCharBounds (Font font, + FontRenderContext rc); + +} diff --git a/libjava/classpath/gnu/java/awt/peer/EmbeddedWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/EmbeddedWindowPeer.java new file mode 100644 index 000000000..4c64a1d2d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/EmbeddedWindowPeer.java @@ -0,0 +1,47 @@ +/* EmbeddedWindowPeer.java -- Interface for window peers that may be + embedded into other applications + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.peer; + +import java.awt.peer.FramePeer; + +public interface EmbeddedWindowPeer extends FramePeer +{ + void embed (long handle); +} diff --git a/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java b/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java new file mode 100644 index 000000000..fe128c26c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/GLightweightPeer.java @@ -0,0 +1,461 @@ +/* GLightweightPeer.java -- + Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.peer; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.BufferCapabilities; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.PaintEvent; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; +import java.awt.peer.ContainerPeer; +import java.awt.peer.LightweightPeer; + +/** + * A stub class that implements the ComponentPeer and ContainerPeer + * interfaces using callbacks into the Component and Container + * classes. GLightweightPeer allows the Component and Container + * classes to treat lightweight and heavyweight peers in the same way. + * + * Lightweight components are painted directly onto their parent + * containers through an Image object provided by the toolkit. + */ +public class GLightweightPeer + implements LightweightPeer, ContainerPeer +{ + public GLightweightPeer() + { + // Nothing to do here. + } + + // -------- java.awt.peer.ContainerPeer implementation: + + public Insets insets() + { + // Nothing to do here for lightweights. + return null; + } + + public Insets getInsets() + { + // Nothing to do here for lightweights. + return null; + } + + public void beginValidate() + { + // Nothing to do here for lightweights. + } + + public void endValidate() + { + // Nothing to do here for lightweights. + } + + public void beginLayout() + { + // Nothing to do here for lightweights. + } + + public void endLayout() + { + // Nothing to do here for lightweights. + } + + public boolean isPaintPending() + { + // Nothing to do here for lightweights. + return false; + } + + // -------- java.awt.peer.ComponentPeer implementation: + + public int checkImage(Image img, int width, int height, ImageObserver o) + { + // Nothing to do here for lightweights. + return -1; + } + + public Image createImage(ImageProducer prod) + { + // Nothing to do here for lightweights. + return null; + } + + /* This method is not called. */ + public Image createImage(int width, int height) + { + // Nothing to do here for lightweights. + return null; + } + + public void disable() + { + // Nothing to do here for lightweights. + } + + public void dispose() + { + // Nothing to do here for lightweights. + } + + public void enable() + { + // Nothing to do here for lightweights. + } + + public GraphicsConfiguration getGraphicsConfiguration() + { + // Nothing to do here for lightweights. + return null; + } + + public FontMetrics getFontMetrics(Font f) + { + // We shouldn't end up here, but if we do we can still try do something + // reasonable. + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.getFontMetrics(f); + } + + /* Returning null here tells the Component object that called us to + * use its parent's Graphics. */ + public Graphics getGraphics() + { + // Nothing to do here for lightweights. + return null; + } + + public Point getLocationOnScreen() + { + // Nothing to do here for lightweights. + return null; + } + + public Dimension getMinimumSize() + { + return minimumSize(); + } + + public Dimension getPreferredSize() + { + return preferredSize(); + } + + /* Returning null here tells the Component object that called us to + * use its parent's Toolkit. */ + public Toolkit getToolkit() + { + // Nothing to do here for lightweights. + return null; + } + + public void handleEvent(AWTEvent e) + { + // This can only happen when an application posts a PaintEvent for + // a lightweight component directly. We still support painting for + // this case. + if (e instanceof PaintEvent) + { + PaintEvent pe = (PaintEvent) e; + Component target = (Component) e.getSource(); + if (target != null && target.isShowing()) + { + Graphics g = target.getGraphics(); + if (g != null) + { + try + { + Rectangle clip = pe.getUpdateRect(); + g.setClip(clip); + target.paint(g); + } + finally + { + g.dispose(); + } + } + } + } + } + + public void hide() + { + // Nothing to do here for lightweights. + } + + public boolean isFocusable() + { + // Nothing to do here for lightweights. + return false; + } + + public boolean isFocusTraversable() + { + // Nothing to do here for lightweights. + return false; + } + + public Dimension minimumSize() + { + return new Dimension(0, 0); + } + + public Dimension preferredSize() + { + return new Dimension(0, 0); + } + + public void paint(Graphics graphics) + { + // Nothing to do here for lightweights. + } + + public boolean prepareImage(Image img, int width, int height, + ImageObserver o) + { + // Nothing to do here for lightweights. + return false; + } + + public void print(Graphics graphics) + { + // Nothing to do here for lightweights. + } + + public void repaint(long tm, int x, int y, int width, int height) + { + // Nothing to do here for lightweights. + } + + public void requestFocus() + { + // Nothing to do here for lightweights. + } + + public boolean requestFocus(Component source, boolean bool1, boolean bool2, + long x) + { + // Nothing to do here for lightweights. + return false; + } + + public void reshape(int x, int y, int width, int height) + { + // Nothing to do here for lightweights. + } + + public void setBackground(Color color) + { + // Nothing to do here for lightweights. + } + + public void setBounds(int x, int y, int width, int height) + { + // Nothing to do here for lightweights. + } + + /** + * Sets the cursor on the heavy-weight parent peer. + * Called by the MouseListener on mouse enter. + */ + public void setCursor(Cursor cursor) + { + // Nothing to do here for lightweights. + } + + public void setEnabled(boolean enabled) + { + // Nothing to do here for lightweights. + } + + public void setEventMask(long eventMask) + { + // Nothing to do here for lightweights. + } + + public void setFont(Font font) + { + // Nothing to do here for lightweights. + } + + public void setForeground(Color color) + { + // Nothing to do here for lightweights. + } + + public void setVisible(boolean visible) + { + // Nothing to do here for lightweights. + } + + public void show() + { + // Nothing to do here for lightweights. + } + + public ColorModel getColorModel() + { + // Nothing to do here for lightweights. + return null; + } + + public boolean isObscured() + { + // Nothing to do here for lightweights. + return false; + } + + public boolean canDetermineObscurity() + { + // Nothing to do here for lightweights. + return false; + } + + public void coalescePaintEvent(PaintEvent e) + { + // Nothing to do here for lightweights. + } + + public void updateCursorImmediately() + { + // Nothing to do here for lightweights. + } + + public VolatileImage createVolatileImage(int width, int height) + { + // Nothing to do here for lightweights. + return null; + } + + public boolean handlesWheelScrolling() + { + // Nothing to do here for lightweights. + return false; + } + + public void createBuffers(int x, BufferCapabilities capabilities) + throws AWTException + { + // Nothing to do here for lightweights. + } + + public Image getBackBuffer() + { + // Nothing to do here for lightweights. + return null; + } + + public void flip(BufferCapabilities.FlipContents contents) + { + // Nothing to do here for lightweights. + } + + public void destroyBuffers() + { + // Nothing to do here for lightweights. + } + + public boolean isRestackSupported() + { + // Nothing to do here for lightweights. + return false; + } + + public void cancelPendingPaint(int x, int y, int width, int height) + { + // Nothing to do here for lightweights. + } + + public void restack() + { + // Nothing to do here for lightweights. + } + + public Rectangle getBounds() + { + // Nothing to do here for lightweights. + return null; + } + + public void reparent(ContainerPeer parent) + { + // Nothing to do here for lightweights. + } + + public void setBounds(int x, int y, int z, int width, int height) + { + // Nothing to do here for lightweights. + } + + public boolean isReparentSupported() + { + // Nothing to do here for lightweights. + return true; + } + + public void layout() + { + // Nothing to do here for lightweights. + } + + public boolean requestFocus(Component lightweightChild, boolean temporary, + boolean focusedWindowChangeAllowed, + long time, sun.awt.CausedFocusEvent.Cause cause) + { + // Always grant focus request. + return true; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/GnomeDesktopPeer.java b/libjava/classpath/gnu/java/awt/peer/GnomeDesktopPeer.java new file mode 100644 index 000000000..2347371ad --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/GnomeDesktopPeer.java @@ -0,0 +1,155 @@ +/* GnomeDesktopPeer.java -- Offers a GNOME Desktop peer for DesktopPeer + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.java.awt.peer; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +/** + * @author Mario Torre <neugens@limasoftware.net> + */ +public class GnomeDesktopPeer + extends ClasspathDesktopPeer +{ + /** + * Query string to use if a GNOME desktop is detected to get the name of the + * default browser. This requires gconftool-2 (part of GNOME). + */ + private static final String BROWSER_QUERY_GNOME = + "gconftool-2 -g /desktop/gnome/url-handlers/http/command"; + + protected String getCommand(String action) + { + // check if a command already exists + String command = super.getCommand(action); + + if (command == null) + { + try + { + if (action == _BROWSE) + { + command = execQuery(BROWSER_QUERY_GNOME); + } + else if (action == _PRINT) + { + command = null; + } + else + { + command = "gnome-open"; + } + } + catch (Exception e) + { + command = null; + } + } + + return command; + } + + public void browse(URI url) throws IOException + { + checkPermissions(); + + String browser = getCommand(_BROWSE); + + if (browser == null) + throw new UnsupportedOperationException(); + + browser = browser + " " + url.toString(); + + Runtime.getRuntime().exec(browser); + } + + protected boolean supportCommand(String check) + { + if (check == _PRINT) + { + return super.supportCommand(check); + } + + return true; + } + + public void mail() throws IOException + { + checkPermissions(); + + String mail = getCommand(_MAIL); + + if (mail == null) + throw new UnsupportedOperationException(); + + Runtime.getRuntime().exec(mail + " mailto:"); + } + + protected String execQuery(String command) throws IOException + { + InputStream in = null; + CPStringBuilder output = new CPStringBuilder(); + + try + { + Process process = Runtime.getRuntime().exec(command); + + // Get the input stream and read from it + in = process.getInputStream(); + int c; + while ((c = in.read()) != - 1) + { + output.append((char) c); + } + } + finally + { + if (in != null) + in.close(); + } + + // remove %s from the string, leave only the command line + int index = output.indexOf("%s"); + output.delete(index, index + 1); + + return output.toString().trim(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/KDEDesktopPeer.java b/libjava/classpath/gnu/java/awt/peer/KDEDesktopPeer.java new file mode 100644 index 000000000..44a508435 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/KDEDesktopPeer.java @@ -0,0 +1,135 @@ +/* GnomeDesktopPeer.java -- Offers a KDE Desktop peer for DesktopPeer + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package gnu.java.awt.peer; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author Mario Torre <neugens@limasoftware.net> + */ +public class KDEDesktopPeer + extends ClasspathDesktopPeer +{ + /** + * Query string to use if a GNOME desktop is detected to get the name of the + * default browser. This requires gconftool-2 (part of GNOME). + */ + private static final String BROWSER_QUERY_GNOME = + "gconftool-2 -g /desktop/gnome/url-handlers/http/command"; + + protected String getCommand(String action) + { + // check if a command already exists + String command = super.getCommand(action); + + if (command == null) + { + try + { + if (action == _MAIL) + { + command = "kfmclient exec"; + } + else if (action == _PRINT) + { + command = "kprinter"; + } + else + { + command = "kfmclient openURL"; + } + } + catch (Exception e) + { + command = null; + } + } + + return command; + } + + protected boolean supportCommand(String check) + { + return true; + } + + public void mail() throws IOException + { + checkPermissions(); + + String mail = getCommand(_MAIL); + + if (mail == null) + throw new UnsupportedOperationException(); + + Runtime.getRuntime().exec(mail + " 'mailto: '"); + } + + protected String execQuery(String command) throws IOException + { + InputStream in = null; + CPStringBuilder output = new CPStringBuilder(); + + try + { + Process process = Runtime.getRuntime().exec(command); + + // Get the input stream and read from it + in = process.getInputStream(); + int c; + while ((c = in.read()) != - 1) + { + output.append((char) c); + } + } + finally + { + if (in != null) + in.close(); + } + + // remove %s from the string, leave only the command line + int index = output.indexOf("%s"); + output.delete(index, index + 1); + + return output.toString().trim(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/NativeEventLoopRunningEvent.java b/libjava/classpath/gnu/java/awt/peer/NativeEventLoopRunningEvent.java new file mode 100644 index 000000000..962ecd990 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/NativeEventLoopRunningEvent.java @@ -0,0 +1,58 @@ +/* NativeEventLoopRunningEvent.java -- communicates to EventQueue the + state of the native event loop + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.peer; + +import java.awt.AWTEvent; + +public class NativeEventLoopRunningEvent + extends AWTEvent +{ + private boolean running; + + public NativeEventLoopRunningEvent(Object source) + { + super(source, 2999); + running = ((Boolean) source).booleanValue(); + } + + public boolean isRunning() + { + return running; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/AsyncImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/AsyncImage.java new file mode 100644 index 000000000..786dcf26b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/AsyncImage.java @@ -0,0 +1,283 @@ +/* AsyncImage.java -- Loads images asynchronously + 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.Image; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; + +/** + * Supports asynchronous loading of images. + */ +public class AsyncImage + extends Image +{ + + /** + * Returned as source as long as the image is not complete. + */ + private class NullImageSource + implements ImageProducer + { + private ArrayList<ImageConsumer> consumers; + + NullImageSource() + { + consumers = new ArrayList<ImageConsumer>(); + } + + public void addConsumer(ImageConsumer ic) + { + consumers.add(ic); + } + + public boolean isConsumer(ImageConsumer ic) + { + return consumers.contains(ic); + } + + public void removeConsumer(ImageConsumer ic) + { + consumers.remove(ic); + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + startProduction(ic); + } + + public void startProduction(ImageConsumer ic) + { + consumers.add(ic); + for (int i = consumers.size() - 1; i >= 0; i--) + { + ImageConsumer c = (ImageConsumer) consumers.get(i); + c.setDimensions(1, 1); + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } + } + + } + + /** + * Loads the image asynchronously. + */ + private class Loader + implements Runnable + { + private URL url; + Loader(URL u) + { + url = u; + } + + public void run() + { + Image image; + try + { + GtkImage gtkImage = new GtkImage(url); + image = CairoSurface.getBufferedImage(gtkImage); + } + catch (IllegalArgumentException iae) + { + image = null; + } + realImage = GtkToolkit.imageOrError(image); + synchronized (AsyncImage.this) + { + notifyObservers(ImageObserver.ALLBITS | ImageObserver.HEIGHT + | ImageObserver.WIDTH | ImageObserver.PROPERTIES); + observers = null; // Not needed anymore. + } + } + } + + /** + * The real image. This is null as long as the image is not complete. + */ + Image realImage; + + /** + * The image observers. + * + * This is package private to avoid accessor methods. + */ + HashSet<ImageObserver> observers; + + /** + * Creates a new AsyncImage that loads from the specified URL. + */ + AsyncImage(URL url) + { + observers = new HashSet<ImageObserver>(); + Loader l = new Loader(url); + Thread t = new Thread(l); + t.start(); + } + + public void flush() + { + // Nothing to do here. + } + + public Graphics getGraphics() + { + Image r = realImage; + Graphics g = null; + if (r != null) + g = r.getGraphics(); // Should we return some dummy graphics instead? + return g; + } + + public int getHeight(ImageObserver observer) + { + addObserver(observer); + int height = 0; + Image r = realImage; + if (r != null) + height = r.getHeight(observer); + return height; + } + + public Object getProperty(String name, ImageObserver observer) + { + addObserver(observer); + Image r = realImage; + Object prop = null; + if (r != null) + prop = r.getProperty(name, observer); + return prop; + } + + public ImageProducer getSource() + { + Image r = realImage; + ImageProducer source; + if (r == null) + source = new NullImageSource(); + else + source = r.getSource(); + return source; + } + + public int getWidth(ImageObserver observer) + { + addObserver(observer); + int width = 0; + Image r = realImage; + if (r != null) + width = r.getWidth(observer); + return width; + } + + void addObserver(ImageObserver obs) + { + if (obs != null) + { + synchronized (this) + { + // This field gets null when image loading is complete and we don't + // need to store any more observers. + HashSet<ImageObserver> observs = observers; + if (observs != null) + { + observs.add(obs); + } + else + { + // When the image is complete, notify the observer. Dunno if + // that's really needed, but to be sure. + obs.imageUpdate(this, ImageObserver.WIDTH + | ImageObserver.HEIGHT + |ImageObserver.ALLBITS + | ImageObserver.PROPERTIES, 0, 0, + realImage.getWidth(null), + realImage.getHeight(null)); + } + } + } + } + + static Image realImage(Image img, ImageObserver obs) + { + if (img instanceof AsyncImage) + { + ((AsyncImage) img).addObserver(obs); + Image r = ((AsyncImage) img).realImage; + if (r != null) + img = r; + } + return img; + } + + void notifyObservers(int status) + { + assert Thread.holdsLock(this); + // This field gets null when image loading is complete. + HashSet observs = observers; + if (observs != null) + { + Image r = realImage; + Iterator i = observs.iterator(); + while (i.hasNext()) + { + ImageObserver obs = (ImageObserver) i.next(); + obs.imageUpdate(this, status, 0, 0, r.getWidth(null), + r.getHeight(null)); + } + } + } + + int checkImage(ImageObserver obs) + { + addObserver(obs); + int flags = 0; + if (realImage != null) + flags = ImageObserver.ALLBITS | ImageObserver.WIDTH + | ImageObserver.HEIGHT | ImageObserver.PROPERTIES; + return flags; + } +} 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 000000000..f9609bff6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java @@ -0,0 +1,538 @@ +/* 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.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBufferInt; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SinglePixelPackedSampleModel; +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, buffer; + + /** + * Image size. + */ + private int imageWidth, imageHeight; + + /** + * The cairo surface that we actually draw on. + */ + CairoSurface surface; + + /** + * Cache BufferedImageGraphics surfaces. + */ + static WeakHashMap<BufferedImage, CairoSurface> bufferedImages + = new WeakHashMap<BufferedImage, CairoSurface>(); + + /** + * Its corresponding cairo_t. + */ + private long cairo_t; + + private boolean hasFastCM; + private boolean hasAlpha; + + + public BufferedImageGraphics(BufferedImage bi) + { + this.image = bi; + imageWidth = bi.getWidth(); + imageHeight = bi.getHeight(); + + if (!(image.getSampleModel() instanceof SinglePixelPackedSampleModel)) + hasFastCM = false; + else if(bi.getColorModel().equals(CairoSurface.cairoCM_opaque)) + { + hasFastCM = true; + hasAlpha = false; + } + else if(bi.getColorModel().equals(CairoSurface.cairoColorModel) + || bi.getColorModel().equals(CairoSurface.cairoCM_pre)) + { + hasFastCM = true; + hasAlpha = true; + } + else + hasFastCM = false; + + // Cache surfaces. + if( bufferedImages.get( bi ) != null ) + surface = bufferedImages.get( bi ); + else + { + surface = new CairoSurface( imageWidth, imageHeight ); + bufferedImages.put(bi, surface); + } + + cairo_t = surface.newCairoContext(); + + // Get pixels out of buffered image and set in cairo surface + Raster raster = bi.getRaster(); + int[] pixels; + + if (hasFastCM) + { + SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel)image.getSampleModel(); + int minX = image.getRaster().getSampleModelTranslateX(); + int minY = image.getRaster().getSampleModelTranslateY(); + + // Pull pixels directly out of data buffer + pixels = ((DataBufferInt)raster.getDataBuffer()).getData(); + + // Discard pixels that fall outside of the image's bounds + // (ie, this image is actually a subimage of a different image) + if (!(sm.getScanlineStride() == imageWidth && minX == 0 && minY == 0)) + { + int[] pixels2 = new int[imageWidth * imageHeight]; + int scanline = sm.getScanlineStride(); + + for (int i = 0; i < imageHeight; i++) + System.arraycopy(pixels, (i - minY) * scanline - minX, pixels2, + i * imageWidth, imageWidth); + + pixels = pixels2; + } + + // Fill the alpha channel as opaque if image does not have alpha + if( !hasAlpha ) + for(int i = 0; i < pixels.length; i++) + pixels[i] &= 0xFFFFFFFF; + } + else + { + pixels = CairoGraphics2D.findSimpleIntegerArray(image.getColorModel(), + image.getData()); + if (pixels != null) + System.arraycopy(pixels, 0, surface.getData(), + 0, pixels.length); + } + + setup( cairo_t ); + setClip(0, 0, imageWidth, imageHeight); + } + + BufferedImageGraphics(BufferedImageGraphics copyFrom) + { + image = copyFrom.image; + surface = copyFrom.surface; + cairo_t = surface.newCairoContext(); + imageWidth = copyFrom.imageWidth; + imageHeight = copyFrom.imageHeight; + + hasFastCM = copyFrom.hasFastCM; + hasAlpha = copyFrom.hasAlpha; + + copy( copyFrom, cairo_t ); + } + + /** + * Update a rectangle of the bufferedImage. This can be improved upon a lot. + */ + private void updateBufferedImage(int x, int y, int width, int height) + { + Rectangle bounds = new Rectangle(x, y, width, height); + bounds = getTransformedBounds(bounds, transform).getBounds(); + x = bounds.x; + y = bounds.y; + width = bounds.width; + height = bounds.height; + + int[] pixels = surface.getData(); + + if( x > imageWidth || y > imageHeight ) + return; + + // Deal with negative width/height. + if (height < 0) + { + y += height; + height = -height; + } + if (width < 0) + { + x += width; + width = -width; + } + + // Clip edges. + if( x < 0 ) + x = 0; + if( y < 0 ) + 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); + // The setRGB method assumes (or should assume) that pixels are NOT + // alpha-premultiplied, but Cairo stores data with premultiplication + // (thus the pixels returned in getPixels are premultiplied). + // This is ignored for consistency, however, since in + // CairoGrahpics2D.drawImage we also use non-premultiplied data + + } + else + { + int[] db = ((DataBufferInt)image.getRaster().getDataBuffer()). + getData(); + + // This should not fail, as we check the image sample model when we + // set the hasFastCM flag + SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel)image.getSampleModel() ; + + int minX = image.getRaster().getSampleModelTranslateX(); + int minY = image.getRaster().getSampleModelTranslateY(); + + if (sm.getScanlineStride() == imageWidth && minX == 0) + { + System.arraycopy(pixels, y * imageWidth, + db, (y - minY) * imageWidth, + height * imageWidth); + } + else + { + int scanline = sm.getScanlineStride(); + for (int i = y; i < (height + y); i++) + System.arraycopy(pixels, i * imageWidth + x, db, + (i - minY) * scanline + x - minX, width); + + } + } + } + + /** + * 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) + { + // Find total bounds of shape + Rectangle r = findStrokedBounds(s); + if (shiftDrawCalls) + { + r.width++; + r.height++; + } + + // Do the drawing + if (comp == null || comp instanceof AlphaComposite) + { + super.draw(s); + updateBufferedImage(r.x, r.y, r.width, r.height); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.setTransform(transform); + g2d.draw(s); + + drawComposite(r.getBounds2D(), null); + } + } + + public void fill(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + { + super.fill(s); + Rectangle r = s.getBounds(); + updateBufferedImage(r.x, r.y, r.width, r.height); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.setTransform(transform); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + if (comp == null || comp instanceof AlphaComposite) + { + super.drawRenderedImage(image, xform); + updateBufferedImage(0, 0, imageWidth, imageHeight); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.setTransform(transform); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } + + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (comp == null || comp instanceof AlphaComposite) + { + boolean rv = super.drawImage(img, xform, bgcolor, obs); + updateBufferedImage(0, 0, imageWidth, imageHeight); + return rv; + } + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Rectangle2D bounds = new Rectangle(bImg.getMinX(), bImg.getMinY(), + bImg.getWidth(), bImg.getHeight()); + if (xform != null) + bounds = getTransformedBounds(bounds, xform); + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + return drawComposite(bounds, obs); + } + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + // Find absolute bounds, in user-space, of this glyph vector + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + + // Perform draw operation + if (comp == null || comp instanceof AlphaComposite) + { + super.drawGlyphVector(gv, x, y); + + // this returns an integer-based Rectangle (rather than a + // Rectangle2D), which takes care of any necessary rounding for us. + bounds = bounds.getBounds(); + + updateBufferedImage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), (int)bounds.getHeight()); + } + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.setTransform(transform); + g2d.drawGlyphVector(gv, x, y); + + drawComposite(bounds, null); + } + } + + /** + * Perform composite drawing from the buffer onto the main image. + * + * The image to be composited should already be drawn into the buffer, in the + * proper place, after all necessary transforms have been applied. + * + * @param bounds The bounds to draw, in user-space. + * @param observer The image observer, if any (may be null). + * @return True on success, false on failure. + */ + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Find bounds in device space + bounds = getTransformedBounds(bounds, transform); + + // Clip bounds by the stored clip, and by the internal buffer + Rectangle2D devClip = this.getClipInDevSpace(); + Rectangle2D.intersect(bounds, devClip, bounds); + devClip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, devClip, bounds); + + // Round bounds as needed, but be careful in our rounding + // (otherwise it may leave unpainted stripes) + double x = bounds.getX(); + double y = bounds.getY(); + double maxX = x + bounds.getWidth(); + double maxY = y + bounds.getHeight(); + x = Math.round(x); + y = Math.round(y); + bounds.setRect(x, y, Math.round(maxX - x), Math.round(maxY - y)); + + // Find subimage of internal buffer for updating + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Find subimage of main image for updating + BufferedImage current = image; + current = current.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + current.getRaster()); + + // Set cairo's composite to direct SRC, since we've already done our own + // compositing + Composite oldcomp = comp; + setComposite(AlphaComposite.Src); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(current, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + null, null); + setComposite(oldcomp); + updateColor(); + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + buffer = new BufferedImage(image.getWidth(), image.getHeight(), + BufferedImage.TYPE_INT_ARGB); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + return image.getColorModel(); + } + + protected ColorModel getBufferCM() + { + return ColorModel.getRGBdefault(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java b/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java new file mode 100644 index 000000000..05d35c573 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java @@ -0,0 +1,2176 @@ +/* CairoGraphics2D.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 gnu.classpath.Configuration; + +import gnu.java.awt.ClasspathToolkit; + +import java.awt.AWTPermission; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.TexturePaint; +import java.awt.Toolkit; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +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; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.ImagingOpException; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.awt.image.renderable.RenderContext; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.HashMap; +import java.util.Map; + +/** + * 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 +{ + static + { + if (true) // GCJ LOCAL + { + System.loadLibrary("gtkpeer"); + } + } + + /** + * 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; + boolean customPaint; + + /** + * The current stroke + */ + Stroke stroke; + + /* + * Current foreground and background color. + */ + Color fg, bg; + + /** + * Current clip shape. + */ + Shape clip; + + /** + * Current transform. + */ + AffineTransform transform; + + /** + * Current font. + */ + Font font; + + /** + * The current compositing context, if any. + */ + Composite comp; + CompositeContext compCtx; + + /** + * Rendering hint map. + */ + private RenderingHints hints; + + /** + * Status of the anti-alias flag in cairo. + */ + private boolean antialias = false; + private boolean ignoreAA = false; + + /** + * 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. + */ + protected 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(); + + static ColorModel rgb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF); + static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, + 0xFF000000); + + /** + * Native constants for interpolation methods. + * Note, this corresponds to an enum in native/jni/gtk-peer/cairographics2d.h + */ + public static final int INTERPOLATION_NEAREST = 0, + INTERPOLATION_BILINEAR = 1, + INTERPOLATION_BICUBIC = 5, + ALPHA_INTERPOLATION_SPEED = 2, + ALPHA_INTERPOLATION_QUALITY = 3, + ALPHA_INTERPOLATION_DEFAULT = 4; + // TODO: Does ALPHA_INTERPOLATION really correspond to CAIRO_FILTER_FAST/BEST/GOOD? + + /** + * Constructor does nothing. + */ + public CairoGraphics2D() + { + } + + /** + * 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())); + setFont(new Font("SansSerif", Font.PLAIN, 12)); + setColor(Color.black); + setBackground(Color.white); + setPaint(Color.black); + setStroke(new BasicStroke()); + setTransform(new AffineTransform()); + cairoSetAntialias(nativePointer, antialias); + } + + /** + * Same as above, but copies the state of another CairoGraphics2D. + */ + public void copy(CairoGraphics2D g, long cairo_t_pointer) + { + nativePointer = init(cairo_t_pointer); + paint = g.paint; + stroke = g.stroke; + setRenderingHints(g.hints); + + Color foreground; + + if (g.fg.getAlpha() != -1) + foreground = new Color(g.fg.getRed(), g.fg.getGreen(), g.fg.getBlue(), + g.fg.getAlpha()); + else + foreground = new Color(g.fg.getRGB()); + + if (g.bg != null) + { + if (g.bg.getAlpha() != -1) + bg = new Color(g.bg.getRed(), g.bg.getGreen(), g.bg.getBlue(), + g.bg.getAlpha()); + else + bg = new Color(g.bg.getRGB()); + } + + firstClip = g.firstClip; + originalClip = g.originalClip; + clip = g.getClip(); + + if (g.transform == null) + transform = null; + else + transform = new AffineTransform(g.transform); + + setFont(g.font); + setColor(foreground); + setBackground(bg); + setPaint(paint); + setStroke(stroke); + setTransformImpl(transform); + setClip(clip); + setComposite(comp); + + antialias = !g.antialias; + setAntialias(g.antialias); + } + + /** + * Generic destructor - call the native dispose() method. + */ + public void finalize() + { + dispose(); + } + + /** + * 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); + nativePointer = 0; + if (compCtx != null) + compCtx.dispose(); + } + + /** + * Allocate the cairographics2d structure and set the cairo_t pointer in it. + * @param pointer - a cairo_t pointer, casted to a long. + */ + protected native long init(long pointer); + + /** + * These are declared abstract as there may be context-specific issues. + */ + public abstract Graphics create(); + + public abstract GraphicsConfiguration getDeviceConfiguration(); + + protected abstract void copyAreaImpl(int x, int y, int width, int height, + int dx, int dy); + + + /** + * Find the bounds of this graphics context, in device space. + * + * @return the bounds in device-space + */ + protected abstract Rectangle2D getRealBounds(); + + ////// Native Methods //////////////////////////////////////////////////// + + /** + * Dispose of allocate native resouces. + */ + public native void disposeNative(long pointer); + + /** + * Draw pixels as an RGBA int matrix + * @param w - width + * @param h - height + * @param stride - stride of the array width + * @param i2u - affine transform array + */ + protected native void drawPixels(long pointer, int[] pixels, int w, int h, + int stride, double[] i2u, double alpha, + int interpolation); + + protected native void setGradient(long pointer, 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); + + protected native void setPaintPixels(long pointer, int[] pixels, int w, + int h, int stride, boolean repeat, + int x, int y); + + /** + * Set the current transform matrix + */ + protected native void cairoSetMatrix(long pointer, double[] m); + + /** + * Scaling method + */ + protected native void cairoScale(long pointer, double x, double y); + + /** + * Set the compositing operator + */ + protected native void cairoSetOperator(long pointer, int cairoOperator); + + /** + * Sets the current color in RGBA as a 0.0-1.0 double + */ + protected native void cairoSetRGBAColor(long pointer, double red, double green, + double blue, double alpha); + + /** + * Sets the current winding rule in Cairo + */ + protected native void cairoSetFillRule(long pointer, int cairoFillRule); + + /** + * Set the line style, cap, join and miter limit. + * Cap and join parameters are in the BasicStroke enumerations. + */ + protected native void cairoSetLine(long pointer, double width, int cap, + int join, double miterLimit); + + /** + * Set the dash style + */ + protected native void cairoSetDash(long pointer, double[] dashes, int ndash, + double offset); + + /* + * Draws a Glyph Vector + */ + protected native void cairoDrawGlyphVector(long pointer, GdkFontPeer font, + float x, float y, int n, + int[] codes, float[] positions, long[] fontset); + + /** + * Set the font in cairo. + */ + protected native void cairoSetFont(long pointer, GdkFontPeer font); + + /** + * Appends a rectangle to the current path + */ + protected native void cairoRectangle(long pointer, double x, double y, + double width, double height); + + /** + * Appends an arc to the current path + */ + protected native void cairoArc(long pointer, double x, double y, + double radius, double angle1, double angle2); + + /** + * Save / restore a cairo path + */ + protected native void cairoSave(long pointer); + protected native void cairoRestore(long pointer); + + /** + * New current path + */ + protected native void cairoNewPath(long pointer); + + /** + * Close current path + */ + protected native void cairoClosePath(long pointer); + + /** moveTo */ + protected native void cairoMoveTo(long pointer, double x, double y); + + /** lineTo */ + protected native void cairoLineTo(long pointer, double x, double y); + + /** Cubic curve-to */ + protected native void cairoCurveTo(long pointer, double x1, double y1, + double x2, double y2, + double x3, double y3); + + /** + * Stroke current path + */ + protected native void cairoStroke(long pointer); + + /** + * Fill current path + */ + protected native void cairoFill(long pointer, double alpha); + + /** + * Clip current path + */ + protected native void cairoClip(long pointer); + + /** + * Clear clip + */ + protected native void cairoResetClip(long pointer); + + /** + * Set antialias. + */ + protected native void cairoSetAntialias(long pointer, boolean aa); + + + ///////////////////////// TRANSFORMS /////////////////////////////////// + /** + * Set the current transform + */ + public void setTransform(AffineTransform tx) + { + // Transform clip into target space using the old transform. + updateClip(transform); + + // Update the native transform. + setTransformImpl(tx); + + // Transform the clip back into user space using the inverse new transform. + try + { + updateClip(transform.createInverse()); + } + catch (NoninvertibleTransformException ex) + { + // TODO: How can we deal properly with this? + ex.printStackTrace(); + } + + if (clip != null) + setClip(clip); + } + + private void setTransformImpl(AffineTransform tx) + { + transform = tx; + if (transform != null) + { + double[] m = new double[6]; + transform.getMatrix(m); + cairoSetMatrix(nativePointer, m); + } + } + + public void transform(AffineTransform tx) + { + if (transform == null) + transform = new AffineTransform(tx); + else + transform.concatenate(tx); + + if (clip != null) + { + try + { + AffineTransform clipTransform = tx.createInverse(); + updateClip(clipTransform); + } + catch (NoninvertibleTransformException ex) + { + // TODO: How can we deal properly with this? + ex.printStackTrace(); + } + } + + setTransformImpl(transform); + } + + 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)); + } + + /** + * 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) + { + if (transform != null) + transform.translate(tx, ty); + else + transform = AffineTransform.getTranslateInstance(tx, ty); + + if (clip != null) + { + // FIXME: this should actuall try to transform the shape + // rather than degrade to bounds. + if (clip instanceof Rectangle2D) + { + Rectangle2D r = (Rectangle2D) clip; + r.setRect(r.getX() - tx, r.getY() - ty, r.getWidth(), + r.getHeight()); + } + else + { + AffineTransform clipTransform = + AffineTransform.getTranslateInstance(-tx, -ty); + updateClip(clipTransform); + } + } + + setTransformImpl(transform); + } + + public void translate(int x, int y) + { + translate((double) x, (double) y); + } + + public void shear(double shearX, double shearY) + { + transform(AffineTransform.getShearInstance(shearX, shearY)); + } + + ///////////////////////// DRAWING STATE /////////////////////////////////// + + public void clip(Shape s) + { + // Do not touch clip when s == null. + if (s == null) + { + // The spec says this should clear the clip. The reference + // implementation throws a NullPointerException instead. I think, + // in this case we should conform to the specs, as it shouldn't + // affect compatibility. + setClip(null); + return; + } + + // If the current clip is still null, initialize it. + if (clip == null) + { + clip = getRealBounds(); + } + + // This is so common, let's optimize this. + if (clip instanceof Rectangle2D && s instanceof Rectangle2D) + { + Rectangle2D clipRect = (Rectangle2D) clip; + Rectangle2D r = (Rectangle2D) s; + Rectangle2D.intersect(clipRect, r, clipRect); + 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() + { + return paint; + } + + public AffineTransform getTransform() + { + return (AffineTransform) transform.clone(); + } + + public void setPaint(Paint p) + { + if (p == null) + return; + + paint = p; + if (paint instanceof Color) + { + setColor((Color) paint); + customPaint = false; + } + + 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 = height / (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); + setPaintPixels(nativePointer, pixels, width, height, width, true, 0, 0); + customPaint = false; + } + + 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(); + setGradient(nativePointer, 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()); + customPaint = false; + } + else + { + customPaint = true; + } + } + + /** + * Sets a custom paint + * + * @param bounds the bounding box, in user space + */ + protected void setCustomPaint(Rectangle bounds) + { + if (paint instanceof Color || paint instanceof TexturePaint + || paint instanceof GradientPaint) + return; + + int userX = bounds.x; + int userY = bounds.y; + int userWidth = bounds.width; + int userHeight = bounds.height; + + // Find bounds in device space + Rectangle2D bounds2D = getTransformedBounds(bounds, transform); + int deviceX = (int)bounds2D.getX(); + int deviceY = (int)bounds2D.getY(); + int deviceWidth = (int)Math.ceil(bounds2D.getWidth()); + int deviceHeight = (int)Math.ceil(bounds2D.getHeight()); + + // Get raster of the paint background + PaintContext pc = paint.createContext(CairoSurface.cairoColorModel, + new Rectangle(deviceX, deviceY, + deviceWidth, + deviceHeight), + bounds, + transform, hints); + + Raster raster = pc.getRaster(deviceX, deviceY, deviceWidth, + deviceHeight); + + // Clear the transform matrix in Cairo, since the raster returned by the + // PaintContext is already in device-space + AffineTransform oldTx = new AffineTransform(transform); + setTransformImpl(new AffineTransform()); + + // Set pixels in cairo, aligning the top-left of the background image + // to the top-left corner in device space + if (pc.getColorModel().equals(CairoSurface.cairoColorModel) + && raster.getSampleModel().getTransferType() == DataBuffer.TYPE_INT) + { + // Use a fast copy if the paint context can uses a Cairo-compatible + // color model + setPaintPixels(nativePointer, + (int[])raster.getDataElements(0, 0, deviceWidth, + deviceHeight, null), + deviceWidth, deviceHeight, deviceWidth, false, + deviceX, deviceY); + } + + else if (pc.getColorModel().equals(CairoSurface.cairoCM_opaque) + && raster.getSampleModel().getTransferType() == DataBuffer.TYPE_INT) + { + // We can also optimize if the context uses a similar color model + // but without an alpha channel; we just add the alpha + int[] pixels = (int[])raster.getDataElements(0, 0, deviceWidth, + deviceHeight, null); + + for (int i = 0; i < pixels.length; i++) + pixels[i] = 0xff000000 | (pixels[i] & 0x00ffffff); + + setPaintPixels(nativePointer, pixels, deviceWidth, deviceHeight, + deviceWidth, false, deviceX, deviceY); + } + + else + { + // Fall back on wrapping the raster in a BufferedImage, and + // use BufferedImage.getRGB() to do color-model conversion + WritableRaster wr = Raster.createWritableRaster(raster.getSampleModel(), + new Point(raster.getMinX(), + raster.getMinY())); + wr.setRect(raster); + + BufferedImage img2 = new BufferedImage(pc.getColorModel(), wr, + pc.getColorModel().isAlphaPremultiplied(), + null); + + setPaintPixels(nativePointer, + img2.getRGB(0, 0, deviceWidth, deviceHeight, null, 0, + deviceWidth), + deviceWidth, deviceHeight, deviceWidth, false, + deviceX, deviceY); + } + + // Restore transform + setTransformImpl(oldTx); + } + + public Stroke getStroke() + { + return stroke; + } + + public void setStroke(Stroke st) + { + stroke = st; + if (stroke instanceof BasicStroke) + { + BasicStroke bs = (BasicStroke) stroke; + cairoSetLine(nativePointer, bs.getLineWidth(), bs.getEndCap(), + bs.getLineJoin(), 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]; + + cairoSetDash(nativePointer, double_dashes, double_dashes.length, + (double) bs.getDashPhase()); + } + else + cairoSetDash(nativePointer, new double[0], 0, 0.0); + } + } + + /** + * Utility method to find the bounds of a shape, including the stroke width. + * + * @param s the shape + * @return the bounds of the shape, including stroke width + */ + protected Rectangle findStrokedBounds(Shape s) + { + Rectangle r = s.getBounds(); + + if (stroke instanceof BasicStroke) + { + int strokeWidth = (int)Math.ceil(((BasicStroke)stroke).getLineWidth()); + r.x -= strokeWidth / 2; + r.y -= strokeWidth / 2; + r.height += strokeWidth; + r.width += strokeWidth; + } + else + { + Shape s2 = stroke.createStrokedShape(s); + r = s2.getBounds(); + } + + return r; + } + + public void setPaintMode() + { + setComposite(AlphaComposite.SrcOver); + } + + public void setXORMode(Color c) + { + // FIXME: implement + } + + public void setColor(Color c) + { + if (c == null) + c = Color.BLACK; + + fg = c; + paint = c; + updateColor(); + } + + /** + * Set the current fg value as the cairo color. + */ + void updateColor() + { + if (fg == null) + fg = Color.BLACK; + + cairoSetRGBAColor(nativePointer, fg.getRed() / 255.0, + fg.getGreen() / 255.0,fg.getBlue() / 255.0, + fg.getAlpha() / 255.0); + } + + public Color getColor() + { + return fg; + } + + public void clipRect(int x, int y, int width, int height) + { + if (clip == null) + setClip(new Rectangle(x, y, width, height)); + else if (clip instanceof Rectangle) + { + computeIntersection(x, y, width, height, (Rectangle) clip); + setClip(clip); + } + else + clip(new Rectangle(x, y, width, height)); + } + + public Shape getClip() + { + if (clip == null) + return null; + else if (clip instanceof Rectangle2D) + return clip.getBounds2D(); //getClipInDevSpace(); + else + { + GeneralPath p = new GeneralPath(); + PathIterator pi = clip.getPathIterator(null); + p.append(pi, false); + return p; + } + } + + public Rectangle getClipBounds() + { + if (clip == null) + return null; + else + return clip.getBounds(); + } + + protected Rectangle2D getClipInDevSpace() + { + Rectangle2D uclip = clip.getBounds2D(); + if (transform == null) + return uclip; + else + return getTransformedBounds(clip.getBounds2D(), transform); + } + + public void setClip(int x, int y, int width, int height) + { + if( width < 0 || height < 0 ) + return; + + setClip(new Rectangle2D.Double(x, y, width, height)); + } + + public void setClip(Shape s) + { + // 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 ) + { + originalClip = s; + firstClip = false; + } + + clip = s; + cairoResetClip(nativePointer); + + if (clip != null) + { + cairoNewPath(nativePointer); + if (clip instanceof Rectangle2D) + { + Rectangle2D r = (Rectangle2D) clip; + cairoRectangle(nativePointer, r.getX(), r.getY(), r.getWidth(), + r.getHeight()); + } + else + walkPath(clip.getPathIterator(null), false); + + cairoClip(nativePointer); + } + } + + public void setBackground(Color c) + { + if (c == null) + c = Color.WHITE; + bg = c; + } + + public Color getBackground() + { + return bg; + } + + /** + * Return the current composite. + */ + public Composite getComposite() + { + if (comp == null) + return AlphaComposite.SrcOver; + else + return comp; + } + + /** + * Sets the current composite context. + */ + public void setComposite(Composite comp) + { + if (this.comp == comp) + return; + + this.comp = comp; + if (compCtx != null) + compCtx.dispose(); + compCtx = null; + + if (comp instanceof AlphaComposite) + { + AlphaComposite a = (AlphaComposite) comp; + cairoSetOperator(nativePointer, a.getRule()); + } + + else + { + cairoSetOperator(nativePointer, AlphaComposite.SRC_OVER); + + if (comp != null) + { + // FIXME: this check is only required "if this Graphics2D + // context is drawing to a Component on the display screen". + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new AWTPermission("readDisplayPixels")); + + compCtx = comp.createContext(getBufferCM(), getNativeCM(), hints); + } + } + } + + /** + * Returns the Colour Model describing the native, raw image data for this + * specific peer. + * + * @return ColorModel the ColorModel of native data in this peer + */ + protected abstract ColorModel getNativeCM(); + + /** + * Returns the Color Model describing the buffer that this peer uses + * for custom composites. + * + * @return ColorModel the ColorModel of the composite buffer in this peer. + */ + protected ColorModel getBufferCM() + { + // This may be overridden by some subclasses + return getNativeCM(); + } + + ///////////////////////// DRAWING PRIMITIVES /////////////////////////////////// + + public void draw(Shape s) + { + if ((stroke != null && ! (stroke instanceof BasicStroke)) + || (comp instanceof AlphaComposite && ((AlphaComposite) comp).getAlpha() != 1.0)) + { + // Cairo doesn't support stroking with alpha, so we create the stroked + // shape and fill with alpha instead + fill(stroke.createStrokedShape(s)); + return; + } + + if (customPaint) + { + Rectangle r = findStrokedBounds(s); + setCustomPaint(r); + } + + setAntialias(!hints.get(RenderingHints.KEY_ANTIALIASING) + .equals(RenderingHints.VALUE_ANTIALIAS_OFF)); + createPath(s, true); + cairoStroke(nativePointer); + } + + public void fill(Shape s) + { + createPath(s, false); + + if (customPaint) + setCustomPaint(s.getBounds()); + + setAntialias(!hints.get(RenderingHints.KEY_ANTIALIASING) + .equals(RenderingHints.VALUE_ANTIALIAS_OFF)); + double alpha = 1.0; + if (comp instanceof AlphaComposite) + alpha = ((AlphaComposite) comp).getAlpha(); + cairoFill(nativePointer, alpha); + } + + private void createPath(Shape s, boolean isDraw) + { + cairoNewPath(nativePointer); + + // Optimize rectangles, since there is a direct Cairo function + if (s instanceof Rectangle2D) + { + Rectangle2D r = (Rectangle2D) s; + + // Pixels need to be shifted in draw operations to ensure that they + // light up entire pixels, but we also need to make sure the rectangle + // does not get distorted by this shifting operation + double x = shiftX(r.getX(),shiftDrawCalls && isDraw); + double y = shiftY(r.getY(), shiftDrawCalls && isDraw); + double w = Math.round(r.getWidth()); + double h = Math.round(r.getHeight()); + cairoRectangle(nativePointer, x, y, w, h); + } + + // Lines are easy too + else if (s instanceof Line2D) + { + Line2D l = (Line2D) s; + cairoMoveTo(nativePointer, shiftX(l.getX1(), shiftDrawCalls && isDraw), + shiftY(l.getY1(), shiftDrawCalls && isDraw)); + cairoLineTo(nativePointer, shiftX(l.getX2(), shiftDrawCalls && isDraw), + shiftY(l.getY2(), shiftDrawCalls && isDraw)); + } + + // We can optimize ellipses too; however we don't bother optimizing arcs: + // the iterator is fast enough (an ellipse requires 5 steps using the + // iterator, while most arcs are only 2-3) + else if (s instanceof Ellipse2D) + { + Ellipse2D e = (Ellipse2D) s; + + double radius = Math.min(e.getHeight(), e.getWidth()) / 2; + + // Cairo only draws circular shapes, but we can use a stretch to make + // them into ellipses + double xscale = 1, yscale = 1; + if (e.getHeight() != e.getWidth()) + { + cairoSave(nativePointer); + + if (e.getHeight() < e.getWidth()) + xscale = e.getWidth() / (radius * 2); + else + yscale = e.getHeight() / (radius * 2); + + if (xscale != 1 || yscale != 1) + cairoScale(nativePointer, xscale, yscale); + } + + cairoArc(nativePointer, + shiftX(e.getCenterX() / xscale, shiftDrawCalls && isDraw), + shiftY(e.getCenterY() / yscale, shiftDrawCalls && isDraw), + radius, 0, Math.PI * 2); + + if (xscale != 1 || yscale != 1) + cairoRestore(nativePointer); + } + + // All other shapes are broken down and drawn in steps using the + // PathIterator + else + walkPath(s.getPathIterator(null), shiftDrawCalls && isDraw); + } + + /** + * 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) + { + if (bg != null) + cairoSetRGBAColor(nativePointer, bg.getRed() / 255.0, + bg.getGreen() / 255.0, bg.getBlue() / 255.0, + bg.getAlpha() / 255.0); + + Composite oldcomp = comp; + setComposite(AlphaComposite.Src); + fillRect(x, y, width, height); + + setComposite(oldcomp); + updateColor(); + } + + 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); + } + + public void drawArc(int x, int y, int width, int height, int startAngle, + int arcAngle) + { + draw(new Arc2D.Double((double) x, (double) y, (double) width, + (double) height, (double) startAngle, + (double) arcAngle, Arc2D.OPEN)); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + // The coordinates being pairwise identical means one wants + // to draw a single pixel. This is emulated by drawing + // a one pixel sized rectangle. + if (x1 == x2 && y1 == y2) + fill(new Rectangle(x1, y1, 1, 1)); + else + draw(new Line2D.Double(x1, y1, x2, y2)); + } + + public void drawRect(int x, int y, int width, int height) + { + draw(new Rectangle(x, y, width, height)); + } + + 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.PIE)); + } + + public void fillRect(int x, int y, int width, int height) + { + fill (new Rectangle(x, y, width, height)); + } + + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + fill(new Polygon(xPoints, yPoints, nPoints)); + } + + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + draw(new Polygon(xPoints, yPoints, nPoints)); + } + + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) + { + for (int i = 1; i < nPoints; i++) + draw(new Line2D.Double(xPoints[i - 1], yPoints[i - 1], + xPoints[i], yPoints[i])); + } + + 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) + { + draw(new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight)); + } + + 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) + { + 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) + { + // FIXME: does this handle a rotation transform properly? + // (the width/height might not be correct) + 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( x + dx + width < r.getX() || y + dy + height < r.getY() ) + return; + + // Clip edges if necessary + if( x + dx < r.getX() ) // left + { + width = x + dx + width; + x = (int)r.getX() - dx; + } + + 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 /////////////////////////////////// + + public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) + { + hints.put(hintKey, hintValue); + + shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE) + || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); + } + + public Object getRenderingHint(RenderingHints.Key hintKey) + { + return hints.get(hintKey); + } + + public void setRenderingHints(Map<?,?> hints) + { + this.hints = new RenderingHints(getDefaultHints()); + this.hints.putAll(hints); + + shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE) + || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT); + + if (compCtx != null) + { + compCtx.dispose(); + compCtx = comp.createContext(getNativeCM(), getNativeCM(), this.hints); + } + } + + public void addRenderingHints(Map hints) + { + this.hints.putAll(hints); + } + + public RenderingHints getRenderingHints() + { + return hints; + } + + private int getInterpolation() + { + if (this.hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) + return INTERPOLATION_NEAREST; + + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + return INTERPOLATION_BILINEAR; + + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC)) + return INTERPOLATION_BICUBIC; + + else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) + return ALPHA_INTERPOLATION_SPEED; + + else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) + return ALPHA_INTERPOLATION_QUALITY; + + else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) + return ALPHA_INTERPOLATION_DEFAULT; + + // Do bilinear interpolation as default + return INTERPOLATION_BILINEAR; + } + + /** + * Set antialias if needed. If the ignoreAA flag is set, this method will + * return without doing anything. + * + * @param needAA RenderingHints.VALUE_ANTIALIAS_ON or RenderingHints.VALUE_ANTIALIAS_OFF + */ + private void setAntialias(boolean needAA) + { + if (ignoreAA) + return; + + if (needAA != antialias) + { + antialias = !antialias; + cairoSetAntialias(nativePointer, antialias); + } + } + + ///////////////////////// IMAGE. METHODS /////////////////////////////////// + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (img == null) + return false; + + if (xform == null) + xform = new AffineTransform(); + + // 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; + + try + { + invertedXform = xform.createInverse(); + } + catch (NoninvertibleTransformException e) + { + throw new ImagingOpException("Unable to invert transform " + + xform.toString()); + } + + // Unrecognized image - convert to a BufferedImage + // Note - this can get us in trouble when the gdk lock is re-acquired. + // for example by VolatileImage. See ComponentGraphics for how we work + // around this. + img = AsyncImage.realImage(img, obs); + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + + BufferedImage b = (BufferedImage) img; + Raster raster; + 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 ) + raster = BufferedImageGraphics.bufferedImages.get( b ); + else + raster = b.getRaster(); + + invertedXform.getMatrix(i2u); + + double alpha = 1.0; + if (comp instanceof AlphaComposite) + alpha = ((AlphaComposite) comp).getAlpha(); + + if(raster instanceof CairoSurface + && ((CairoSurface)raster).sharedBuffer == true) + { + drawCairoSurface((CairoSurface)raster, xform, alpha, getInterpolation()); + updateColor(); + return true; + } + + if( bgcolor != null ) + { + Color oldColor = bg; + setBackground(bgcolor); + + Rectangle2D bounds = new Rectangle2D.Double(0, 0, width, height); + bounds = getTransformedBounds(bounds, xform); + + clearRect((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), (int)bounds.getHeight()); + + setBackground(oldColor); + } + + int[] pixels = b.getRGB(0, 0, width, height, null, 0, width); + // FIXME: The above method returns data in the standard ARGB colorspace, + // meaning data should NOT be alpha pre-multiplied; however Cairo expects + // data to be premultiplied. + + cairoSave(nativePointer); + Rectangle2D bounds = new Rectangle2D.Double(0, 0, width, height); + bounds = getTransformedBounds(bounds, xform); + cairoRectangle(nativePointer, bounds.getX(), bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + cairoClip(nativePointer); + + drawPixels(nativePointer, pixels, width, height, width, i2u, alpha, + getInterpolation()); + + cairoRestore(nativePointer); + + // Cairo seems to lose the current color which must be restored. + updateColor(); + return true; + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + drawRaster(image.getColorModel(), image.getData(), xform, null); + } + + public void drawRenderableImage(RenderableImage image, AffineTransform xform) + { + drawRenderedImage(image.createRendering(new RenderContext(xform)), xform); + } + + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) + { + return drawImage(img, xform, null, obs); + } + + public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y) + { + Image filtered = image; + if (op != null) + filtered = op.filter(image, null); + drawImage(filtered, new AffineTransform(1f, 0f, 0f, 1f, x, y), null, null); + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + return drawImage(img, new AffineTransform(1f, 0f, 0f, 1f, x, y), null, + observer); + } + + public boolean drawImage(Image img, int x, int y, Color bgcolor, + ImageObserver observer) + { + return drawImage(img, x, y, img.getWidth(observer), + img.getHeight(observer), bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) + { + 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); + } + + 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) + return false; + + 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; + + // 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 + { cx = dx2; cw = dx1 - dx2; } + if( dy1 < dy2 ) + { cy = dy1; ch = dy2 - dy1; } + else + { cy = dy2; ch = dy1 - dy2; } + + clipRect( cx, cy, cw, ch ); + + 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, null, observer); + } + + /** + * Optimized method for drawing a CairoSurface onto this graphics context. + * + * @param surface The surface to draw. + * @param tx The transformation matrix (cannot be null). + * @param alpha The alpha value to paint with ( 0 <= alpha <= 1). + * @param interpolation The interpolation type. + */ + protected void drawCairoSurface(CairoSurface surface, AffineTransform tx, + double alpha, int interpolation) + { + // Find offset required if this surface is a sub-raster, and append offset + // to transformation. + if (surface.getSampleModelTranslateX() != 0 + || surface.getSampleModelTranslateY() != 0) + { + Point2D origin = new Point2D.Double(0, 0); + Point2D offset = new Point2D.Double(surface.getSampleModelTranslateX(), + surface.getSampleModelTranslateY()); + + tx.transform(origin, origin); + tx.transform(offset, offset); + + tx.translate(offset.getX() - origin.getX(), + offset.getY() - origin.getY()); + } + + // Find dimensions of this surface relative to the root parent surface + Rectangle bounds = new Rectangle(-surface.getSampleModelTranslateX(), + -surface.getSampleModelTranslateY(), + surface.width, surface.height); + + // Clip to the translated image + // We use direct cairo methods to avoid the overhead of maintaining a + // java copy of the clip, since we will be reverting it immediately + // after drawing + Shape newBounds = tx.createTransformedShape(bounds); + cairoSave(nativePointer); + walkPath(newBounds.getPathIterator(null), false); + cairoClip(nativePointer); + + // Draw the surface + try + { + double[] i2u = new double[6]; + tx.createInverse().getMatrix(i2u); + surface.nativeDrawSurface(surface.surfacePointer, nativePointer, i2u, + alpha, interpolation); + } + catch (NoninvertibleTransformException ex) + { + // This should never happen(?), so we don't need to do anything here. + ; + } + + // Restore clip + cairoRestore(nativePointer); + } + + + ///////////////////////// TEXT METHODS //////////////////////////////////// + + public void drawString(String str, float x, float y) + { + if (str == null || str.length() == 0) + return; + GdkFontPeer fontPeer = (GdkFontPeer) font.getPeer(); + TextLayout tl = (TextLayout) fontPeer.textLayoutCache.get(str); + if (tl == null) + { + tl = new TextLayout( str, getFont(), getFontRenderContext() ); + fontPeer.textLayoutCache.put(str, tl); + } + + // Set antialias to text_antialiasing, and set the ignoreAA flag so that + // the setting doesn't get overridden in a draw() or fill() call. + setAntialias(!hints.get(RenderingHints.KEY_TEXT_ANTIALIASING) + .equals(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)); + ignoreAA = true; + + tl.draw(this, x, y); + ignoreAA = false; + } + + public void drawString(String str, int x, int y) + { + drawString (str, (float) x, (float) y); + } + + public void drawString(AttributedCharacterIterator ci, int x, int y) + { + drawString (ci, (float) x, (float) y); + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + double alpha = 1.0; + + if( gv.getNumGlyphs() <= 0 ) + return; + + if (customPaint) + setCustomPaint(gv.getOutline().getBounds()); + + if (comp instanceof AlphaComposite) + alpha = ((AlphaComposite) comp).getAlpha(); + + setAntialias(!hints.get(RenderingHints.KEY_TEXT_ANTIALIASING) + .equals(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)); + ignoreAA = true; + + if (gv instanceof FreetypeGlyphVector && alpha == 1.0 + && !((FreetypeGlyphVector)gv).hasTransforms()) + { + int n = gv.getNumGlyphs (); + int[] codes = gv.getGlyphCodes (0, n, null); + long[] fontset = ((FreetypeGlyphVector)gv).getGlyphFonts (0, n, null); + float[] positions = gv.getGlyphPositions (0, n, null); + + setFont (gv.getFont ()); + GdkFontPeer fontPeer = (GdkFontPeer) font.getPeer(); + synchronized (fontPeer) + { + cairoDrawGlyphVector(nativePointer, fontPeer, + x, y, n, codes, positions, fontset); + } + } + else + { + translate(x, y); + fill(gv.getOutline()); + translate(-x, -y); + } + + ignoreAA = false; + } + + public void drawString(AttributedCharacterIterator ci, float x, float y) + { + GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), ci); + drawGlyphVector(gv, x, y); + } + + /** + * Should perhaps be contexct dependent, but this is left for now as an + * overloadable default implementation. + */ + public FontRenderContext getFontRenderContext() + { + 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. + + public FontMetrics getFontMetrics() + { + return getFontMetrics(getFont()); + } + + public FontMetrics getFontMetrics(Font f) + { + return ((GdkFontPeer) f.getPeer()).getFontMetrics(f); + } + + public void setFont(Font f) + { + // Sun's JDK does not throw NPEs, instead it leaves the current setting + // unchanged. So do we. + if (f == null) + return; + + if (f.getPeer() instanceof GdkFontPeer) + font = f; + else + font = + ((ClasspathToolkit)(Toolkit.getDefaultToolkit())) + .getFont(f.getName(), f.getAttributes()); + + GdkFontPeer fontpeer = (GdkFontPeer) getFont().getPeer(); + synchronized (fontpeer) + { + cairoSetFont(nativePointer, fontpeer); + } + } + + 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) + { + 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() + { + return (getClass().getName() + + "[font=" + getFont().toString() + + ",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; + + double alpha = 1.0; + if (comp instanceof AlphaComposite) + alpha = ((AlphaComposite) comp).getAlpha(); + + drawPixels(nativePointer, pixels, r.getWidth(), r.getHeight(), + r.getWidth(), i2u, alpha, getInterpolation()); + + // Cairo seems to lose the current color which must be restored. + updateColor(); + + return true; + } + + /** + * Shifts an x-coordinate by 0.5 in device space. + */ + private double shiftX(double coord, boolean doShift) + { + if (doShift) + { + double shift = 0.5; + if (!transform.isIdentity()) + shift /= transform.getScaleX(); + return (coord + shift); + } + else + return coord; + } + + /** + * Shifts a y-coordinate by 0.5 in device space. + */ + private double shiftY(double coord, boolean doShift) + { + if (doShift) + { + double shift = 0.5; + if (!transform.isIdentity()) + shift /= transform.getScaleY(); + return (coord + shift); + } + 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(nativePointer, p.getWindingRule()); + for (; ! p.isDone(); p.next()) + { + int seg = p.currentSegment(coords); + switch (seg) + { + case PathIterator.SEG_MOVETO: + x = shiftX(coords[0], doShift); + y = shiftY(coords[1], doShift); + cairoMoveTo(nativePointer, x, y); + break; + case PathIterator.SEG_LINETO: + x = shiftX(coords[0], doShift); + y = shiftY(coords[1], doShift); + cairoLineTo(nativePointer, 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) * (shiftX(coords[0], doShift) - x); + double y1 = y + (2.0 / 3.0) * (shiftY(coords[1], doShift) - y); + + double x2 = x1 + (1.0 / 3.0) * (shiftX(coords[2], doShift) - x); + double y2 = y1 + (1.0 / 3.0) * (shiftY(coords[3], doShift) - y); + + x = shiftX(coords[2], doShift); + y = shiftY(coords[3], doShift); + cairoCurveTo(nativePointer, x1, y1, x2, y2, x, y); + break; + case PathIterator.SEG_CUBICTO: + x = shiftX(coords[4], doShift); + y = shiftY(coords[5], doShift); + cairoCurveTo(nativePointer, shiftX(coords[0], doShift), + shiftY(coords[1], doShift), + shiftX(coords[2], doShift), + shiftY(coords[3], doShift), x, y); + break; + case PathIterator.SEG_CLOSE: + cairoClosePath(nativePointer); + break; + } + } + } + + /** + * Used by setRenderingHints() + */ + private Map<RenderingHints.Key, Object> getDefaultHints() + { + HashMap<RenderingHints.Key, Object> defaultHints = + new HashMap<RenderingHints.Key, Object>(); + + 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(); + } + + /** + * Helper method to transform the clip. This is called by the various + * transformation-manipulation methods to update the clip (which is in + * userspace) accordingly. + * + * The transform usually is the inverse transform that was applied to the + * graphics object. + * + * @param t the transform to apply to the clip + */ + private void updateClip(AffineTransform t) + { + if (clip == null) + return; + + // If the clip is a rectangle, and the transformation preserves the shape + // (translate/stretch only), then keep the clip as a rectangle + double[] matrix = new double[4]; + t.getMatrix(matrix); + if (clip instanceof Rectangle2D && matrix[1] == 0 && matrix[2] == 0) + { + Rectangle2D rect = (Rectangle2D)clip; + double[] origin = new double[] {rect.getX(), rect.getY()}; + double[] dimensions = new double[] {rect.getWidth(), rect.getHeight()}; + t.transform(origin, 0, origin, 0, 1); + t.deltaTransform(dimensions, 0, dimensions, 0, 1); + rect.setRect(origin[0], origin[1], dimensions[0], dimensions[1]); + } + else + { + if (! (clip instanceof GeneralPath)) + clip = new GeneralPath(clip); + + GeneralPath p = (GeneralPath) clip; + p.transform(t); + } + } + + private static Rectangle computeIntersection(int x, int y, int w, int h, + Rectangle rect) + { + int x2 = rect.x; + int y2 = rect.y; + int w2 = rect.width; + int h2 = rect.height; + + int dx = (x > x2) ? x : x2; + int dy = (y > y2) ? y : y2; + int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx); + int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy); + + if (dw >= 0 && dh >= 0) + rect.setBounds(dx, dy, dw, dh); + else + rect.setBounds(0, 0, 0, 0); + + return rect; + } + + static Rectangle2D getTransformedBounds(Rectangle2D bounds, AffineTransform tx) + { + double x1 = bounds.getX(); + double x2 = bounds.getX() + bounds.getWidth(); + double x3 = x1; + double x4 = x2; + double y1 = bounds.getY(); + double y2 = y1; + double y3 = bounds.getY() + bounds.getHeight(); + double y4 = y3; + + double[] points = new double[] {x1, y1, x2, y2, x3, y3, x4, y4}; + tx.transform(points, 0, points, 0, 4); + + double minX = points[0]; + double maxX = minX; + double minY = points[1]; + double maxY = minY; + for (int i = 0; i < 8; i++) + { + if (points[i] < minX) + minX = points[i]; + if (points[i] > maxX) + maxX = points[i]; + i++; + + if (points[i] < minY) + minY = points[i]; + if (points[i] > maxY) + maxY = points[i]; + } + + return new Rectangle2D.Double(minX, minY, (maxX - minX), (maxY - minY)); + } +} 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 000000000..71f6638e4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurface.java @@ -0,0 +1,428 @@ +/* CairoSurface.java + Copyright (C) 2006, 2007 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.java.awt.Buffers; + +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.DirectColorModel; +import java.awt.image.Raster; +import java.awt.image.RasterFormatException; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.Hashtable; + +/** + * CairoSurface - wraps a Cairo surface. + * + * @author Sven de Marothy + */ +public class CairoSurface extends WritableRaster +{ + int width = -1, height = -1; + + /** + * The native pointer to the Cairo surface. + */ + long surfacePointer; + + /** + * Whether the data buffer is shared between java and cairo. + */ + boolean sharedBuffer; + + // FIXME: use only the cairoCM_pre colormodel + // since that's what Cairo really uses (is there a way to do this cheaply? + // we use a non-multiplied model most of the time to avoid costly coercion + // operations...) + static ColorModel cairoColorModel = new DirectColorModel(32, 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + + static ColorModel cairoCM_pre = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + 32, 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000, + true, + Buffers.smallestAppropriateTransferType(32)); + + // This CM corresponds to the CAIRO_FORMAT_RGB24 type in Cairo + static ColorModel cairoCM_opaque = new DirectColorModel(24, 0x00FF0000, + 0x0000FF00, + 0x000000FF); + /** + * Allocates and clears the buffer and creates the cairo surface. + * @param width - the image size + * @param height - the image size + * @param stride - the buffer row stride. (in ints) + */ + private native void create(int width, int height, int stride, int[] buf); + + /** + * Destroys the cairo surface and frees the buffer. + */ + private native void destroy(long surfacePointer, int[] buf); + + /** + * Draws this image to a given CairoGraphics context, + * with an affine transform given by i2u. + */ + public native void nativeDrawSurface(long surfacePointer, long contextPointer, + double[] i2u, double alpha, + int interpolation); + + /** + * Synchronizes the image's data buffers, copying any changes made in the + * Java array into the native array. + * + * This method should only be called if (sharedBuffers == false). + */ + native void syncNativeToJava(long surfacePointer, int[] buffer); + + /** + * Synchronizes the image's data buffers, copying any changes made in the + * native array into the Java array. + * + * This method should only be called if (sharedBuffers == false). + */ + native void syncJavaToNative(long surfacePointer, int[] buffer); + + /** + * Return the buffer, with the sample values of each pixel reversed + * (ie, in ABGR instead of ARGB). + * + * @return A pointer to a flipped buffer. The memory is allocated in native + * code, and must be explicitly freed when it is no longer needed. + */ + native long getFlippedBuffer(long surfacePointer); + + /** + * Create a cairo_surface_t with specified width and height. + * The format will be ARGB32 with premultiplied alpha and native bit + * and word ordering. + */ + public CairoSurface(int width, int height) + { + this(0, 0, width, height); + } + + public CairoSurface(int x, int y, int width, int height) + { + super(createCairoSampleModel(width, height), null, new Point(x, y)); + + if(width <= 0 || height <= 0) + throw new IllegalArgumentException("Image must be at least 1x1 pixels."); + + this.width = width; + this.height = height; + dataBuffer = new DataBufferInt(width * height); + create(width, height, width, getData()); + + if(surfacePointer == 0) + throw new Error("Could not allocate bitmap."); + } + + /** + * Create a Cairo Surface that is a subimage of another Cairo Surface + */ + public CairoSurface(SampleModel sm, CairoSurface parent, Rectangle bounds, + Point origin) + { + super(sm, parent.dataBuffer, bounds, origin, parent); + + this.width = super.width; + this.height = super.height; + this.surfacePointer = parent.surfacePointer; + this.sharedBuffer = parent.sharedBuffer; + this.dataBuffer = parent.dataBuffer; + } + + /** + * Create a cairo_surface_t from a GtkImage instance. + * (data is copied, not shared) + */ + CairoSurface(GtkImage image) + { + this(image.width, image.height); + + // Copy the pixel data from the GtkImage. + int[] data = image.getPixels(); + + // Swap ordering from GdkPixbuf to Cairo + if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) + { + for (int i = 0; i < data.length; i++ ) + { + // On a big endian system we get a RRGGBBAA data array. + int alpha = data[i] & 0xFF; + if( alpha == 0 ) // I do not know why we need this, but it works. + data[i] = 0; + else + { + // Cairo needs a ARGB32 native array. + data[i] = (data[i] >>> 8) | (alpha << 24); + } + } + } + else + { + for (int i = 0; i < data.length; i++ ) + { + // On a little endian system we get a AABBGGRR data array. + int alpha = data[i] & 0xFF000000; + if( alpha == 0 ) // I do not know why we need this, but it works. + data[i] = 0; + else + { + int b = (data[i] & 0xFF0000) >> 16; + int g = (data[i] & 0xFF00); + int r = (data[i] & 0xFF) << 16; + // Cairo needs a ARGB32 native array. + data[i] = alpha | r | g | b; + } + } + } + + System.arraycopy(data, 0, getData(), 0, data.length); + } + + /** + * Dispose of the native data. + */ + public void dispose() + { + if(surfacePointer != 0 && parent == null) + destroy(surfacePointer, getData()); + } + + /** + * 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(surfacePointer)); + } + + /** + * Convenience method to quickly grab the data array backing this Raster. + * + * @return The array behind the databuffer. + */ + public int[] getData() + { + return ((DataBufferInt)dataBuffer).getData(); + } + + /** + * 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) + { + return new BufferedImage(cairoColorModel, surface, + cairoColorModel.isAlphaPremultiplied(), + new Hashtable()); + } + + /** + * 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 nativeNewCairoContext(long surfacePointer); + + public long newCairoContext() + { + return nativeNewCairoContext(surfacePointer); + } + + /** + * Copy a portion of this surface to another area on the surface. The given + * parameters must be within bounds - count on a segfault otherwise. + * + * @param x The x coordinate of the area to be copied from. + * @param y The y coordinate of the area to be copied from. + * @param width The width of the area to be copied. + * @param height The height of the area to be copied. + * @param dx The destination x coordinate. + * @param dy The destination y coordinate. + * @param stride The scanline stride. + */ + public void copyAreaNative(int x, int y, int width, + int height, int dx, int dy, int stride) + { + copyAreaNative2(surfacePointer, x, y, width, height, dx, dy, stride); + } + native void copyAreaNative2(long surfacePointer, + int x, int y, int width, int height, + int dx, int dy, int stride); + + /** + * Creates a SampleModel that matches Cairo's native format + */ + protected static SampleModel createCairoSampleModel(int w, int h) + { + return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, + new int[]{0x00FF0000, 0x0000FF00, + 0x000000FF, 0xFF000000}); + } + + /** + * Returns whether this ColorModel is compatible with Cairo's native types. + * + * @param cm The color model to check. + * @return Whether it is compatible. + */ + public static boolean isCompatibleColorModel(ColorModel cm) + { + return (cm.equals(cairoCM_pre) || cm.equals(cairoCM_opaque) || + cm.equals(cairoColorModel)); + } + + /** + * Returns whether this SampleModel is compatible with Cairo's native types. + * + * @param sm The sample model to check. + * @return Whether it is compatible. + */ + public static boolean isCompatibleSampleModel(SampleModel sm) + { + return (sm instanceof SinglePixelPackedSampleModel + && sm.getDataType() == DataBuffer.TYPE_INT + && Arrays.equals(((SinglePixelPackedSampleModel)sm).getBitMasks(), + new int[]{0x00FF0000, 0x0000FF00, + 0x000000FF, 0xFF000000})); + } + + ///// Methods interhited from Raster and WritableRaster ///// + public Raster createChild(int parentX, int parentY, int width, int height, + int childMinX, int childMinY, int[] bandList) + { + return createWritableChild(parentX, parentY, width, height, + childMinX, childMinY, bandList); + } + + public WritableRaster createCompatibleWritableRaster() + { + return new CairoSurface(width, height); + } + + public WritableRaster createCompatibleWritableRaster (int x, int y, + int w, int h) + { + return new CairoSurface(x, y, w, h); + } + + public Raster createTranslatedChild(int childMinX, int childMinY) + { + return createWritableTranslatedChild(childMinX, childMinY); + } + + public WritableRaster createWritableChild(int parentX, int parentY, + int w, int h, int childMinX, + int childMinY, int[] bandList) + { + if (parentX < minX || parentX + w > minX + width + || parentY < minY || parentY + h > minY + height) + throw new RasterFormatException("Child raster extends beyond parent"); + + SampleModel sm = (bandList == null) ? + sampleModel : + sampleModel.createSubsetSampleModel(bandList); + + return new CairoSurface(sm, this, + new Rectangle(childMinX, childMinY, w, h), + new Point(sampleModelTranslateX + childMinX - parentX, + sampleModelTranslateY + childMinY - parentY)); + } + + public WritableRaster createWritableTranslatedChild(int x, int y) + { + int tcx = sampleModelTranslateX - minX + x; + int tcy = sampleModelTranslateY - minY + y; + + return new CairoSurface(sampleModel, this, + new Rectangle(x, y, width, height), + new Point(tcx, tcy)); + } +} 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 000000000..a0c6caa9a --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/CairoSurfaceGraphics.java @@ -0,0 +1,355 @@ +/* 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.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.RenderedImage; +import java.util.Hashtable; + +/** + * Implementation of Graphics2D on a Cairo surface. + */ +public class CairoSurfaceGraphics extends CairoGraphics2D +{ + protected CairoSurface surface; + private BufferedImage buffer; + 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 ); + } + + public Graphics create() + { + return new CairoSurfaceGraphics(this); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); + } + + 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); + } + + /** + * Overloaded methods that do actual drawing need to account for custom + * composites + */ + public void draw(Shape s) + { + if (!surface.sharedBuffer) + surface.syncJavaToNative(surface.surfacePointer, surface.getData()); + + // Find total bounds of shape + Rectangle r = findStrokedBounds(s); + if (shiftDrawCalls) + { + r.width++; + r.height++; + } + + // Do the drawing + if (comp == null || comp instanceof AlphaComposite) + super.draw(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.setTransform(transform); + g2d.draw(s); + + drawComposite(r.getBounds2D(), null); + } + + if (!surface.sharedBuffer) + surface.syncNativeToJava(surface.surfacePointer, surface.getData()); + } + + public void fill(Shape s) + { + if (!surface.sharedBuffer) + surface.syncJavaToNative(surface.surfacePointer, surface.getData()); + + if (comp == null || comp instanceof AlphaComposite) + super.fill(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.setTransform(transform); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } + + if (!surface.sharedBuffer) + surface.syncNativeToJava(surface.surfacePointer, surface.getData()); + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + if (!surface.sharedBuffer) + surface.syncJavaToNative(surface.surfacePointer, surface.getData()); + + if (comp == null || comp instanceof AlphaComposite) + super.drawRenderedImage(image, xform); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.setTransform(transform); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } + + if (!surface.sharedBuffer) + surface.syncNativeToJava(surface.surfacePointer, surface.getData()); + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (!surface.sharedBuffer) + surface.syncJavaToNative(surface.surfacePointer, surface.getData()); + + boolean ret; + if (comp == null || comp instanceof AlphaComposite) + ret = super.drawImage(img, xform, bgcolor, obs); + + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Rectangle2D bounds = new Rectangle(bImg.getMinX(), bImg.getMinY(), + bImg.getWidth(), bImg.getHeight()); + if (xform != null) + bounds = getTransformedBounds(bounds, xform); + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + ret = drawComposite(bounds, obs); + } + + if (!surface.sharedBuffer) + surface.syncNativeToJava(surface.surfacePointer, surface.getData()); + + return ret; + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + if (!surface.sharedBuffer) + surface.syncJavaToNative(surface.surfacePointer, surface.getData()); + + if (comp == null || comp instanceof AlphaComposite) + super.drawGlyphVector(gv, x, y); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } + + if (!surface.sharedBuffer) + surface.syncNativeToJava(surface.surfacePointer, surface.getData()); + } + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Find bounds in device space + bounds = getTransformedBounds(bounds, transform); + + // Clip bounds by the stored clip, and by the internal buffer + Rectangle2D devClip = this.getClipInDevSpace(); + Rectangle2D.intersect(bounds, devClip, bounds); + devClip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, devClip, bounds); + + // Round bounds as needed, but be careful in our rounding + // (otherwise it may leave unpainted stripes) + double x = bounds.getX(); + double y = bounds.getY(); + double maxX = x + bounds.getWidth(); + double maxY = y + bounds.getHeight(); + x = Math.round(x); + y = Math.round(y); + bounds.setRect(x, y, Math.round(maxX - x), Math.round(maxY - y)); + + // Find subimage of internal buffer for updating + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Find subimage of main image for updating + BufferedImage current = CairoSurface.getBufferedImage(surface); + current = current.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + buffer2.getRaster()); + + // Set cairo's composite to direct SRC, since we've already done our own + // compositing + Composite oldcomp = comp; + setComposite(AlphaComposite.Src); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(buffer2, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + null, null); + setComposite(oldcomp); + updateColor(); + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + buffer = new BufferedImage(getBufferCM(), + surface.createCompatibleWritableRaster(), + getBufferCM().isAlphaPremultiplied(), + new Hashtable()); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + return CairoSurface.cairoCM_pre; + } + + protected ColorModel getBufferCM() + { + return CairoSurface.cairoColorModel; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java new file mode 100644 index 000000000..50161b2b7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphics.java @@ -0,0 +1,941 @@ +/* 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 gnu.classpath.Pointer; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.util.Hashtable; + +/** + * 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 static final boolean hasXRenderExtension = hasXRender(); + + private GtkComponentPeer component; + protected long cairo_t; + private BufferedImage buffer, componentBuffer; + + private static ThreadLocal<Integer> hasLock = new ThreadLocal<Integer>(); + private static Integer ONE = Integer.valueOf(1); + + 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); + + /** + * Obtain and hold a GDK lock, which is required for all drawing operations + * in this graphics context (since it is backed by an X surface). + * + * This method causes the GDK locking behaviour to be re-entrant. No race + * conditions are caused since a ThreadLocal is used and each thread has its + * own lock counter. + */ + private void lock() + { + Integer i = hasLock.get(); + if (i == null) + { + start_gdk_drawing(); + hasLock.set(ONE); + } + else + hasLock.set(Integer.valueOf(i.intValue() + 1)); + } + + /** + * Release the re-entrant GDK lock. + */ + private void unlock() + { + Integer i = hasLock.get(); + if (i == null) + throw new IllegalStateException(); + if (i == ONE) + { + hasLock.set(null); + end_gdk_drawing(); + } + else if (i.intValue() == 2) + hasLock.set(ONE); + else + hasLock.set(Integer.valueOf(i.intValue() - 1)); + } + + /** + * Creates a cairo_t for a volatile image + */ + protected native long initFromVolatile( long pixmapPtr); + + /** + * 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(); + + /** + * This is a utility method (used by GtkComponentPeer) for grabbing the + * image of a component. + */ + private static native Pointer nativeGrab(GtkComponentPeer component); + + private native void copyAreaNative(GtkComponentPeer component, int x, int y, + int width, int height, int dx, int dy); + + private native void drawVolatile(GtkComponentPeer component, + long vimg, int x, int y, + int width, int height, int cx, int cy, + int cw, int ch); + + /** + * Not really related (moveme?). Utility method used by GtkComponent. + */ + public static GtkImage grab( GtkComponentPeer component ) + { + return new GtkImage( nativeGrab( component ) ); + } + + /** + * 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( hasXRenderExtension ) + 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) + { + if (comp == null || comp instanceof AlphaComposite) + super.draw(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setStroke(this.getStroke()); + g2d.setColor(this.getColor()); + g2d.draw(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void fill(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.fill(s); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawRenderedImage(image, xform); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawRenderedImage(image, xform); + + drawComposite(buffer.getRaster().getBounds(), null); + } + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + boolean rv; + if (comp == null || comp instanceof AlphaComposite) + rv = super.drawImage(img, xform, bgcolor, obs); + + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find translated bounds + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(), + bImg.getHeight() + bImg.getMinY()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing + rv = drawComposite(new Rectangle2D.Double(origin.getX(), + origin.getY(), + pt.getX(), pt.getY()), + obs); + } + return rv; + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawGlyphVector(gv, x, y); + + else + { + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setStroke(this.getStroke()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + // If it is a GtkVolatileImage with an "easy" transform then + // draw directly. Always pass a BufferedImage to super to avoid + // deadlock (see Note in CairoGraphics.drawImage()). + if (img instanceof GtkVolatileImage) + { + GtkVolatileImage vimg = (GtkVolatileImage) img; + int type = transform.getType(); + if ((type == AffineTransform.TYPE_IDENTITY + || type == AffineTransform.TYPE_TRANSLATION) + && (clip == null || clip instanceof Rectangle2D)) + { + Rectangle2D r = (Rectangle2D) clip; + if (r == null) + r = getRealBounds(); + x += transform.getTranslateX(); + y += transform.getTranslateY(); + drawVolatile(component, vimg.nativePointer, + x, y, vimg.width, vimg.height, + (int) (r.getX() + transform.getTranslateX()), + (int) (r.getY() + transform.getTranslateY()), + (int) r.getWidth(), + (int) r.getHeight()); + return true; + } + else + return super.drawImage(vimg.getSnapshot(), x, y, observer); + } + + BufferedImage bimg; + if (img instanceof BufferedImage) + bimg = (BufferedImage) img; + else + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + bimg = (BufferedImage) Toolkit.getDefaultToolkit().createImage(source); + } + return super.drawImage(bimg, x, y, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) + { + // If it is a GtkVolatileImage with an "easy" transform then + // draw directly. Always pass a BufferedImage to super to avoid + // deadlock (see Note in CairoGraphics.drawImage()). + if (img instanceof GtkVolatileImage + && (clip == null || clip instanceof Rectangle2D)) + { + GtkVolatileImage vimg = (GtkVolatileImage) img; + int type = transform.getType(); + if ((type == AffineTransform.TYPE_IDENTITY + || type == AffineTransform.TYPE_TRANSLATION) + && (clip == null || clip instanceof Rectangle2D)) + { + Rectangle2D r = (Rectangle2D) clip; + if (r == null) + r = getRealBounds(); + x += transform.getTranslateX(); + y += transform.getTranslateY(); + drawVolatile(component, vimg.nativePointer, + x, y, width, height, + (int) (r.getX() + transform.getTranslateX()), + (int) (r.getY() + transform.getTranslateY()), + (int) r.getWidth(), + (int) r.getHeight()); + return true; + } + else + return super.drawImage(vimg.getSnapshot(), x, y, + width, height, observer); + } + + BufferedImage bimg; + img = AsyncImage.realImage(img, observer); + if (img instanceof BufferedImage) + bimg = (BufferedImage) img; + else + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + bimg = (BufferedImage) Toolkit.getDefaultToolkit().createImage(source); + } + return super.drawImage(bimg, x, y, width, height, observer); + } + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Clip source to visible areas that need updating + Rectangle2D clip = this.getClipBounds(); + Rectangle2D.intersect(bounds, clip, bounds); + clip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, clip, bounds); + + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Get destination clip to bounds + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + + Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], + points[2] - points[0], + points[3] - points[1]); + + Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); + + // Get current image on the component + GtkImage img = grab(component); + Graphics gr = componentBuffer.createGraphics(); + gr.drawImage(img, 0, 0, null); + gr.dispose(); + + BufferedImage cBuffer = componentBuffer; + if (!deviceBounds.equals(cBuffer.getRaster().getBounds())) + cBuffer = cBuffer.getSubimage((int)deviceBounds.getX(), + (int)deviceBounds.getY(), + (int)deviceBounds.getWidth(), + (int)deviceBounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), cBuffer.getRaster(), + cBuffer.getRaster()); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + boolean rv = super.drawImage(cBuffer, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + null, null); + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + WritableRaster rst; + rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(), + component.awtComponent.getHeight()), + new Point(0,0)); + + buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst, + GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(), + new Hashtable()); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + + if (componentBuffer == null) + { + WritableRaster rst; + rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(), + component.awtComponent.getHeight()), + new Point(0,0)); + + componentBuffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst, + GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(), + new Hashtable()); + } + } + + protected ColorModel getNativeCM() + { + return GtkVolatileImage.gdkColorModel; + } + + /* --- START OVERRIDDEN NATIVE METHODS ---- + * All native methods in CairoGraphics2D should be overridden here and + * enclosed in locks, since the cairo surface is backed by an X surface + * in this graphics context and the X surface requires external locking. + * + * We lock everything "just in case", since it's difficult to know which + * calls are and aren't thread-safe. Overriding and locking the native + * methods allows superclass code in CairoGraphics2D to execute properly, + * without the need to override every single method. + * + * CAVEAT: if native code obtains a lock (using gdk_threads_enter(), not the + * lock() method provided here) and then calls back into Java and one of these + * methods ends up being called, we will deadlock. The lock is only reentrant + * when called via our lock() method. + */ + + /* These methods are already locked in the superclass CairoGraphics2D + * so they do not need to be overridden: + * + * public void disposeNative + * + * protected void cairoDrawGlyphVector + * + * protected void cairoSetFont + */ + + @Override + protected long init(long pointer) + { + long ret; + + try + { + lock(); + ret = super.init(pointer); + } + finally + { + unlock(); + } + + return ret; + } + + @Override + protected void drawPixels(long pointer, int[] pixels, int w, int h, + int stride, double[] i2u, double alpha, + int interpolation) + { + try + { + lock(); + super.drawPixels(pointer, pixels, w, h, stride, i2u, alpha, + interpolation); + } + finally + { + unlock(); + } + } + + @Override + protected void setGradient(long pointer, 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) + { + try + { + lock(); + super.setGradient(pointer, x1, y1, x2, y2, r1, g1, b1, a1, r2, g2, b2, a2, + cyclic); + } + finally + { + unlock(); + } + } + + @Override + protected void setPaintPixels(long pointer, int[] pixels, int w, int h, + int stride, boolean repeat, int x, int y) + { + try + { + lock(); + super.setPaintPixels(pointer, pixels, w, h, stride, repeat, x, y); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSetMatrix(long pointer, double[] m) + { + try + { + lock(); + super.cairoSetMatrix(pointer, m); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoScale(long pointer, double x, double y) + { + try + { + lock(); + super.cairoScale(pointer, x, y); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSetOperator(long pointer, int cairoOperator) + { + try + { + lock(); + super.cairoSetOperator(pointer, cairoOperator); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSetRGBAColor(long pointer, double red, double green, + double blue, double alpha) + { + try + { + lock(); + super.cairoSetRGBAColor(pointer, red, green, blue, alpha); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSetFillRule(long pointer, int cairoFillRule) + { + try + { + lock(); + super.cairoSetFillRule(pointer, cairoFillRule); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSetLine(long pointer, double width, int cap, int join, + double miterLimit) + { + try + { + lock(); + super.cairoSetLine(pointer, width, cap, join, miterLimit); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSetDash(long pointer, double[] dashes, int ndash, + double offset) + { + try + { + lock(); + super.cairoSetDash(pointer, dashes, ndash, offset); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoRectangle(long pointer, double x, double y, + double width, double height) + { + try + { + lock(); + super.cairoRectangle(pointer, x, y, width, height); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoArc(long pointer, double x, double y, + double radius, double angle1, double angle2) + { + try + { + lock(); + super.cairoArc(pointer, x, y, radius, angle1, angle2); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSave(long pointer) + { + try + { + lock(); + super.cairoSave(pointer); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoRestore(long pointer) + { + try + { + lock(); + super.cairoRestore(pointer); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoNewPath(long pointer) + { + try + { + lock(); + super.cairoNewPath(pointer); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoClosePath(long pointer) + { + try + { + lock(); + super.cairoClosePath(pointer); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoMoveTo(long pointer, double x, double y) + { + try + { + lock(); + super.cairoMoveTo(pointer, x, y); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoLineTo(long pointer, double x, double y) + { + try + { + lock(); + super.cairoLineTo(pointer, x, y); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoCurveTo(long pointer, double x1, double y1, double x2, + double y2, double x3, double y3) + { + try + { + lock(); + super.cairoCurveTo(pointer, x1, y1, x2, y2, x3, y3); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoStroke(long pointer) + { + try + { + lock(); + super.cairoStroke(pointer); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoFill(long pointer, double alpha) + { + try + { + lock(); + super.cairoFill(pointer, alpha); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoClip(long pointer) + { + try + { + lock(); + super.cairoClip(pointer); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoResetClip(long pointer) + { + try + { + lock(); + super.cairoResetClip(pointer); + } + finally + { + unlock(); + } + } + + @Override + protected void cairoSetAntialias(long pointer, boolean aa) + { + try + { + lock(); + super.cairoSetAntialias(pointer, aa); + } + finally + { + unlock(); + } + } + + @Override + protected void drawCairoSurface(CairoSurface surface, AffineTransform tx, + double alpha, int interpolation) + { + try + { + lock(); + super.drawCairoSurface(surface, tx, alpha, interpolation); + } + finally + { + unlock(); + } + } +} 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 000000000..a73012d9f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/ComponentGraphicsCopy.java @@ -0,0 +1,122 @@ +/* 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.Image; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +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 000000000..8fd734799 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java @@ -0,0 +1,630 @@ +/* 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.font.FontRenderContext; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.font.TextAttribute; +import java.awt.font.TransformAttribute; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +public class FreetypeGlyphVector extends GlyphVector +{ + /** + * The associated font and its peer. + */ + private Font font; + private GdkFontPeer peer; // ATTN: Accessed from native code. + + private Rectangle2D logicalBounds; + + private float[] glyphPositions; + /** + * 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; + + /** + * The set of fonts used in this glyph vector. + */ + private long[] fontSet = null; + + /** + * Glyph transforms. Supports all transform operations. + * + * The identity transform should not be stored in this array; use a null + * instead (will result in performance improvements). + */ + private AffineTransform[] glyphTransforms; + + private GlyphMetrics[] metricsCache; + + private native void dispose(long[] fonts); + + /** + * Returns a pointer to the native PangoFcFont object. + * + * The object will be referenced with g_object_ref n times before being + * returned, and must be unreferenced a corresponding number of times. + * + * @param n Number of times to reference the object. + * @return Pointer to the native default font. + */ + private native long getNativeFontPointer(int n); + + /** + * Create a glyphvector from a given (Freetype) font and a String. + */ + public FreetypeGlyphVector(Font f, String s, FontRenderContext frc) + { + this(f, s.toCharArray(), 0, s.length(), frc, Font.LAYOUT_LEFT_TO_RIGHT); + } + + /** + * Create a glyphvector from a given (Freetype) font and a String. + */ + public FreetypeGlyphVector(Font f, char[] chars, int start, int len, + FontRenderContext frc, int flags) + { + this.s = new String(chars, start, len); + + this.font = f; + this.frc = frc; + if( !(font.getPeer() instanceof GdkFontPeer ) ) + throw new IllegalArgumentException("Not a valid font."); + peer = (GdkFontPeer)font.getPeer(); + + getGlyphs(); + if( flags == Font.LAYOUT_RIGHT_TO_LEFT ) + { + // reverse the glyph ordering. + int[] temp = new int[ nGlyphs ]; + for(int i = 0; i < nGlyphs; i++) + temp[i] = glyphCodes[nGlyphs - i - 1]; + glyphCodes = temp; + } + 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; + + if (fontSet == null) + { + fontSet = new long[nGlyphs]; + Arrays.fill(fontSet, getNativeFontPointer(nGlyphs)); + } + + performDefaultLayout(); + } + + /** + * Cloning constructor + */ + private FreetypeGlyphVector( FreetypeGlyphVector gv ) + { + font = gv.font; + peer = gv.peer; + frc = gv.frc; + s = gv.s; + nGlyphs = gv.nGlyphs; + logicalBounds = gv.logicalBounds.getBounds2D(); + + if( gv.metricsCache != null ) + { + metricsCache = new GlyphMetrics[ nGlyphs ]; + System.arraycopy(gv.metricsCache, 0, metricsCache, 0, nGlyphs); + } + + glyphCodes = new int[ nGlyphs ]; + fontSet = new long[nGlyphs]; + glyphPositions = new float[(nGlyphs + 1) * 2]; + glyphTransforms = new AffineTransform[ nGlyphs ]; + Arrays.fill(glyphTransforms, null); + + for(int i = 0; i < nGlyphs; i++ ) + { + if (gv.glyphTransforms[i] != null) + glyphTransforms[ i ] = new AffineTransform(gv.glyphTransforms[i]); + glyphCodes[i] = gv.glyphCodes[ i ]; + } + System.arraycopy(gv.glyphPositions, 0, glyphPositions, 0, + glyphPositions.length); + System.arraycopy(gv.glyphCodes, 0, glyphCodes, 0, nGlyphs); + System.arraycopy(gv.fontSet, 0, fontSet, 0, nGlyphs); + } + + public void finalize() + { + dispose(fontSet); + } + + /** + * Create the array of glyph codes. + */ + private void getGlyphs() + { + nGlyphs = s.codePointCount( 0, s.length() ); + glyphCodes = new int[ nGlyphs ]; + fontSet = new long[ nGlyphs ]; + int[] codePoints = new int[ nGlyphs ]; + int stringIndex = 0; + + for(int i = 0; i < nGlyphs; i++) + { + codePoints[i] = s.codePointAt( stringIndex ); + // UTF32 surrogate handling + if( codePoints[i] != (int)s.charAt( stringIndex ) ) + stringIndex ++; + stringIndex ++; + + if (Character.isISOControl(codePoints[i])) + { + // Replace with 'hair space'. Should better be 'zero-width space' + // but that doesn't seem to be supported by default font. + codePoints[i] = 8202; + } + } + + getGlyphs( codePoints, glyphCodes, fontSet ); + } + + /** + * Returns the glyph code within the font for a given character + */ + public native void getGlyphs(int[] codepoints, int[] glyphs, long[] fonts); + + /** + * Returns the kerning of a glyph pair + */ + private native void getKerning(int leftGlyph, int rightGlyph, long font, + float[] p); + + private native double[] getMetricsNative(int glyphCode, long font); + + private native GeneralPath getGlyphOutlineNative(int glyphIndex, long font); + + + public Object clone() + { + return new FreetypeGlyphVector( this ); + } + + /** + * 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() + { + logicalBounds = null; // invalidate caches. + glyphTransforms = new AffineTransform[nGlyphs]; + Arrays.fill(glyphTransforms, null); + glyphPositions = new float[(nGlyphs + 1) * 2]; + + GlyphMetrics gm = null; + float x = 0; + float y = 0; + float[] p = {0.0f, 0.0f}; + for(int i = 0; i < nGlyphs; i++) + { + gm = getGlyphMetrics( i ); + glyphPositions[i*2] = x; + glyphPositions[i*2 + 1] = y; + + x += gm.getAdvanceX(); + y += gm.getAdvanceY(); + + // Get the kerning only if it's not the last glyph, and the two glyphs are + // using the same font + if (i != nGlyphs-1 && fontSet[i] == fontSet[i+1]) + { + getKerning(glyphCodes[i], glyphCodes[i + 1], fontSet[i], p); + x += p[0]; + y += p[1]; + } + } + glyphPositions[nGlyphs * 2] = x; + glyphPositions[nGlyphs * 2 + 1] = y; + + // Apply any transform that may be in the font's attributes + TransformAttribute ta; + ta = (TransformAttribute)font.getAttributes().get(TextAttribute.TRANSFORM); + if (ta != null) + { + AffineTransform tx = ta.getTransform(); + + // Transform glyph positions + tx.transform(glyphPositions, 0, glyphPositions, 0, + glyphPositions.length / 2); + + // Also store per-glyph scale/shear/rotate (but not translation) + double[] matrix = new double[4]; + tx.getMatrix(matrix); + AffineTransform deltaTx = new AffineTransform(matrix); + if (!deltaTx.isIdentity()) + Arrays.fill(glyphTransforms, deltaTx); + } + } + + /** + * 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 || codeReturn.length < numEntries) + rval = new int[ numEntries ]; + else + rval = codeReturn; + + System.arraycopy(glyphCodes, beginGlyphIndex, rval, 0, numEntries); + + return rval; + } + + /** + * Returns pointers to the fonts used in this glyph vector. + * + * The array index matches that of the glyph vector itself. + */ + protected long[] getGlyphFonts(int beginGlyphIndex, int numEntries, + long[] codeReturn) + { + long[] rval; + + if( codeReturn == null || codeReturn.length < numEntries) + rval = new long[ numEntries ]; + else + rval = codeReturn; + + System.arraycopy(fontSet, beginGlyphIndex, rval, 0, numEntries); + + return rval; + } + + public Shape getGlyphLogicalBounds(int glyphIndex) + { + GlyphMetrics gm = getGlyphMetrics( glyphIndex ); + if( gm == null ) + return null; + Rectangle2D r = gm.getBounds2D(); + Point2D p = getGlyphPosition( glyphIndex ); + + double[] bounds = new double[] {p.getX() + r.getX() - gm.getLSB(), + p.getY() + r.getY(), + p.getX() + r.getX() - gm.getLSB() + gm.getAdvanceX(), + p.getY() + r.getY() + r.getHeight()}; + + if (glyphTransforms[glyphIndex] != null) + glyphTransforms[glyphIndex].transform(bounds, 0, bounds, 0, 2); + + return new Rectangle2D.Double(bounds[0], bounds[1], bounds[2] - bounds[0], + bounds[3] - bounds[1]); + } + + /* + * FIXME: Not all glyph types are supported. + * (The JDK doesn't really seem to do so either) + */ + public void setupGlyphMetrics() + { + metricsCache = new GlyphMetrics[ nGlyphs ]; + + for(int i = 0; i < nGlyphs; i++) + { + GlyphMetrics gm = (GlyphMetrics)peer.getGlyphMetrics(glyphCodes[i]); + if( gm == null ) + { + double[] val = getMetricsNative(glyphCodes[i], fontSet[i]); + if( val == null ) + gm = null; + else + { + gm = new GlyphMetrics(true, + (float)val[1], + (float)val[2], + new Rectangle2D.Double(val[3], val[4], + val[5], val[6] ), + GlyphMetrics.STANDARD ); + peer.putGlyphMetrics( glyphCodes[ i ], gm ); + } + } + metricsCache[ i ] = gm; + } + } + + /** + * Returns the metrics of a single glyph. + */ + public GlyphMetrics getGlyphMetrics(int glyphIndex) + { + if( metricsCache == null ) + setupGlyphMetrics(); + + return metricsCache[ glyphIndex ]; + } + + /** + * Returns the outline of a single glyph. + * + * Despite what the Sun API says, this method returns the glyph relative to + * the origin of the *entire string*, not each individual glyph. + */ + public Shape getGlyphOutline(int glyphIndex) + { + GeneralPath gp = getGlyphOutlineNative(glyphCodes[glyphIndex], + fontSet[glyphIndex]); + + AffineTransform tx = AffineTransform.getTranslateInstance(glyphPositions[glyphIndex*2], + glyphPositions[glyphIndex*2+1]); + if (glyphTransforms[glyphIndex] != null) + tx.concatenate( glyphTransforms[glyphIndex]); + + gp.transform(tx); + return gp; + } + + /** + * Returns the position of a single glyph. + */ + public Point2D getGlyphPosition(int glyphIndex) + { + return new Point2D.Float(glyphPositions[glyphIndex*2], + glyphPositions[glyphIndex*2 + 1]); + } + + /** + * Returns the positions of multiple glyphs. + */ + public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, + float[] positionReturn) + { + if (positionReturn == null || positionReturn.length < (numEntries * 2)) + positionReturn = new float[numEntries*2]; + + System.arraycopy(glyphPositions, beginGlyphIndex*2, positionReturn, 0, + numEntries*2); + return positionReturn; + } + + /** + * Returns the transform of a glyph. + */ + public AffineTransform getGlyphTransform(int glyphIndex) + { + return glyphTransforms[glyphIndex]; + } + + /** + * Checks whether any transform has been set on any glyphs. + */ + protected boolean hasTransforms() + { + for (int i = 0; i < glyphTransforms.length; i++) + if (glyphTransforms[i] != null) + return true; + + return false; + } + + /** + * 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); + if( logicalBounds != null ) + return logicalBounds; + + Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 ); + for( int i = 1; i < nGlyphs; i++ ) + { + Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i ); + + rect = rect.createUnion( r2 ); + } + + logicalBounds = rect; + 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 ); + GeneralPath gp = (GeneralPath)getOutline(); + gp.transform( tx ); + return gp; + } + + /** + * 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) + { + glyphPositions[glyphIndex*2] = (float)(newPos.getX()); + glyphPositions[glyphIndex*2 + 1] = (float)(newPos.getY()); + logicalBounds = null; + } + + /** + * Sets the transform of a single glyph. + */ + public void setGlyphTransform(int glyphIndex, AffineTransform newTX) + { + // The identity transform should never be in the glyphTransforms array; + // using and checking for nulls can be much faster. + if (newTX != null && newTX.isIdentity()) + newTX = null; + + // If the old and new transforms are identical, bail + if (glyphTransforms[glyphIndex] == null && newTX == null) + return; + + if (newTX != null && newTX.equals(glyphTransforms[glyphIndex])) + return; + + // Invalidate bounds cache and set new transform + logicalBounds = null; + glyphTransforms[glyphIndex] = newTX; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java new file mode 100644 index 000000000..6b099063f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java @@ -0,0 +1,545 @@ +/* GdkFontPeer.java -- Implements FontPeer with GTK+ + Copyright (C) 1999, 2004, 2005, 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 gnu.classpath.Configuration; +import gnu.classpath.Pointer; + +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.font.opentype.NameDecoder; + +import gnu.java.lang.CPStringBuilder; + +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.font.TextLayout; +import java.awt.geom.Rectangle2D; +import java.text.CharacterIterator; +import java.util.Locale; +import java.util.Map; +import java.nio.ByteBuffer; +import java.util.HashMap; + +public class GdkFontPeer extends ClasspathFontPeer +{ + static final FontRenderContext DEFAULT_CTX = + new FontRenderContext(null, false, false); + + /** + * Caches TextLayout instances for use in charsWidth() and drawString(). + * The size of the cache has been chosen so that relativly large GUIs with + * text documents are still efficient. + */ + HashMap<String,TextLayout> textLayoutCache = new GtkToolkit.LRUCache<String,TextLayout>(500); + + private class GdkFontMetrics extends FontMetrics + { + + public GdkFontMetrics (Font font) + { + super(initFont(font)); + } + + public int stringWidth (String str) + { + TextLayout tl = textLayoutCache.get(str); + if (tl == null) + { + tl = new TextLayout(str, font, DEFAULT_CTX); + textLayoutCache.put(str, tl); + } + return (int) tl.getAdvance(); + } + + public int charWidth (char ch) + { + return stringWidth (new String (new char[] { ch })); + } + + public int charsWidth (char data[], int off, int len) + { + return stringWidth (new String (data, off, len)); + } + + public int getHeight() + { + return (int) height; + } + + public int getLeading () + { + return (int) (height - (ascent + descent)); + } + + public int getAscent () + { + return (int) ascent; + } + + public int getMaxAscent () + { + return (int) ascent; + } + + public int getDescent () + { + return (int) descent; + } + + public int getMaxDescent () + { + return (int) maxDescent; + } + + public int getMaxAdvance () + { + return (int) maxAdvance; + } + } + + static native void initStaticState(); + private final int native_state = GtkGenericPeer.getUniqueInteger (); + + /** + * Cache GlyphMetrics objects. + */ + private HashMap<Integer,GlyphMetrics> metricsCache; + + private static final int FONT_METRICS_ASCENT = 0; + private static final int FONT_METRICS_MAX_ASCENT = 1; + private static final int FONT_METRICS_DESCENT = 2; + private static final int FONT_METRICS_MAX_DESCENT = 3; + private static final int FONT_METRICS_MAX_ADVANCE = 4; + private static final int FONT_METRICS_HEIGHT = 5; + private static final int FONT_METRICS_UNDERLINE_OFFSET = 6; + private static final int FONT_METRICS_UNDERLINE_THICKNESS = 7; + + float ascent; + float descent; + float maxAscent; + float maxDescent; + float maxAdvance; + float height; + float underlineOffset; + float underlineThickness; + + GdkFontMetrics metrics; + + static + { + if (true) // GCJ LOCAL + { + System.loadLibrary("gtkpeer"); + } + + initStaticState (); + + } + + private ByteBuffer nameTable = null; + + /** + * The pointer to the native font data. + * + * This field is manipulated by native code. Don't change or remove + * without adjusting the native code. + */ + private Pointer nativeFont; + + private native void initState (); + private native void dispose (); + private native void setFont (String family, int style, int size); + + native synchronized void getFontMetrics(double [] metrics); + native synchronized void getTextMetrics(String str, double [] metrics); + + native void releasePeerGraphicsResource(); + + + protected void finalize () + { + releasePeerGraphicsResource(); + dispose (); + } + + /* + * Helpers for the 3-way overloading that this class seems to suffer + * from. Remove them if you feel like they're a performance bottleneck, + * for the time being I prefer my code not be written and debugged in + * triplicate. + */ + + private String buildString(CharacterIterator iter) + { + CPStringBuilder sb = new CPStringBuilder(); + for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) + sb.append(c); + return sb.toString(); + } + + private String buildString(CharacterIterator iter, int begin, int limit) + { + CPStringBuilder sb = new CPStringBuilder(); + int i = 0; + for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next(), i++) + { + if (begin <= i) + sb.append(c); + if (limit <= i) + break; + } + return sb.toString(); + } + + private String buildString(char[] chars, int begin, int limit) + { + return new String(chars, begin, limit - begin); + } + + /* Public API */ + + public GdkFontPeer (String name, int style) + { + // All fonts get a default size of 12 if size is not specified. + this(name, style, 12); + } + + public GdkFontPeer (String name, int style, int size) + { + super(name, style, size); + initState (); + setFont (this.familyName, this.style, (int)this.size); + metricsCache = new HashMap<Integer,GlyphMetrics>(); + setupMetrics(); + } + + public GdkFontPeer (String name, Map attributes) + { + super(name, attributes); + initState (); + setFont (this.familyName, this.style, (int)this.size); + metricsCache = new HashMap<Integer,GlyphMetrics>(); + setupMetrics(); + } + + + /** + * Makes sure to return a Font based on the given Font that has as + * peer a GdkFontPeer. Used in the initializer. + */ + static Font initFont(Font font) + { + if (font == null) + return new Font("Dialog", Font.PLAIN, 12); + else if (font.getPeer() instanceof GdkFontPeer) + return font; + else + { + ClasspathToolkit toolkit; + toolkit = (ClasspathToolkit) Toolkit.getDefaultToolkit(); + return toolkit.getFont(font.getName(), font.getAttributes()); + } + } + + private void setupMetrics() + { + double [] hires = new double[8]; + getFontMetrics(hires); + ascent = (float) hires[FONT_METRICS_ASCENT]; + maxAscent = (float) hires[FONT_METRICS_MAX_ASCENT]; + descent = (float) hires[FONT_METRICS_DESCENT]; + maxDescent = (float) hires[FONT_METRICS_MAX_DESCENT]; + maxAdvance = (float) hires[FONT_METRICS_MAX_ADVANCE]; + height = (float) hires[FONT_METRICS_HEIGHT]; + underlineOffset = (float) hires[FONT_METRICS_UNDERLINE_OFFSET]; + underlineThickness = (float) hires[FONT_METRICS_UNDERLINE_THICKNESS]; + } + + /** + * Unneeded, but implemented anyway. + */ + public String getSubFamilyName(Font font, Locale locale) + { + 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) + { + 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, int c) + { + // FIXME: inquire with pango + return true; + } + + public int canDisplayUpTo (Font font, CharacterIterator i, int start, int limit) + { + // FIXME: inquire with pango + return -1; + } + + public GlyphVector createGlyphVector (Font font, + FontRenderContext ctx, + CharacterIterator i) + { + return new FreetypeGlyphVector(font, buildString (i), ctx); + } + + public GlyphVector createGlyphVector (Font font, + FontRenderContext ctx, + int[] glyphCodes) + { + return new FreetypeGlyphVector(font, glyphCodes, ctx); + } + + public byte getBaselineFor (Font font, char c) + { + // FIXME: Actually check. + return Font.ROMAN_BASELINE; + } + + private class GdkFontLineMetrics extends LineMetrics + { + private int nchars; + public GdkFontLineMetrics (GdkFontPeer fp, int n) + { + nchars = n; + } + + public float getAscent() + { + return ascent; + } + + public int getBaselineIndex() + { + // FIXME + return Font.ROMAN_BASELINE; + } + + public float[] getBaselineOffsets() + { + return new float[3]; + } + + public float getDescent() + { + return descent; + } + + public float getHeight() + { + return height; + } + + public float getLeading() + { + return height - (ascent + descent); + } + + public int getNumChars() + { + return nchars; + } + + public float getStrikethroughOffset() + { + // FreeType doesn't seem to provide a value here. + return ascent / 2; + } + + public float getStrikethroughThickness() + { + // FreeType doesn't seem to provide a value here. + return 1.f; + } + + public float getUnderlineOffset() + { + return underlineOffset; + } + + public float getUnderlineThickness() + { + return underlineThickness; + } + + } + + public LineMetrics getLineMetrics (Font font, CharacterIterator ci, + int begin, int limit, FontRenderContext rc) + { + return new GdkFontLineMetrics (this, limit - begin); + } + + public Rectangle2D getMaxCharBounds (Font font, FontRenderContext rc) + { + throw new UnsupportedOperationException (); + } + + public int getMissingGlyphCode (Font font) + { + throw new UnsupportedOperationException (); + } + + public String getGlyphName (Font font, int glyphIndex) + { + throw new UnsupportedOperationException (); + } + + public int getNumGlyphs (Font font) + { + 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 boolean hasUniformLineMetrics (Font font) + { + return true; + } + + public GlyphVector layoutGlyphVector (Font font, FontRenderContext frc, + char[] chars, int start, int limit, + int flags) + { + return new FreetypeGlyphVector(font, chars, start, limit - start, + frc, flags); + } + + public LineMetrics getLineMetrics (Font font, String str, + FontRenderContext frc) + { + return new GdkFontLineMetrics (this, str.length ()); + } + + public FontMetrics getFontMetrics (Font font) + { + if (metrics == null) + metrics = new GdkFontMetrics(font); + return metrics; + } + + /** + * Returns a cached GlyphMetrics object for a given glyphcode, + * or null if it doesn't exist in the cache. + */ + GlyphMetrics getGlyphMetrics( int glyphCode ) + { + return metricsCache.get(new Integer(glyphCode)); + } + + /** + * Put a GlyphMetrics object in the cache. + */ + void putGlyphMetrics( int glyphCode, GlyphMetrics metrics ) + { + metricsCache.put( new Integer( glyphCode ), metrics ); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java new file mode 100644 index 000000000..40474ff3b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java @@ -0,0 +1,156 @@ +/* GdkGraphicsConfiguration.java -- describes characteristics of graphics + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2006 Free Software Foundation + +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.BufferCapabilities; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.ImageCapabilities; +import java.awt.Rectangle; +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 opaqueColorModel; + + ColorModel bitmaskColorModel; + + ColorModel translucentColorModel; + + public GdkGraphicsConfiguration(GdkScreenGraphicsDevice dev) + { + 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() + { + return gdkScreenGraphicsDevice; + } + + public BufferedImage createCompatibleImage(int w, int h) + { + return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + } + + public BufferedImage createCompatibleImage(int w, int h, + int transparency) + { + return createCompatibleImage(w, h); + } + + public VolatileImage createCompatibleVolatileImage(int w, int h) + { + return new GtkVolatileImage(w, h); + } + + public VolatileImage createCompatibleVolatileImage(int w, int h, + ImageCapabilities caps) + throws java.awt.AWTException + { + return new GtkVolatileImage(w, h, caps); + } + + public ColorModel getColorModel() + { + return opaqueColorModel; + } + + public ColorModel getColorModel(int transparency) + { + switch (transparency) + { + case Transparency.OPAQUE: + return opaqueColorModel; + case Transparency.BITMASK: + return bitmaskColorModel; + default: + case Transparency.TRANSLUCENT: + return translucentColorModel; + } + } + + public AffineTransform getDefaultTransform() + { + // FIXME: extract the GDK DPI information here. + return new AffineTransform(); + } + + public AffineTransform getNormalizingTransform() + { + // FIXME: extract the GDK DPI information here. + return new AffineTransform(); + } + + public Rectangle getBounds() + { + return gdkScreenGraphicsDevice.getBounds(); + } + + public BufferCapabilities getBufferCapabilities() + { + return new BufferCapabilities(getImageCapabilities(), + getImageCapabilities(), + BufferCapabilities.FlipContents.UNDEFINED); + } + + public ImageCapabilities getImageCapabilities() + { + return new ImageCapabilities(false); + } + + public VolatileImage createCompatibleVolatileImage(int width, int height, int transparency) + { + // 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 new file mode 100644 index 000000000..d931f4419 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphicsEnvironment.java @@ -0,0 +1,172 @@ +/* GdkGraphicsEnvironment.java -- information about the graphics environment + Copyright (C) 2004, 2005, 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 gnu.classpath.Configuration; +import gnu.java.awt.ClasspathGraphicsEnvironment; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.util.Locale; + +import gnu.classpath.Pointer; + +public class GdkGraphicsEnvironment extends ClasspathGraphicsEnvironment +{ + private final int native_state = GtkGenericPeer.getUniqueInteger (); + + private GdkScreenGraphicsDevice defaultDevice; + + private GdkScreenGraphicsDevice[] devices; + + /** + * The pointer to the native display resource. + * + * This field is manipulated by native code. Don't change or remove + * without adjusting the native code. + */ + private Pointer display; + + static + { + if (true) // GCJ LOCAL + { + System.loadLibrary("gtkpeer"); + } + + GtkToolkit.initializeGlobalIDs(); + initIDs(); + } + + private static native void initIDs(); + + public GdkGraphicsEnvironment () + { + nativeInitState(); + } + + native void nativeInitState(); + + public GraphicsDevice[] getScreenDevices () + { + if (devices == null) + { + devices = nativeGetScreenDevices(); + } + + return (GraphicsDevice[]) devices.clone(); + } + + private native GdkScreenGraphicsDevice[] nativeGetScreenDevices(); + + public GraphicsDevice getDefaultScreenDevice () + { + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); + + synchronized (GdkGraphicsEnvironment.class) + { + if (defaultDevice == null) + { + defaultDevice = nativeGetDefaultScreenDevice(); + } + } + + return defaultDevice; + } + + private native GdkScreenGraphicsDevice nativeGetDefaultScreenDevice(); + + public Graphics2D createGraphics (BufferedImage image) + { + Raster raster = image.getRaster(); + if(raster instanceof CairoSurface) + return ((CairoSurface)raster).getGraphics(); + + return new BufferedImageGraphics( image ); + } + + private native int nativeGetNumFontFamilies(); + private native void nativeGetFontFamilies(String[] family_names); + + public Font[] getAllFonts () + { + throw new java.lang.UnsupportedOperationException (); + } + + public String[] getAvailableFontFamilyNames () + { + String[] family_names; + int array_size; + + array_size = nativeGetNumFontFamilies(); + family_names = new String[array_size]; + + nativeGetFontFamilies(family_names); + return family_names; + } + + public String[] getAvailableFontFamilyNames (Locale l) + { + throw new java.lang.UnsupportedOperationException (); + } + + /** + * Used by GtkMouseInfoPeer. + */ + native int[] getMouseCoordinates(); + native boolean isWindowUnderMouse(GtkWindowPeer windowPeer); + + public WritableRaster createRaster(ColorModel cm, SampleModel sm) + { + if (CairoSurface.isCompatibleSampleModel(sm) + && CairoSurface.isCompatibleColorModel(cm)) + return new CairoSurface(sm.getWidth(), sm.getHeight()); + else + return null; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java new file mode 100644 index 000000000..1b247c6eb --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java @@ -0,0 +1,785 @@ +/* GdkPixbufDecoder.java -- Image data decoding object + Copyright (C) 2003, 2004, 2005, 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.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Locale; +import java.util.Vector; + +import javax.imageio.IIOImage; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.spi.ImageWriterSpi; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; + +import gnu.classpath.Configuration; +import gnu.classpath.Pointer; + +public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder +{ + static + { + if (true) // GCJ LOCAL + { + System.loadLibrary("gtkpeer"); + } + + initStaticState (); + } + + /** + * Lock that should be held for all gdkpixbuf operations. We don't use + * the global gdk_threads_enter/leave functions since gdkpixbuf + * operations can be done in parallel to drawing and manipulating gtk + * widgets. + */ + static Object pixbufLock = new Object(); + + static native void initStaticState(); + private final int native_state = GtkGenericPeer.getUniqueInteger (); + + // initState() has been called, but pumpDone() has not yet been called. + private boolean needsClose = false; + + // the current set of ImageConsumers for this decoder + Vector curr; + + /** + * The pointer to the native pixbuf loader. + * + * This field is manipulated by native code. Don't change or remove + * without adjusting the native code. + */ + private Pointer nativeDecoder; + + // interface to GdkPixbuf + // These native functions should be called with the pixbufLock held. + native void initState (); + native void pumpBytes (byte[] bytes, int len) throws IOException; + native void pumpDone () throws IOException; + native void finish (boolean needsClose); + + /** + * Converts given image to bytes. + * Will call the GdkPixbufWriter for each chunk. + */ + static native void streamImage(int[] bytes, String format, + int width, int height, + boolean hasAlpha, GdkPixbufWriter writer); + + // gdk-pixbuf provids data in RGBA format + static final ColorModel cm = new DirectColorModel (32, 0xff000000, + 0x00ff0000, + 0x0000ff00, + 0x000000ff); + public GdkPixbufDecoder (DataInput datainput) + { + super (datainput); + } + + public GdkPixbufDecoder (InputStream in) + { + super (in); + } + + public GdkPixbufDecoder (String filename) + { + super (filename); + } + + public GdkPixbufDecoder (URL url) + { + super (url); + } + + public GdkPixbufDecoder (byte[] imagedata, int imageoffset, int imagelength) + { + super (imagedata, imageoffset, imagelength); + } + + // called back by native side: area_prepared_cb + void areaPrepared (int width, int height) + { + + if (curr == null) + return; + + for (int i = 0; i < curr.size (); i++) + { + ImageConsumer ic = (ImageConsumer) curr.elementAt (i); + ic.setDimensions (width, height); + ic.setColorModel (cm); + ic.setHints (ImageConsumer.RANDOMPIXELORDER); + } + } + + // called back by native side: area_updated_cb + void areaUpdated (int x, int y, int width, int height, + int pixels[], int scansize) + { + if (curr == null) + return; + + for (int i = 0; i < curr.size (); i++) + { + ImageConsumer ic = (ImageConsumer) curr.elementAt (i); + ic.setPixels (x, y, width, height, cm, pixels, 0, scansize); + } + } + + // called from an async image loader of one sort or another, this method + // repeatedly reads bytes from the input stream and passes them through a + // GdkPixbufLoader using the native method pumpBytes. pumpBytes in turn + // decodes the image data and calls back areaPrepared and areaUpdated on + // this object, feeding back decoded pixel blocks, which we pass to each + // of the ImageConsumers in the provided Vector. + + public void produce (Vector v, InputStream is) throws IOException + { + curr = v; + + byte bytes[] = new byte[4096]; + int len = 0; + synchronized(pixbufLock) + { + initState(); + } + needsClose = true; + + // Note: We don't want the pixbufLock while reading from the InputStream. + while ((len = is.read (bytes)) != -1) + { + synchronized(pixbufLock) + { + pumpBytes (bytes, len); + } + } + + synchronized(pixbufLock) + { + pumpDone(); + } + + needsClose = false; + + for (int i = 0; i < curr.size (); i++) + { + ImageConsumer ic = (ImageConsumer) curr.elementAt (i); + ic.imageComplete (ImageConsumer.STATICIMAGEDONE); + } + + curr = null; + } + + public void finalize() + { + synchronized(pixbufLock) + { + finish(needsClose); + } + } + + + public static class ImageFormatSpec + { + public String name; + public boolean writable = false; + public ArrayList<String> mimeTypes = new ArrayList<String>(); + public ArrayList<String> extensions = new ArrayList<String>(); + + public ImageFormatSpec(String name, boolean writable) + { + this.name = name; + this.writable = writable; + } + + public synchronized void addMimeType(String m) + { + mimeTypes.add(m); + } + + public synchronized void addExtension(String e) + { + extensions.add(e); + } + } + + static ArrayList<ImageFormatSpec> imageFormatSpecs; + + public static ImageFormatSpec registerFormat(String name, boolean writable) + { + ImageFormatSpec ifs = new ImageFormatSpec(name, writable); + synchronized(GdkPixbufDecoder.class) + { + if (imageFormatSpecs == null) + imageFormatSpecs = new ArrayList<ImageFormatSpec>(); + imageFormatSpecs.add(ifs); + } + return ifs; + } + + static String[] getFormatNames(boolean writable) + { + ArrayList<String> names = new ArrayList<String>(); + synchronized (imageFormatSpecs) + { + Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator(); + while (i.hasNext()) + { + ImageFormatSpec ifs = i.next(); + if (writable && !ifs.writable) + continue; + names.add(ifs.name); + + /* + * In order to make the filtering code work, we need to register + * this type under every "format name" likely to be used as a synonym. + * This generally means "all the extensions people might use". + */ + + Iterator<String> j = ifs.extensions.iterator(); + while (j.hasNext()) + names.add(j.next()); + } + } + return names.toArray(new String[names.size()]); + } + + static String[] getFormatExtensions(boolean writable) + { + ArrayList<String> extensions = new ArrayList<String>(); + synchronized (imageFormatSpecs) + { + Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator(); + while (i.hasNext()) + { + ImageFormatSpec ifs = i.next(); + if (writable && !ifs.writable) + continue; + Iterator<String> j = ifs.extensions.iterator(); + while (j.hasNext()) + extensions.add(j.next()); + } + } + return extensions.toArray(new String[extensions.size()]); + } + + static String[] getFormatMimeTypes(boolean writable) + { + ArrayList<String> mimeTypes = new ArrayList<String>(); + synchronized (imageFormatSpecs) + { + Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator(); + while (i.hasNext()) + { + ImageFormatSpec ifs = i.next(); + if (writable && !ifs.writable) + continue; + Iterator<String> j = ifs.mimeTypes.iterator(); + while (j.hasNext()) + mimeTypes.add(j.next()); + } + } + return mimeTypes.toArray(new String[mimeTypes.size()]); + } + + + static String findFormatName(Object ext, boolean needWritable) + { + if (ext == null) + return null; + + if (!(ext instanceof String)) + throw new IllegalArgumentException("extension is not a string"); + + String str = (String) ext; + + Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator(); + while (i.hasNext()) + { + ImageFormatSpec ifs = i.next(); + + if (needWritable && !ifs.writable) + continue; + + if (ifs.name.equals(str)) + return str; + + Iterator<String> j = ifs.extensions.iterator(); + while (j.hasNext()) + { + String extension = j.next(); + if (extension.equals(str)) + return ifs.name; + } + + j = ifs.mimeTypes.iterator(); + while (j.hasNext()) + { + String mimeType = j.next(); + if (mimeType.equals(str)) + return ifs.name; + } + } + throw new IllegalArgumentException("unknown extension '" + str + "'"); + } + + private static GdkPixbufReaderSpi readerSpi; + private static GdkPixbufWriterSpi writerSpi; + + public static synchronized GdkPixbufReaderSpi getReaderSpi() + { + if (readerSpi == null) + readerSpi = new GdkPixbufReaderSpi(); + return readerSpi; + } + + public static synchronized GdkPixbufWriterSpi getWriterSpi() + { + if (writerSpi == null) + writerSpi = new GdkPixbufWriterSpi(); + return writerSpi; + } + + public static void registerSpis(IIORegistry reg) + { + reg.registerServiceProvider(getReaderSpi(), ImageReaderSpi.class); + reg.registerServiceProvider(getWriterSpi(), ImageWriterSpi.class); + } + + public static class GdkPixbufWriterSpi extends ImageWriterSpi + { + public GdkPixbufWriterSpi() + { + super("GdkPixbuf", "2.x", + GdkPixbufDecoder.getFormatNames(true), + GdkPixbufDecoder.getFormatExtensions(true), + GdkPixbufDecoder.getFormatMimeTypes(true), + "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter", + new Class[] { ImageOutputStream.class }, + new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" }, + false, null, null, null, null, + false, null, null, null, null); + } + + public boolean canEncodeImage(ImageTypeSpecifier ts) + { + return true; + } + + public ImageWriter createWriterInstance(Object ext) + { + return new GdkPixbufWriter(this, ext); + } + + public String getDescription(java.util.Locale loc) + { + return "GdkPixbuf Writer SPI"; + } + + } + + public static class GdkPixbufReaderSpi extends ImageReaderSpi + { + public GdkPixbufReaderSpi() + { + super("GdkPixbuf", "2.x", + GdkPixbufDecoder.getFormatNames(false), + GdkPixbufDecoder.getFormatExtensions(false), + GdkPixbufDecoder.getFormatMimeTypes(false), + "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader", + new Class[] { ImageInputStream.class }, + new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" }, + false, null, null, null, null, + false, null, null, null, null); + } + + public boolean canDecodeInput(Object obj) + { + return true; + } + + public ImageReader createReaderInstance(Object ext) + { + return new GdkPixbufReader(this, ext); + } + + public String getDescription(Locale loc) + { + return "GdkPixbuf Reader SPI"; + } + } + + private static class GdkPixbufWriter + extends ImageWriter implements Runnable + { + String ext; + public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext) + { + super(ownerSpi); + this.ext = findFormatName(ext, true); + } + + public IIOMetadata convertImageMetadata (IIOMetadata inData, + ImageTypeSpecifier imageType, + ImageWriteParam param) + { + return null; + } + + public IIOMetadata convertStreamMetadata (IIOMetadata inData, + ImageWriteParam param) + { + return null; + } + + public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType, + ImageWriteParam param) + { + return null; + } + + public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param) + { + return null; + } + + public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param) + throws IOException + { + RenderedImage image = i.getRenderedImage(); + Raster ras = image.getData(); + int width = ras.getWidth(); + int height = ras.getHeight(); + ColorModel model = image.getColorModel(); + int[] pixels = CairoGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras); + + if (pixels == null) + { + 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 = CairoGraphics2D.findSimpleIntegerArray (img.getColorModel(), + img.getRaster()); + model = img.getColorModel(); + } + + Thread workerThread = new Thread(this, "GdkPixbufWriter"); + workerThread.start(); + processImageStarted(1); + synchronized(pixbufLock) + { + streamImage(pixels, this.ext, width, height, model.hasAlpha(), + this); + } + synchronized(data) + { + data.add(DATADONE); + data.notifyAll(); + } + + while (workerThread.isAlive()) + { + try + { + workerThread.join(); + } + catch (InterruptedException ioe) + { + // Ignored. + } + } + + if (exception != null) + throw exception; + + processImageComplete(); + } + + /** + * Object marking end of data from native streamImage code. + */ + private static final Object DATADONE = new Object(); + + /** + * Holds the data gotten from the native streamImage code. + * A worker thread will pull data out. + * Needs to be synchronized for access. + * The special object DATADONE is added when all data has been delivered. + */ + private ArrayList<Object> data = new ArrayList<Object>(); + + /** + * Holds any IOException thrown by the run method that needs + * to be rethrown by the write method. + */ + private IOException exception; + + /** Callback for streamImage native code. **/ + private void write(byte[] bs) + { + synchronized(data) + { + data.add(bs); + data.notifyAll(); + } + } + + public void run() + { + boolean done = false; + while (!done) + { + synchronized(data) + { + while (data.isEmpty()) + { + try + { + data.wait(); + } + catch (InterruptedException ie) + { + /* ignore */ + } + } + + Object o = data.remove(0); + if (o == DATADONE) + done = true; + else + { + DataOutput out = (DataOutput) getOutput(); + try + { + out.write((byte[]) o); + } + catch (IOException ioe) + { + // We are only interested in the first exception. + if (exception == null) + exception = ioe; + } + } + } + } + } + } + + private static class GdkPixbufReader + extends ImageReader + implements ImageConsumer + { + // ImageConsumer parts + GdkPixbufDecoder dec; + BufferedImage bufferedImage; + ColorModel defaultModel; + int width; + int height; + String ext; + + public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext) + { + super(ownerSpi); + this.ext = findFormatName(ext, false); + } + + public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext, + GdkPixbufDecoder d) + { + this(ownerSpi, ext); + dec = d; + } + + public void setDimensions(int w, int h) + { + processImageStarted(1); + width = w; + height = h; + } + + public void setProperties(Hashtable props) {} + + public void setColorModel(ColorModel model) + { + defaultModel = model; + } + + public void setHints(int flags) {} + + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, + int offset, int scansize) + { + } + + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, + int offset, int scansize) + { + if (model == null) + model = defaultModel; + + if (bufferedImage == null) + { + 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[]; + if (model != null) + { + pixels2 = new int[pixels.length]; + for (int yy = 0; yy < h; yy++) + for (int xx = 0; xx < w; xx++) + { + int i = yy * scansize + xx; + pixels2[i] = model.getRGB (pixels[i]); + } + } + else + pixels2 = pixels; + + bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize); + processImageProgress(y / (height == 0 ? 1 : height)); + } + + public void imageComplete(int status) + { + processImageComplete(); + } + + public BufferedImage getBufferedImage() + { + if (bufferedImage == null && dec != null) + dec.startProduction (this); + return bufferedImage; + } + + // ImageReader parts + + public int getNumImages(boolean allowSearch) + throws IOException + { + return 1; + } + + public IIOMetadata getImageMetadata(int i) + { + return null; + } + + public IIOMetadata getStreamMetadata() + throws IOException + { + return null; + } + + public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) + throws IOException + { + BufferedImage img = getBufferedImage(); + Vector<ImageTypeSpecifier> vec = new Vector<ImageTypeSpecifier>(); + vec.add(new ImageTypeSpecifier(img)); + return vec.iterator(); + } + + public int getHeight(int imageIndex) + throws IOException + { + return getBufferedImage().getHeight(); + } + + public int getWidth(int imageIndex) + throws IOException + { + return getBufferedImage().getWidth(); + } + + public void setInput(Object input, + boolean seekForwardOnly, + boolean ignoreMetadata) + { + super.setInput(input, seekForwardOnly, ignoreMetadata); + Object get = getInput(); + if (get instanceof InputStream) + dec = new GdkPixbufDecoder((InputStream) get); + else if (get instanceof DataInput) + dec = new GdkPixbufDecoder((DataInput) get); + else + throw new IllegalArgumentException("input object not supported: " + + get); + } + + public BufferedImage read(int imageIndex, ImageReadParam param) + throws IOException + { + return getBufferedImage (); + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkRobotPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkRobotPeer.java new file mode 100644 index 000000000..2609bad09 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkRobotPeer.java @@ -0,0 +1,99 @@ +/* GdkRobot.java -- an XTest implementation of RobotPeer + Copyright (C) 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.AWTException; +import java.awt.GraphicsDevice; +import java.awt.Rectangle; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.peer.RobotPeer; + +/** + * Implements the RobotPeer interface using the XTest extension. + * + * @author Thomas Fitzsimmons + */ +public class GdkRobotPeer implements RobotPeer +{ + // gdk-pixbuf provides data in RGBA format + static final ColorModel cm = new DirectColorModel (32, 0xff000000, + 0x00ff0000, + 0x0000ff00, + 0x000000ff); + + public GdkRobotPeer (GraphicsDevice screen) throws AWTException + { + // FIXME: make use of screen parameter when GraphicsDevice is + // implemented. + if (!initXTest ()) + throw new AWTException ("XTest extension not supported"); + } + + native boolean initXTest (); + + // RobotPeer methods + public native void mouseMove (int x, int y); + public native void mousePress (int buttons); + public native void mouseRelease (int buttons); + public native void mouseWheel (int wheelAmt); + public native void keyPress (int keycode); + public native void keyRelease (int keycode); + native int[] nativeGetRGBPixels (int x, int y, int width, int height); + + public int getRGBPixel (int x, int y) + { + return cm.getRGB (nativeGetRGBPixels (x, y, 1, 1)[0]); + } + + public int[] getRGBPixels (Rectangle r) + { + int[] gdk_pixels = nativeGetRGBPixels (r.x, r.y, r.width, r.height); + int[] pixels = new int[r.width * r.height]; + + for (int i = 0; i < r.width * r.height; i++) + pixels[i] = cm.getRGB (gdk_pixels[i]); + + return pixels; + } + + public void dispose() + { + // Nothing to do here yet. + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java new file mode 100644 index 000000000..1c849dfc0 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkScreenGraphicsDevice.java @@ -0,0 +1,362 @@ +/* GdkScreenGraphicsDevice.java -- information about a screen device + Copyright (C) 2004, 2005, 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.DisplayMode; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.Rectangle; +import java.awt.Window; +import java.util.ArrayList; + +import gnu.classpath.Configuration; +import gnu.classpath.Pointer; + +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; + + /** 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; + + /** + * The pointer to the native screen resource. + * + * This field is manipulated by native code. Don't change or remove + * without adjusting the native code. + */ + private Pointer screen; + + static + { + if (true) // GCJ LOCAL + { + System.loadLibrary("gtkpeer"); + } + + GtkToolkit.initializeGlobalIDs(); + initIDs(); + } + + static native void initIDs(); + + 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 () + { + if (idString == null) + idString = nativeGetIDString(); + + return idString; + } + + private native String nativeGetIDString(); + + public GraphicsConfiguration[] getConfigurations () + { + return (GraphicsConfiguration[]) configurations.clone(); + } + + public GraphicsConfiguration getDefaultConfiguration () + { + return configurations[0]; + } + + + /** + * Returns the current display mode of this device, or null if unknown. + * + * @return the current display mode + * @see #setDisplayMode(DisplayMode) + * @see #getDisplayModes() + * @since 1.4 + */ + public DisplayMode getDisplayMode() + { + 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<DisplayMode> list = new ArrayList<DisplayMode>(); + 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 list.toArray(new DisplayMode[list.size()]); + } + + native X11DisplayMode[] nativeGetDisplayModes(GdkGraphicsEnvironment env); + + /** + * Real fullscreen exclusive mode is not supported. + * + * @return <code>false</code> + * @since 1.4 + */ + public boolean isFullScreenSupported() + { + 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/GtkButtonPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkButtonPeer.java new file mode 100644 index 000000000..6ff56c0f2 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkButtonPeer.java @@ -0,0 +1,93 @@ +/* GtkButtonPeer.java -- Implements ButtonPeer with GTK + Copyright (C) 1998, 1999, 2004, 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.Button; +import java.awt.event.ActionEvent; +import java.awt.peer.ButtonPeer; + +// A composite widget. GtkButtons have transparent backgrounds. An +// AWT Button is opaque. To compensate, a GtkButtonPeer is a +// GtkButton packed in a GtkEventBox. +public class GtkButtonPeer extends GtkComponentPeer + implements ButtonPeer +{ + native void create (String label); + + public native void connectSignals (); + + /** + * Overridden to set Font of Label inside Button inside EventBox. + */ + protected native void gtkWidgetModifyFont(String name, int style, int size); + native void gtkSetLabel (String label); + native void gtkWidgetSetForeground (int red, int green, int blue); + native void gtkWidgetSetBackground (int red, int green, int blue); + native void gtkActivate (); + native void gtkWidgetRequestFocus (); + native void setNativeBounds (int x, int y, int width, int height); + + // Because this is a composite widget, we need to retrieve the + // GtkButton's preferred dimensions, not the enclosing + // GtkEventBox's. + native void gtkWidgetGetPreferredDimensions (int[] dim); + + public GtkButtonPeer (Button b) + { + super (b); + } + + void create () + { + create (((Button) awtComponent).getLabel ()); + } + + public void setLabel (String label) + { + gtkSetLabel(label); + } + + void postActionEvent (int mods) + { + q().postEvent (new ActionEvent (awtWidget, + ActionEvent.ACTION_PERFORMED, + ((Button) awtComponent).getActionCommand (), + mods)); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java new file mode 100644 index 000000000..30c39dede --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java @@ -0,0 +1,60 @@ +/* GtkCanvasPeer.java + Copyright (C) 1998, 1999 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.Canvas; +import java.awt.Dimension; +import java.awt.peer.CanvasPeer; + +public class GtkCanvasPeer extends GtkComponentPeer implements CanvasPeer +{ + native void create (); + + public GtkCanvasPeer (Canvas c) + { + super (c); + } + + // Preferred size for a drawing widget is always what the user + // requested. + public Dimension preferredSize() + { + return awtComponent.getSize(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCheckboxMenuItemPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCheckboxMenuItemPeer.java new file mode 100644 index 000000000..be9247e8d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCheckboxMenuItemPeer.java @@ -0,0 +1,74 @@ +/* GtkCheckboxMenuItemPeer.java -- Implements CheckboxMenuItemPeer with GTK+ + Copyright (C) 1999, 2005, 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.CheckboxMenuItem; +import java.awt.ItemSelectable; +import java.awt.event.ItemEvent; +import java.awt.peer.CheckboxMenuItemPeer; + +public class GtkCheckboxMenuItemPeer extends GtkMenuItemPeer + implements CheckboxMenuItemPeer +{ + protected native void create (String label); + + public GtkCheckboxMenuItemPeer (CheckboxMenuItem menu) + { + super (menu); + setState (menu.getState ()); + } + + public native void setState(boolean t); + + /** + * Called from the signal handler of the gtk widget. Posts a + * ItemEvent to indicate a state changed, then calls super to post + * an ActionEvent. + */ + protected void postMenuActionEvent () + { + CheckboxMenuItem item = (CheckboxMenuItem)awtWidget; + q().postEvent (new ItemEvent ((ItemSelectable)awtWidget, + ItemEvent.ITEM_STATE_CHANGED, + item.getActionCommand(), + item.getState() ? ItemEvent.DESELECTED : ItemEvent.SELECTED)); + + super.postMenuActionEvent(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCheckboxPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCheckboxPeer.java new file mode 100644 index 000000000..6321bc64d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCheckboxPeer.java @@ -0,0 +1,255 @@ +/* GtkCheckboxPeer.java -- Implements CheckboxPeer with GTK + Copyright (C) 1998, 1999, 2002, 2003, 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.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.event.ItemEvent; +import java.awt.peer.CheckboxPeer; +import java.util.WeakHashMap; + +/** + * This class wraps either a GtkCheckButton or a GtkOptionButton + * depending on if this peer's owner belongs to a CheckboxGroup. + */ +public class GtkCheckboxPeer extends GtkComponentPeer + implements CheckboxPeer +{ + // The CheckboxGroup to which this GtkCheckboxPeer's owner belongs. + public CheckboxGroup current_group; + // The current state of the GTK checkbox. + private boolean currentState; + + // A map from CheckboxGroup to GSList* GTK option group pointer. + private static WeakHashMap<CheckboxGroup,Long> groupMap + = new WeakHashMap<CheckboxGroup,Long>(); + + public native void createCheckButton (); + public native void createRadioButton (long groupPointer); + + public native void addToGroup (long groupPointer); + public native void removeFromGroup (); + public native void switchToGroup (long groupPointer); + + public native void connectSignals (); + + /** + * Overridden to set Font of label inside button. + */ + protected native void gtkWidgetModifyFont(String name, int style, int size); + native void gtkButtonSetLabel (String label); + native void gtkToggleButtonSetActive (boolean is_active); + + public GtkCheckboxPeer (Checkbox c) + { + super (c); + } + + public void create () + { + Checkbox checkbox = (Checkbox) awtComponent; + current_group = checkbox.getCheckboxGroup (); + if (current_group == null) + { + // Initially we're not part of a group so we're backed by a + // GtkCheckButton. + createCheckButton(); + } + else + { + // Initially we're part of a group. + + // See if this group is already stored in our map. + Long groupPointer = null; + synchronized (groupMap) + { + groupPointer = groupMap.get(current_group); + } + + if (groupPointer == null) + { + // We don't know about this group. Create a new native + // group pointer for this group and store it in our map. + createRadioButton(0); + } + else + { + // We already know about this group. Pass the + // corresponding native group pointer value to the native + // create method. + createRadioButton(groupPointer.longValue()); + } + } + currentState = checkbox.getState(); + gtkToggleButtonSetActive(currentState); + + String label = checkbox.getLabel(); + if (label != null) + gtkButtonSetLabel(label); + } + + /** + * Sets native GtkCheckButton is state is different from current + * state. Will set currentState to state to prevent posting an + * event since events should only be posted for user initiated + * clicks on the GtkCheckButton. + */ + public synchronized void setState (boolean state) + { + if (currentState != state) + { + currentState = state; + gtkToggleButtonSetActive(state); + } + } + + public void setLabel (String label) + { + gtkButtonSetLabel (label); + } + + public void setCheckboxGroup (CheckboxGroup group) + { + if (current_group == null && group != null) + { + // This peer's owner is currently not in a group, and now + // we're adding it to a group. This means that the backing + // GtkWidget will change from a GtkCheckButton to a + // GtkRadioButton. + + current_group = group; + + // See if the new group is already stored in our map. + Long groupPointer = null; + synchronized (groupMap) + { + groupPointer = groupMap.get(current_group); + } + + if (groupPointer == null) + { + // We don't know about this group. Create a new native + // group pointer for this group and store it in our map. + addToGroup(0); + } + else + { + // We already know about this group. Pass the + // corresponding native group pointer value to the native + // create method. + addToGroup(groupPointer.longValue()); + } + } + else if (current_group != null && group == null) + { + // This peer's owner is currently in a group, and now we're + // removing it from a group. This means that the backing + // GtkWidget will change from a GtkRadioButton to a + // GtkCheckButton. + removeFromGroup(); + current_group = null; + } + else if (current_group == null && group == null) + { + // This peer's owner is currently not in a group, and we're + // not adding it to a group, so simply return. + return; + } + else if (current_group != group) + { + // This peer's owner is currently in a group, and now we're + // putting it in another group. This means that we must + // remove the backing GtkRadioButton from one group and add it + // to the other group. + + current_group = group; + + // See if the new group is already stored in our map. + Long groupPointer = null; + synchronized (groupMap) + { + groupPointer = groupMap.get(current_group); + } + + if (groupPointer == null) + { + // We don't know about this group. Create a new native + // group pointer for this group and store it in our map. + switchToGroup(0); + } + else + { + // We already know about this group. Pass the + // corresponding native group pointer value to the native + // create method. + switchToGroup(groupPointer.longValue()); + } + } + } + + // Override the superclass postItemEvent so that the peer doesn't + // need information that we have. + // called back by native side: item_toggled_cb + public synchronized void postItemEvent(Object item, boolean state) + { + // Only fire event is state actually changed. + if (currentState != state) + { + currentState = state; + super.postItemEvent(awtComponent, + state ? ItemEvent.SELECTED : ItemEvent.DESELECTED); + } + } + + public void addToGroupMap(long groupPointer) + { + synchronized (groupMap) + { + groupMap.put(current_group, new Long (groupPointer)); + } + } + + public void dispose () + { + groupMap.clear(); + current_group = null; + currentState = false; + super.dispose (); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkChoicePeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkChoicePeer.java new file mode 100644 index 000000000..59cacf0b6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkChoicePeer.java @@ -0,0 +1,142 @@ +/* GtkChoicePeer.java -- Implements ChoicePeer with GTK + Copyright (C) 1998, 1999, 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.Choice; +import java.awt.AWTEvent; +import java.awt.event.ItemEvent; +import java.awt.peer.ChoicePeer; + +public class GtkChoicePeer extends GtkComponentPeer + implements ChoicePeer +{ + private int selected; + + public GtkChoicePeer (Choice c) + { + super (c); + + int count = c.getItemCount (); + if (count > 0) + { + for (int i = 0; i < count; i++) + add(c.getItem(i), i); + + selected = c.getSelectedIndex(); + if (selected >= 0) + select( selected ); + } + else + selected = -1; + } + + native void create (); + + native int nativeGetSelected (); + + native void connectSignals (); + + native void selectNative (int position); + + native void selectNativeUnlocked (int position); + + public native void add (String item, int index); + + native void nativeRemove(int index); + + native void nativeRemoveAll(); + + public void select (int position) + { + if (Thread.currentThread() == GtkMainThread.mainThread) + selectNativeUnlocked (position); + else + selectNative (position); + } + + public void remove( int index ) + { + // Ensure the triggering of an event when removing item zero if zero is the + // selected item, even though the selected index doesn't change. + if( index == 0 && selected == 0 ) + selected = -1; + nativeRemove( index ); + } + + public void removeAll() + { + selected = -1; // we do not want to trigger a select event here. + nativeRemoveAll(); + } + + public void addItem (String item, int position) + { + add (item, position); + } + + /** + * Callback from the native side on an item-select event, + * which posts an event. The event is only posted if it represents an actual + * change. Selected is set to the peer's state initially, so that the + * first call to select(int) from the constructor will not trigger an event. + * (it should not) + */ + protected void postChoiceItemEvent ( int index ) + { + if( selected != index ) + { + selected = index; + postItemEvent (((Choice) awtComponent).getItem( selected ), + ItemEvent.SELECTED); + } + } + + /** + * Catches the event and calls Choice.select() if the component state + * needs updating. + */ + public void handleEvent (AWTEvent event) + { + super.handleEvent (event); + if (event instanceof ItemEvent) + if (((ItemEvent)event).getItemSelectable() == awtComponent + && ((ItemEvent)event).getStateChange() == ItemEvent.SELECTED) + ((Choice)awtComponent).select( selected ); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java new file mode 100644 index 000000000..4250cabaa --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java @@ -0,0 +1,436 @@ +/* GtkClipboard.java + Copyright (C) 1999, 2005, 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 gnu.java.lang.CPStringBuilder; + +import java.awt.Image; + +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Reader; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; + +import java.util.List; +import java.util.Iterator; + +public class GtkClipboard extends Clipboard +{ + /** + * The one and only gtk+ clipboard instance for the CLIPBOARD selection. + */ + final static GtkClipboard clipboard = new GtkClipboard("System Clipboard"); + + /** + * The one and only gtk+ clipboard instance for the PRIMARY selection. + */ + final static GtkClipboard selection = new GtkClipboard("System Selection"); + + // Given to the native side so it can signal special targets that + // can be converted to one of the special predefined DataFlavors. + static final String stringMimeType + = DataFlavor.stringFlavor.getMimeType(); + static final String imageMimeType + = DataFlavor.imageFlavor.getMimeType(); + static final String filesMimeType + = DataFlavor.javaFileListFlavor.getMimeType(); + + // Indicates whether the results of the clipboard selection can be + // cached by GtkSelection. True if + // gdk_display_supports_selection_notification. + static final boolean canCache = initNativeState(clipboard, selection, + stringMimeType, + imageMimeType, + filesMimeType); + + /** + * Creates the clipboard and sets the initial contents to the + * current gtk+ selection. + */ + private GtkClipboard(String name) + { + super(name); + setContents(new GtkSelection(this), null); + } + + /** + * Returns the one and only GtkClipboard instance for the CLIPBOARD + * selection. + */ + static GtkClipboard getClipboardInstance() + { + return clipboard; + } + + /** + * Returns the one and only GtkClipboard instance for the PRIMARY + * selection. + */ + static GtkClipboard getSelectionInstance() + { + return selection; + } + + /** + * Sets the GtkSelection facade as new contents of the clipboard. + * Called from gtk+ when another application grabs the clipboard and + * we loose ownership. + * + * @param cleared If true this is a clear event (someone takes the + * clipboard from us) otherwise it is an owner changed event. + */ + private synchronized void setSystemContents(boolean cleared) + { + // We need to notify clipboard owner listeners when we were the + // owner (the selection was explictly set) and someone takes the + // clipboard away from us and asks us the clear any held storage, + // or if we weren't the owner of the clipboard to begin with, but + // the clipboard contents changed. We could refine this and check + // whether the actual available formats did in fact change, but we + // assume listeners will check for that anyway (and if possible we + // ask to cache the available formats so even if multiple + // listeners check after a notification the overhead should be + // minimal). + boolean owner = ! (contents instanceof GtkSelection); + boolean needNotification = (cleared && owner) || (! cleared && ! owner); + if (needNotification) + GtkClipboardNotifier.announce(this); + } + + /** + * Sets the new contents and advertises the available flavors to the + * gtk+ clipboard. + */ + public synchronized void setContents(Transferable contents, + ClipboardOwner owner) + { + super.setContents(contents, owner); + + if (contents == null) + { + advertiseContent(null, false, false, false); + return; + } + + // We don't need to do anything for a GtkSelection facade. + if (contents instanceof GtkSelection) + return; + + boolean text = false; + boolean images = false; + boolean files = false; + + if (contents instanceof StringSelection + || contents.isDataFlavorSupported(DataFlavor.stringFlavor) + || contents.isDataFlavorSupported(DataFlavor.plainTextFlavor) + || contents.isDataFlavorSupported(DataFlavor.getTextPlainUnicodeFlavor())) + text = true; + + DataFlavor[] flavors = contents.getTransferDataFlavors(); + String[] mimeTargets = new String[flavors.length]; + for (int i = 0; i < flavors.length; i++) + { + DataFlavor flavor = flavors[i]; + String mimeType = flavor.getMimeType(); + mimeTargets[i] = mimeType; + + if (! text) + if ("text".equals(flavor.getPrimaryType()) + || flavor.isRepresentationClassReader()) + text = true; + + if (! images && flavors[i].equals(DataFlavor.imageFlavor)) + { + try + { + Object o = contents.getTransferData(DataFlavor.imageFlavor); + if (o instanceof Image) + images = true; + } + catch (UnsupportedFlavorException ufe) + { + } + catch (IOException ioe) + { + } + catch (ClassCastException cce) + { + } + } + + if (flavors[i].equals(DataFlavor.javaFileListFlavor)) + files = true; + } + + advertiseContent(mimeTargets, text, images, files); + } + + /** + * Advertises new contents to the gtk+ clipboard given a string + * array of (mime-type) targets. When the boolean flags text, images + * and/or files are set then gtk+ is asked to also advertise the + * availability of any text, image or uri/file content types it + * supports. If targets is null (and all flags false) then the + * selection has explicitly been erased. + */ + private native void advertiseContent(String[] targets, + boolean text, + boolean images, + boolean files); + + /** + * Called by the gtk+ clipboard when an application has requested + * text. Return a string representing the current clipboard + * contents or null when no text can be provided. + */ + private String provideText() + { + Transferable contents = this.contents; + if (contents == null || contents instanceof GtkSelection) + return null; + + // Handle StringSelection special since that is just pure text. + if (contents instanceof StringSelection) + { + try + { + return (String) contents.getTransferData(DataFlavor.stringFlavor); + } + catch (UnsupportedFlavorException ufe) + { + } + catch (IOException ioe) + { + } + catch (ClassCastException cce) + { + } + } + + // Try to get a plain text reader for the current contents and + // turn the result into a string. + try + { + DataFlavor plainText = DataFlavor.getTextPlainUnicodeFlavor(); + Reader r = plainText.getReaderForText(contents); + if (r != null) + { + CPStringBuilder sb = new CPStringBuilder(); + char[] cs = new char[1024]; + int l = r.read(cs); + while (l != -1) + { + sb.append(cs, 0, l); + l = r.read(cs); + } + return sb.toString(); + } + } + catch (IllegalArgumentException iae) + { + } + catch (UnsupportedEncodingException iee) + { + } + catch (UnsupportedFlavorException ufe) + { + } + catch (IOException ioe) + { + } + + return null; + } + + /** + * Called by the gtk+ clipboard when an application has requested an + * image. Returns a GtkImage representing the current clipboard + * contents or null when no image can be provided. + */ + private GtkImage provideImage() + { + Transferable contents = this.contents; + if (contents == null || contents instanceof GtkSelection) + return null; + + try + { + Object o = contents.getTransferData(DataFlavor.imageFlavor); + if( o instanceof GtkImage ) + return (GtkImage) o; + else + return new GtkImage(((Image)o).getSource()); + } + catch (UnsupportedFlavorException ufe) + { + } + catch (IOException ioe) + { + } + catch (ClassCastException cce) + { + } + + return null; + } + + /** + * Called by the gtk+ clipboard when an application has requested a + * uri-list. Return a string array containing the URIs representing + * the current clipboard contents or null when no URIs can be + * provided. + */ + private String[] provideURIs() + { + Transferable contents = this.contents; + if (contents == null || contents instanceof GtkSelection) + return null; + + try + { + List list = (List) contents.getTransferData(DataFlavor.javaFileListFlavor); + String[] uris = new String[list.size()]; + int u = 0; + Iterator it = list.iterator(); + while (it.hasNext()) + uris[u++] = ((File) it.next()).toURI().toString(); + return uris; + } + catch (UnsupportedFlavorException ufe) + { + } + catch (IOException ioe) + { + } + catch (ClassCastException cce) + { + } + + return null; + } + + /** + * Called by gtk+ clipboard when an application requests the given + * target mime-type. Returns a byte array containing the requested + * data, or null when the contents cannot be provided in the + * requested target mime-type. Only called after any explicit text, + * image or file/uri requests have been handled earlier and failed. + */ + private byte[] provideContent(String target) + { + // Sanity check. The callback could be triggered just after we + // changed the clipboard. + Transferable contents = this.contents; + if (contents == null || contents instanceof GtkSelection) + return null; + + // XXX - We are being called from a gtk+ callback. Which means we + // should return as soon as possible and not call arbitrary code + // that could deadlock or go bonkers. But we don't really know + // what DataTransfer contents object we are dealing with. Same for + // the other provideXXX() methods. + try + { + DataFlavor flavor = new DataFlavor(target); + Object o = contents.getTransferData(flavor); + + if (o instanceof byte[]) + return (byte[]) o; + + if (o instanceof InputStream) + { + InputStream is = (InputStream) o; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] bs = new byte[1024]; + int l = is.read(bs); + while (l != -1) + { + baos.write(bs, 0, l); + l = is.read(bs); + } + return baos.toByteArray(); + } + + if (o instanceof Serializable) + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(o); + oos.close(); + return baos.toByteArray(); + } + } + catch (ClassNotFoundException cnfe) + { + } + catch (UnsupportedFlavorException ufe) + { + } + catch (IOException ioe) + { + } + catch (ClassCastException cce) + { + } + + return null; + } + + /** + * Initializes the gtk+ clipboards and caches any native side + * structures needed. Returns whether or not the contents of the + * Clipboard can be cached (gdk_display_supports_selection_notification). + */ + private static native boolean initNativeState(GtkClipboard clipboard, + GtkClipboard selection, + String stringTarget, + String imageTarget, + String filesTarget); +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java new file mode 100644 index 000000000..8e5934557 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboardNotifier.java @@ -0,0 +1,129 @@ +/* GtkClipboardNotifier.java -- Helper for announcing GtkSelection changes. + Copyright (C) 2005, 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; + + +class GtkClipboardNotifier extends Thread +{ + /** Whether to announce a new GtkSelection has been set for CLIPBOARD. */ + static private boolean announceClipboardChange; + + /** Whether to announce a new GtkSelection has been set for PRIMARY. */ + static private boolean announcePrimaryChange; + + /** + * The one and only instance. All operations are synchronized on + * this. + */ + private static GtkClipboardNotifier notifier = new GtkClipboardNotifier(); + + /** + * Creates a deamon thread that monitors this for change + * announcements. + */ + private GtkClipboardNotifier() + { + super("GtkClipBoardNotifier"); + setDaemon(true); + start(); + } + + /** + * Notifies that a new GtkSelection has to be announced. + * + * @param clipboard either the GtkClipboard.clipboard or the + * GtkClipboard.selection. + */ + static void announce(GtkClipboard clipboard) + { + synchronized (notifier) + { + if (clipboard == GtkClipboard.clipboard) + announceClipboardChange = true; + else + announcePrimaryChange = true; + notifier.notifyAll(); + } + } + + public void run() + { + GtkClipboard clipboard; + while (true) + { + synchronized (this) + { + while (! announceClipboardChange && ! announcePrimaryChange) + { + try + { + this.wait(); + } + catch (InterruptedException ie) + { + // ignore + } + } + + if (announceClipboardChange) + { + clipboard = GtkClipboard.clipboard; + announceClipboardChange = false; + } + else + { + clipboard = GtkClipboard.selection; + announcePrimaryChange = false; + } + } + + // Do the actual announcement without the lock held. We will + // notice a new change after this notification has finished. + try + { + clipboard.setContents(new GtkSelection(clipboard), null); + } + catch (Throwable t) + { + // should never happen, but might if we have some faulty listener. + t.printStackTrace(); + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java new file mode 100644 index 000000000..6e5069bc1 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java @@ -0,0 +1,920 @@ +/* GtkComponentPeer.java -- Implements ComponentPeer with GTK + Copyright (C) 1998, 1999, 2002, 2004, 2005, 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.AWTEvent; +import java.awt.AWTException; +import java.awt.BufferCapabilities; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +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.Insets; +import java.awt.ItemSelectable; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.FocusEvent; +import java.awt.event.ItemEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.PaintEvent; +import java.awt.event.TextEvent; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; +import java.awt.peer.ComponentPeer; +import java.awt.peer.ContainerPeer; +import java.awt.peer.LightweightPeer; +import java.util.Timer; +import java.util.TimerTask; + +public class GtkComponentPeer extends GtkGenericPeer + implements ComponentPeer +{ + VolatileImage backBuffer; + BufferCapabilities caps; + + Component awtComponent; + + Insets insets; + + /** + * The current repaint area. Use should be guarded by synchronizing on this. + */ + private Rectangle currentPaintArea; + + /* this isEnabled differs from Component.isEnabled, in that it + knows if a parent is disabled. In that case Component.isEnabled + may return true, but our isEnabled will always return false */ + native boolean isEnabled (); + static native boolean modalHasGrab(); + + native int[] gtkWidgetGetForeground (); + native int[] gtkWidgetGetBackground (); + native void gtkWidgetGetDimensions (int[] dim); + native void gtkWidgetGetPreferredDimensions (int[] dim); + native void gtkWindowGetLocationOnScreen (int[] point); + native void gtkWindowGetLocationOnScreenUnlocked (int[] point); + native void gtkWidgetGetLocationOnScreen (int[] point); + native void gtkWidgetGetLocationOnScreenUnlocked (int[] point); + native void gtkWidgetSetCursor (int type, GtkImage image, int x, int y); + native void gtkWidgetSetCursorUnlocked (int type, GtkImage image, + int x, int y); + native void gtkWidgetSetBackground (int red, int green, int blue); + native void gtkWidgetSetForeground (int red, int green, int blue); + native void gtkWidgetSetSensitive (boolean sensitive); + native void gtkWidgetSetParent (ComponentPeer parent); + native void gtkWidgetRequestFocus (); + native void gtkWidgetDispatchKeyEvent (int id, long when, int mods, + int keyCode, int keyLocation); + native boolean gtkWidgetHasFocus(); + native boolean gtkWidgetCanFocus(); + + native void realize(); + native void setNativeEventMask (); + + void create () + { + throw new RuntimeException (); + } + + native void connectSignals (); + + protected GtkComponentPeer (Component awtComponent) + { + super (awtComponent); + this.awtComponent = awtComponent; + insets = new Insets (0, 0, 0, 0); + + create (); + + connectSignals (); + + if (awtComponent.getForeground () != null) + setForeground (awtComponent.getForeground ()); + if (awtComponent.getBackground () != null) + setBackground (awtComponent.getBackground ()); + if (awtComponent.getFont() != null) + setFont(awtComponent.getFont()); + + Component parent = awtComponent.getParent (); + + setParentAndBounds (); + + 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()) + setCursor (); + } + + void setParentAndBounds () + { + setParent (); + + setComponentBounds (); + + setVisibleAndEnabled (); + } + + void setParent () + { + ComponentPeer p; + Component component = awtComponent; + do + { + component = component.getParent (); + p = component.getPeer (); + } + while (p instanceof java.awt.peer.LightweightPeer); + + if (p != null) + gtkWidgetSetParent (p); + } + + /* + * Set the bounds of this peer's AWT Component based on dimensions + * returned by the native windowing system. Most Components impose + * their dimensions on the peers which is what the default + * implementation does. However some peers, like GtkFileDialogPeer, + * need to pass their size back to the AWT Component. + */ + void setComponentBounds () + { + Rectangle bounds = awtComponent.getBounds (); + setBounds (bounds.x, bounds.y, bounds.width, bounds.height); + } + + void setVisibleAndEnabled () + { + setVisible (awtComponent.isVisible ()); + setEnabled (awtComponent.isEnabled ()); + } + + public int checkImage (Image image, int width, int height, + ImageObserver observer) + { + return getToolkit().checkImage(image, width, height, observer); + } + + public Image createImage (ImageProducer producer) + { + return new GtkImage (producer); + } + + public Image createImage (int width, int height) + { + return CairoSurface.getBufferedImage(width, height); + } + + public void disable () + { + setEnabled (false); + } + + public void enable () + { + setEnabled (true); + } + + public ColorModel getColorModel () + { + return ColorModel.getRGBdefault (); + } + + public FontMetrics getFontMetrics (Font font) + { + return getToolkit().getFontMetrics(font); + } + + // getGraphics may be overridden by derived classes but it should + // never return null. + public Graphics getGraphics () + { + return ComponentGraphics.getComponentGraphics(this); + } + + public Point getLocationOnScreen () + { + int point[] = new int[2]; + if (Thread.currentThread() == GtkMainThread.mainThread) + gtkWidgetGetLocationOnScreenUnlocked (point); + else + gtkWidgetGetLocationOnScreen (point); + return new Point (point[0], point[1]); + } + + public Dimension getMinimumSize () + { + return minimumSize (); + } + + public Dimension getPreferredSize () + { + return preferredSize (); + } + + public Toolkit getToolkit () + { + return Toolkit.getDefaultToolkit(); + } + + public void handleEvent (AWTEvent event) + { + int id = event.getID(); + KeyEvent ke = null; + + switch (id) + { + case PaintEvent.PAINT: + paintComponent((PaintEvent) event); + break; + case PaintEvent.UPDATE: + updateComponent((PaintEvent) event); + break; + case KeyEvent.KEY_PRESSED: + ke = (KeyEvent) event; + gtkWidgetDispatchKeyEvent (ke.getID (), ke.getWhen (), ke.getModifiersEx (), + ke.getKeyCode (), ke.getKeyLocation ()); + break; + case KeyEvent.KEY_RELEASED: + ke = (KeyEvent) event; + gtkWidgetDispatchKeyEvent (ke.getID (), ke.getWhen (), ke.getModifiersEx (), + ke.getKeyCode (), ke.getKeyLocation ()); + break; + } + } + + // This method and its overrides are the only methods in the peers + // that should call awtComponent.paint. + protected void paintComponent (PaintEvent event) + { + // Do not call Component.paint if the component is not showing or + // if its bounds form a degenerate rectangle. + if (!awtComponent.isShowing() + || (awtComponent.getWidth() < 1 || awtComponent.getHeight() < 1)) + return; + + // Creating and disposing a GdkGraphics every time paint is called + // seems expensive. However, the graphics state does not carry + // over between calls to paint, and resetting the graphics object + // may even be more costly than simply creating a new one. + + // Make sure that the paintArea includes the area from the event + // in the case when an application sends PaintEvents directly. + coalescePaintEvent(event); + Rectangle paintArea; + synchronized (this) + { + paintArea = currentPaintArea; + currentPaintArea = null; + } + + if (paintArea != null) + { + Graphics g = getGraphics(); + try + { + g.setClip(paintArea); + awtComponent.paint(g); + } + finally + { + g.dispose(); + } + } + } + + // This method and its overrides are the only methods in the peers + // that should call awtComponent.update. + protected void updateComponent (PaintEvent event) + { + // Do not call Component.update if the component is not showing or + // if its bounds form a degenerate rectangle. + if (!awtComponent.isShowing() + || (awtComponent.getWidth() < 1 || awtComponent.getHeight() < 1)) + return; + + // Make sure that the paintArea includes the area from the event + // in the case when an application sends PaintEvents directly. + coalescePaintEvent(event); + Rectangle paintArea; + synchronized (this) + { + paintArea = currentPaintArea; + currentPaintArea = null; + } + + if (paintArea != null) + { + Graphics g = getGraphics(); + try + { + g.setClip(paintArea); + awtComponent.update(g); + } + finally + { + g.dispose(); + } + } + } + + public boolean isFocusTraversable () + { + return true; + } + + public Dimension minimumSize () + { + int dim[] = new int[2]; + + gtkWidgetGetPreferredDimensions (dim); + + return new Dimension (dim[0], dim[1]); + } + + public void paint (Graphics g) + { + } + + public Dimension preferredSize () + { + int dim[] = new int[2]; + + gtkWidgetGetPreferredDimensions (dim); + + return new Dimension (dim[0], dim[1]); + } + + public boolean prepareImage (Image image, int width, int height, + ImageObserver observer) + { + return getToolkit().prepareImage(image, width, height, observer); + } + + public void print (Graphics g) + { + g.drawImage( ComponentGraphics.grab( this ), 0, 0, null ); + } + + public void repaint (long tm, int x, int y, int width, int height) + { + if (width < 1 || height < 1) + return; + + if (tm <= 0) + q().postEvent(new PaintEvent(awtComponent, PaintEvent.UPDATE, + new Rectangle(x, y, width, height))); + else + RepaintTimerTask.schedule(tm, x, y, width, height, awtComponent); + } + + /** + * Used for scheduling delayed paint updates on the event queue. + */ + private static class RepaintTimerTask extends TimerTask + { + private static final Timer repaintTimer = new Timer(true); + + private int x, y, width, height; + private Component awtComponent; + + RepaintTimerTask(Component c, int x, int y, int width, int height) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.awtComponent = c; + } + + public void run() + { + q().postEvent (new PaintEvent (awtComponent, PaintEvent.UPDATE, + new Rectangle (x, y, width, height))); + } + + static void schedule(long tm, int x, int y, int width, int height, + Component c) + { + repaintTimer.schedule(new RepaintTimerTask(c, x, y, width, height), tm); + } + } + + public void requestFocus () + { + assert false: "Call new requestFocus() method instead"; + } + + public void reshape (int x, int y, int width, int height) + { + setBounds (x, y, width, height); + } + + public void setBackground (Color c) + { + gtkWidgetSetBackground (c.getRed(), c.getGreen(), c.getBlue()); + } + + native void setNativeBounds (int x, int y, int width, int height); + + public void setBounds (int x, int y, int width, int height) + { + int new_x = x; + int new_y = y; + + Component parent = awtComponent.getParent (); + + // Heavyweight components that are children of one or more + // lightweight containers have to be handled specially. Because + // calls to GLightweightPeer.setBounds do nothing, GTK has no + // knowledge of the lightweight containers' positions. So we have + // to add the offsets manually when placing a heavyweight + // component within a lightweight container. The lightweight + // container may itself be in a lightweight container and so on, + // so we need to continue adding offsets until we reach a + // container whose position GTK knows -- that is, the first + // non-lightweight. + Insets i; + while (parent.isLightweight()) + { + i = ((Container) parent).getInsets(); + + new_x += parent.getX() + i.left; + new_y += parent.getY() + i.top; + + parent = parent.getParent(); + } + // We only need to convert from Java to GTK coordinates if we're + // placing a heavyweight component in a Window. + if (parent instanceof Window) + { + GtkWindowPeer peer = (GtkWindowPeer) parent.getPeer (); + // important: we want the window peer's insets here, not the + // window's, since user sub-classes of Window can override + // getInset and we only want to correct for the frame borders, + // not for any user-defined inset values + Insets insets = peer.getInsets (); + + int menuBarHeight = 0; + if (peer instanceof GtkFramePeer) + menuBarHeight = ((GtkFramePeer) peer).getMenuBarHeight (); + + new_x -= insets.left; + new_y -= insets.top; + new_y += menuBarHeight; + } + + setNativeBounds (new_x, new_y, width, height); + + // If the height or width were (or are now) smaller than zero + // then we want to adjust the visibility. + setVisible(awtComponent.isVisible()); + } + + void setCursor () + { + setCursor (awtComponent.getCursor ()); + } + + public void setCursor (Cursor cursor) + { + int x, y; + GtkImage image; + int type = cursor.getType(); + if (cursor instanceof GtkCursor) + { + GtkCursor gtkCursor = (GtkCursor) cursor; + image = gtkCursor.getGtkImage(); + Point hotspot = gtkCursor.getHotspot(); + x = hotspot.x; + y = hotspot.y; + } + else + { + image = null; + x = 0; + y = 0; + } + + if (Thread.currentThread() == GtkMainThread.mainThread) + gtkWidgetSetCursorUnlocked(cursor.getType(), image, x, y); + else + gtkWidgetSetCursor(cursor.getType(), image, x, y); + } + + public void setEnabled (boolean b) + { + gtkWidgetSetSensitive (b); + } + + public void setFont (Font f) + { + // FIXME: This should really affect the widget tree below me. + // Currently this is only handled if the call is made directly on + // a text widget, which implements setFont() itself. + gtkWidgetModifyFont(f.getName(), f.getStyle(), f.getSize()); + } + + public void setForeground (Color c) + { + gtkWidgetSetForeground (c.getRed(), c.getGreen(), c.getBlue()); + } + + public Color getForeground () + { + int rgb[] = gtkWidgetGetForeground (); + return new Color (rgb[0], rgb[1], rgb[2]); + } + + public Color getBackground () + { + int rgb[] = gtkWidgetGetBackground (); + return new Color (rgb[0], rgb[1], rgb[2]); + } + + public native void setVisibleNative (boolean b); + public native void setVisibleNativeUnlocked (boolean b); + + public void setVisible (boolean b) + { + // Only really set visible when component is bigger than zero pixels. + if (b && ! (awtComponent instanceof Window)) + { + Rectangle bounds = awtComponent.getBounds(); + b = (bounds.width > 0) && (bounds.height > 0); + } + + if (Thread.currentThread() == GtkMainThread.mainThread) + setVisibleNativeUnlocked (b); + else + setVisibleNative (b); + } + + public void hide () + { + setVisible (false); + } + + public void show () + { + setVisible (true); + } + + protected void postMouseEvent(int id, long when, int mods, int x, int y, + int clickCount, boolean popupTrigger) + { + // It is important to do the getLocationOnScreen() here, instead + // of using the old MouseEvent constructors, because + // Component.getLocationOnScreen() locks on the AWT lock, which can + // trigger a deadlock. You don't want this. + Point locOnScreen = getLocationOnScreen(); + q().postEvent(new MouseEvent(awtComponent, id, when, mods, x, y, + locOnScreen.x + x, locOnScreen.y + y, + clickCount, popupTrigger, + MouseEvent.NOBUTTON)); + } + + /** + * Callback for component_scroll_cb. + */ + protected void postMouseWheelEvent(int id, long when, int mods, + int x, int y, int clickCount, + boolean popupTrigger, + int type, int amount, int rotation) + { + q().postEvent(new MouseWheelEvent(awtComponent, id, when, mods, + x, y, clickCount, popupTrigger, + type, amount, rotation)); + } + + protected void postExposeEvent (int x, int y, int width, int height) + { + q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT, + new Rectangle (x, y, width, height))); + } + + protected void postKeyEvent (int id, long when, int mods, + int keyCode, char keyChar, int keyLocation) + { + KeyEvent keyEvent = new KeyEvent (awtComponent, id, when, mods, + keyCode, keyChar, keyLocation); + + EventQueue q = q(); + + // Also post a KEY_TYPED event if keyEvent is a key press that + // doesn't represent an action or modifier key. + if (keyEvent.getID () == KeyEvent.KEY_PRESSED + && (!keyEvent.isActionKey () + && keyCode != KeyEvent.VK_SHIFT + && keyCode != KeyEvent.VK_CONTROL + && keyCode != KeyEvent.VK_ALT)) + { + synchronized(q) + { + q.postEvent(keyEvent); + keyEvent = new KeyEvent(awtComponent, KeyEvent.KEY_TYPED, when, + mods, KeyEvent.VK_UNDEFINED, keyChar, + keyLocation); + q.postEvent(keyEvent); + } + } + else + q.postEvent(keyEvent); + } + + /** + * Referenced from native code. + * + * @param id + * @param temporary + */ + protected void postFocusEvent (int id, boolean temporary) + { + q().postEvent (new FocusEvent (awtComponent, id, temporary)); + } + + protected void postItemEvent (Object item, int stateChange) + { + q().postEvent (new ItemEvent ((ItemSelectable)awtComponent, + ItemEvent.ITEM_STATE_CHANGED, + item, stateChange)); + } + + protected void postTextEvent () + { + q().postEvent (new TextEvent (awtComponent, TextEvent.TEXT_VALUE_CHANGED)); + } + + public GraphicsConfiguration getGraphicsConfiguration () + { + // FIXME: The component might be showing on a non-default screen. + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice dev = env.getDefaultScreenDevice(); + return dev.getDefaultConfiguration(); + } + + public void setEventMask (long mask) + { + // FIXME: just a stub for now. + } + + public boolean isFocusable () + { + return false; + } + + public boolean requestFocus (Component request, boolean temporary, + boolean allowWindowFocus, long time) + { + assert request == awtComponent || isLightweightDescendant(request); + boolean retval = false; + if (gtkWidgetHasFocus()) + { + KeyboardFocusManager kfm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Component currentFocus = kfm.getFocusOwner(); + if (currentFocus == request) + // Nothing to do in this trivial case. + retval = true; + else + { + // Requested component is a lightweight descendant of this one + // or the actual heavyweight. + // Since this (native) component is already focused, we simply + // change the actual focus and be done. + postFocusEvent(FocusEvent.FOCUS_GAINED, temporary); + retval = true; + } + } + else + { + if (gtkWidgetCanFocus()) + { + if (allowWindowFocus) + { + Window window = getWindowFor(request); + GtkWindowPeer wPeer = (GtkWindowPeer) window.getPeer(); + if (! wPeer.gtkWindowHasFocus()) + wPeer.requestWindowFocus(); + } + // Store requested focus component so that the corresponding + // event is dispatched correctly. + gtkWidgetRequestFocus(); + retval = true; + } + } + return retval; + } + + private Window getWindowFor(Component c) + { + Component comp = c; + while (! (comp instanceof Window)) + comp = comp.getParent(); + return (Window) comp; + } + + /** + * Returns <code>true</code> if the component is a direct (== no intermediate + * heavyweights) lightweight descendant of this peer's component. + * + * @param c the component to check + * + * @return <code>true</code> if the component is a direct (== no intermediate + * heavyweights) lightweight descendant of this peer's component + */ + protected boolean isLightweightDescendant(Component c) + { + Component comp = c; + while (comp.getPeer() instanceof LightweightPeer) + comp = comp.getParent(); + return comp == awtComponent; + } + + public boolean isObscured () + { + return false; + } + + public boolean canDetermineObscurity () + { + return false; + } + + public void coalescePaintEvent (PaintEvent e) + { + synchronized (this) + { + Rectangle newRect = e.getUpdateRect(); + if (currentPaintArea == null) + currentPaintArea = newRect; + else + Rectangle.union(currentPaintArea, newRect, currentPaintArea); + } + } + + public void updateCursorImmediately () + { + if (awtComponent.getCursor() != null) + setCursor(awtComponent.getCursor()); + } + + public boolean handlesWheelScrolling () + { + return false; + } + + // Convenience method to create a new volatile image on the screen + // on which this component is displayed. + public VolatileImage createVolatileImage (int width, int height) + { + return new GtkVolatileImage (this, width, height, null); + } + + // Creates buffers used in a buffering strategy. + public void createBuffers (int numBuffers, BufferCapabilities caps) + throws AWTException + { + // numBuffers == 2 implies double-buffering, meaning one back + // buffer and one front buffer. + if (numBuffers == 2) + backBuffer = new GtkVolatileImage(this, awtComponent.getWidth(), + awtComponent.getHeight(), + caps.getBackBufferCapabilities()); + else + throw new AWTException("GtkComponentPeer.createBuffers:" + + " multi-buffering not supported"); + this.caps = caps; + } + + // Return the back buffer. + public Image getBackBuffer () + { + return backBuffer; + } + + // FIXME: flip should be implemented as a fast native operation + public void flip (BufferCapabilities.FlipContents contents) + { + getGraphics().drawImage(backBuffer, + awtComponent.getWidth(), + awtComponent.getHeight(), + null); + + // create new back buffer and clear it to the background color. + if (contents == BufferCapabilities.FlipContents.BACKGROUND) + { + backBuffer = createVolatileImage(awtComponent.getWidth(), + awtComponent.getHeight()); + backBuffer.getGraphics().clearRect(0, 0, + awtComponent.getWidth(), + awtComponent.getHeight()); + } + // FIXME: support BufferCapabilities.FlipContents.PRIOR + } + + // Release the resources allocated to back buffers. + public void destroyBuffers () + { + backBuffer.flush(); + } + + public String toString () + { + return "peer of " + awtComponent.toString(); + } + public Rectangle getBounds() + { + // FIXME: implement + return null; + } + public void reparent(ContainerPeer parent) + { + // FIXME: implement + + } + public void setBounds(int x, int y, int width, int height, int z) + { + // FIXME: implement + setBounds (x, y, width, height); + + } + public boolean isReparentSupported() + { + // FIXME: implement + + return false; + } + public void layout() + { + // FIXME: implement + + } + + public boolean requestFocus(Component lightweightChild, boolean temporary, + boolean focusedWindowChangeAllowed, + long time, sun.awt.CausedFocusEvent.Cause cause) + { + // TODO: Implement this properly and remove the other requestFocus() + // methods. + return true; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java new file mode 100644 index 000000000..b7eacb9f6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java @@ -0,0 +1,138 @@ +/* GtkContainerPeer.java -- Implements ContainerPeer with GTK + Copyright (C) 1998, 1999, 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.Component; +import java.awt.Container; +import java.awt.Font; +import java.awt.Insets; +import java.awt.peer.ContainerPeer; + +public class GtkContainerPeer extends GtkComponentPeer + implements ContainerPeer +{ + Container c; + + public GtkContainerPeer(Container c) + { + super (c); + this.c = c; + } + + public void beginValidate () + { + } + + public void endValidate () + { + } + + public Insets getInsets() + { + return insets; + } + + public Insets insets() + { + return getInsets (); + } + + public void setBounds (int x, int y, int width, int height) + { + super.setBounds (x, y, width, height); + } + + public void setFont(Font f) + { + super.setFont(f); + Component[] components = ((Container) awtComponent).getComponents(); + for (int i = 0; i < components.length; i++) + { + if (components[i].isLightweight ()) + components[i].setFont (f); + else + { + GtkComponentPeer peer = (GtkComponentPeer) components[i].getPeer(); + if (peer != null && ! peer.awtComponent.isFontSet()) + peer.setFont(f); + } + } + } + + public void beginLayout () { } + public void endLayout () { } + public boolean isPaintPending () { return false; } + + public void setBackground (Color c) + { + super.setBackground(c); + + Object components[] = ((Container) awtComponent).getComponents(); + for (int i = 0; i < components.length; i++) + { + Component comp = (Component) components[i]; + + // If the child's background has not been explicitly set yet, + // it should inherit this container's background. This makes the + // child component appear as if it has a transparent background. + // Note that we do not alter the background property of the child, + // but only repaint the child with the parent's background color. + if (!comp.isBackgroundSet() && comp.getPeer() != null) + comp.getPeer().setBackground(c); + } + } + + public boolean isRestackSupported() + { + // FIXME: implement + return false; + } + + public void cancelPendingPaint(int x, int y, int width, int height) + { + // FIXME: implement + } + + public void restack() + { + //FIXME: implement + + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkCursor.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCursor.java new file mode 100644 index 000000000..e382d6331 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkCursor.java @@ -0,0 +1,72 @@ +/* GtkCursor.java -- Simple wrapper for custom cursor. + 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.Cursor; +import java.awt.Image; +import java.awt.Point; + +/** + * Simple wrapper for custom Cursor. + */ +public class GtkCursor extends Cursor +{ + private final GtkImage image; + private final Point hotspot; + + GtkCursor(Image image, Point hotspot, String name) + { + super(name); + if (image instanceof GtkImage) + this.image = (GtkImage) image; + else + this.image = new GtkImage(image.getSource()); + this.hotspot = hotspot; + } + + GtkImage getGtkImage() + { + return image; + } + + Point getHotspot() + { + return hotspot; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java new file mode 100644 index 000000000..3393eb9d0 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java @@ -0,0 +1,63 @@ +/* GtkDialogPeer.java -- Implements DialogPeer with GTK + Copyright (C) 1998, 1999, 2002, 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.Dialog; +import java.awt.peer.DialogPeer; + +public class GtkDialogPeer extends GtkWindowPeer + implements DialogPeer +{ + public GtkDialogPeer (Dialog dialog) + { + super (dialog); + } + + void create () + { + Dialog dialog = (Dialog) awtComponent; + + // Create a decorated dialog window. + create (GDK_WINDOW_TYPE_HINT_DIALOG, !((Dialog) awtComponent).isUndecorated ()); + + gtkWindowSetModal (dialog.isModal ()); + setTitle (dialog.getTitle ()); + setResizable (dialog.isResizable ()); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkEmbeddedWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkEmbeddedWindowPeer.java new file mode 100644 index 000000000..0533d2759 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkEmbeddedWindowPeer.java @@ -0,0 +1,73 @@ +/* GtkEmbeddedWindowPeer.java -- Implements EmbeddedWindowPeer using a + GtkPlug + Copyright (C) 2003 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.java.awt.EmbeddedWindow; +import gnu.java.awt.peer.EmbeddedWindowPeer; + +public class GtkEmbeddedWindowPeer extends GtkFramePeer + implements EmbeddedWindowPeer +{ + native void create (long socket_id); + + void create () + { + create (((EmbeddedWindow) awtComponent).getHandle ()); + } + + native void construct (long socket_id); + + // FIXME: embed doesn't work right now, though I believe it should. + // This means that you can't call setVisible (true) on an + // EmbeddedWindow before calling setHandle with a valid handle. The + // problem is that somewhere after the call to + // GtkEmbeddedWindow.create and before the call to + // GtkEmbeddedWindow.construct, the GtkPlug peer is being realized. + // GtkSocket silently fails to embed an already-realized GtkPlug. + public void embed (long handle) + { + construct (handle); + } + + public GtkEmbeddedWindowPeer (EmbeddedWindow w) + { + super (w); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java new file mode 100644 index 000000000..cddc530fd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java @@ -0,0 +1,229 @@ +/* GtkFileDialogPeer.java -- Implements FileDialogPeer with GTK + Copyright (C) 1998, 1999, 2002, 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.Dialog; +import java.awt.FileDialog; +import java.awt.event.PaintEvent; +import java.awt.peer.FileDialogPeer; +import java.io.File; +import java.io.FilenameFilter; + +public class GtkFileDialogPeer extends GtkDialogPeer implements FileDialogPeer +{ + static final String FS = System.getProperty("file.separator"); + + private String currentFile = null; + private String currentDirectory = null; + private FilenameFilter filter; + + native void create (GtkContainerPeer parent, int mode); + native void connectSignals (); + native void nativeSetFile (String file); + public native String nativeGetDirectory(); + public native void nativeSetDirectory(String directory); + native void nativeSetFilenameFilter (FilenameFilter filter); + + public void create() + { + create((GtkContainerPeer) awtComponent.getParent().getPeer(), + ((FileDialog) awtComponent).getMode()); + + FileDialog fd = (FileDialog) awtComponent; + + nativeSetDirectory(System.getProperty("user.dir")); + setDirectory(fd.getDirectory()); + setFile(fd.getFile()); + + FilenameFilter filter = fd.getFilenameFilter(); + if (filter != null) + setFilenameFilter(filter); + } + + public GtkFileDialogPeer (FileDialog fd) + { + super (fd); + } + + void setComponentBounds () + { + if (awtComponent.getHeight () == 0 + && awtComponent.getWidth () == 0) + { + int[] dims = new int[2]; + gtkWidgetGetPreferredDimensions (dims); + + if (dims[0] != awtComponent.getWidth() + || dims[1] != awtComponent.getHeight()) + awtComponent.setSize(dims[0], dims[1]); + } + super.setComponentBounds (); + } + + public void setFile (String fileName) + { + /* If nothing changed do nothing. This usually happens because + the only way we have to set the file name in FileDialog is by + calling its SetFile which will call us back. */ + if ((fileName == null && currentFile == null) + || (fileName != null && fileName.equals (currentFile))) + return; + + if (fileName == null || fileName.equals ("")) + { + currentFile = ""; + nativeSetFile (""); + return; + } + + // GtkFileChooser requires absolute filenames. If the given filename + // is not absolute, let's construct it based on current directory. + currentFile = fileName; + if (fileName.indexOf(FS) == 0) + nativeSetFile(fileName); + else + nativeSetFile(nativeGetDirectory() + FS + fileName); + } + + public void setDirectory (String directory) + { + /* If nothing changed so nothing. This usually happens because + the only way we have to set the directory in FileDialog is by + calling its setDirectory which will call us back. */ + if ((directory == null && currentDirectory == null) + || (directory != null && directory.equals(currentDirectory))) + return; + + if (directory == null || directory.equals("")) + { + currentDirectory = FS; + nativeSetDirectory(FS); + return; + } + + // GtkFileChooser requires absolute directory names. If the given directory + // name is not absolute, construct it based on current directory if it is not + // null. Otherwise, use FS. + currentDirectory = directory; + if (directory.indexOf(FS) == 0) + nativeSetDirectory(directory); + else + nativeSetDirectory(nativeGetDirectory() + FS + directory); + } + + public void setFilenameFilter (FilenameFilter filter) + { + this.filter = filter; + nativeSetFilenameFilter(filter); + } + + /* This method interacts with the native callback function of the + same name. The native function will extract the filename from the + GtkFileFilterInfo object and send it to this method, which will + in turn call the filter's accept() method and give back the return + value. */ + // called back by native side: filename_filter_cb + boolean filenameFilterCallback (String fullname) + { + String filename = fullname.substring(fullname.lastIndexOf(FS) + 1); + String dirname = fullname.substring(0, fullname.lastIndexOf(FS)); + File dir = new File(dirname); + return filter.accept(dir, filename); + } + + // Sun does not call FileDialog.update. + protected void updateComponent (PaintEvent event) + { + // Override GtkComponetPeer.updateComponent to do nothing. + } + + // called back by native side: handle_response_cb + // only called from the GTK thread + void gtkHideFileDialog () + { + // hide calls back the peer's setVisible method, so locking is a + // problem. + ((Dialog) awtComponent).hide(); + } + + // called back by native side: handle_response_cb + void gtkDisposeFileDialog () + { + ((Dialog) awtComponent).dispose(); + } + + // Callback to set the file and directory values when the user is finished + // with the dialog. + // called back by native side: handle_response_cb + void gtkSetFilename (String fileName) + { + FileDialog fd = (FileDialog) awtWidget; + if (fileName == null) + { + currentFile = null; + fd.setFile(null); + return; + } + + int sepIndex = fileName.lastIndexOf (FS); + if (sepIndex < 0) + { + /* This should never happen on Unix (all paths start with '/') */ + currentFile = fileName; + } + else + { + if (fileName.length() > (sepIndex + 1)) + { + String fn = fileName.substring (sepIndex + 1); + currentFile = fn; + } + else + { + currentFile = null; + } + + String dn = fileName.substring (0, sepIndex + 1); + currentDirectory = dn; + fd.setDirectory(dn); + } + + fd.setFile (currentFile); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java new file mode 100644 index 000000000..a36854ce0 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java @@ -0,0 +1,254 @@ +/* GtkFramePeer.java -- Implements FramePeer with GTK + Copyright (C) 1999, 2002, 2004, 2006, 2007 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.Frame; +import java.awt.Image; +import java.awt.MenuBar; +import java.awt.Rectangle; +import java.awt.peer.FramePeer; +import java.awt.peer.MenuBarPeer; + +public class GtkFramePeer extends GtkWindowPeer + implements FramePeer +{ + private int menuBarHeight; + private MenuBarPeer menuBar; + native int getMenuBarHeight (MenuBarPeer bar); + native void setMenuBarWidthUnlocked (MenuBarPeer bar, int width); + native void setMenuBarWidth (MenuBarPeer bar, int width); + native void setMenuBarPeer (MenuBarPeer bar); + native void removeMenuBarPeer (); + native void gtkFixedSetVisible (boolean visible); + + private native void maximize(); + private native void unmaximize(); + private native void iconify(); + private native void deiconify(); + + int getMenuBarHeight () + { + return menuBar == null ? 0 : getMenuBarHeight (menuBar); + } + + public void setMenuBar (MenuBar bar) + { + if (bar == null && menuBar != null) + { + // We're removing the menubar. + gtkFixedSetVisible (false); + menuBar = null; + removeMenuBarPeer (); + insets.top -= menuBarHeight; + menuBarHeight = 0; + // if component has already been validated, we need to revalidate. + // otherwise, it will be validated when it is shown. + if (awtComponent.isValid()) + awtComponent.validate (); + gtkFixedSetVisible (true); + } + else if (bar != null && menuBar == null) + { + // We're adding a menubar where there was no menubar before. + gtkFixedSetVisible (false); + menuBar = (MenuBarPeer) bar.getPeer(); + setMenuBarPeer (menuBar); + int menuBarWidth = + awtComponent.getWidth () - insets.left - insets.right; + if (menuBarWidth > 0) + setMenuBarWidth (menuBar, menuBarWidth); + menuBarHeight = getMenuBarHeight (); + insets.top += menuBarHeight; + // if component has already been validated, we need to revalidate. + // otherwise, it will be validated when it is shown. + if (awtComponent.isValid()) + awtComponent.validate (); + gtkFixedSetVisible (true); + } + else if (bar != null && menuBar != null) + { + // We're swapping the menubar. + gtkFixedSetVisible (false); + removeMenuBarPeer(); + int oldHeight = menuBarHeight; + int menuBarWidth = + awtComponent.getWidth () - insets.left - insets.right; + menuBar = (MenuBarPeer) bar.getPeer (); + setMenuBarPeer (menuBar); + if (menuBarWidth > 0) + setMenuBarWidth (menuBar, menuBarWidth); + menuBarHeight = getMenuBarHeight (); + if (oldHeight != menuBarHeight) + { + insets.top += (menuBarHeight - oldHeight); + awtComponent.validate (); + } + gtkFixedSetVisible (true); + } + } + + public void setBounds (int x, int y, int width, int height) + { + int menuBarWidth = width - insets.left - insets.right; + if (menuBar != null && menuBarWidth > 0) + setMenuBarWidth (menuBar, menuBarWidth); + + super.setBounds(x, y, width, height + menuBarHeight); + } + + public void setResizable (boolean resizable) + { + // Call setSize; otherwise when resizable is changed from true to + // false the frame will shrink to the dimensions it had before it + // was resizable. + setSize (awtComponent.getWidth() - insets.left - insets.right, + awtComponent.getHeight() - insets.top - insets.bottom + + menuBarHeight); + gtkWindowSetResizable (resizable); + } + + protected void postInsetsChangedEvent (int top, int left, + int bottom, int right) + { + insets.top = top + menuBarHeight; + insets.left = left; + insets.bottom = bottom; + insets.right = right; + } + + public GtkFramePeer (Frame frame) + { + super (frame); + } + + void create () + { + // Create a normal decorated window. + create (GDK_WINDOW_TYPE_HINT_NORMAL, + !((Frame) awtComponent).isUndecorated ()); + + Frame frame = (Frame) awtComponent; + + setMenuBar (frame.getMenuBar ()); + + setTitle (frame.getTitle ()); + gtkWindowSetResizable (frame.isResizable ()); + setIconImage(frame.getIconImage()); + } + + native void nativeSetIconImage (GtkImage image); + + public void setIconImage (Image image) + { + if (image != null) + { + GtkImage gtkImage; + if (image instanceof GtkImage) + gtkImage = (GtkImage) image; + else + gtkImage = new GtkImage(image.getSource()); + + if (gtkImage.isLoaded && ! gtkImage.errorLoading) + nativeSetIconImage(gtkImage); + } + } + + protected void postConfigureEvent (int x, int y, int width, int height) + { + if (menuBar != null && width > 0) + setMenuBarWidthUnlocked (menuBar, width); + + // Since insets.top already includes the MenuBar's height, we need + // to subtract the MenuBar's height from the top inset. + int frame_height = height - menuBarHeight; + + // Likewise, since insets.top includes the MenuBar height, we need + // to add back the MenuBar height to the frame's y position. If + // no MenuBar exists in this frame, the MenuBar height will be 0. + int frame_y = y + menuBarHeight; + + super.postConfigureEvent(x, frame_y, width, frame_height); + } + + public int getState () + { + return windowState; + } + + public void setState (int state) + { + switch (state) + { + case Frame.NORMAL: + if ((windowState & Frame.ICONIFIED) != 0) + deiconify(); + if ((windowState & Frame.MAXIMIZED_BOTH) != 0) + unmaximize(); + break; + case Frame.ICONIFIED: + iconify(); + break; + case Frame.MAXIMIZED_BOTH: + maximize(); + } + } + + public void setMaximizedBounds (Rectangle r) + { + + } + public void setBoundsPrivate(int x, int y, int width, int height) + { + // TODO Auto-generated method stub + + } + + public boolean requestWindowFocus() + { + // TODO Auto-generated method stub + return false; + } + + public Rectangle getBoundsPrivate() + { + // TODO: Implement this properly. + throw new InternalError("Not yet implemented"); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkGenericPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkGenericPeer.java new file mode 100644 index 000000000..1b2c07b5d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkGenericPeer.java @@ -0,0 +1,145 @@ +/* GtkGenericPeer.java - Has a hashcode. Yuck. + Copyright (C) 1998, 1999, 2002, 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.EventQueue; +import java.awt.Font; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; + +import gnu.classpath.Pointer; + +public class GtkGenericPeer +{ + // Used by Native State Association (NSA) functions to map + // gtk_widget to peer object. + final int native_state = getUniqueInteger (); + + // Next native state value we will assign. + private static int next_native_state = 0; + + // The widget or other java-side object we wrap. + protected final Object awtWidget; + + /** + * The pointer to the native GTK widget. + * + * This field is manipulated by native code. Don't change or remove + * without adjusting the native code. + */ + private Pointer widget; + + /** + * The pointer to the global reference to this object. The native + * code creates a JNI global reference of the peer object to be able + * to pass it to the event callbacks. It gets stored here, so that + * we can later delete it in the dispose() method. + * + * This field is manipulated by native code. Don't change or remove + * without adjusting the native code. + */ + private Pointer globalRef; + + /** + * We initialize the field IDs that are used by native code here because + * these remain valid until a class gets unloaded. + */ + static + { + GtkToolkit.initializeGlobalIDs(); + initIDs(); + } + + /** + * Initializes the field IDs that are used by the native code. + */ + private static native void initIDs(); + + /** + * Dispose of our native state. Calls gtk_widget_destroy on the + * native widget and removes the awtWidget from the native state + * tables. Should be overridden by subclasses if this is not (all) + * that needs to be done. + */ + public native void dispose (); + + static EventQueue q () + { + return Toolkit.getDefaultToolkit ().getSystemEventQueue (); + } + + protected GtkGenericPeer (Object awtWidget) + { + this.awtWidget = awtWidget; + } + + protected void postActionEvent (String command, int mods) + { + q().postEvent (new ActionEvent (awtWidget, ActionEvent.ACTION_PERFORMED, + command, mods)); + } + + // Return a unique integer for use in the native state mapping + // code. We can't use a hash code since that is not guaranteed to + // be unique. + static synchronized int getUniqueInteger () + { + // Let's assume this will never wrap. + return next_native_state++; + } + + /** + * Helper method to set Font for Gtk Widget. + */ + protected void gtkWidgetModifyFont(Font f) + { + gtkWidgetModifyFont(f.getName(), f.getStyle(), f.getSize()); + } + + /** + * Sets font for this Gtk Widget. Should be overridden by peers which + * are composed of different widgets or are contained in bins. + */ + protected native void gtkWidgetModifyFont(String name, int style, int size); + + static void printCurrentThread () + { + System.out.println ("gtkgenericpeer, thread: " + Thread.currentThread ()); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java new file mode 100644 index 000000000..b0a5aa0b1 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImage.java @@ -0,0 +1,541 @@ +/* GtkImage.java + Copyright (C) 2005, 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.Image; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.MemoryImageSource; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +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; + +/** + * 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 + * corresponds to the Image implementations returned by the Toolkit.createImage + * methods, and is basically immutable. + * + * @author Sven de Marothy + */ +public class GtkImage extends Image +{ + int width = -1, height = -1; + + /** + * Properties. + */ + Hashtable<?,?> props; + + /** + * Loaded or not flag, for asynchronous compatibility. + */ + boolean isLoaded; + + /** + * Pointer to the GdkPixbuf - + * don't change the name without changing the native code. + */ + Pointer pixbuf; + + /** + * Observer queue. + */ + Vector<ImageObserver> observers; + + /** + * Error flag for loading. + */ + boolean errorLoading; + + /** + * Original source, if created from an ImageProducer. + */ + ImageProducer source; + + /* + * The 32-bit AABBGGRR format the GDK uses. + */ + static ColorModel nativeModel = new DirectColorModel(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 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 pixbufLock held. + */ + native int[] getPixels(); + + /** + * Sets the pixel data from a java array. + * 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 pixbufLock held. + */ + private native boolean loadPixbuf(String name); + + /** + * Loads an image using gdk-pixbuf from data. + * Should be called with the pixbufLock held. + */ + private native boolean loadImageFromData(byte[] data); + + /** + * Allocates a Gtk Pixbuf + * Should be called with the pixbufLock held. + */ + private native void createPixbuf(); + + /** + * Frees the above. + * Should be called with the pixbufLock held. + */ + private native void freePixbuf(); + + /** + * Sets the pixbuf to scaled copy of src image. hints are rendering hints. + * Should be called with the pixbufLock held. + */ + private native void createScaledPixbuf(GtkImage src, int hints); + + /** + * Constructs a GtkImage from an ImageProducer. Asynchronity is handled in + * the following manner: + * A GtkImageConsumer gets the image data, and calls setImage() when + * completely finished. The GtkImage is not considered loaded until the + * GtkImageConsumer is completely finished. We go for all "all or nothing". + */ + public GtkImage (ImageProducer producer) + { + isLoaded = false; + observers = new Vector<ImageObserver>(); + source = producer; + errorLoading = false; + source.startProduction(new GtkImageConsumer(this, source)); + } + + /** + * Constructs a blank GtkImage. This is called when + * GtkToolkit.createImage (String) is called with an empty string + * argument (""). A blank image is loaded immediately upon + * construction and has width -1 and height -1. + */ + public GtkImage () + { + isLoaded = true; + observers = null; + props = new Hashtable<String,Object>(); + errorLoading = false; + } + + /** + * Constructs a GtkImage by loading a given file. + * + * @throws IllegalArgumentException if the image could not be loaded. + */ + public GtkImage (String filename) + { + File f = new File(filename); + try + { + String path = f.getCanonicalPath(); + synchronized(pixbufLock) + { + if (loadPixbuf(f.getCanonicalPath()) != true) + throw new IllegalArgumentException("Couldn't load image: " + + filename); + } + } + catch(IOException e) + { + IllegalArgumentException iae; + iae = new IllegalArgumentException("Couldn't load image: " + + filename); + iae.initCause(e); + throw iae; + } + + isLoaded = true; + observers = null; + props = new Hashtable<String,Object>(); + } + + /** + * Constructs a GtkImage from a byte array of an image file. + * + * @throws IllegalArgumentException if the image could not be + * loaded. + */ + public GtkImage (byte[] data) + { + synchronized(pixbufLock) + { + if (loadImageFromData (data) != true) + throw new IllegalArgumentException ("Couldn't load image."); + } + + isLoaded = true; + observers = null; + props = new Hashtable<String,Object>(); + errorLoading = false; + } + + /** + * Constructs a GtkImage from a URL. May result in an error image. + */ + public GtkImage (URL url) + { + isLoaded = false; + observers = new Vector<ImageObserver>(); + errorLoading = false; + if( url == null) + return; + ByteArrayOutputStream baos = new ByteArrayOutputStream (5000); + try + { + BufferedInputStream bis = new BufferedInputStream (url.openStream()); + + byte[] buf = new byte[5000]; + int n = 0; + + while ((n = bis.read(buf)) != -1) + baos.write(buf, 0, n); + bis.close(); + } + catch(IOException e) + { + throw new IllegalArgumentException ("Couldn't load image."); + } + byte[] array = baos.toByteArray(); + synchronized(pixbufLock) + { + if (loadImageFromData(array) != true) + throw new IllegalArgumentException ("Couldn't load image."); + } + + isLoaded = true; + observers = null; + props = new Hashtable<String,Object>(); + } + + /** + * Constructs a scaled version of the src bitmap, using the GDK. + */ + private GtkImage (GtkImage src, int width, int height, int hints) + { + this.width = width; + this.height = height; + props = new Hashtable<String,Object>(); + isLoaded = true; + observers = null; + + // Use the GDK scaling method. + synchronized(pixbufLock) + { + createScaledPixbuf(src, hints); + } + } + + /** + * Package private constructor to create a GtkImage from a given + * PixBuf pointer. + */ + GtkImage (Pointer pixbuf) + { + this.pixbuf = pixbuf; + synchronized(pixbufLock) + { + createFromPixbuf(); + } + isLoaded = true; + observers = null; + props = new Hashtable<String,Object>(); + } + + /** + * 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<String,Object>(); + isLoaded = true; + observers = null; + initFromBuffer( bufferPointer ); + } + + /** + * Returns an empty GtkImage with the errorLoading flag set. + * Called from GtkToolKit when some error occured, but an image needs + * to be returned anyway. + */ + static synchronized GtkImage getErrorImage() + { + if (errorImage == null) + { + errorImage = new GtkImage(); + errorImage.errorLoading = true; + } + return errorImage; + } + + /** + * Native helper function for constructor that takes a pixbuf Pointer. + * Should be called with the pixbufLock held. + */ + private native void createFromPixbuf(); + + /** + * Callback from the image consumer. + */ + public void setImage(int width, int height, + int[] pixels, Hashtable<?,?> properties) + { + this.width = width; + this.height = height; + props = (properties != null) ? properties : new Hashtable<String,Object>(); + + if (width <= 0 || height <= 0 || pixels == null) + { + errorLoading = true; + return; + } + + synchronized(pixbufLock) + { + createPixbuf(); + setPixels(pixels); + } + isLoaded = true; + deliver(); + } + + // java.awt.Image methods //////////////////////////////////////////////// + + public synchronized int getWidth (ImageObserver observer) + { + if (addObserver(observer)) + return -1; + + return width; + } + + public synchronized int getHeight (ImageObserver observer) + { + if (addObserver(observer)) + return -1; + + return height; + } + + public synchronized Object getProperty (String name, ImageObserver observer) + { + if (addObserver(observer)) + return UndefinedProperty; + + Object value = props.get (name); + return (value == null) ? UndefinedProperty : value; + } + + /** + * Returns the source of this image. + */ + public ImageProducer getSource () + { + if (!isLoaded) + return null; + + int[] pixels; + synchronized (pixbufLock) + { + if (!errorLoading) + pixels = getPixels(); + else + return null; + } + return new MemoryImageSource(width, height, nativeModel, pixels, + 0, width); + } + + /** + * Does nothing. Should not be called. + */ + public Graphics getGraphics () + { + throw new IllegalAccessError("This method only works for off-screen" + +" Images."); + } + + /** + * Returns a scaled instance of this pixbuf. + */ + public Image getScaledInstance(int width, + int height, + int hints) + { + if (width <= 0 || height <= 0) + throw new IllegalArgumentException("Width and height of scaled bitmap" + + "must be >= 0"); + + return new GtkImage(this, width, height, hints); + } + + /** + * If the image is loaded and comes from an ImageProducer, + * regenerate the image from there. + * + * I have no idea if this is ever actually used. Since GtkImage can't be + * instantiated directly, how is the user to know if it was created from + * an ImageProducer or not? + */ + public synchronized void flush () + { + if (isLoaded && source != null) + { + observers = new Vector<ImageObserver>(); + isLoaded = false; + synchronized(pixbufLock) + { + freePixbuf(); + } + source.startProduction(new GtkImageConsumer(this, source)); + } + } + + public void finalize() + { + if (isLoaded) + { + synchronized(pixbufLock) + { + freePixbuf(); + } + } + } + + /** + * Returns the image status, used by GtkToolkit + */ + public int checkImage (ImageObserver observer) + { + if (addObserver(observer)) + { + if (errorLoading == true) + return ImageObserver.ERROR; + else + return 0; + } + + return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT; + } + + + // Private methods //////////////////////////////////////////////// + + /** + * Delivers notifications to all queued observers. + */ + private void deliver() + { + int flags = ImageObserver.HEIGHT | + ImageObserver.WIDTH | + ImageObserver.PROPERTIES | + ImageObserver.ALLBITS; + + if (observers != null) + for(int i=0; i < observers.size(); i++) + ((ImageObserver)observers.elementAt(i)).imageUpdate(this, flags, 0, 0, + width, height); + + observers = null; + } + + /** + * Adds an observer, if we need to. + * @return true if an observer was added. + */ + private boolean addObserver(ImageObserver observer) + { + if (!isLoaded) + { + if(observer != null) + if (!observers.contains (observer)) + observers.addElement (observer); + return true; + } + return false; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkImageConsumer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImageConsumer.java new file mode 100644 index 000000000..65cae7a89 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkImageConsumer.java @@ -0,0 +1,169 @@ +/* GtkImageConsumer.java + Copyright (C) 2005, 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.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.awt.image.MemoryImageSource; +import java.nio.ByteOrder; +import java.util.Hashtable; + +/** + * Helper class to GtkImage. Sits and gathers pixels for a GtkImage and then + * calls GtkImage.setImage(). + * + * @author Sven de Marothy + */ +public class GtkImageConsumer implements ImageConsumer +{ + private GtkImage target; + private int width, height; + private Hashtable<?,?> properties; + private int[] pixelCache = null; + private ImageProducer source; + + public GtkImageConsumer(GtkImage target, ImageProducer source) + { + this.target = target; + this.source = source; + } + + public synchronized void imageComplete (int status) + { + // we need to reuse the pixel cache for memory image sources since + // a memory image's backing array can be updated "live". + if (!(source instanceof MemoryImageSource)) + source.removeConsumer(this); + target.setImage(width, height, pixelCache, properties); + } + + public synchronized void setColorModel (ColorModel model) + { + // This method is to inform on what the most used color model + // in the image is, for optimization reasons. We ignore this + // information. + } + + public synchronized void setDimensions (int width, int height) + { + pixelCache = new int[width*height]; + + this.width = width; + this.height = height; + } + + public synchronized void setHints (int flags) + { + // This method informs us in which order the pixels are + // delivered, for progressive-loading support, etc. + // Since we wait until it's all loaded, we can ignore the hints. + } + + public synchronized void setPixels (int x, int y, int width, int height, + ColorModel cm, byte[] pixels, + int offset, int scansize) + { + setPixels (x, y, width, height, cm, convertPixels (pixels), offset, + scansize); + } + + public synchronized void setPixels (int x, int y, int width, int height, + ColorModel cm, int[] pixels, + int offset, int scansize) + { + if (pixelCache == null) + return; // Not sure this should ever happen. + + if (cm.equals(GtkImage.nativeModel)) + for (int i = 0; i < height; i++) + System.arraycopy (pixels, offset + (i * scansize), + pixelCache, (y + i) * this.width + x, + width); + else + { + if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) + { + for (int i = 0; i < height; i++) + for (int j = 0; j < width; j++) + { + // get in RRGGBBAA and convert to AARRGGBB + int pix = cm.getRGB(pixels[offset + (i * scansize) + x + j]); + int a = ((pix & 0xFF000000) >> 24) & 0xFF; + int rgb = (pix & 0x00FFFFFF) << 8; + pix = rgb | a; + pixelCache[(y + i) * this.width + x + j] = pix; + } + } + else + { + for (int i = 0; i < height; i++) + for (int j = 0; j < width; j++) + { + // get in AARRGGBB and convert to AABBGGRR + int pix = cm.getRGB(pixels[offset + (i * scansize) + x + j]); + byte b = (byte)(pix & 0xFF); + byte r = (byte)(((pix & 0x00FF0000) >> 16) & 0xFF); + pix &= 0xFF00FF00; + pix |= ((b & 0xFF) << 16); + pix |= (r & 0xFF); + pixelCache[(y + i) * this.width + x + j] = pix; + } + } + } + } + + /** + * This is an old method, no idea if it's correct. + */ + private int[] convertPixels (byte[] pixels) + { + int ret[] = new int[pixels.length]; + + for (int i = 0; i < pixels.length; i++) + ret[i] = pixels[i] & 0xFF; + + return ret; + } + + public synchronized void setProperties (Hashtable<?,?> props) + { + this.properties = props; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkLabelPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkLabelPeer.java new file mode 100644 index 000000000..0bdacbf33 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkLabelPeer.java @@ -0,0 +1,102 @@ +/* GtkLabelPeer.java -- Implements LabelPeer with GTK + Copyright (C) 1998, 1999, 2005, 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.Label; +import java.awt.peer.LabelPeer; + +// A composite widget. GtkLabels have transparent backgrounds. An +// AWT Label is opaque. To compensate, a GtkLabelPeer is a GtkLabel +// packed in a GtkEventBox. +public class GtkLabelPeer extends GtkComponentPeer + implements LabelPeer +{ + native void create (String text, float alignment); + + /** + * Overridden to set the Font of the label inside the gtk_event_box. + */ + protected native void gtkWidgetModifyFont(String name, int style, int size); + + native void nativeSetAlignment (float alignment); + + public native void setNativeText(String text); + native void setNativeBounds (int x, int y, int width, int height); + + // Because this is a composite widget, we need to retrieve the + // GtkLabel's preferred dimensions, not the enclosing GtkEventBox's. + native void gtkWidgetGetPreferredDimensions (int[] dim); + + void create () + { + Label label = (Label) awtComponent; + create (label.getText (), getGtkAlignment (label.getAlignment ())); + } + + public void setText(String text) + { + if (text != null) + setNativeText(text); + } + + public GtkLabelPeer (Label l) + { + super (l); + } + + public void setAlignment (int alignment) + { + nativeSetAlignment (getGtkAlignment (alignment)); + } + + float getGtkAlignment (int alignment) + { + switch (alignment) + { + case Label.LEFT: + return 0.0f; + case Label.CENTER: + return 0.5f; + case Label.RIGHT: + return 1.0f; + } + + return 0.0f; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkListPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkListPeer.java new file mode 100644 index 000000000..b1cc6e522 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkListPeer.java @@ -0,0 +1,187 @@ +/* GtkListPeer.java -- Implements ListPeer with GTK + Copyright (C) 1998, 1999, 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.AWTEvent; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.List; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.ListPeer; + +public class GtkListPeer extends GtkComponentPeer + implements ListPeer +{ + void create () + { + List list = (List) awtComponent; + + create (list.getRows ()); + + setMultipleMode (list.isMultipleMode ()); + } + + native void create (int rows); + native void connectSignals (); + + /** + * Overridden to set the Font of the text insode the gtk_scrolled_window. + */ + protected native void gtkWidgetModifyFont (String name, int style, int size); + + native void gtkWidgetRequestFocus (); + + native void getSize (int rows, int visibleRows, int dims[]); + + public GtkListPeer (List list) + { + super (list); + + setMultipleMode (list.isMultipleMode ()); + + if (list.getItemCount () > 0) + append (list.getItems ()); + } + + native void append (String items[]); + + public native void add (String item, int index); + + public void addItem (String item, int index) + { + add (item, index); + } + + public void clear () + { + removeAll (); + } + + public native void delItems (int start, int end); + public native void deselect (int index); + + public Dimension getMinimumSize (int rows) + { + return minimumSize (rows); + } + + public Dimension getPreferredSize (int rows) + { + return preferredSize (rows); + } + + public native int[] getSelectedIndexes (); + public native void makeVisible (int index); + + public Dimension minimumSize (int rows) + { + int dims[] = new int[2]; + + int visibleRows = ((List) awtComponent).getRows(); + getSize (rows, visibleRows, dims); + return new Dimension (dims[0], dims[1]); + } + + public Dimension preferredSize (int rows) + { + // getSize returns the minimum size of the list. + // The width is too narrow for a typical list. + List l = (List) awtComponent; + Dimension d = getMinimumSize(); + FontMetrics fm = l.getFontMetrics(l.getFont()); + return new Dimension(d.width + fm.stringWidth("1234567890"), d.height); + } + + public void removeAll () + { + delItems (0, -1); + } + + public native void select (int index); + public native void setMultipleMode (boolean b); + + public void setMultipleSelections (boolean b) + { + setMultipleMode (b); + } + + public void handleEvent (AWTEvent e) + { + if (e.getID () == MouseEvent.MOUSE_CLICKED && isEnabled ()) + { + // Only generate the ActionEvent on the second click of a + // multiple click. + MouseEvent me = (MouseEvent) e; + if (!me.isConsumed () + && (me.getModifiersEx () & MouseEvent.BUTTON1_DOWN_MASK) != 0 + && me.getClickCount() == 2) + { + String selectedItem = ((List) awtComponent).getSelectedItem (); + + // Double-click only generates an Action event if + // something is selected. + if (selectedItem != null) + postActionEvent (((List) awtComponent).getSelectedItem (), + me.getModifiersEx ()); + } + } + + if (e.getID () == KeyEvent.KEY_PRESSED) + { + KeyEvent ke = (KeyEvent) e; + if (!ke.isConsumed () && ke.getKeyCode () == KeyEvent.VK_ENTER) + { + String selectedItem = ((List) awtComponent).getSelectedItem (); + + // Enter only generates an Action event if something is + // selected. + if (selectedItem != null) + postActionEvent (selectedItem, ke.getModifiersEx ()); + } + } + + super.handleEvent (e); + } + + protected void postItemEvent (int item, int stateChange) + { + postItemEvent (new Integer (item), stateChange); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkMainThread.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMainThread.java new file mode 100644 index 000000000..0ee61df84 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMainThread.java @@ -0,0 +1,188 @@ +/* GtkMainThread.java -- Wrapper for the GTK main thread, and some utilities. + 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 gnu.java.awt.peer.NativeEventLoopRunningEvent; + +/** + * The Java thread representing the native GTK main loop, that is, + * GtkMainThread.mainThread, terminates when GtkToolkit.gtkMain() + * returns. That happens in response to the last window peer being + * disposed (see GtkWindowPeer.dispose). + * + * When GtkMainThread.destroyWindow is called for the last window, it + * in turn calls GtkMainThread.endMainThread, which calls gtk_quit. + * gtk_quit signals gtk_main to return, which causes GtkMainThread.run + * to return. + * + * There should only be one native GTK main loop running at any given + * time. In order to safely start and stop the GTK main loop, we use + * a running flag and corresponding runningLock. startMainThread will + * not return until the native GTK main loop has started, as confirmed + * by the native set_running_flag callback setting the running flag to + * true. Without this protection, gtk_quit could be called before the + * main loop has actually started, which causes GTK assertion + * failures. Likewise endMainThread will not return until the native + * GTK main loop has ended. + * + * post_running_flag_callback is called during gtk_main initialization + * and no window can be created before startMainThread returns. This + * ensures that calling post_running_flag_callback is the first action + * taken by the native GTK main loop. + * + * GtkMainThread.mainThread is started when the window count goes from + * zero to one. + * + * GtkMainThread keeps the AWT event queue informed of its status by + * posting NativeEventLoopRunningEvents. The AWT event queue uses + * this status to determine whether or not the AWT exit conditions + * have been met (see EventQueue.isShutdown). + */ +public class GtkMainThread extends Thread +{ + /** Count of the number of open windows */ + private static int numberOfWindows = 0; + + /** Lock for the above */ + private static Object nWindowsLock = new Object(); + + /** Indicates whether or not the GTK main loop is running. */ + private static boolean running = false; + + /** Lock for the above. */ + private static Object runningLock = new Object(); + + /** The main thread instance (singleton) */ + public static GtkMainThread mainThread; + + /** Constructs a main thread */ + private GtkMainThread() + { + super("GTK main thread"); + } + + public void run () + { + GtkToolkit.gtkMain (); + } + + private static void setRunning(boolean running) + { + synchronized (runningLock) + { + GtkMainThread.running = running; + runningLock.notifyAll(); + } + } + + private static void startMainThread() + { + synchronized (runningLock) + { + if (!running) + { + mainThread = new GtkMainThread(); + mainThread.start(); + + while (!running) + { + try + { + runningLock.wait(); + } + catch (InterruptedException e) + { + System.err.println ("GtkMainThread.startMainThread:" + + " interrupted while waiting " + + " for GTK main loop to start"); + } + } + GtkGenericPeer.q() + .postEvent(new NativeEventLoopRunningEvent(Boolean.TRUE)); + } + } + } + + private static void endMainThread() + { + synchronized (runningLock) + { + if (running) + { + GtkToolkit.gtkQuit(); + + while (running) + { + try + { + runningLock.wait(); + } + catch (InterruptedException e) + { + System.err.println ("GtkMainThread.endMainThread:" + + " interrupted while waiting " + + " for GTK main loop to stop"); + } + } + GtkGenericPeer.q() + .postEvent(new NativeEventLoopRunningEvent(Boolean.FALSE)); + } + } + } + + public static void createWindow() + { + synchronized (nWindowsLock) + { + if (numberOfWindows == 0) + startMainThread(); + numberOfWindows++; + } + } + + public static void destroyWindow() + { + synchronized (nWindowsLock) + { + numberOfWindows--; + if (numberOfWindows == 0) + endMainThread(); + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuBarPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuBarPeer.java new file mode 100644 index 000000000..c3427b18f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuBarPeer.java @@ -0,0 +1,113 @@ +/* GtkMenuBarPeer.java -- Implements MenuBarPeer with GTK+ + Copyright (C) 1999, 2005, 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.Menu; +import java.awt.MenuBar; +import java.awt.peer.MenuBarPeer; + +public class GtkMenuBarPeer extends GtkMenuComponentPeer + implements MenuBarPeer +{ + /** Whether we already have an help menu set on this peer. */ + private boolean hasHelpMenu; + + /** + * Creates the gtk+ widget for this peer and puts it in the nsa + * table. Called from the (super class) constructor. + */ + protected native void create(); + + /** + * Adds a new GtkMenuPeer to the end of the GtkMenuBarPeer. + */ + private native void addMenu(GtkMenuPeer menu); + + /** + * Creates a new GtkMenuBarPeer associated with the given MenuBar. + */ + public GtkMenuBarPeer(MenuBar menubar) + { + super(menubar); + } + + /** + * Adds a help menu to this MenuBar. Gnome styleguides say the help + * menu is just the last item in the menubar (they are NOT right + * justified). + */ + public void addHelpMenu (Menu menu) + { + if (hasHelpMenu) + { + // Remove the (help) menu, which is after all the other items. + delMenu(((MenuBar) awtWidget).getMenuCount()); + hasHelpMenu = false; + } + + if (menu != null) + { + addMenu(menu); + hasHelpMenu = true; + } + } + + /** + * Deletes the menu at (zero-based) index from this GtkMenuBar. + */ + public native void delMenu(int index); + + /** + * Adds the GtkMenuPeer associated with the Menu to this + * GtkMenuBarPeer. Makes sure that any help menus keep the last menu + * on the bar. + */ + public void addMenu(Menu m) + { + // Make sure the help menu is the last one. + if (hasHelpMenu) + { + addHelpMenu(null); + addMenu((GtkMenuPeer) m.getPeer()); + addHelpMenu(((MenuBar) awtWidget).getHelpMenu()); + } + else + addMenu((GtkMenuPeer) m.getPeer()); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuComponentPeer.java new file mode 100644 index 000000000..78f640361 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuComponentPeer.java @@ -0,0 +1,104 @@ +/* GtkMenuComponentPeer.java -- Implements MenuComponentPeer with GTK+ + Copyright (C) 1999, 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.MenuComponent; +import java.awt.MenuContainer; +import java.awt.peer.MenuComponentPeer; + +public abstract class GtkMenuComponentPeer extends GtkGenericPeer + implements MenuComponentPeer +{ + /** + * Creates the associated gtk+ widget and stores it in the nsa table + * for this peer. Called by the constructor. + */ + protected abstract void create (); + + /** + * Sets font based on MenuComponent font, or containing menu(bar) + * parent font. + */ + private void setFont() + { + MenuComponent mc = ((MenuComponent) awtWidget); + Font f = mc.getFont(); + + if (f == null) + { + MenuContainer parent = mc.getParent (); + // Submenus inherit the font of their containing Menu(Bar). + if (parent instanceof MenuComponent) + f = parent.getFont (); + } + + setFont(f); + } + + /** + * Will call the abstract <code>create()</code> that needs to be + * overridden by subclasses, to create the MenuComponent. It will + * then correctly setup the font for the component based on the + * component and/or its containing parent component. + */ + public GtkMenuComponentPeer(MenuComponent component) + { + super(component); + create(); + setFont(); + } + + /** + * Removes the awtWidget components from the native state tables. + * Subclasses should call <code>super.dispose()</code> if they don't + * remove these themselves. + */ + public native void dispose(); + + /** + * Sets the font for this particular MenuComponent only (not any + * containing items, if any). + */ + public void setFont(Font font) + { + if (font != null) + gtkWidgetModifyFont(font); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuItemPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuItemPeer.java new file mode 100644 index 000000000..ea523e953 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuItemPeer.java @@ -0,0 +1,116 @@ +/* GtkMenuItemPeer.java -- Implements MenuItemPeer with GTK+ + Copyright (C) 1999, 2005, 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.MenuItem; +import java.awt.peer.MenuItemPeer; + +public class GtkMenuItemPeer extends GtkMenuComponentPeer + implements MenuItemPeer +{ + /** + * Creates the associated gtk+ widget and stores it in the nsa table + * for this peer. Called by the create() method with the label name + * of the associated MenuItem. Needs to be overridden my subclasses + * that want to create a different gtk+ widget. + */ + protected native void create (String label); + + /** + * Called from constructor to enable signals from an item. If a + * subclass needs different (or no) signals connected this method + * should be overridden. + */ + protected native void connectSignals (); + + /** + * Overridden to set font on menu item label. + */ + protected native void gtkWidgetModifyFont(String name, int style, int size); + + /** + * Creates the associated gtk+ widget and stores it in the nsa table + * for this peer. Called by the (super class) constructor. + * Overridden to get the label if the assiociated MenuItem and to + * call create(String). + */ + protected void create() + { + create (((MenuItem) awtWidget).getLabel()); + } + + /** + * Creates a new GtkMenuItemPeer associated with the given MenuItem. + * It will call create(), setFont(), setEnabled() and + * connectSignals() in that order. + */ + public GtkMenuItemPeer(MenuItem item) + { + super(item); + setEnabled (item.isEnabled()); + connectSignals(); + } + + /** + * Calls setEnabled(false). + */ + public void disable() + { + setEnabled(false); + } + + /** + * Calls setEnabled(true). + */ + public void enable() + { + setEnabled(true); + } + + public native void setEnabled(boolean b); + public native void setLabel(String label); + + /** + * Callback setup through connectSignals(). + */ + protected void postMenuActionEvent () + { + postActionEvent (((MenuItem)awtWidget).getActionCommand (), 0); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuPeer.java new file mode 100644 index 000000000..c55347393 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMenuPeer.java @@ -0,0 +1,126 @@ +/* GtkMenuPeer.java -- Implements MenuPeer with GTK+ + Copyright (C) 1999, 2005, 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.Component; +import java.awt.Menu; +import java.awt.MenuContainer; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; + +public class GtkMenuPeer extends GtkMenuItemPeer + implements MenuPeer +{ + /** + * Creates the associated gtk+ widget and stores it in the nsa table + * for this peer. Called by the create() method with the label name + * of the associated MenuItem. Overridden to greate a Menu widget. + */ + protected native void create (String label); + + private native void addItem(MenuItemPeer item, int key, + boolean shiftModifier); + + /** XXX - Document this and the override in GtkPopupMenuPeer. */ + native void setupAccelGroup (GtkGenericPeer container); + + private native void addTearOff (); + + /** + * Overridden to not connect any signals. + */ + protected void connectSignals() + { + // No signals to connect. + } + + public GtkMenuPeer (Menu menu) + { + super (menu); + + if (menu.isTearOff()) + addTearOff(); + + MenuContainer parent = menu.getParent (); + if (parent instanceof Menu) + setupAccelGroup ((GtkMenuPeer)((Menu)parent).getPeer ()); + else if (parent instanceof Component) + setupAccelGroup ((GtkComponentPeer)((Component)parent).getPeer ()); + else + setupAccelGroup (null); // XXX, should we warn about unknown parent? + } + + public void addItem (MenuItem item) + { + int key = 0; + boolean shiftModifier = false; + + MenuShortcut ms = item.getShortcut (); + if (ms != null) + { + key = ms.getKey (); + shiftModifier = ms.usesShiftModifier (); + } + + addItem ((MenuItemPeer) item.getPeer (), key, shiftModifier); + } + + public void addItem (MenuItemPeer item, MenuShortcut ms) + { + int key = 0; + boolean shiftModifier = false; + + if (ms != null) + { + key = ms.getKey (); + shiftModifier = ms.usesShiftModifier (); + } + + addItem (item, key, shiftModifier); + } + + public native void delItem(int index); + + public void addSeparator() + { + // FIXME: implement + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkMouseInfoPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMouseInfoPeer.java new file mode 100644 index 000000000..55c9146c4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkMouseInfoPeer.java @@ -0,0 +1,65 @@ +/* GtkToolkit.java -- Implements an AWT Toolkit using GTK for peers + 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.Point; +import java.awt.GraphicsDevice; +import java.awt.Window; +import java.awt.peer.MouseInfoPeer; + +/** + * The MouseInfoPeer is so small, I'm including it here. + */ +public class GtkMouseInfoPeer implements MouseInfoPeer +{ + private static GdkGraphicsEnvironment gde = new GdkGraphicsEnvironment(); + + public int fillPointWithCoords(Point p) + { + int[] coords = gde.getMouseCoordinates(); + p.x = coords[1]; + p.y = coords[2]; + return coords[0]; + } + + public boolean isWindowUnderMouse(Window w) + { + return gde.isWindowUnderMouse((GtkWindowPeer) w.getPeer()); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java new file mode 100644 index 000000000..00b506c10 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java @@ -0,0 +1,67 @@ +/* GtkPanelPeer.java -- Implements PanelPeer with GTK + Copyright (C) 1998, 1999, 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.AWTEvent; +import java.awt.Panel; +import java.awt.event.MouseEvent; +import java.awt.peer.PanelPeer; + +public class GtkPanelPeer extends GtkContainerPeer + implements PanelPeer +{ + native void create (); + + public GtkPanelPeer (Panel p) + { + super (p); + } + + public void handleEvent(AWTEvent event) + { + int id = event.getID(); + + if (id == MouseEvent.MOUSE_PRESSED) + awtComponent.requestFocusInWindow(); + + super.handleEvent(event); + } + + native void connectSignals (); +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkPopupMenuPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkPopupMenuPeer.java new file mode 100644 index 000000000..1b0ec8e63 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkPopupMenuPeer.java @@ -0,0 +1,68 @@ +/* GtkPopupMenuPeer.java -- Implements PopupMenuPeer with GTK+ + Copyright (C) 1999, 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.Component; +import java.awt.Event; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.peer.PopupMenuPeer; + +public class GtkPopupMenuPeer extends GtkMenuPeer + implements PopupMenuPeer +{ + public GtkPopupMenuPeer (PopupMenu menu) + { + super (menu); + } + + native void setupAccelGroup (GtkGenericPeer container); + + native void show (int x, int y, long time); + public void show (Component origin, int x, int y) + { + Point abs = origin.getLocationOnScreen (); + show (abs.x + x, abs.y + y, 0); + } + + public void show (Event e) + { + + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkScrollPanePeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkScrollPanePeer.java new file mode 100644 index 000000000..657a27608 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkScrollPanePeer.java @@ -0,0 +1,111 @@ +/* GtkScrollPanePeer.java -- Implements ScrollPanePeer with GTK + Copyright (C) 1998, 1999, 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.Adjustable; +import java.awt.Dimension; +import java.awt.ScrollPane; +import java.awt.peer.ScrollPanePeer; + +public class GtkScrollPanePeer extends GtkContainerPeer + implements ScrollPanePeer +{ + native void create (int width, int height); + + void create () + { + create (awtComponent.getWidth (), awtComponent.getHeight ()); + } + + // native void gtkScrolledWindowSetScrollPosition(int x, int y); + native void gtkScrolledWindowSetHScrollIncrement (int u); + native void gtkScrolledWindowSetVScrollIncrement (int u); + + public GtkScrollPanePeer (ScrollPane sp) + { + super (sp); + + setPolicy (sp.getScrollbarDisplayPolicy ()); + } + + native void setPolicy (int policy); + public void childResized (int width, int height) + { + int dim[] = new int[2]; + + gtkWidgetGetDimensions (dim); + + // If the child is in this range, GTK adds both scrollbars, but + // the AWT doesn't. So set the peer's scroll policy to + // GTK_POLICY_NEVER. + if ((width > dim[0] - getVScrollbarWidth () && width <= dim[0]) + && (height > dim[1] - getHScrollbarHeight () && height <= dim[1])) + setPolicy (ScrollPane.SCROLLBARS_NEVER); + else + setPolicy (((ScrollPane) awtComponent).getScrollbarDisplayPolicy ()); + } + + public native int getHScrollbarHeight(); + public native int getVScrollbarWidth(); + public native void setScrollPosition(int x, int y); + + public Dimension getPreferredSize () + { + return awtComponent.getSize(); + } + + public void setUnitIncrement (Adjustable adj, int u) + { + if (adj.getOrientation()==Adjustable.HORIZONTAL) + gtkScrolledWindowSetHScrollIncrement (u); + else + gtkScrolledWindowSetVScrollIncrement (u); + } + + public void setValue (Adjustable adj, int v) + { +// System.out.println("SPP: setVal: "+adj+":"+v); +// Point p=myScrollPane.getScrollPosition (); +// if (adj.getOrientation()==Adjustable.HORIZONTAL) +// gtkScrolledWindowSetScrollPosition (v,p.y); +// else +// gtkScrolledWindowSetScrollPosition (p.x,v); +// adj.setValue(v); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkScrollbarPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkScrollbarPeer.java new file mode 100644 index 000000000..d3f160c83 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkScrollbarPeer.java @@ -0,0 +1,92 @@ +/* GtkScrollbarPeer.java -- Implements ScrollbarPeer with GTK+ + Copyright (C) 1998, 1999, 2005, 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.Scrollbar; +import java.awt.event.AdjustmentEvent; +import java.awt.peer.ScrollbarPeer; + +public class GtkScrollbarPeer extends GtkComponentPeer + implements ScrollbarPeer +{ + void create () + { + Scrollbar sb = (Scrollbar) awtComponent; + + create (sb.getOrientation (), sb.getValue (), + sb.getMinimum (), sb.getMaximum (), + sb.getUnitIncrement (), sb.getBlockIncrement (), + sb.getVisibleAmount ()); + } + + native void create (int orientation, int value, + int min, int max, int stepIncr, int pageIncr, + int visibleAmount); + + native void connectSignals (); + + public GtkScrollbarPeer (Scrollbar s) + { + super (s); + } + + public native void setLineIncrement(int amount); + public native void setPageIncrement(int amount); + + public void setValues(int value, int visible, int min, int max) + { + Scrollbar sb = (Scrollbar) awtComponent; + if (!sb.getValueIsAdjusting()) + setBarValues(value, visible, min, max); + } + + private native void setBarValues(int value, int visible, int min, int max); + + /** + * Called from the native site when the scrollbar changed. + * Posts a "user generated" AdjustmentEvent to the queue. + */ + protected void postAdjustmentEvent (int type, int value) + { + Scrollbar bar = (Scrollbar) awtComponent; + q().postEvent(new AdjustmentEvent(bar, + AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED, + type, value, true)); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkSelection.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkSelection.java new file mode 100644 index 000000000..78ed69676 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkSelection.java @@ -0,0 +1,675 @@ +/* GtkClipboard.java - Class representing gtk+ clipboard selection. + Copyright (C) 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.Pointer; + +import java.awt.datatransfer.*; + +import java.io.*; +import java.net.*; +import java.util.*; + +import java.awt.Image; + +/** + * Class representing the gtk+ clipboard selection. This is used when + * another program owns the clipboard. Whenever the system clipboard + * selection changes we create a new instance to notify the program + * that the available flavors might have changed. When requested it + * (lazily) caches the targets, and (text, image, or files/uris) + * clipboard contents. + */ +public class GtkSelection implements Transferable +{ + /** + * Static lock used for requests of mimetypes and contents retrieval. + */ + static private Object requestLock = new Object(); + + /** + * Whether we belong to the Clipboard (true) or to the Primary selection. + */ + private final boolean clipboard; + + /** + * Whether a request for mimetypes, text, images, uris or byte[] is + * currently in progress. Should only be tested or set with + * requestLock held. When true no other requests should be made till + * it is false again. + */ + private boolean requestInProgress; + + /** + * Indicates a requestMimeTypes() call was made and the + * corresponding mimeTypesAvailable() callback was triggered. + */ + private boolean mimeTypesDelivered; + + /** + * Set and returned by getTransferDataFlavors. Only valid when + * mimeTypesDelivered is true. + */ + private DataFlavor[] dataFlavors; + + /** + * Indicates a requestText() call was made and the corresponding + * textAvailable() callback was triggered. + */ + private boolean textDelivered; + + /** + * Set as response to a requestText() call and possibly returned by + * getTransferData() for text targets. Only valid when textDelivered + * is true. + */ + private String text; + + /** + * Indicates a requestImage() call was made and the corresponding + * imageAvailable() callback was triggered. + */ + private boolean imageDelivered; + + /** + * Set as response to a requestImage() call and possibly returned by + * getTransferData() for image targets. Only valid when + * imageDelivered is true and image is null. + */ + private Pointer imagePointer; + + /** + * Cached image value. Only valid when imageDelivered is + * true. Created from imagePointer. + */ + private Image image; + + /** + * Indicates a requestUris() call was made and the corresponding + * urisAvailable() callback was triggered. + */ + private boolean urisDelivered; + + /** + * Set as response to a requestURIs() call. Only valid when + * urisDelivered is true + */ + private List<File> uris; + + /** + * Indicates a requestBytes(String) call was made and the + * corresponding bytesAvailable() callback was triggered. + */ + private boolean bytesDelivered; + + /** + * Set as response to a requestBytes(String) call. Only valid when + * bytesDelivered is true. + */ + private byte[] bytes; + + /** + * Should only be created by the GtkClipboard class. The clipboard + * should be either GtkClipboard.clipboard or + * GtkClipboard.selection. + */ + GtkSelection(GtkClipboard clipboard) + { + this.clipboard = (clipboard == GtkClipboard.clipboard); + } + + /** + * Gets an array of mime-type strings from the gtk+ clipboard and + * transforms them into an array of DataFlavors. + */ + public DataFlavor[] getTransferDataFlavors() + { + DataFlavor[] result; + synchronized (requestLock) + { + // Did we request already and cache the result? + if (mimeTypesDelivered) + result = (DataFlavor[]) dataFlavors.clone(); + else + { + // Wait till there are no pending requests. + while (requestInProgress) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + + // If nobody else beat us and cached the result we try + // ourselves to get it. + if (! mimeTypesDelivered) + { + requestInProgress = true; + requestMimeTypes(clipboard); + while (! mimeTypesDelivered) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + requestInProgress = false; + } + result = dataFlavors; + if (! GtkClipboard.canCache) + { + dataFlavors = null; + mimeTypesDelivered = false; + } + requestLock.notifyAll(); + } + } + return result; + } + + /** + * Callback that sets the available DataFlavors[]. Note that this + * should not call any code that could need the main gdk lock. + */ + private void mimeTypesAvailable(String[] mimeTypes) + { + synchronized (requestLock) + { + if (mimeTypes == null) + dataFlavors = new DataFlavor[0]; + else + { + // Most likely the mimeTypes include text in which case we add an + // extra element. + ArrayList<DataFlavor> flavorsList = + new ArrayList<DataFlavor>(mimeTypes.length + 1); + + for (int i = 0; i < mimeTypes.length; i++) + { + try + { + if (mimeTypes[i] == GtkClipboard.stringMimeType) + { + // XXX - Fix DataFlavor.getTextPlainUnicodeFlavor() + // and also add it to the list. + flavorsList.add(DataFlavor.stringFlavor); + flavorsList.add(DataFlavor.plainTextFlavor); + } + else if (mimeTypes[i] == GtkClipboard.imageMimeType) + flavorsList.add(DataFlavor.imageFlavor); + else if (mimeTypes[i] == GtkClipboard.filesMimeType) + flavorsList.add(DataFlavor.javaFileListFlavor); + else + { + // We check the target to prevent duplicates + // of the "magic" targets above. + DataFlavor target = new DataFlavor(mimeTypes[i]); + if (! flavorsList.contains(target)) + flavorsList.add(target); + } + } + catch (ClassNotFoundException cnfe) + { + cnfe.printStackTrace(); + } + catch (NullPointerException npe) + { + npe.printStackTrace(); + } + } + + dataFlavors = new DataFlavor[flavorsList.size()]; + flavorsList.toArray(dataFlavors); + } + + mimeTypesDelivered = true; + requestLock.notifyAll(); + } + } + + /** + * Gets the available data flavors for this selection and checks + * that at least one of them is equal to the given DataFlavor. + */ + public boolean isDataFlavorSupported(DataFlavor flavor) + { + DataFlavor[] dfs = getTransferDataFlavors(); + for (int i = 0; i < dfs.length; i++) + if (flavor.equals(dfs[i])) + return true; + + return false; + } + + /** + * Helper method that tests whether we already have the text for the + * current gtk+ selection on the clipboard and if not requests it + * and waits till it is available. + */ + private String getText() + { + String result; + synchronized (requestLock) + { + // Did we request already and cache the result? + if (textDelivered) + result = text; + else + { + // Wait till there are no pending requests. + while (requestInProgress) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + + // If nobody else beat us we try ourselves to get and + // caching the result. + if (! textDelivered) + { + requestInProgress = true; + requestText(clipboard); + while (! textDelivered) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + requestInProgress = false; + } + result = text; + if (! GtkClipboard.canCache) + { + text = null; + textDelivered = false; + } + requestLock.notifyAll(); + } + } + return result; + } + + /** + * Callback that sets the available text on the clipboard. Note that + * this should not call any code that could need the main gdk lock. + */ + private void textAvailable(String text) + { + synchronized (requestLock) + { + this.text = text; + textDelivered = true; + requestLock.notifyAll(); + } + } + + /** + * Helper method that tests whether we already have an image for the + * current gtk+ selection on the clipboard and if not requests it + * and waits till it is available. + */ + private Image getImage() + { + Image result; + synchronized (requestLock) + { + // Did we request already and cache the result? + if (imageDelivered) + result = image; + else + { + // Wait till there are no pending requests. + while (requestInProgress) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + + // If nobody else beat us we try ourselves to get and + // caching the result. + if (! imageDelivered) + { + requestInProgress = true; + requestImage(clipboard); + while (! imageDelivered) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + requestInProgress = false; + } + + if (imagePointer != null) + image = new GtkImage(imagePointer); + + imagePointer = null; + result = image; + if (! GtkClipboard.canCache) + { + image = null; + imageDelivered = false; + } + requestLock.notifyAll(); + } + } + return result; + } + + /** + * Callback that sets the available image on the clipboard. Note + * that this should not call any code that could need the main gdk + * lock. Note that we get a Pointer to a GdkPixbuf which we cannot + * turn into a real GtkImage at this point. That will be done on the + * "user thread" in getImage(). + */ + private void imageAvailable(Pointer pointer) + { + synchronized (requestLock) + { + this.imagePointer = pointer; + imageDelivered = true; + requestLock.notifyAll(); + } + } + + /** + * Helper method that test whether we already have a list of + * URIs/Files and if not requests them and waits till they are + * available. + */ + private List<File> getURIs() + { + List<File> result; + synchronized (requestLock) + { + // Did we request already and cache the result? + if (urisDelivered) + result = uris; + else + { + // Wait till there are no pending requests. + while (requestInProgress) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + + // If nobody else beat us we try ourselves to get and + // caching the result. + if (! urisDelivered) + { + requestInProgress = true; + requestURIs(clipboard); + while (! urisDelivered) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + requestInProgress = false; + } + result = uris; + if (! GtkClipboard.canCache) + { + uris = null; + urisDelivered = false; + } + requestLock.notifyAll(); + } + } + return result; + } + + /** + * Callback that sets the available File list. Note that this should + * not call any code that could need the main gdk lock. + */ + private void urisAvailable(String[] uris) + { + synchronized (requestLock) + { + if (uris != null && uris.length != 0) + { + ArrayList<File> list = new ArrayList<File>(uris.length); + for (int i = 0; i < uris.length; i++) + { + try + { + URI uri = new URI(uris[i]); + if (uri.getScheme().equals("file")) + list.add(new File(uri)); + } + catch (URISyntaxException use) + { + } + } + this.uris = list; + } + + urisDelivered = true; + requestLock.notifyAll(); + } + } + + /** + * Helper method that requests a byte[] for the given target + * mime-type flavor and waits till it is available. Note that unlike + * the other get methods this one doesn't cache the result since + * there are possibly many targets. + */ + private byte[] getBytes(String target) + { + byte[] result; + synchronized (requestLock) + { + // Wait till there are no pending requests. + while (requestInProgress) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + + // Request bytes and wait till they are available. + requestInProgress = true; + requestBytes(clipboard, target); + while (! bytesDelivered) + { + try + { + requestLock.wait(); + } + catch (InterruptedException ie) + { + // ignored + } + } + result = bytes; + bytes = null; + bytesDelivered = false; + requestInProgress = false; + + requestLock.notifyAll(); + } + return result; + } + + /** + * Callback that sets the available byte array on the + * clipboard. Note that this should not call any code that could + * need the main gdk lock. + */ + private void bytesAvailable(byte[] bytes) + { + synchronized (requestLock) + { + this.bytes = bytes; + bytesDelivered = true; + requestLock.notifyAll(); + } + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException + { + // Note the fall throughs for the "magic targets" if they fail we + // try one more time through getBytes(). + if (flavor.equals(DataFlavor.stringFlavor)) + { + String text = getText(); + if (text != null) + return text; + } + + if (flavor.equals(DataFlavor.plainTextFlavor)) + { + String text = getText(); + if (text != null) + return new StringBufferInputStream(text); + } + + if (flavor.equals(DataFlavor.imageFlavor)) + { + Image image = getImage(); + if (image != null) + return image; + } + + if (flavor.equals(DataFlavor.javaFileListFlavor)) + { + List<File> uris = getURIs(); + if (uris != null) + return uris; + } + + byte[] bytes = getBytes(flavor.getMimeType()); + if (bytes == null) + throw new UnsupportedFlavorException(flavor); + + if (flavor.isMimeTypeSerializedObject()) + { + try + { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais); + return ois.readObject(); + } + catch (IOException ioe) + { + ioe.printStackTrace(); + } + catch (ClassNotFoundException cnfe) + { + cnfe.printStackTrace(); + } + } + + if (flavor.isRepresentationClassInputStream()) + return new ByteArrayInputStream(bytes); + + // XXX, need some more conversions? + + throw new UnsupportedFlavorException(flavor); + } + + /* + * Requests text, Image or an byte[] for a particular target from the + * other application. These methods return immediately. When the + * content is available the contentLock will be notified through + * textAvailable, imageAvailable, urisAvailable or bytesAvailable and the + * appropriate field is set. + * The clipboard argument is true if we want the Clipboard, and false + * if we want the (primary) selection. + */ + private native void requestText(boolean clipboard); + private native void requestImage(boolean clipboard); + private native void requestURIs(boolean clipboard); + private native void requestBytes(boolean clipboard, String target); + + /* Similar to the above but for requesting the supported targets. */ + private native void requestMimeTypes(boolean clipboard); +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkTextAreaPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkTextAreaPeer.java new file mode 100644 index 000000000..0c7d3a860 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkTextAreaPeer.java @@ -0,0 +1,223 @@ +/* GtkTextAreaPeer.java -- Implements TextAreaPeer with GTK + Copyright (C) 1998, 1999, 2002 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.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Rectangle; +import java.awt.TextArea; +import java.awt.im.InputMethodRequests; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextComponentPeer; + +public class GtkTextAreaPeer extends GtkComponentPeer + implements TextComponentPeer, TextAreaPeer +{ + private static transient int DEFAULT_ROWS = 10; + private static transient int DEFAULT_COLS = 80; + + native void create (int width, int height, int scrollbarVisibility); + + /** + * Overridden to set Font for text widget inside scrolled window. + */ + protected native void gtkWidgetModifyFont(String name, int style, int size); + + native void gtkWidgetRequestFocus (); + + public native void connectSignals (); + + public native int getCaretPosition (); + public native void setCaretPosition (int pos); + public native int getSelectionStart (); + public native int getSelectionEnd (); + public native String getText (); + public native void select (int start, int end); + public native void setEditable (boolean state); + public native void setText (String text); + + public int getIndexAtPoint(int x, int y) + { + // FIXME + return 0; + } + + public Rectangle getCharacterBounds (int pos) + { + // FIXME + return null; + } + + public long filterEvents (long filter) + { + // FIXME + return filter; + } + + void create () + { + Font f = awtComponent.getFont (); + + // By default, Sun sets a TextArea's font when its peer is + // created. If f != null then the peer's font is set by + // GtkComponent.create. + if (f == null) + { + f = new Font ("Dialog", Font.PLAIN, 12); + awtComponent.setFont (f); + } + + FontMetrics fm = getFontMetrics (f); + + TextArea ta = ((TextArea) awtComponent); + int sizeRows = ta.getRows (); + int sizeCols = ta.getColumns (); + + sizeRows = sizeRows == 0 ? DEFAULT_ROWS : sizeRows; + sizeCols = sizeCols == 0 ? DEFAULT_COLS : sizeCols; + + int width = sizeCols * fm.getMaxAdvance (); + int height = sizeRows * (fm.getMaxDescent () + fm.getMaxAscent ()); + + create (width, height, ta.getScrollbarVisibility ()); + setEditable (ta.isEditable ()); + } + + public GtkTextAreaPeer (TextArea ta) + { + super (ta); + + setText (ta.getText ()); + setCaretPosition (0); + } + + public native void insert (String str, int pos); + public native void replaceRange (String str, int start, int end); + + public Dimension getMinimumSize (int rows, int cols) + { + return minimumSize (rows == 0 ? DEFAULT_ROWS : rows, + cols == 0 ? DEFAULT_COLS : cols); + } + + public Dimension getPreferredSize (int rows, int cols) + { + return preferredSize (rows == 0 ? DEFAULT_ROWS : rows, + cols == 0 ? DEFAULT_COLS : cols); + } + + native int getHScrollbarHeight (); + native int getVScrollbarWidth (); + + // Deprecated + public Dimension minimumSize (int rows, int cols) + { + TextArea ta = ((TextArea) awtComponent); + int height = 0; + int width = 0; + + if (ta.getScrollbarVisibility () == TextArea.SCROLLBARS_BOTH + || ta.getScrollbarVisibility () == TextArea.SCROLLBARS_HORIZONTAL_ONLY) + height = getHScrollbarHeight (); + + if (ta.getScrollbarVisibility () == TextArea.SCROLLBARS_BOTH + || ta.getScrollbarVisibility () == TextArea.SCROLLBARS_VERTICAL_ONLY) + width = getVScrollbarWidth (); + + Font f = awtComponent.getFont (); + if (f == null) + return new Dimension (width, height); + + FontMetrics fm = getFontMetrics (f); + + int sizeRows = rows == 0 ? DEFAULT_ROWS : rows; + int sizeCols = cols == 0 ? DEFAULT_COLS : cols; + + width += sizeCols * fm.getMaxAdvance (); + height += sizeRows * (fm.getMaxDescent () + fm.getMaxAscent ()); + + return new Dimension (width, height); + } + + public Dimension preferredSize (int rows, int cols) + { + TextArea ta = ((TextArea) awtComponent); + int height = 0; + int width = 0; + + if (ta.getScrollbarVisibility () == TextArea.SCROLLBARS_BOTH + || ta.getScrollbarVisibility () == TextArea.SCROLLBARS_HORIZONTAL_ONLY) + height = getHScrollbarHeight (); + + if (ta.getScrollbarVisibility () == TextArea.SCROLLBARS_BOTH + || ta.getScrollbarVisibility () == TextArea.SCROLLBARS_VERTICAL_ONLY) + width = getVScrollbarWidth (); + + Font f = awtComponent.getFont (); + if (f == null) + return new Dimension (width, height); + + FontMetrics fm = getFontMetrics (f); + + int sizeRows = rows == 0 ? DEFAULT_ROWS : rows; + int sizeCols = cols == 0 ? DEFAULT_COLS : cols; + + width += sizeCols * fm.getMaxAdvance (); + height += sizeRows * (fm.getMaxDescent () + fm.getMaxAscent ()); + + return new Dimension (width, height); + } + + public void replaceText (String str, int start, int end) + { + replaceRange (str, start, end); + } + + public void insertText (String str, int pos) + { + insert (str, pos); + } + + public InputMethodRequests getInputMethodRequests() + { + // FIXME: implement + return null; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkTextFieldPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkTextFieldPeer.java new file mode 100644 index 000000000..9e62c8e79 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkTextFieldPeer.java @@ -0,0 +1,200 @@ +/* GtkTextFieldPeer.java -- Implements TextFieldPeer with GTK + Copyright (C) 1998, 1999, 2002 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.AWTEvent; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Rectangle; +import java.awt.TextField; +import java.awt.event.KeyEvent; +import java.awt.im.InputMethodRequests; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.TextComponentPeer; + +public class GtkTextFieldPeer extends GtkComponentPeer + implements TextComponentPeer, TextFieldPeer +{ + native void create (int width); + native void gtkWidgetSetBackground (int red, int green, int blue); + native void gtkWidgetSetForeground (int red, int green, int blue); + + public native void connectSignals (); + + public native int getCaretPosition (); + public native void setCaretPosition (int pos); + public native int getSelectionStart (); + public native int getSelectionEnd (); + public native String getText (); + public native void select (int start, int end); + public native void setEditable (boolean state); + public native void setText (String text); + + public int getIndexAtPoint(int x, int y) + { + // FIXME + return 0; + } + + public Rectangle getCharacterBounds (int pos) + { + // FIXME + return null; + } + + public long filterEvents (long filter) + { + // FIXME + return filter; + } + + void create () + { + Font f = awtComponent.getFont (); + + // By default, Sun sets a TextField's font when its peer is + // created. If f != null then the peer's font is set by + // GtkComponent.create. + if (f == null) + { + f = new Font ("Dialog", Font.PLAIN, 12); + awtComponent.setFont (f); + } + + FontMetrics fm = getFontMetrics (f); + + TextField tf = ((TextField) awtComponent); + int cols = tf.getColumns (); + + int text_width = cols * fm.getMaxAdvance (); + + create (text_width); + + setEditable (tf.isEditable ()); + } + + native int gtkEntryGetBorderWidth (); + + public GtkTextFieldPeer (TextField tf) + { + super (tf); + + setText (tf.getText ()); + setCaretPosition (0); + + if (tf.echoCharIsSet ()) + setEchoChar (tf.getEchoChar ()); + } + + public Dimension getMinimumSize (int cols) + { + return minimumSize (cols); + } + + public Dimension getPreferredSize (int cols) + { + return preferredSize (cols); + } + + public native void setEchoChar (char c); + + // Deprecated + public Dimension minimumSize (int cols) + { + int dim[] = new int[2]; + + gtkWidgetGetPreferredDimensions (dim); + + Font f = awtComponent.getFont (); + if (f == null) + return new Dimension (2 * gtkEntryGetBorderWidth (), dim[1]); + + FontMetrics fm = getFontMetrics (f); + + int text_width = cols * fm.getMaxAdvance (); + + int width = text_width + 2 * gtkEntryGetBorderWidth (); + + return new Dimension (width, dim[1]); + } + + public Dimension preferredSize (int cols) + { + int dim[] = new int[2]; + + gtkWidgetGetPreferredDimensions (dim); + + Font f = awtComponent.getFont (); + if (f == null) + return new Dimension (2 * gtkEntryGetBorderWidth (), dim[1]); + + FontMetrics fm = getFontMetrics (f); + + int text_width = cols * fm.getMaxAdvance (); + + int width = text_width + 2 * gtkEntryGetBorderWidth (); + + return new Dimension (width, dim[1]); + } + + public void setEchoCharacter (char c) + { + setEchoChar (c); + } + + public void handleEvent (AWTEvent e) + { + if (e.getID () == KeyEvent.KEY_PRESSED) + { + KeyEvent ke = (KeyEvent) e; + + if (!ke.isConsumed () + && ke.getKeyCode () == KeyEvent.VK_ENTER) + postActionEvent (getText (), ke.getModifiersEx ()); + } + + super.handleEvent (e); + } + public InputMethodRequests getInputMethodRequests() + { + // FIXME: implement + return null; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java new file mode 100644 index 000000000..150656511 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java @@ -0,0 +1,766 @@ +/* GtkToolkit.java -- Implements an AWT Toolkit using GTK for peers + Copyright (C) 1998, 1999, 2002, 2003, 2004, 2005, 2006, 2007 + 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 gnu.java.awt.AWTUtilities; +import gnu.java.awt.EmbeddedWindow; +import gnu.java.awt.dnd.GtkMouseDragGestureRecognizer; +import gnu.java.awt.dnd.peer.gtk.GtkDragSourceContextPeer; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.peer.EmbeddedWindowPeer; + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.PrintJob; +import java.awt.Rectangle; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.font.TextAttribute; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.CheckboxMenuItemPeer; +import java.awt.peer.CheckboxPeer; +import java.awt.peer.ChoicePeer; +import java.awt.peer.DialogPeer; +import java.awt.peer.FileDialogPeer; +import java.awt.peer.FontPeer; +import java.awt.peer.FramePeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.ListPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.MouseInfoPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.PopupMenuPeer; +import java.awt.peer.RobotPeer; +import java.awt.peer.ScrollPanePeer; +import java.awt.peer.ScrollbarPeer; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.WindowPeer; +import java.io.InputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +import javax.imageio.spi.IIORegistry; + +/* This class uses a deprecated method java.awt.peer.ComponentPeer.getPeer(). + This merits comment. We are basically calling Sun's bluff on this one. + We think Sun has deprecated it simply to discourage its use as it is + bad programming style. However, we need to get at a component's peer in + 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. */ + +public class GtkToolkit extends gnu.java.awt.ClasspathToolkit +{ + static final Object GTK_LOCK; + + private static EventQueue q; + + static native void gtkInit(int portableNativeSync, Object lock); + + static native void gtkMain(); + + static native void gtkQuit(); + + /** + * Initializes field IDs that are used by native code. + */ + private static native void initIDs(); + + /** + * True when the field IDs are already initialized, false otherwise. + */ + private static boolean initializedGlobalIDs = false; + + /** + * Initializes some global fieldIDs for use in the native code. This is + * called by a couple of classes in the GTK peers to ensure that + * some necessary stuff is loaded. + */ + static synchronized void initializeGlobalIDs() + { + if (! initializedGlobalIDs) + { + initIDs(); + initializedGlobalIDs = true; + } + } + + static + { + if (true) // GCJ LOCAL + { + System.loadLibrary("gtkpeer"); + } + + /** + * Gotta do that first. + */ + initializeGlobalIDs(); + + int portableNativeSync; + String portNatSyncProp = + System.getProperty("gnu.classpath.awt.gtk.portable.native.sync"); + + if (portNatSyncProp == null) + portableNativeSync = -1; // unset + else if (Boolean.valueOf(portNatSyncProp).booleanValue()) + portableNativeSync = 1; // true + else + portableNativeSync = 0; // false + + GTK_LOCK = new String("GTK LOCK"); + gtkInit(portableNativeSync, GTK_LOCK); + } + + public GtkToolkit () + { + } + + public native void beep(); + + private native void getScreenSizeDimensions(int[] xy); + + public int checkImage (Image image, int width, int height, + ImageObserver observer) + { + int status = ImageObserver.ALLBITS + | ImageObserver.WIDTH + | ImageObserver.HEIGHT; + + if (image instanceof GtkImage) + return ((GtkImage) image).checkImage (observer); + + if (image instanceof AsyncImage) + return ((AsyncImage) image).checkImage(observer); + + if (observer != null) + observer.imageUpdate (image, status, + -1, -1, + image.getWidth (observer), + image.getHeight (observer)); + + return status; + } + + /** + * Helper to return either a Image -- the argument -- or a + * GtkImage with the errorLoading flag set if the argument is null. + */ + static Image imageOrError(Image b) + { + if (b == null) + return GtkImage.getErrorImage(); + else + return b; + } + + public Image createImage (String filename) + { + if (filename.length() == 0) + return new GtkImage (); + + Image image; + try + { + image = CairoSurface.getBufferedImage( new GtkImage( filename ) ); + } + catch (IllegalArgumentException iae) + { + image = null; + } + return imageOrError(image); + } + + public Image createImage (URL url) + { + return new AsyncImage(url); + } + + public Image createImage (ImageProducer producer) + { + if (producer == null) + return null; + + Image image; + try + { + image = CairoSurface.getBufferedImage( new GtkImage( producer ) ); + } + catch (IllegalArgumentException iae) + { + image = null; + } + return imageOrError(image); + } + + public Image createImage (byte[] imagedata, int imageoffset, + int imagelength) + { + Image image; + try + { + byte[] data = new byte[ imagelength ]; + System.arraycopy(imagedata, imageoffset, data, 0, imagelength); + image = CairoSurface.getBufferedImage( new GtkImage( data ) ); + } + catch (IllegalArgumentException iae) + { + image = null; + } + return imageOrError(image); + } + + /** + * Creates an ImageProducer from the specified URL. The image is assumed + * to be in a recognised format. + * + * @param url URL to read image data from. + */ + public ImageProducer createImageProducer(URL url) + { + return createImage( url ).getSource(); + } + + /** + * Returns the native color model (which isn't the same as the default + * ARGB color model, but doesn't have to be). + */ + public ColorModel getColorModel () + { + /* Return the GDK-native ABGR format */ + return new DirectColorModel(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + } + + public String[] getFontList () + { + return (new String[] { "Dialog", + "DialogInput", + "Monospaced", + "Serif", + "SansSerif" }); + } + + static class LRUCache<K,V> extends LinkedHashMap<K,V> + { + int max_entries; + public LRUCache(int max) + { + super(max, 0.75f, true); + max_entries = max; + } + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > max_entries; + } + } + + private LRUCache<Map,ClasspathFontPeer> fontCache = + new LRUCache<Map,ClasspathFontPeer>(50); + private LRUCache<Object,Image> imageCache = new LRUCache<Object,Image>(50); + + public FontMetrics getFontMetrics (Font font) + { + return ((GdkFontPeer) font.getPeer()).getFontMetrics(font); + } + + public Image getImage (String filename) + { + if (imageCache.containsKey(filename)) + return imageCache.get(filename); + else + { + Image im = createImage(filename); + imageCache.put(filename, im); + return im; + } + } + + public Image getImage (URL url) + { + if (imageCache.containsKey(url)) + return imageCache.get(url); + else + { + Image im = createImage(url); + imageCache.put(url, im); + return im; + } + } + + public PrintJob getPrintJob (Frame frame, String jobtitle, Properties props) + { + SecurityManager sm; + sm = System.getSecurityManager(); + if (sm != null) + sm.checkPrintJobAccess(); + + return null; + } + + public native int getScreenResolution(); + + public Dimension getScreenSize () + { + int dim[] = new int[2]; + getScreenSizeDimensions(dim); + return new Dimension(dim[0], dim[1]); + } + + public Clipboard getSystemClipboard() + { + SecurityManager secman = System.getSecurityManager(); + if (secman != null) + secman.checkSystemClipboardAccess(); + + return GtkClipboard.getClipboardInstance(); + } + + public Clipboard getSystemSelection() + { + SecurityManager secman = System.getSecurityManager(); + if (secman != null) + secman.checkSystemClipboardAccess(); + + return GtkClipboard.getSelectionInstance(); + } + + /** + * Prepares a GtkImage. For every other kind of Image it just + * assumes the image is already prepared for rendering. + */ + public boolean prepareImage (Image image, int width, int height, + ImageObserver observer) + { + /* GtkImages are always prepared, as long as they're loaded. */ + if (image instanceof GtkImage) + return ((((GtkImage)image).checkImage (observer) + & ImageObserver.ALLBITS) != 0); + + if (image instanceof AsyncImage) + { + AsyncImage aImg = (AsyncImage) image; + aImg.addObserver(observer); + return aImg.realImage != null; + } + + /* Assume anything else is too */ + return true; + } + + public native void sync(); + + protected void setComponentState (Component c, GtkComponentPeer cp) + { + /* Make the Component reflect Peer defaults */ + if (c.getForeground () == null) + c.setForeground (cp.getForeground ()); + if (c.getBackground () == null) + c.setBackground (cp.getBackground ()); + // if (c.getFont () == null) + // c.setFont (cp.getFont ()); + + /* Make the Peer reflect the state of the Component */ + if (! (c instanceof Window)) + { + cp.setCursor (c.getCursor ()); + + Rectangle bounds = c.getBounds (); + cp.setBounds (bounds.x, bounds.y, bounds.width, bounds.height); + cp.setVisible (c.isVisible ()); + } + } + + protected ButtonPeer createButton (Button b) + { + checkHeadless(); + return new GtkButtonPeer (b); + } + + protected CanvasPeer createCanvas (Canvas c) + { + checkHeadless(); + return new GtkCanvasPeer (c); + } + + protected CheckboxPeer createCheckbox (Checkbox cb) + { + checkHeadless(); + return new GtkCheckboxPeer (cb); + } + + protected CheckboxMenuItemPeer createCheckboxMenuItem (CheckboxMenuItem cmi) + { + checkHeadless(); + return new GtkCheckboxMenuItemPeer (cmi); + } + + protected ChoicePeer createChoice (Choice c) + { + checkHeadless(); + return new GtkChoicePeer (c); + } + + protected DialogPeer createDialog (Dialog d) + { + checkHeadless(); + GtkMainThread.createWindow(); + return new GtkDialogPeer (d); + } + + protected FileDialogPeer createFileDialog (FileDialog fd) + { + checkHeadless(); + return new GtkFileDialogPeer (fd); + } + + protected FramePeer createFrame (Frame f) + { + checkHeadless(); + GtkMainThread.createWindow(); + return new GtkFramePeer (f); + } + + protected LabelPeer createLabel (Label label) + { + checkHeadless(); + return new GtkLabelPeer (label); + } + + protected ListPeer createList (List list) + { + checkHeadless(); + return new GtkListPeer (list); + } + + protected MenuPeer createMenu (Menu m) + { + checkHeadless(); + return new GtkMenuPeer (m); + } + + protected MenuBarPeer createMenuBar (MenuBar mb) + { + checkHeadless(); + return new GtkMenuBarPeer (mb); + } + + protected MenuItemPeer createMenuItem (MenuItem mi) + { + checkHeadless(); + return new GtkMenuItemPeer (mi); + } + + protected PanelPeer createPanel (Panel p) + { + checkHeadless(); + return new GtkPanelPeer (p); + } + + protected PopupMenuPeer createPopupMenu (PopupMenu target) + { + checkHeadless(); + return new GtkPopupMenuPeer (target); + } + + protected ScrollPanePeer createScrollPane (ScrollPane sp) + { + checkHeadless(); + return new GtkScrollPanePeer (sp); + } + + protected ScrollbarPeer createScrollbar (Scrollbar sb) + { + checkHeadless(); + return new GtkScrollbarPeer (sb); + } + + protected TextAreaPeer createTextArea (TextArea ta) + { + checkHeadless(); + return new GtkTextAreaPeer (ta); + } + + protected TextFieldPeer createTextField (TextField tf) + { + checkHeadless(); + return new GtkTextFieldPeer (tf); + } + + protected WindowPeer createWindow (Window w) + { + checkHeadless(); + GtkMainThread.createWindow(); + return new GtkWindowPeer (w); + } + + public EmbeddedWindowPeer createEmbeddedWindow (EmbeddedWindow w) + { + checkHeadless(); + GtkMainThread.createWindow(); + return new GtkEmbeddedWindowPeer (w); + } + + /** + * @deprecated part of the older "logical font" system in earlier AWT + * implementations. Our newer Font class uses getClasspathFontPeer. + */ + protected FontPeer getFontPeer (String name, int style) { + // All fonts get a default size of 12 if size is not specified. + return getFontPeer(name, style, 12); + } + + /** + * Private method that allows size to be set at initialization time. + */ + private FontPeer getFontPeer (String name, int style, int size) + { + Map<TextAttribute,Object> attrs = new HashMap<TextAttribute,Object>(); + ClasspathFontPeer.copyStyleToAttrs (style, attrs); + ClasspathFontPeer.copySizeToAttrs (size, attrs); + return getClasspathFontPeer (name, attrs); + } + + /** + * Newer method to produce a peer for a Font object, even though Sun's + * design claims Font should now be peerless, we do not agree with this + * model, hence "ClasspathFontPeer". + */ + + public ClasspathFontPeer getClasspathFontPeer (String name, + Map<?,?> attrs) + { + Map<Object,Object> keyMap = new HashMap<Object,Object>(attrs); + // We don't know what kind of "name" the user requested (logical, face, + // family), and we don't actually *need* to know here. The worst case + // involves failure to consolidate fonts with the same backend in our + // cache. This is harmless. + keyMap.put ("GtkToolkit.RequestedFontName", name); + if (fontCache.containsKey (keyMap)) + return fontCache.get (keyMap); + else + { + ClasspathFontPeer newPeer = new GdkFontPeer (name, attrs); + fontCache.put (keyMap, newPeer); + return newPeer; + } + } + + protected EventQueue getSystemEventQueueImpl() + { + synchronized (GtkToolkit.class) + { + if (q == null) + { + q = new EventQueue(); + } + } + return q; + } + + public Cursor createCustomCursor(Image image, Point hotspot, String name) + { + return new GtkCursor(image, hotspot, name); + } + + protected native void loadSystemColors (int[] systemColors); + + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e) + { + if (GraphicsEnvironment.isHeadless()) + throw new InvalidDnDOperationException(); + return new GtkDragSourceContextPeer(e); + } + + public <T extends DragGestureRecognizer> T + createDragGestureRecognizer(Class<T> recognizer, DragSource ds, + Component comp, int actions, + DragGestureListener l) + { + if (recognizer.getName().equals("java.awt.dnd.MouseDragGestureRecognizer") + && ! GraphicsEnvironment.isHeadless()) + { + GtkMouseDragGestureRecognizer gestureRecognizer + = new GtkMouseDragGestureRecognizer(ds, comp, actions, l); + gestureRecognizer.registerListeners(); + return recognizer.cast(gestureRecognizer); + } + else + { + return null; + } + } + + public Map<TextAttribute,?> mapInputMethodHighlight(InputMethodHighlight highlight) + { + throw new Error("not implemented"); + } + + public Rectangle getBounds() + { + int[] dims = new int[2]; + getScreenSizeDimensions(dims); + return new Rectangle(0, 0, dims[0], dims[1]); + } + + // ClasspathToolkit methods + + public GraphicsEnvironment getLocalGraphicsEnvironment() + { + return new GdkGraphicsEnvironment(); + } + + public Font createFont(int format, InputStream stream) + { + throw new UnsupportedOperationException(); + } + + public RobotPeer createRobot (GraphicsDevice screen) throws AWTException + { + return new GdkRobotPeer (screen); + } + + public boolean getLockingKeyState(int keyCode) + { + int state = getLockState(keyCode); + + if (state != -1) + return state == 1; + + if (AWTUtilities.isValidKey(keyCode)) + throw new UnsupportedOperationException + ("cannot get locking state of key code " + keyCode); + + throw new IllegalArgumentException("invalid key code " + keyCode); + } + + protected native int getLockState(int keyCode); + + public void registerImageIOSpis(IIORegistry reg) + { + GdkPixbufDecoder.registerSpis(reg); + } + + protected MouseInfoPeer getMouseInfoPeer() + { + return new GtkMouseInfoPeer(); + } + + public boolean isFrameStateSupported(int state) + { + // GTK supports ICONFIED, NORMAL and MAXIMIZE_BOTH, but + // not (yet?) MAXIMIZE_VERT and MAXIMIZE_HORIZ. + return state == Frame.NORMAL || state == Frame.ICONIFIED + || state == Frame.MAXIMIZED_BOTH; + } + + private void checkHeadless() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + + public native int getMouseNumberOfButtons(); + + @Override + public boolean isModalExclusionTypeSupported + (Dialog.ModalExclusionType modalExclusionType) + { + return false; + } + + @Override + public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) + { + return false; + } + +} // class GtkToolkit diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java new file mode 100644 index 000000000..663839f23 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkVolatileImage.java @@ -0,0 +1,207 @@ +/* GtkVolatileImage.java -- wraps an X pixmap + Copyright (C) 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.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.ImageCapabilities; +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.VolatileImage; +import java.awt.image.WritableRaster; + +public class GtkVolatileImage extends VolatileImage +{ + int width, height; + private ImageCapabilities caps; + + final GtkComponentPeer component; + + static ColorModel gdkColorModel = new DirectColorModel(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + + /** + * Don't touch, accessed from native code. + */ + long nativePointer; + + native long init(GtkComponentPeer component, int width, int height); + + native void destroy(long pointer); + + native int[] nativeGetPixels(long pointer); + + /** + * Gets the pixels in the current image from GDK. + * + * Note that pixels are in 32-bit RGBA, non-premultiplied, which is different + * from Cairo's premultiplied ARGB, which is different from Java's standard + * non-premultiplied ARGB. Caution is advised when using this method, to + * ensure that the data format remains consistent with what you expect. + * + * @return the current pixels, as reported by GDK. + */ + public int[] getPixels() + { + return nativeGetPixels(nativePointer); + } + + native void nativeCopyArea(long pointer, int x, int y, int w, int h, int dx, + int dy ); + public void copyArea(int x, int y, int w, int h, int dx, int dy) + { + nativeCopyArea(nativePointer, x, y, w, h, dx, dy); + } + + native void nativeDrawVolatile(long pointer, long srcPtr, int x, int y, + int w, int h ); + public void drawVolatile(long srcPtr, int x, int y, int w, int h ) + { + nativeDrawVolatile(nativePointer, srcPtr, x, y, w, h); + } + + public GtkVolatileImage(GtkComponentPeer component, + int width, int height, ImageCapabilities caps) + { + this.width = width; + this.height = height; + this.caps = caps; + this.component = component; + nativePointer = init( component, width, height ); + } + + public GtkVolatileImage(int width, int height, ImageCapabilities caps) + { + this(null, width, height, caps); + } + + public GtkVolatileImage(int width, int height) + { + this(null, width, height, null); + } + + public void finalize() + { + dispose(); + } + + public void dispose() + { + destroy(nativePointer); + } + + public BufferedImage getSnapshot() + { + WritableRaster raster = Raster.createWritableRaster(createGdkSampleModel(width, height), + new Point(0, 0)); + raster.setDataElements(0, 0, getPixels()); + return new BufferedImage(gdkColorModel, raster, + gdkColorModel.isAlphaPremultiplied(), null); + } + + public Graphics getGraphics() + { + return createGraphics(); + } + + public Graphics2D createGraphics() + { + return new VolatileImageGraphics( this ); + } + + public int validate(GraphicsConfiguration gc) + { + return VolatileImage.IMAGE_OK; + } + + public boolean contentsLost() + { + return false; + } + + public ImageCapabilities getCapabilities() + { + return caps; + } + + public int getWidth() + { + return width; + } + + public int getHeight() + { + return height; + } + + public int getWidth(java.awt.image.ImageObserver observer) + { + return width; + } + + public int getHeight(java.awt.image.ImageObserver observer) + { + return height; + } + + public Object getProperty(String name, ImageObserver observer) + { + return null; + } + + /** + * Creates a SampleModel that matches GDK's native format + */ + protected static SampleModel createGdkSampleModel(int w, int h) + { + return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, + new int[]{0x000000FF, 0x0000FF00, + 0x00FF0000, 0xFF000000}); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java new file mode 100644 index 000000000..c8e1bceb7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java @@ -0,0 +1,437 @@ +/* GtkWindowPeer.java -- Implements WindowPeer with GTK + Copyright (C) 1998, 1999, 2002, 2005, 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 gnu.java.awt.ComponentReshapeEvent; + +import java.awt.Component; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.ComponentEvent; +import java.awt.event.FocusEvent; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; +import java.awt.peer.WindowPeer; + +public class GtkWindowPeer extends GtkContainerPeer + implements WindowPeer +{ + protected static final int GDK_WINDOW_TYPE_HINT_NORMAL = 0; + protected static final int GDK_WINDOW_TYPE_HINT_DIALOG = 1; + protected static final int GDK_WINDOW_TYPE_HINT_MENU = 2; + protected static final int GDK_WINDOW_TYPE_HINT_TOOLBAR = 3; + protected static final int GDK_WINDOW_TYPE_HINT_SPLASHSCREEN = 4; + protected static final int GDK_WINDOW_TYPE_HINT_UTILITY = 5; + protected static final int GDK_WINDOW_TYPE_HINT_DOCK = 6; + protected static final int GDK_WINDOW_TYPE_HINT_DESKTOP = 7; + + protected int windowState = Frame.NORMAL; + + // Cached awt window component location, width and height. + private int x, y, width, height; + + native void gtkWindowSetTitle (String title); + native void gtkWindowSetResizable (boolean resizable); + native void gtkWindowSetModal (boolean modal); + native void gtkWindowSetAlwaysOnTop ( boolean alwaysOnTop ); + native boolean gtkWindowHasFocus(); + native void realize (); + + public void dispose() + { + super.dispose(); + GtkMainThread.destroyWindow(); + } + + /** Returns the cached width of the AWT window component. */ + int getX () + { + return x; + } + + /** Returns the cached width of the AWT window component. */ + int getY () + { + return y; + } + + /** Returns the cached width of the AWT window component. */ + int getWidth () + { + return width; + } + + /** Returns the cached height of the AWT window component. */ + int getHeight () + { + return height; + } + + native void create (int type, boolean decorated, GtkWindowPeer parent); + + void create (int type, boolean decorated) + { + Window window = (Window) awtComponent; + GtkWindowPeer parent_peer = null; + Component parent = awtComponent.getParent(); + x = awtComponent.getX(); + y = awtComponent.getY(); + height = awtComponent.getHeight(); + width = awtComponent.getWidth(); + + if (!window.isFocusableWindow()) + type = GDK_WINDOW_TYPE_HINT_MENU; + + if (parent != null) + parent_peer = (GtkWindowPeer) awtComponent.getParent().getPeer(); + + create (type, decorated, parent_peer); + } + + void create () + { + // Create a normal undecorated window. + create (GDK_WINDOW_TYPE_HINT_NORMAL, false); + } + + void setParent () + { + setVisible (awtComponent.isVisible ()); + setEnabled (awtComponent.isEnabled ()); + } + + void setVisibleAndEnabled () + { + } + + public native void setVisibleNative (boolean b); + public native void setVisibleNativeUnlocked (boolean b); + + native void connectSignals (); + + public GtkWindowPeer (Window window) + { + super (window); + // Set reasonable font for the window. + window.setFont(new Font("Dialog", Font.PLAIN, 12)); + } + + public native void toBack(); + public native void toFront(); + + native void nativeSetBounds (int x, int y, int width, int height); + native void nativeSetBoundsUnlocked (int x, int y, int width, int height); + native void nativeSetLocation (int x, int y); + native void nativeSetLocationUnlocked (int x, int y); + + // Called from show. + protected void setLocation (int x, int y) + { + nativeSetLocation (x, y); + } + + public void setBounds (int x, int y, int width, int height) + { + if (x != getX() || y != getY() || width != getWidth() + || height != getHeight()) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + nativeSetBounds (x, y, + width - insets.left - insets.right, + height - insets.top - insets.bottom); + } + } + + public void setTitle (String title) + { + gtkWindowSetTitle (title); + } + + // Called from setResizable + protected native void setSize (int width, int height); + + /** + * Needed by both GtkFramePeer and GtkDialogPeer subclasses, so + * implemented here. But never actually called on a GtkWindowPeer + * itself. + */ + public void setResizable (boolean resizable) + { + // Call setSize; otherwise when resizable is changed from true to + // false the window will shrink to the dimensions it had before it + // was resizable. + x = awtComponent.getX(); + y = awtComponent.getY(); + width = awtComponent.getWidth(); + height = awtComponent.getHeight(); + setSize (width - insets.left - insets.right, + height - insets.top - insets.bottom); + gtkWindowSetResizable (resizable); + } + + protected void postInsetsChangedEvent (int top, int left, + int bottom, int right) + { + insets.top = top; + insets.left = left; + insets.bottom = bottom; + insets.right = right; + } + + // called back by native side: window_configure_cb + // only called from GTK thread + protected void postConfigureEvent (int x, int y, int width, int height) + { + int frame_x = x - insets.left; + int frame_y = y - insets.top; + int frame_width = width + insets.left + insets.right; + int frame_height = height + insets.top + insets.bottom; + + // Update the component's knowledge about the size. + // Important: Please look at the big comment in ComponentReshapeEvent + // to learn why we did it this way. If you change this code, make + // sure that the peer->AWT bounds update still works. + // (for instance: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29448 ) + + // We do this befor we post the ComponentEvent, because (in Window) + // we invalidate() / revalidate() when a ComponentEvent is seen, + // and the AWT must already know about the new size then. + if (frame_x != this.x || frame_y != this.y || frame_width != this.width + || frame_height != this.height) + { + ComponentReshapeEvent ev = new ComponentReshapeEvent(awtComponent, + frame_x, + frame_y, + frame_width, + frame_height); + awtComponent.dispatchEvent(ev); + } + + if (frame_width != getWidth() || frame_height != getHeight()) + { + this.width = frame_width; + this.height = frame_height; + q().postEvent(new ComponentEvent(awtComponent, + ComponentEvent.COMPONENT_RESIZED)); + } + + if (frame_x != getX() || frame_y != getY()) + { + this.x = frame_x; + this.y = frame_y; + q().postEvent(new ComponentEvent(awtComponent, + ComponentEvent.COMPONENT_MOVED)); + } + + } + + public void show () + { + x = awtComponent.getX(); + y = awtComponent.getY(); + width = awtComponent.getWidth(); + height = awtComponent.getHeight(); + setLocation(x, y); + setVisible (true); + } + + void postWindowEvent (int id, Window opposite, int newState) + { + if (id == WindowEvent.WINDOW_STATE_CHANGED) + { + if (windowState != newState) + { + // Post old styleWindowEvent with WINDOW_ICONIFIED or + // WINDOW_DEICONIFIED if appropriate. + if ((windowState & Frame.ICONIFIED) != 0 + && (newState & Frame.ICONIFIED) == 0) + q().postEvent(new WindowEvent((Window) awtComponent, + WindowEvent.WINDOW_DEICONIFIED, + opposite, 0, 0)); + else if ((windowState & Frame.ICONIFIED) == 0 + && (newState & Frame.ICONIFIED) != 0) + q().postEvent(new WindowEvent((Window) awtComponent, + WindowEvent.WINDOW_ICONIFIED, + opposite, 0, 0)); + // Post new-style WindowStateEvent. + q().postEvent (new WindowEvent ((Window) awtComponent, id, + opposite, windowState, newState)); + windowState = newState; + } + } + else + q().postEvent (new WindowEvent ((Window) awtComponent, id, opposite)); + } + + /** + * Update the always-on-top status of the native window. + */ + public void updateAlwaysOnTop() + { + gtkWindowSetAlwaysOnTop( ((Window)awtComponent).isAlwaysOnTop() ); + } + + protected void postExposeEvent (int x, int y, int width, int height) + { + // Translate GTK co-ordinates, which do not include a window + // frame's insets, to AWT co-ordinates, which do include a window + // frame's insets. GtkWindowPeer should always have all-zero + // insets but GtkFramePeer and GtkDialogPeer insets will be + // non-zero. + q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT, + new Rectangle (x + insets.left, + y + insets.top, + width, height))); + } + + public boolean requestWindowFocus() + { + // TODO Auto-generated method stub + return false; + } + + public boolean requestFocus (Component request, boolean temporary, + boolean allowWindowFocus, long time) + { + assert request == awtComponent || isLightweightDescendant(request); + boolean retval = false; + if (gtkWindowHasFocus()) + { + KeyboardFocusManager kfm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Component currentFocus = kfm.getFocusOwner(); + if (currentFocus == request) + // Nothing to do in this trivial case. + retval = true; + else + { + // Requested component is a lightweight descendant of this one + // or the actual heavyweight. + // Since this (native) component is already focused, we simply + // change the actual focus and be done. + postFocusEvent(FocusEvent.FOCUS_GAINED, temporary); + retval = true; + } + } + else + { + if (allowWindowFocus) + { + retval = requestWindowFocus(); + } + } + return retval; + } + + public Graphics getGraphics () + { + Graphics g = super.getGraphics (); + // Translate AWT co-ordinates, which include a window frame's + // insets, to GTK co-ordinates, which do not include a window + // frame's insets. GtkWindowPeer should always have all-zero + // insets but GtkFramePeer and GtkDialogPeer insets will be + // non-zero. + g.translate (-insets.left, -insets.top); + return g; + } + + protected void postMouseEvent(int id, long when, int mods, int x, int y, + int clickCount, boolean popupTrigger) + { + // Translate AWT co-ordinates, which include a window frame's + // insets, to GTK co-ordinates, which do not include a window + // frame's insets. GtkWindowPeer should always have all-zero + // insets but GtkFramePeer and GtkDialogPeer insets will be + // non-zero. + super.postMouseEvent (id, when, mods, + x + insets.left, y + insets.top, + clickCount, popupTrigger); + } + + public Point getLocationOnScreen() + { + int point[] = new int[2]; + if (Thread.currentThread() == GtkMainThread.mainThread) + gtkWindowGetLocationOnScreenUnlocked(point); + else + gtkWindowGetLocationOnScreen(point); + return new Point(point[0], point[1]); + } + + // We override this to keep it in sync with our internal + // representation. + public Rectangle getBounds() + { + return new Rectangle(x, y, width, height); + } + + public void updateIconImages() + { + // TODO: Implement properly. + } + + public void updateMinimumSize() + { + // TODO: Implement properly. + } + + public void setModalBlocked(java.awt.Dialog d, boolean b) + { + // TODO: Implement properly. + } + + public void updateFocusableWindowState() + { + // TODO: Implement properly. + } + + public void setAlwaysOnTop(boolean b) + { + // TODO: Implement properly. + } +} 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 000000000..2dfcdbdec --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java @@ -0,0 +1,325 @@ +/* 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.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.util.Hashtable; + +public class VolatileImageGraphics extends ComponentGraphics +{ + private GtkVolatileImage owner; + private BufferedImage buffer; + + public VolatileImageGraphics(GtkVolatileImage img) + { + this.owner = img; + cairo_t = initFromVolatile( owner.nativePointer ); + setup( cairo_t ); + } + + private VolatileImageGraphics(VolatileImageGraphics copy) + { + this.owner = copy.owner; + cairo_t = initFromVolatile(owner.nativePointer); + 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() + { + GraphicsConfiguration conf; + if (owner.component != null) + { + conf = owner.component.getGraphicsConfiguration(); + } + else + { + return java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + } + return conf; + } + + public Graphics create() + { + return new VolatileImageGraphics( this ); + } + + public void draw(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.draw(s); + + // Custom composite + else + { + // Draw operation to temporary buffer + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setColor(this.getColor()); + g2d.setStroke(this.getStroke()); + g2d.draw(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void fill(Shape s) + { + if (comp == null || comp instanceof AlphaComposite) + super.fill(s); + + // Custom composite + else + { + // Draw operation to temporary buffer + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.fill(s); + + drawComposite(s.getBounds2D(), null); + } + } + + public void drawGlyphVector(GlyphVector gv, float x, float y) + { + if (comp == null || comp instanceof AlphaComposite) + super.drawGlyphVector(gv, x, y); + + // Custom composite + else + { + // Draw operation to temporary buffer + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + + g2d.setPaint(this.getPaint()); + g2d.setColor(this.getColor()); + g2d.drawGlyphVector(gv, x, y); + + Rectangle2D bounds = gv.getLogicalBounds(); + bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + drawComposite(bounds, null); + } + } + + protected boolean drawImage(Image img, AffineTransform xform, + Color bgcolor, ImageObserver obs) + { + if (comp == null || comp instanceof AlphaComposite) + return super.drawImage(img, xform, bgcolor, obs); + + // Custom composite + else + { + // Get buffered image of source + if( !(img instanceof BufferedImage) ) + { + ImageProducer source = img.getSource(); + if (source == null) + return false; + img = Toolkit.getDefaultToolkit().createImage(source); + } + BufferedImage bImg = (BufferedImage) img; + + // Find dimensions of translation + Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY()); + Point2D pt = new Point2D.Double(bImg.getWidth(), bImg.getHeight()); + if (xform != null) + { + origin = xform.transform(origin, origin); + pt = xform.transform(pt, pt); + } + + // Create buffer and draw image + createBuffer(); + + Graphics2D g2d = (Graphics2D)buffer.getGraphics(); + g2d.setRenderingHints(this.getRenderingHints()); + g2d.drawImage(img, xform, obs); + + // Perform compositing from buffer to screen + return drawComposite(new Rectangle2D.Double((int)origin.getX(), + (int)origin.getY(), + (int)pt.getX(), + (int)pt.getY()), + obs); + } + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + if (img instanceof GtkVolatileImage + && (comp == null || comp instanceof AlphaComposite)) + { + 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) + && (comp == null || comp instanceof AlphaComposite)) + { + owner.drawVolatile( ((GtkVolatileImage)img).nativePointer, + x, y, width, height ); + return true; + } + return super.drawImage( img, x, y, width, height, observer ); + } + + protected Rectangle2D getRealBounds() + { + return new Rectangle2D.Double(0, 0, owner.width, owner.height); + } + + private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) + { + // Clip source to visible areas that need updating + Rectangle2D clip = this.getClipBounds(); + Rectangle2D.intersect(bounds, clip, bounds); + + BufferedImage buffer2 = buffer; + if (!bounds.equals(buffer2.getRaster().getBounds())) + buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); + + // Get current on-screen pixels (destination) and clip to bounds + BufferedImage current = owner.getSnapshot(); + + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + + Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], + points[2] - points[0], + points[3] - points[1]); + Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); + + current = current.getSubimage((int)deviceBounds.getX(), + (int)deviceBounds.getY(), + (int)deviceBounds.getWidth(), + (int)deviceBounds.getHeight()); + + // Perform actual composite operation + compCtx.compose(buffer2.getRaster(), current.getRaster(), + buffer2.getRaster()); + + // This MUST call directly into the "action" method in CairoGraphics2D, + // not one of the wrappers, to ensure that the composite isn't processed + // more than once! + Composite oldComp = comp; // so that ComponentGraphics doesn't + comp = null; // process the composite again + boolean rv = super.drawImage(buffer2, + AffineTransform.getTranslateInstance(bounds.getX(), + bounds.getY()), + null, null); + comp = oldComp; + + return rv; + } + + private void createBuffer() + { + if (buffer == null) + { + WritableRaster rst; + rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(owner.width, + owner.height), + new Point(0,0)); + + buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst, + GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(), + new Hashtable()); + } + else + { + Graphics2D g2d = ((Graphics2D)buffer.getGraphics()); + + g2d.setBackground(new Color(0,0,0,0)); + g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } + } + + protected ColorModel getNativeCM() + { + // We should really return GtkVolatileImage.gdkColorModel , + // but CairoGraphics2D doesn't handle alpha premultiplication properly (see + // the fixme in drawImage) so we use the naive Cairo model instead to trick + // the compositing context. + // Because getNativeCM() == getBufferCM() for this peer, it doesn't break. + return CairoSurface.cairoCM_pre; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/package.html b/libjava/classpath/gnu/java/awt/peer/gtk/package.html new file mode 100644 index 000000000..8dd4dd892 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/gtk/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.awt.peer.gtk package. + Copyright (C) 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.awt.peer.gtk</title></head> + +<body> +<p>This package implements the GTK peer for java.awt.</p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/awt/peer/headless/HeadlessGraphicsEnvironment.java b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessGraphicsEnvironment.java new file mode 100644 index 000000000..401b895b8 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessGraphicsEnvironment.java @@ -0,0 +1,118 @@ +/* HeadlessGraphicsEnvironment.java -- A graphics environment for headless mode + 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.headless; + +import gnu.java.awt.java2d.RasterGraphics; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Locale; + +public class HeadlessGraphicsEnvironment + extends GraphicsEnvironment +{ + + public Graphics2D createGraphics(BufferedImage image) + { + Graphics2D g2d; + try + { + // Try to get a CairoGraphics (accellerated) when available. Do this + // via reflection to avoid having a hard compile time dependency. + Class cairoSurfaceCl = + Class.forName("gnu.java.awt.peer.gtk.CairoSurface"); + Raster raster = image.getRaster(); + if (cairoSurfaceCl.isInstance(raster)) + { + Method getGraphicsM = cairoSurfaceCl.getMethod("getGraphics", + new Class[0]); + g2d = (Graphics2D) getGraphicsM.invoke(raster, new Object[0]); + } + else + { + Class bigCl = + Class.forName("gnu.java.awt.peer.gtk.BufferedImageGraphics"); + Constructor bigC = + bigCl.getConstructor(new Class[]{BufferedImage.class }); + g2d = (Graphics2D) bigC.newInstance(new Object[]{ image}); + } + } + catch (Exception ex) + { + g2d = new RasterGraphics(image.getRaster(), image.getColorModel()); + } + return g2d; + } + + public Font[] getAllFonts() + { + // FIXME: Implement. + return null; + } + + public String[] getAvailableFontFamilyNames() + { + // FIXME: Implement. + return null; + } + + public String[] getAvailableFontFamilyNames(Locale l) + { + // FIXME: Implement. + return null; + } + + public GraphicsDevice getDefaultScreenDevice() + { + throw new HeadlessException(); + } + + public GraphicsDevice[] getScreenDevices() + { + throw new HeadlessException(); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/headless/HeadlessToolkit.java b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessToolkit.java new file mode 100644 index 000000000..58b5f3334 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/headless/HeadlessToolkit.java @@ -0,0 +1,385 @@ +/* HeadlessToolkit.java -- A toolkit for headless mode + 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.headless; + +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.EmbeddedWindow; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.peer.EmbeddedWindowPeer; + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.PrintJob; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.CheckboxMenuItemPeer; +import java.awt.peer.CheckboxPeer; +import java.awt.peer.ChoicePeer; +import java.awt.peer.DialogPeer; +import java.awt.peer.FileDialogPeer; +import java.awt.peer.FontPeer; +import java.awt.peer.FramePeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.ListPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.PopupMenuPeer; +import java.awt.peer.RobotPeer; +import java.awt.peer.ScrollPanePeer; +import java.awt.peer.ScrollbarPeer; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.WindowPeer; +import java.io.InputStream; +import java.net.URL; +import java.util.Map; +import java.util.Properties; + +public class HeadlessToolkit + extends ClasspathToolkit +{ + + /** + * The graphics environment for headless graphics. + */ + private HeadlessGraphicsEnvironment graphicsEnv; + + public void beep() + { + // TODO Auto-generated method stub + + } + + public int checkImage(Image image, int width, int height, + ImageObserver observer) + { + // TODO Auto-generated method stub + return 0; + } + + protected ButtonPeer createButton(Button target) + { + throw new HeadlessException(); + } + + protected CanvasPeer createCanvas(Canvas target) + { + throw new HeadlessException(); + } + + protected CheckboxPeer createCheckbox(Checkbox target) + { + throw new HeadlessException(); + } + + protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) + { + throw new HeadlessException(); + } + + protected ChoicePeer createChoice(Choice target) + { + throw new HeadlessException(); + } + + protected DialogPeer createDialog(Dialog target) + { + throw new HeadlessException(); + } + + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e) + { + throw new HeadlessException(); + } + + protected FileDialogPeer createFileDialog(FileDialog target) + { + throw new HeadlessException(); + } + + protected FramePeer createFrame(Frame target) + { + throw new HeadlessException(); + } + + public Image createImage(String filename) + { + // FIXME: Implement. + return null; + } + + public Image createImage(URL url) + { + // FIXME: Implement. + return null; + } + + public Image createImage(ImageProducer producer) + { + // FIXME: Implement. + return null; + } + + public Image createImage(byte[] data, int offset, int len) + { + // TODO Auto-generated method stub + return null; + } + + protected LabelPeer createLabel(Label target) + { + throw new HeadlessException(); + } + + protected ListPeer createList(List target) + { + throw new HeadlessException(); + } + + protected MenuPeer createMenu(Menu target) + { + throw new HeadlessException(); + } + + protected MenuBarPeer createMenuBar(MenuBar target) + { + throw new HeadlessException(); + } + + protected MenuItemPeer createMenuItem(MenuItem target) + { + throw new HeadlessException(); + } + + protected PanelPeer createPanel(Panel target) + { + throw new HeadlessException(); + } + + protected PopupMenuPeer createPopupMenu(PopupMenu target) + { + throw new HeadlessException(); + } + + protected ScrollPanePeer createScrollPane(ScrollPane target) + { + throw new HeadlessException(); + } + + protected ScrollbarPeer createScrollbar(Scrollbar target) + { + throw new HeadlessException(); + } + + protected TextAreaPeer createTextArea(TextArea target) + { + throw new HeadlessException(); + } + + protected TextFieldPeer createTextField(TextField target) + { + throw new HeadlessException(); + } + + protected WindowPeer createWindow(Window target) + { + throw new HeadlessException(); + } + + public ColorModel getColorModel() + { + // TODO Auto-generated method stub + return null; + } + + public String[] getFontList() + { + // TODO Auto-generated method stub + return null; + } + + public FontMetrics getFontMetrics(Font name) + { + // TODO Auto-generated method stub + return null; + } + + protected FontPeer getFontPeer(String name, int style) + { + // TODO Auto-generated method stub + return null; + } + + public Image getImage(String name) + { + // TODO Auto-generated method stub + return null; + } + + public Image getImage(URL url) + { + // TODO Auto-generated method stub + return null; + } + + public PrintJob getPrintJob(Frame frame, String title, Properties props) + { + // TODO Auto-generated method stub + return null; + } + + public int getScreenResolution() + { + throw new HeadlessException(); + } + + public Dimension getScreenSize() + { + throw new HeadlessException(); + } + + public Clipboard getSystemClipboard() + { + throw new HeadlessException(); + } + + protected EventQueue getSystemEventQueueImpl() + { + throw new HeadlessException(); + } + + public Map mapInputMethodHighlight(InputMethodHighlight highlight) + { + // TODO Auto-generated method stub + return null; + } + + public boolean prepareImage(Image image, int width, int height, + ImageObserver observer) + { + // TODO Auto-generated method stub + return false; + } + + public void sync() + { + // TODO Auto-generated method stub + + } + + public EmbeddedWindowPeer createEmbeddedWindow(EmbeddedWindow w) + { + throw new HeadlessException(); + } + + public Font createFont(int format, InputStream stream) + { + // TODO Auto-generated method stub + return null; + } + + public RobotPeer createRobot(GraphicsDevice screen) throws AWTException + { + throw new HeadlessException(); + } + + public ClasspathFontPeer getClasspathFontPeer(String name, Map attrs) + { + // TODO Auto-generated method stub + return null; + } + + public GraphicsEnvironment getLocalGraphicsEnvironment() + { + if (graphicsEnv == null) + graphicsEnv = new HeadlessGraphicsEnvironment(); + return graphicsEnv; + } + + @Override + public boolean isModalExclusionTypeSupported + (Dialog.ModalExclusionType modalExclusionType) + { + return false; + } + + @Override + public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) + { + return false; + } + + +} diff --git a/libjava/classpath/gnu/java/awt/peer/package.html b/libjava/classpath/gnu/java/awt/peer/package.html new file mode 100644 index 000000000..846759a28 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.awt.peer package. + Copyright (C) 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.awt.peer</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/awt/peer/qt/MainQtThread.java b/libjava/classpath/gnu/java/awt/peer/qt/MainQtThread.java new file mode 100644 index 000000000..bee979ac7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/MainQtThread.java @@ -0,0 +1,84 @@ +/* MainQtThread.java -- + Copyright (C) 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.qt; + +/** + * This Thread is the main Qt thread. + * + * It doesn't appear to do much here, since all custom code executed by + * this thread is injected into Qt's main event loop through the (native) + * MainThreadInterface class. + */ +public class MainQtThread extends Thread +{ + long QApplicationPointer; + long mainThreadInterface; + String theme; + private boolean running; + private boolean doublebuffer; + + public MainQtThread( String theme, boolean doublebuffer ) + { + this.theme = theme; + this.doublebuffer = doublebuffer; + running = false; + } + + public boolean isRunning() + { + return running; + } + + /** + * Creates the QApplication + */ + public native long init(String theme, boolean doublebuffer); + + /** + * Runs the QApplication (doesn't return.) + */ + public native void exec(long ptr); + + public void run() + { + QApplicationPointer = init(theme, doublebuffer); + running = true; + exec(QApplicationPointer); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/NativeWrapper.java b/libjava/classpath/gnu/java/awt/peer/qt/NativeWrapper.java new file mode 100644 index 000000000..706e0df6c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/NativeWrapper.java @@ -0,0 +1,43 @@ +/* NativeWrapper.java -- + Copyright (C) 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.qt; + +public class NativeWrapper +{ + protected long nativeObject; +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QMatrix.java b/libjava/classpath/gnu/java/awt/peer/qt/QMatrix.java new file mode 100644 index 000000000..ca4d55b59 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QMatrix.java @@ -0,0 +1,72 @@ +/* QMatrix.java -- + Copyright (C) 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.qt; + +import java.awt.geom.AffineTransform; + +/** + * A simple wrapper class for a QMatrix, + * interfacing it to an AffineTransform. + */ +public class QMatrix extends NativeWrapper +{ + + public QMatrix( AffineTransform t ) + { + double[] matrix = new double[6]; + t.getMatrix( matrix ); + init( matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5] ); + } + + private native void init(double m00, double m10, double m01, double m11, + double m02, double m12 ); + + private native double[] getMatrix(); + + public AffineTransform getTransform() + { + return new AffineTransform( getMatrix() ); + } + + public native void dispose(); + + public void finalize() + { + dispose(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QPainterPath.java b/libjava/classpath/gnu/java/awt/peer/qt/QPainterPath.java new file mode 100644 index 000000000..848b10497 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QPainterPath.java @@ -0,0 +1,140 @@ +/* QPainterPath.java -- + Copyright (C) 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.qt; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.GeneralPath; + +/** + * A simple wrapper class for a QPainterPath, + * createable from an AWT Shape + */ +public class QPainterPath extends NativeWrapper +{ + QPainterPath() + { + } + + public QPainterPath( Shape s ) + { + PathIterator pi = s.getPathIterator( new AffineTransform() ); + double[] coords = new double[6]; + + init( pi.getWindingRule() ); + + while( !pi.isDone() ) + { + switch( pi.currentSegment(coords) ) + { + case PathIterator.SEG_MOVETO: + moveTo( coords[0], coords[1] ); + break; + + case PathIterator.SEG_CLOSE: + close(); + break; + + case PathIterator.SEG_LINETO: + lineTo( coords[0], coords[1] ); + break; + + case PathIterator.SEG_QUADTO: + quadTo( coords[0], coords[1], coords[2], coords[3] ); + break; + + case PathIterator.SEG_CUBICTO: + cubicTo( coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5] ); + break; + } + pi.next(); + } + } + + /** + * Constructor for rectangles. + */ + public QPainterPath( double x, double y, double w, double h ) + { + init(PathIterator.WIND_EVEN_ODD); + moveTo( x, y ); + lineTo( x + w, y ); + lineTo( x + w, y + h ); + lineTo( x , y + h ); + lineTo( x, y ); + close(); + } + + /** + * Constructor for lines. + */ + public QPainterPath( double x1, double y1, double x2, double y2, boolean s ) + { + init(PathIterator.WIND_EVEN_ODD); + moveTo( x1, y1 ); + lineTo( x2, y2 ); + } + + /** + * Returns the QPainterPath as a GeneralPath + */ + public native GeneralPath getPath(); + + private native void init(int windingRule); + + private native void moveTo(double x, double y); + + private native void close(); + + private native void lineTo(double x, double y); + + private native void quadTo(double x1, double y1, double x2, double y2); + + private native void cubicTo(double x1, double y1, double x2, double y2, + double x3, double y3); + + public native void dispose(); + + public void finalize() + { + dispose(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QPen.java b/libjava/classpath/gnu/java/awt/peer/qt/QPen.java new file mode 100644 index 000000000..aee308c26 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QPen.java @@ -0,0 +1,70 @@ +/* QPen.java -- + Copyright (C) 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.qt; + +import java.awt.Stroke; +import java.awt.BasicStroke; + +/** + * A simple wrapper class for a QPen, + * interfacing it to an BasicStroke. + */ +public class QPen extends NativeWrapper +{ + + public QPen( Stroke stroke ) + { + if( !( stroke instanceof BasicStroke ) ) + throw new IllegalArgumentException("Not a basic stroke."); + + BasicStroke s = (BasicStroke)stroke; + if(s.getDashArray() != null) + throw new IllegalArgumentException("Can't handle dashed strokes."); + + init(s.getLineWidth(), s.getEndCap(), s.getLineJoin(), s.getMiterLimit()); + } + + private native void init(double width, int cap, int join, double miterlimit); + + public native void dispose(); + + public void finalize() + { + dispose(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtAudioClip.java b/libjava/classpath/gnu/java/awt/peer/qt/QtAudioClip.java new file mode 100644 index 000000000..ae2e350eb --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtAudioClip.java @@ -0,0 +1,110 @@ +/* QtAudioClip.java -- Qt implementation of the applet AudioClip interface + Copyright (C) 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.qt; + +import java.applet.AudioClip; +import java.awt.Toolkit; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +/** + * Implementation of the applet AudioClip interface on a Qt + * QSound object. + */ +public class QtAudioClip extends NativeWrapper implements AudioClip +{ + private static Toolkit t = null; + + public QtAudioClip(String filename) + { + checkForQt(); + File f = new File(filename); + try + { + String fn = f.getCanonicalPath(); + loadClip( fn ); + } + catch(IOException e) + { + } + } + + public QtAudioClip(URL url) + { + + } + + private native void loadClip(String filename); + + private native void play(boolean looped); + + private native boolean isAvailable(); + + /** + * Checks that Qt and sound is available. + */ + private void checkForQt() + { + if( t == null ) + t = Toolkit.getDefaultToolkit(); + if( !( t instanceof QtToolkit) ) + throw new IllegalStateException("This requires Qt peers."); + } + + // *************** Public methods ******************* + + public void loop() + { + play( true ); + } + + public void play() + { + play( false ); + } + + public native void stop(); + + public native void dispose(); + + public void finalize() + { + dispose(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtButtonPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtButtonPeer.java new file mode 100644 index 000000000..6722eb285 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtButtonPeer.java @@ -0,0 +1,75 @@ +/* QtButtonPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Button; +import java.awt.event.ActionEvent; +import java.awt.peer.ButtonPeer; + +public class QtButtonPeer extends QtComponentPeer implements ButtonPeer +{ + public QtButtonPeer( QtToolkit kit, Button owner ) + { + super( kit, owner ); + } + + public native void init(); + + protected void setup() + { + super.setup(); + setLabel( ((Button)owner).getLabel() ); + } + + /** + * Callback for button click events + */ + void fireClick(int modifiers) + { + ActionEvent e = new ActionEvent(owner, + ActionEvent.ACTION_PERFORMED, + ((Button)owner).getActionCommand(), + System.currentTimeMillis(), + modifiers); + QtToolkit.eventQueue.postEvent(e); + } + + // ************ Public methods ********************* + + public native void setLabel( String label ); +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtCanvasPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtCanvasPeer.java new file mode 100644 index 000000000..2367cd066 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtCanvasPeer.java @@ -0,0 +1,65 @@ +/* QtCanvasPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Canvas; +import java.awt.Dimension; +import java.awt.peer.CanvasPeer; + +public class QtCanvasPeer extends QtComponentPeer implements CanvasPeer +{ + public QtCanvasPeer( QtToolkit kit, Canvas owner ) + { + super( kit, owner ); + } + + public native void init(); + + protected void setup() + { + super.setup(); + } + + /** + * Overloaded, because a Canvas doesn't have a preferred size. + */ + public Dimension getPreferredSize() + { + return owner.getSize(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtCheckboxPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtCheckboxPeer.java new file mode 100644 index 000000000..37fb51c48 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtCheckboxPeer.java @@ -0,0 +1,111 @@ +/* QtCheckboxPeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.event.ItemEvent; +import java.awt.peer.CheckboxPeer; +import java.util.WeakHashMap; + +public class QtCheckboxPeer extends QtComponentPeer implements CheckboxPeer +{ + private CheckboxGroup group; + + // Map QButtonGroup<->CheckboxGroup + private static WeakHashMap groupMap; + + static + { + groupMap = new WeakHashMap(); + } + + public QtCheckboxPeer( QtToolkit kit, Checkbox owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setCheckboxGroup( ((Checkbox)owner).getCheckboxGroup() ); + setLabel( ((Checkbox)owner).getLabel() ); + setState( ((Checkbox)owner).getState() ); + } + + private void fireToggle(boolean checked) + { + if (group == null) + ((Checkbox)owner).setState( checked ); + else + if ( checked ) + group.setSelectedCheckbox((Checkbox)owner); + + int sel = checked ? ItemEvent.SELECTED : ItemEvent.DESELECTED; + ItemEvent e = new ItemEvent((Checkbox)owner, + ItemEvent.ITEM_STATE_CHANGED, + ((Checkbox)owner).getLabel(), + sel); + QtToolkit.eventQueue.postEvent(e); + } + + // ************ Public methods ********************* + + public void setCheckboxGroup( CheckboxGroup group ) + { + if(this.group == group) + return; + + // if we change from a checkbox to a radio button or vice versa + if((this.group == null) != (group == null)) + { + this.group = group; + callInit(); + setup(); + } + + this.group = group; + } + + public native void setLabel( String label ); + + public native void setState( boolean state ); + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtChoicePeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtChoicePeer.java new file mode 100644 index 000000000..d279468cd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtChoicePeer.java @@ -0,0 +1,93 @@ +/* QtChoicePeer.java -- + Copyright (C) 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.qt; + +import java.awt.Choice; +import java.awt.event.ItemEvent; +import java.awt.peer.ChoicePeer; + +public class QtChoicePeer extends QtComponentPeer implements ChoicePeer +{ + public QtChoicePeer( QtToolkit kit, Choice owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + + Choice c = (Choice) owner; + int n = c.getItemCount(); + for ( int i = 0; i < n ; i++ ) + add( c.getItem( i ), i ); + select( c.getSelectedIndex() ); + } + + private void fireChoice( int index ) + { + ((Choice)owner).select( index ); + ItemEvent e = new ItemEvent((Choice)owner, + ItemEvent.ITEM_STATE_CHANGED, + ((Choice)owner).getItem(index), + ItemEvent.SELECTED); + QtToolkit.eventQueue.postEvent(e); + } + + // ************ Public methods ********************* + + public native void add( String item, int index ); + + public void addItem( String item, int index ) + { + add(item, index); + } + + public native void remove( int index ); + + public void removeAll() + { + int n = ((Choice)owner).getItemCount(); + for (int i = 0; i < n; i++) + remove( i ); + } + + public native void select( int index ); +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtComponentGraphics.java b/libjava/classpath/gnu/java/awt/peer/qt/QtComponentGraphics.java new file mode 100644 index 000000000..27e12e659 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtComponentGraphics.java @@ -0,0 +1,120 @@ +/* QtComponentGraphics.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Color; +import java.awt.GraphicsConfiguration; +import java.awt.Graphics; +import java.awt.Rectangle; + +/** + * QtComponentPainter is a Graphics2D context for painting directly to AWT + * components. They require an existing QPainter object (the one passed into + * the native paint method), and are created there (ONLY). + * + * Since this context does direct on-screen drawing it is NOT thread-safe, + * and should NOT be used outside the thread in which it was created. + * + * In other words, + * this is intended for use by QtComponentPeer.paintEvent() only. + * + */ +public class QtComponentGraphics extends QtGraphics +{ + private QtComponentPeer peer; + + /** + * Creates a new ComponentGraphics from an *existing* QPainter object. + * + * @param ptr the pointer to the QPainter object. + */ + public QtComponentGraphics(long ptr, QtComponentPeer component, + int x, int y, int w, int h) + { + nativeObject = ptr; + peer = component; + + Rectangle r = new Rectangle(x, y, w, h); + initialClip = r; + + setAlpha( 1.0 ); + Color c = component.owner.getBackground(); + if(c == null) + setBackground(Color.white); + else + setBackground( c ); + + c = component.owner.getForeground(); + if(c == null) + setColor( Color.black ); + else + setColor( c ); + setup(); + setClip( initialClip ); + } + + /** + * Copying constructor + */ + QtComponentGraphics( QtComponentGraphics g ) + { + super( g ); // Slalom is fun + } + + public Graphics create() + { + return new QtComponentGraphics( this ); + } + + /** + * This is a tricky one + */ + public void copyArea(int x, int y, int width, int height, + int dx, int dy) + { + // FIXME + } + + /** + * Returns the GraphicsConfiguration of the context component. + */ + public GraphicsConfiguration getDeviceConfiguration() + { + return peer.getGraphicsConfiguration(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtComponentPeer.java new file mode 100644 index 000000000..16149b2e6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtComponentPeer.java @@ -0,0 +1,834 @@ +/* QtComponentPeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.BufferCapabilities; +import java.awt.Component; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Image; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.peer.ComponentPeer; +import java.awt.peer.ContainerPeer; +import java.awt.image.ColorModel; +import java.awt.image.VolatileImage; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.event.ComponentEvent; // 100% +import java.awt.event.FocusEvent; // 100% +import java.awt.event.InputEvent; // (abstract) +import java.awt.event.KeyEvent; // 2/3 +import java.awt.event.MouseEvent; // 70%? +import java.awt.event.PaintEvent; // Yup. +import java.awt.event.WindowEvent; // 2/ 12 +import java.util.Timer; +import java.util.TimerTask; + +public class QtComponentPeer extends NativeWrapper implements ComponentPeer +{ + + /** + * Popup trigger button, may differ between platforms + */ + protected static final int POPUP_TRIGGER = 3; + + /** + * The toolkit which manufactured this peer. + */ + protected QtToolkit toolkit; + + /** + * The component which owns this peer. + */ + Component owner; + + /** + * Classpath updates our eventMask. + */ + private long eventMask; + + /** + * if the thing has mouse motion listeners or not. + */ + private boolean hasMotionListeners; + + /** + * The component's double buffer for off-screen drawing. + */ + protected QtImage backBuffer; + + protected long qtApp; + + private boolean settingUp; + + private boolean ignoreResize = false; + + QtComponentPeer( QtToolkit kit, Component owner ) + { + this.owner = owner; + this.toolkit = kit; + qtApp = QtToolkit.guiThread.QApplicationPointer; + nativeObject = 0; + synchronized(this) + { + callInit(); // Calls the init method FROM THE MAIN THREAD. + try + { + wait(); // Wait for the thing to be created. + } + catch(InterruptedException e) + { + } + } + setup(); + hasMotionListeners = false; + } + + protected native void callInit(); + + /** + * Init does the creation of native widgets, it is therefore + * called from the main thread. (the constructor waits for this to happen.) + */ + protected void init() + { + } + + protected void setup() + { + settingUp = true; + if (owner != null) + { + if (owner instanceof javax.swing.JComponent) + setBackground(owner.getBackground()); + else + owner.setBackground(getNativeBackground()); + + if (owner.getForeground() != null) + setForeground(owner.getForeground()); + else + setForeground( Color.black ); + + if (owner.getCursor() != null) + if (owner.getCursor().getType() != Cursor.DEFAULT_CURSOR) + setCursor(owner.getCursor()); + + if (owner.getFont() != null) + setFont(owner.getFont()); + + setEnabled( owner.isEnabled() ); + + backBuffer = null; + updateBounds(); + + setVisible( owner.isVisible() ); + QtToolkit.repaintThread.queueComponent(this); + } + settingUp = false; + } + + native void QtUpdate(); + native void QtUpdateArea( int x, int y, int w, int h ); + private synchronized native void disposeNative(); + private native void setGround( int r, int g, int b, boolean isForeground ); + private native void setBoundsNative( int x, int y, int width, int height ); + private native void setCursor( int ctype ); + private native Color getNativeBackground(); + private native void setFontNative( QtFontPeer fp ); + private native int whichScreen(); + private native void reparentNative( QtContainerPeer parent ); + private native void getLocationOnScreenNative( Point p ); + + private boolean drawableComponent() + { + return ((this instanceof QtContainerPeer && + !(this instanceof QtScrollPanePeer)) || + (this instanceof QtCanvasPeer)); + } + + void updateBounds() + { + Rectangle r = owner.getBounds(); + setBounds( r.x, r.y, r.width, r.height ); + } + + synchronized void updateBackBuffer(int width, int height) + { + if(width <= 0 || height <= 0) + return; + + if( !drawableComponent() && backBuffer == null) + return; + + if( backBuffer != null ) + { + if( width < backBuffer.width && height < backBuffer.height ) + return; + backBuffer.dispose(); + } + backBuffer = new QtImage(width, height); + } + + + // ************ Event methods ********************* + + /** + * Window closing event + */ + protected void closeEvent() + { + if (owner instanceof Window) + { + WindowEvent e = new WindowEvent((Window)owner, + WindowEvent.WINDOW_CLOSING); + QtToolkit.eventQueue.postEvent(e); + } + } + + protected void enterEvent(int modifiers, int x, int y, int dummy) + { + MouseEvent e = new MouseEvent(owner, + MouseEvent.MOUSE_ENTERED, + System.currentTimeMillis(), + (modifiers & 0x2FF), x, y, 0, false); + QtToolkit.eventQueue.postEvent(e); + } + + protected void focusInEvent() + { + FocusEvent e = new FocusEvent(owner, FocusEvent.FOCUS_GAINED); + QtToolkit.eventQueue.postEvent(e); + } + + protected void focusOutEvent() + { + FocusEvent e = new FocusEvent(owner, FocusEvent.FOCUS_LOST); + QtToolkit.eventQueue.postEvent(e); + } + + protected void keyPressEvent(int modifiers, int code, int unicode, int dummy) + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + KeyEvent e = new KeyEvent(owner, + KeyEvent.KEY_PRESSED, + System.currentTimeMillis(), + modifiers, code, (char)(unicode & 0xFFFF), + KeyEvent.KEY_LOCATION_UNKNOWN); + if (!manager.dispatchEvent (e)) + QtToolkit.eventQueue.postEvent(e); + } + + protected void keyReleaseEvent(int modifiers, int code, int unicode, int dummy) + { + KeyEvent e = new KeyEvent(owner, + KeyEvent.KEY_RELEASED, + System.currentTimeMillis(), + modifiers, code, (char)(unicode & 0xFFFF), + KeyEvent.KEY_LOCATION_UNKNOWN); + QtToolkit.eventQueue.postEvent(e); + } + + protected void leaveEvent(int modifiers, int x, int y, int dummy) + { + MouseEvent e = new MouseEvent(owner, + MouseEvent.MOUSE_EXITED, + System.currentTimeMillis(), + (modifiers & 0x2FF), x, y, 0, false); + QtToolkit.eventQueue.postEvent(e); + } + + // FIXME: Coalesce press-release events into clicks. + protected void mouseDoubleClickEvent( int modifiers, int x, int y, int clickCount) + { + if( (eventMask & AWTEvent.MOUSE_EVENT_MASK) == 0 ) + return; + int button = 0; + if((modifiers & InputEvent.BUTTON1_DOWN_MASK) == + InputEvent.BUTTON1_DOWN_MASK) button = 1; + if((modifiers & InputEvent.BUTTON2_DOWN_MASK) == + InputEvent.BUTTON2_DOWN_MASK) button = 2; + if((modifiers & InputEvent.BUTTON3_DOWN_MASK) == + InputEvent.BUTTON3_DOWN_MASK) button = 3; + MouseEvent e = new MouseEvent(owner, + MouseEvent.MOUSE_CLICKED, + System.currentTimeMillis(), + (modifiers & 0x2FF), x, y, clickCount, + false, button); + QtToolkit.eventQueue.postEvent(e); + } + + protected void mouseMoveEvent( int modifiers, int x, int y, int clickCount) + { + if( (eventMask & AWTEvent.MOUSE_EVENT_MASK) == 0 ) + return; + + int button = 0; + if((modifiers & InputEvent.BUTTON1_DOWN_MASK) == + InputEvent.BUTTON1_DOWN_MASK) button = 1; + if((modifiers & InputEvent.BUTTON2_DOWN_MASK) == + InputEvent.BUTTON2_DOWN_MASK) button = 2; + if((modifiers & InputEvent.BUTTON3_DOWN_MASK) == + InputEvent.BUTTON3_DOWN_MASK) button = 3; + + int type = (button != 0) ? + MouseEvent.MOUSE_DRAGGED :MouseEvent.MOUSE_MOVED; + + MouseEvent e = new MouseEvent(owner, + type, + System.currentTimeMillis(), + (modifiers & 0x2FF), x, y, clickCount, + false, button); + QtToolkit.eventQueue.postEvent(e); + } + + protected void mousePressEvent( int modifiers, int x, int y, int clickCount) + { + if( (eventMask & AWTEvent.MOUSE_EVENT_MASK) == 0 ) + return; + int button = 0; + if((modifiers & InputEvent.BUTTON1_DOWN_MASK) == + InputEvent.BUTTON1_DOWN_MASK) button = 1; + if((modifiers & InputEvent.BUTTON2_DOWN_MASK) == + InputEvent.BUTTON2_DOWN_MASK) button = 2; + if((modifiers & InputEvent.BUTTON3_DOWN_MASK) == + InputEvent.BUTTON3_DOWN_MASK) button = 3; + MouseEvent e = new MouseEvent(owner, + MouseEvent.MOUSE_PRESSED, + System.currentTimeMillis(), + (modifiers & 0x2FF), x, y, clickCount, + ( button == POPUP_TRIGGER ), + button); + QtToolkit.eventQueue.postEvent(e); + } + + protected void mouseReleaseEvent( int modifiers, int x, int y, int clickCount) + { + if( (eventMask & AWTEvent.MOUSE_EVENT_MASK) == 0 ) + return; + int button = 0; + if((modifiers & InputEvent.BUTTON1_DOWN_MASK) == + InputEvent.BUTTON1_DOWN_MASK) button = 1; + if((modifiers & InputEvent.BUTTON2_DOWN_MASK) == + InputEvent.BUTTON2_DOWN_MASK) button = 2; + if((modifiers & InputEvent.BUTTON3_DOWN_MASK) == + InputEvent.BUTTON3_DOWN_MASK) button = 3; + + MouseEvent e = new MouseEvent(owner, + MouseEvent.MOUSE_RELEASED, + System.currentTimeMillis(), + (modifiers & 0x2FF), x, y, clickCount, + false, button); + QtToolkit.eventQueue.postEvent(e); + } + + protected void moveEvent(int x, int y, int oldx, int oldy) + { + if( !ignoreResize ) + { + // Since Component.setLocation calls back to setBounds, + // we need to ignore that. + ignoreResize = true; + owner.setLocation( x, y ); + ignoreResize = false; + } + } + + protected void resizeEvent(int oldWidth, int oldHeight, + int width, int height) + { + if(!(owner instanceof Window)) + return; + updateBackBuffer(width, height); + ignoreResize = true; + owner.setSize(width, height); + ignoreResize = false; + ComponentEvent e = new ComponentEvent(owner, + ComponentEvent.COMPONENT_RESIZED); + QtToolkit.eventQueue.postEvent(e); + QtToolkit.repaintThread.queueComponent(this); + } + + protected void showEvent() + { + if (owner instanceof Window) + { + WindowEvent e = new WindowEvent((Window)owner, + WindowEvent.WINDOW_OPENED); + QtToolkit.eventQueue.postEvent(e); + } + else + { + ComponentEvent e = new ComponentEvent(owner, + ComponentEvent.COMPONENT_SHOWN); + QtToolkit.eventQueue.postEvent(e); + } + } + + protected void hideEvent() + { + ComponentEvent e = new ComponentEvent(owner, + ComponentEvent.COMPONENT_HIDDEN); + QtToolkit.eventQueue.postEvent(e); + } + + // ************ Public methods ********************* + + /** Classpath-specific method */ + public void setEventMask(long x) + { + eventMask = x; + } + + + public boolean canDetermineObscurity() + { + return true; + } + + public int checkImage(Image img, + int w, + int h, + ImageObserver o) + { + return toolkit.checkImage(img, w, h, o); + } + + public void createBuffers(int numBuffers, BufferCapabilities caps) + throws AWTException + { + // FIXME + } + + public Image createImage(ImageProducer producer) + { + return toolkit.createImage(producer); + } + + public Image createImage(int width, int height) + { + return new QtImage(width, height); + } + + public void coalescePaintEvent(PaintEvent e) + { + // FIXME + } + + public VolatileImage createVolatileImage(int w, int h) + { + return new QtVolatileImage( w, h ); + } + + public void destroyBuffers() + { + // FIXME + } + + public void disable() + { + setEnabled(false); + } + + public void dispose() + { + disposeNative(); + if( backBuffer != null ) + backBuffer.dispose(); + } + + public void enable() + { + setEnabled(true); + } + + public void finalize() + { + dispose(); + } + + public void flip(BufferCapabilities.FlipContents contents) + { + } + + public Image getBackBuffer() + { + return backBuffer; + } + + public ColorModel getColorModel() + { + return toolkit.getColorModel(); + } + + public FontMetrics getFontMetrics(Font font) + { + return new QtFontMetrics( font, getGraphics() ); + } + + public Graphics getGraphics() + { + if( backBuffer == null ) + { + Rectangle r = owner.getBounds(); + backBuffer = new QtImage( r.width, r.height ); + } + return backBuffer.getDirectGraphics( this ); + } + + public GraphicsConfiguration getGraphicsConfiguration() + { + int id = whichScreen(); // get the ID of the screen the widget is on. + GraphicsDevice[] devs = QtToolkit.graphicsEnv.getScreenDevices(); + return devs[id].getDefaultConfiguration(); + } + + public Point getLocationOnScreen() + { + Point p = new Point(); + synchronized( p ) + { + getLocationOnScreenNative( p ); + try + { + p.wait(); // Wait for the thing to be created. + } + catch(InterruptedException e) + { + } + } + return p; + } + + private native void getSizeNative(Dimension d, boolean preferred); + + private Dimension getSize(boolean preferred) + { + Dimension d = new Dimension(); + synchronized( d ) + { + getSizeNative(d, preferred); + try + { + d.wait(); // Wait for the thing to be created. + } + catch(InterruptedException e) + { + } + } + return d; + } + + public Dimension getMinimumSize() + { + return getSize( false ); + } + + public Dimension getPreferredSize() + { + return getSize( true ); + } + + public Toolkit getToolkit() + { + return toolkit; + } + + public native boolean handlesWheelScrolling(); + + public void hide() + { + setVisible(false); + } + + public native boolean isFocusable(); + + public boolean isFocusTraversable() + { + // FIXME + return false; + } + + public native boolean isObscured(); + + public Dimension minimumSize() + { + return getMinimumSize(); + } + + public Dimension preferredSize() + { + return getPreferredSize(); + } + + public native void requestFocus(); + + public boolean requestFocus (Component source, boolean bool1, + boolean bool2, long x) + { + // FIXME + return true; + } + + public void reshape(int x, + int y, + int width, + int height) + { + setBounds( x, y, width, height ); + } + + public void setBackground(Color c) + { + if(c == null && !settingUp) + return; + setGround(c.getRed(), c.getGreen(), c.getBlue(), false); + } + + public void setBounds(int x, int y, int width, int height) + { + if( ignoreResize ) + return; + updateBackBuffer(width, height); + QtToolkit.repaintThread.queueComponent(this); + setBoundsNative(x, y, width, height); + } + + public void setCursor(Cursor cursor) + { + if (cursor != null) + setCursor(cursor.getType()); + } + + public native void setEnabled(boolean b); + + public void setFont(Font f) + { + if( f == null || f.getPeer() == null) + throw new IllegalArgumentException("Null font."); + setFontNative( (QtFontPeer)f.getPeer() ); + } + + public void setForeground(Color c) + { + if(c == null && !settingUp) + return; + setGround(c.getRed(), c.getGreen(), c.getBlue(), true); + } + + public native void setVisible(boolean b); + + public void show() + { + setVisible(true); + } + + public void handleEvent (AWTEvent e) + { + int eventID = e.getID(); + Rectangle r; + + switch (eventID) + { + case ComponentEvent.COMPONENT_SHOWN: + QtToolkit.repaintThread.queueComponent(this); + break; + case PaintEvent.PAINT: + case PaintEvent.UPDATE: + r = ((PaintEvent)e).getUpdateRect(); + QtToolkit.repaintThread.queueComponent(this, r.x, r.y, + r.width, r.height); + break; + case KeyEvent.KEY_PRESSED: + break; + case KeyEvent.KEY_RELEASED: + break; + } + } + + /** + * paint() is called back from the native side in response to a native + * repaint event. + */ + public void paint(Graphics g) + { + Rectangle r = g.getClipBounds(); + + if (backBuffer != null) + backBuffer.drawPixelsScaledFlipped ((QtGraphics) g, + 0, 0, 0, /* bg colors */ + false, false, /* no flipping */ + r.x, r.y, r.width, r.height, + r.x, r.y, r.width, r.height, + false ); /* no compositing */ + } + + public void paintBackBuffer() throws InterruptedException + { + if( backBuffer != null ) + { + backBuffer.clear(); + Graphics2D bbg = (Graphics2D)backBuffer.getGraphics(); + owner.paint(bbg); + bbg.dispose(); + } + } + + public void paintBackBuffer(int x, int y, int w, int h) + throws InterruptedException + { + if( backBuffer != null ) + { + Graphics2D bbg = (Graphics2D)backBuffer.getGraphics(); + bbg.setBackground( getNativeBackground() ); + bbg.clearRect(x, y, w, h); + bbg.setClip(x, y, w, h); + owner.paint(bbg); + bbg.dispose(); + } + } + + public boolean prepareImage(Image img, + int w, + int h, + ImageObserver o) + { + return toolkit.prepareImage(img, w, h, o); + } + + public void print(Graphics g) + { + // FIXME + } + + /** + * Schedules a timed repaint. + */ + public void repaint(long tm, + int x, + int y, + int w, + int h) + { + if( tm <= 0 ) + { + QtToolkit.repaintThread.queueComponent(this, x, y, w, h); + return; + } + Timer t = new Timer(); + t.schedule(new RepaintTimerTask(this, x, y, w, h), tm); + } + + /** + * Update the cursor (note that setCursor is usually not called) + */ + public void updateCursorImmediately() + { + if (owner.getCursor() != null) + setCursor(owner.getCursor().getType()); + } + + /** + * Timed repainter + */ + private class RepaintTimerTask extends TimerTask + { + private int x, y, w, h; + private QtComponentPeer peer; + RepaintTimerTask(QtComponentPeer peer, int x, int y, int w, int h) + { + this.x=x; + this.y=y; + this.w=w; + this.h=h; + this.peer=peer; + } + public void run() + { + QtToolkit.repaintThread.queueComponent(peer, x, y, w, h); + } + } + + public native Rectangle getBounds(); + + public void reparent(ContainerPeer parent) + { + if(!(parent instanceof QtContainerPeer)) + throw new IllegalArgumentException("Illegal peer."); + reparentNative((QtContainerPeer)parent); + } + + public void setBounds(int x, int y, int width, int height, int z) + { + // TODO Auto-generated method stub + + } + + public boolean isReparentSupported() + { + return true; + } + + // What does this do, anyway? + public void layout() + { + // TODO Auto-generated method stub + } + + public boolean requestFocus(Component lightweightChild, boolean temporary, + boolean focusedWindowChangeAllowed, + long time, sun.awt.CausedFocusEvent.Cause cause) + { + // TODO: Implement this properly and remove the other requestFocus() + // methods. + return true; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtContainerPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtContainerPeer.java new file mode 100644 index 000000000..ee2873730 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtContainerPeer.java @@ -0,0 +1,112 @@ +/* QtContainerPeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Component; +import java.awt.Insets; +import java.awt.peer.ContainerPeer; + +public class QtContainerPeer extends QtComponentPeer implements ContainerPeer +{ + public QtContainerPeer( QtToolkit kit, Component owner ) + { + super( kit, owner ); + } + + protected void init() + { + } + + protected void setup() + { + super.setup(); + } + + // ************ Public methods ********************* + public void beginLayout() + { + // FIXME + } + + public void beginValidate() + { + } + + public void endLayout() + { + QtUpdate(); + } + + public void endValidate() + { + } + + public Insets getInsets() + { + return new Insets(0, 0, 0, 0); + } + + public Insets insets() + { + return getInsets(); + } + + public boolean isPaintPending() + { + // FIXME etc. + return false; + } + + public boolean isRestackSupported() + { + // TODO Auto-generated method stub + return false; + } + + public void cancelPendingPaint(int x, int y, int width, int height) + { + // TODO Auto-generated method stub + + } + + public void restack() + { + // TODO Auto-generated method stub + + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtDialogPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtDialogPeer.java new file mode 100644 index 000000000..2f2c25550 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtDialogPeer.java @@ -0,0 +1,73 @@ +/* QtDialogPeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Dialog; +import java.awt.peer.DialogPeer; + +public class QtDialogPeer extends QtWindowPeer implements DialogPeer +{ + public QtDialogPeer( QtToolkit kit, Dialog owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setTitle( ((Dialog)owner).getTitle() ); + setResizable( ((Dialog)owner).isResizable() ); + setModal( ((Dialog)owner).isModal() ); + } + + native void setModal(boolean modal); + + private native void setBoundsNative(int x, int y, int width, int height, boolean fixed); + + // ************ Public methods ********************* + + public native void setResizable (boolean resizeable); + + public void setBounds(int x, int y, int width, int height) + { + setBoundsNative(x, y, width, height, + !((Dialog)owner).isResizable()); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtEmbeddedWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtEmbeddedWindowPeer.java new file mode 100644 index 000000000..de76cd17f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtEmbeddedWindowPeer.java @@ -0,0 +1,64 @@ +/* QtEmbeddedWindowPeer.java -- embedded window peer + Copyright (C) 2005, 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.qt; + +import java.awt.Component; +import gnu.java.awt.peer.EmbeddedWindowPeer; + +/** + * Embedded window peer for applets. + * FIXME: EmbeddedWindowPeer and this class should extend Window, NOT Frame. + */ +public class QtEmbeddedWindowPeer extends QtFramePeer implements EmbeddedWindowPeer +{ + public QtEmbeddedWindowPeer( QtToolkit kit, Component owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + } + + // ************ Public methods ********************* + + public native void embed( long handle ); +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtFileDialogPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtFileDialogPeer.java new file mode 100644 index 000000000..06943d955 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtFileDialogPeer.java @@ -0,0 +1,83 @@ +/* QtFileDialogPeer.java -- + Copyright (C) 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.qt; + +import java.awt.FileDialog; +import java.io.FilenameFilter; +import java.awt.peer.FileDialogPeer; + +public class QtFileDialogPeer extends QtDialogPeer implements FileDialogPeer +{ + public QtFileDialogPeer( QtToolkit kit, FileDialog owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setMode( ((FileDialog)owner).getMode() ); + } + + /** + * Sets load or save mode + */ + private native void setMode(int mode); + + private void fileDialogDone(String path, String filename) + { + } + + // ************ Public methods ********************* + public void setFile (String file) + { + // FIXME + } + + public void setDirectory (String dir) + { + // FIXME + } + + public void setFilenameFilter (FilenameFilter ff) + { + // FIXME + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtFontMetrics.java b/libjava/classpath/gnu/java/awt/peer/qt/QtFontMetrics.java new file mode 100644 index 000000000..1998339dc --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtFontMetrics.java @@ -0,0 +1,125 @@ +/* QtFontMetrics.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.geom.Rectangle2D; + +public class QtFontMetrics extends FontMetrics +{ + + private long nativeObject; + private QtFontPeer peer; + + public QtFontMetrics( Font f ) + { + super( f ); + if(f.getPeer() == null || !(f.getPeer() instanceof QtFontPeer)) + throw new IllegalArgumentException("Invalid Font object."); + peer = (QtFontPeer) f.getPeer(); + init( peer ); + } + + public QtFontMetrics( Font f, Graphics g ) + { + super( f ); + if(f.getPeer() == null || !(f.getPeer() instanceof QtFontPeer)) + throw new IllegalArgumentException("Invalid Font object."); + if( !(g instanceof QtGraphics) ) + throw new IllegalArgumentException("Invalid graphics object."); + peer = (QtFontPeer) f.getPeer(); + initGraphics(peer, (QtGraphics)g ); + } + + QtFontMetrics( QtFontPeer f, Graphics g ) + { + super( null ); + if( !(g instanceof QtGraphics) ) + throw new IllegalArgumentException("Invalid graphics object."); + peer = f; + initGraphics(peer, (QtGraphics)g ); + } + + public QtFontMetrics( QtFontPeer fp ) + { + super( (Font)null ); + peer = fp; + init( peer ); + } + + private native void init(QtFontPeer fp); + + private native void initGraphics(QtFontPeer fp, QtGraphics g); + + private native void dispose(); + + native Rectangle2D getStringBounds(String s); + + // ****************** Package private *************************** + + native boolean canDisplay( int c ); + + // ****************** Public methods **************************** + + public native int getAscent(); + + public native int getDescent(); + + public native int getHeight(); + + public native int getLeading(); + + public native int getMaxAdvance(); + + public native int charWidth(char c); + + public int charsWidth(char[] chars, int off, int len) + { + return stringWidth( new String(chars, off, len) ); + } + + public native int stringWidth(String str); + + public Rectangle2D getStringBounds(String str, Graphics context) + { + QtFontMetrics fm = new QtFontMetrics(peer, context); + return fm.getStringBounds( str ); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtFontPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtFontPeer.java new file mode 100644 index 000000000..03ed1d2df --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtFontPeer.java @@ -0,0 +1,197 @@ +/* QtFontPeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.geom.Rectangle2D; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.text.CharacterIterator; +import java.util.Locale; +import java.util.Map; + +import gnu.java.awt.peer.ClasspathFontPeer; + +public class QtFontPeer extends ClasspathFontPeer +{ + // Pointer to native QFont structure. + private long nativeObject; + private QtFontMetrics metrics; + + + public QtFontPeer (String name, int style) + { + this(name, style, 12); + } + + public QtFontPeer (String name, int style, int size) + { + super(name, style, size); + init(); + } + + public QtFontPeer (String name, Map attributes) + { + super(name, attributes); + init(); + } + + public void init() + { + if(this.familyName == null) + throw new IllegalArgumentException("null family name"); + if(this.familyName.equals("Helvetica")) + this.familyName = "sans serif"; + if(this.familyName.equals("Dialog")) + this.familyName = "sans serif"; + create(this.familyName, this.style, (int)this.size); + metrics = new QtFontMetrics(this); + } + + /** + * Creates the QFont object. + */ + private native void create(String name, int style, int size); + + /** + * Destroys the QFont. + */ + public native void dispose(); + + + // ****************** ClasspathFontPeer Methods. + + public boolean canDisplay (Font font, int c) + { + return metrics.canDisplay( c ); + } + + public int canDisplayUpTo (Font font, CharacterIterator i, + int start, int limit) + { + int index = start; + char c = i.setIndex( index ); + while( index <= limit ) + { + if(!canDisplay(font, c)) + return index; + index++; + c = i.next(); + } + return -1; + } + + public String getSubFamilyName (Font font, Locale locale) + { + throw new UnsupportedOperationException(); + } + + public String getPostScriptName (Font font) + { + throw new UnsupportedOperationException(); + } + + public int getNumGlyphs (Font font) + { + throw new UnsupportedOperationException(); + } + + public int getMissingGlyphCode (Font font) + { + throw new UnsupportedOperationException(); + } + + public byte getBaselineFor (Font font, char c) + { + throw new UnsupportedOperationException(); + } + + public String getGlyphName (Font font, int glyphIndex) + { + throw new UnsupportedOperationException(); + } + + public GlyphVector createGlyphVector (Font font, + FontRenderContext frc, + CharacterIterator ci) + { + throw new UnsupportedOperationException(); + } + + public GlyphVector createGlyphVector (Font font, + FontRenderContext ctx, + int[] glyphCodes) + { + throw new UnsupportedOperationException(); + } + + public GlyphVector layoutGlyphVector (Font font, + FontRenderContext frc, + char[] chars, int start, + int limit, int flags) + { + throw new UnsupportedOperationException(); + } + + public FontMetrics getFontMetrics (Font font) + { + return new QtFontMetrics( this ); + } + + public boolean hasUniformLineMetrics (Font font) + { + throw new UnsupportedOperationException(); + } + + public LineMetrics getLineMetrics (Font font, + CharacterIterator ci, + int begin, int limit, + FontRenderContext rc) + { + throw new UnsupportedOperationException(); + } + + public Rectangle2D getMaxCharBounds (Font font, + FontRenderContext rc) + { + throw new UnsupportedOperationException(); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtFramePeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtFramePeer.java new file mode 100644 index 000000000..d5162106f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtFramePeer.java @@ -0,0 +1,164 @@ +/* QtFramePeer.java -- + Copyright (C) 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.qt; + +import java.awt.Component; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Insets; +import java.awt.MenuBar; +import java.awt.Rectangle; +import java.awt.peer.FramePeer; + +public class QtFramePeer extends QtWindowPeer implements FramePeer +{ + private int theState; // FIXME + + long frameObject; + + public QtFramePeer( QtToolkit kit, Component owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setTitle( ((Frame)owner).getTitle() ); + if( ((Frame)owner).getMenuBar() != null ) + setMenuBar( ((Frame)owner).getMenuBar() ); + } + + private native void setIcon(QtImage image); + + private native void setMaximizedBounds(int w, int h); + + private native void setMenu(QtMenuBarPeer mb); + + private native int menuBarHeight(); + + // ************ Public methods ********************* + + public void destroy() + { + dispose(); + } + + public int getState() + { + // FIXME + return theState; + } + + public Insets getInsets() + { + int mbHeight = ( ((Frame)owner).getMenuBar() != null ) ? + menuBarHeight() : 0; + return new Insets(mbHeight, 0, 0, 0); + } + + public void setIconImage(Image im) + { + if (im instanceof QtImage) + setIcon( (QtImage)im ); + else + setIcon( new QtImage( im.getSource() ) ); + } + + public void setMaximizedBounds(Rectangle rect) + { + // FIXME + } + + public void setMenuBar(MenuBar mb) + { + if( mb != null ) + { + QtMenuBarPeer mbpeer = (QtMenuBarPeer)mb.getPeer(); + if( mbpeer == null ) + { + mb.addNotify(); + mbpeer = (QtMenuBarPeer)mb.getPeer(); + if( mbpeer == null ) + throw new IllegalStateException("No menu bar peer."); + } + mbpeer.addMenus(); + setMenu( mbpeer ); + } + else + setMenu( null ); + } + + public void setResizable(boolean resizeable) + { + // FIXME + } + + public void setState(int s) + { + theState = s; + // FIXME + } + + public void setBoundsPrivate(int x, int y, int width, int height) + { + // TODO Auto-generated method stub + + } + + public void updateAlwaysOnTop() + { + // TODO Auto-generated method stub + + } + + public boolean requestWindowFocus() + { + // TODO Auto-generated method stub + return false; + } + + public Rectangle getBoundsPrivate() + { + // TODO: Implement this properly. + throw new InternalError("Not yet implemented"); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtGraphics.java b/libjava/classpath/gnu/java/awt/peer/qt/QtGraphics.java new file mode 100644 index 000000000..f68cc0dbd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtGraphics.java @@ -0,0 +1,714 @@ +/* QtGraphics.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.AlphaComposite; +import java.awt.AWTPermission; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.GradientPaint; +import java.awt.GraphicsConfiguration; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.Rectangle; +import java.awt.Paint; +import java.awt.Polygon; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; + +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.util.Map; + +/** + * QtGraphics is an abstract implementation of Graphics2D over a QPainter + * object. This is to be subclassed for different drawing contexts, + * which may have different requirements. + */ +public abstract class QtGraphics extends Graphics2D +{ + /** + * Native QPainter pointer. + */ + protected long nativeObject; + + private static final AffineTransform identity = new AffineTransform(); + + // Graphics state + protected Font font; // Current font. + protected Color color, bgcolor; // Current color and background color. + protected Shape clip; // Current clipping area. + protected Shape initialClip; // Initial clip bounds + protected AffineTransform xform; // Current transform + protected Stroke currentStroke; // the current stroke + protected boolean nativeStroking; // whether we're using Qt's stroking or not + protected Composite composite; // current composite operator + protected double currentAlpha; // current alpha + protected Paint currentPaint; // current paint + protected RenderingHints renderingHints; // the rendering hints. + + /** + * Owner Graphics, used by subcontext created by create() + * to avoid GC of the original context. + */ + Graphics parent; + + /** + * Do-nothing constructor. + */ + QtGraphics() + { + } + + /** + * Copying constructor - used by copy() and subclasses. + */ + QtGraphics(QtGraphics parent) + { + cloneNativeContext( parent ); + setFont( parent.getFont() ); + setAlpha( parent.currentAlpha ); + setBackground( parent.getBackground() ); + setColor( parent.getColor() ); + setClip( (initialClip = parent.getClip()) ); + setTransform( parent.getTransform() ); + setStroke( parent.getStroke() ); + setComposite( parent.getComposite() ); + setPaint( parent.getPaint() ); + setRenderingHints( parent.getRenderingHints() ); + } + + /** + * Set up some generic defaults. + */ + protected void setup() + { + font = new Font ("Dialog", Font.PLAIN, 12); + setTransform( identity ); + setStroke( new BasicStroke() ); + renderingHints = new RenderingHints( null ); + } + + public synchronized native void delete(); + + public void dispose() + { + } + + // ********************** etc ******************************* + + private void resetClip() + { + AffineTransform current = getTransform(); + setTransform( identity ); + setClip( initialClip ); + setTransform( current ); + } + + protected native void initImage(QtImage image); + protected native void initVolatileImage(QtVolatileImage image); + + // Creates a new native QPainter object on the same context. + private native void cloneNativeContext( QtGraphics parent ); + private native void setColor(int r, int g, int b, int a); + private native void drawNative( QPainterPath p ); + private native void fillNative( QPainterPath p ); + private native void setClipNative( QPainterPath p ); + private native void setClipRectNative( int x, int y, int w, int h ); + private native void intersectClipNative( QPainterPath p ); + private native void intersectClipRectNative( int x, int y, int w, int h ); + private native void setQtTransform(QMatrix m); + private native void setNativeStroke(QPen p); + private native void setNativeComposite(int alphaMode); + private native void drawStringNative(String string, double x, double y); + private native void setLinearGradient(int r1, int g1, int b1, + int r2, int g2, int b2, + double x1, double y1, + double x2, double y2, boolean cyclic); + private native void setAlphaNative(double alpha); + private native void setFontNative(QtFontPeer font); + private native QPainterPath getClipNative(); + + void setAlpha(double alpha) + { + currentAlpha = alpha; + setAlphaNative(currentAlpha); + } + + // ************ Public methods ********************* + + /** + * Context-sensitive methods are declared abstract. + */ + public abstract Graphics create(); + + public abstract void copyArea(int x, int y, int width, int height, + int dx, int dy); + + public abstract GraphicsConfiguration getDeviceConfiguration(); + + + public Color getColor() + { + return new Color(color.getRed(), color.getGreen(), color.getBlue()); + } + + public void setColor(Color c) + { + if( c == null ) + c = Color.white; + this.color = c; + int alpha = (int)(c.getAlpha() * currentAlpha); + setColor(c.getRed(), c.getGreen(), c.getBlue(), alpha); + } + + public void setBackground(Color color) + { + bgcolor = new Color(color.getRed(), color.getGreen(), color.getBlue()); + } + + public Color getBackground() + { + return new Color(bgcolor.getRed(), bgcolor.getGreen(), bgcolor.getBlue()); + } + + public void setPaintMode() + { + } + + public void setXORMode(Color color) + { + // FIXME + } + + public boolean hit(Rectangle rect, Shape s, boolean onStroke) + { + if( onStroke ) + { + Shape stroked = currentStroke.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 ); + } + + // ******************* Font *********************** + public Font getFont() + { + return font; + } + + public void setFont(Font font) + { + if( font == null ) + return; + this.font = font; + if(font.getPeer() != null && font.getPeer() instanceof QtFontPeer) + setFontNative( (QtFontPeer)font.getPeer() ); + } + + public FontMetrics getFontMetrics(Font font) + { + return new QtFontMetrics(font, this); + } + + // ***************** Clipping ********************* + + /** + * Intersects the current clip with the shape + */ + public void clip(Shape s) + { + intersectClipNative( new QPainterPath( s ) ); + } + + public void clipRect(int x, int y, int width, int height) + { + intersectClipRectNative( x, y, width, height ); + } + + public void setClip(int x, int y, int width, int height) + { + setClipRectNative( x, y, width, height ); + } + + public Shape getClip() + { + return getClipNative().getPath(); + } + + public native Rectangle getClipBounds(); + + /** + * Sets the clip + */ + public void setClip(Shape clip) + { + if (clip == null) + resetClip(); + else + setClipNative(new QPainterPath( clip )); + } + + // ***************** Drawing primitives ********************* + + public void draw(Shape s) + { + if( nativeStroking ) + drawNative( new QPainterPath(s) ); + else + fillNative( new QPainterPath( currentStroke.createStrokedShape( s ) ) ); + } + + public void fill(Shape s) + { + fillNative( new QPainterPath(s) ); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + if( nativeStroking ) + drawNative( new QPainterPath((double)x1, (double)y1, (double)x2, (double)y2, true) ); + else + draw( new Line2D.Double((double)x1, (double)y1, (double)x2, (double)y2) ); + } + + public void drawRect(int x, int y, int width, int height) + { + if( nativeStroking ) + drawNative( new QPainterPath((double)x, (double)y, + (double)width, (double)height) ); + else + fillNative( new QPainterPath + ( currentStroke.createStrokedShape + (new Rectangle2D.Double + ((double)x, (double)y, + (double)width, (double)height) ) ) ); + } + + public void fillRect(int x, int y, int width, int height) + { + fillNative( new QPainterPath( x, y, width, height ) ); + } + + public void clearRect(int x, int y, int width, int height) + { + Color c = color; + setColor( bgcolor ); // FIXME + fillRect( x, y, width, height ); + setColor( c ); + } + + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + draw( new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight) ); + } + + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + fill( new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight) ); + } + + public void drawOval(int x, int y, int width, int height) + { + draw( new Ellipse2D.Double((double)x, (double)y, + (double)width, (double)height) ); + } + + public void fillOval(int x, int y, int width, int height) + { + fill( new Ellipse2D.Double(x, y, width, height) ); + } + + public void drawArc(int x, int y, int width, int height, + int arcStart, int arcAngle) + { + draw( new Arc2D.Double(x, y, width, height, arcStart, arcAngle, + Arc2D.OPEN) ); + } + + public void fillArc(int x, int y, int width, int height, + int arcStart, int arcAngle) + { + fill( new Arc2D.Double(x, y, width, height, arcStart, arcAngle, + Arc2D.CHORD) ); + } + + public void drawPolyline(int xPoints[], int yPoints[], int npoints) + { + for( int i = 0; i < npoints - 1; i++) + drawLine(xPoints[i], yPoints[i], xPoints[i + 1], yPoints[i + 1]); + } + + public void drawPolygon(int xPoints[], int yPoints[], int npoints) + { + draw( new Polygon(xPoints, yPoints, npoints) ); + } + + public void fillPolygon(int xPoints[], int yPoints[], int npoints) + { + fill( new Polygon(xPoints, yPoints, npoints) ); + } + + public native void fill3DRect(int x, int y, int width, int height, boolean raised); + + public native void draw3DRect(int x, int y, int width, int height, boolean raised); + + // *********************** Text rendering ************************* + + public void drawString(String string, int x, int y) + { + drawStringNative(string, (double)x, (double)y); + } + + public void drawString(String string, float x, float y) + { + drawStringNative(string, (double)x, (double)y); + } + + public void drawString (AttributedCharacterIterator ci, int x, int y) + { + // FIXME - to something more correct ? + String s = ""; + for(char c = ci.first(); c != CharacterIterator.DONE; c = ci.next()) + s += c; + drawString(s, x, y); + } + + public void drawString(AttributedCharacterIterator ci, + float x, float y) + { + // FIXME - to something more correct ? + String s = ""; + for(char c = ci.first(); c != CharacterIterator.DONE; c = ci.next()) + s += c; + drawString(s, x, y); + } + + public void drawGlyphVector(GlyphVector v, float x, float y) + { + throw new RuntimeException("Not implemented"); + } + + // ******************* Image drawing ****************************** + public boolean drawImage(Image image, + AffineTransform Tx, + ImageObserver obs) + { + if (image instanceof QtImage) + return ((QtImage)image).drawImage(this, new QMatrix( Tx ), obs); + + return (new QtImage(image.getSource())).drawImage(this, + new QMatrix( Tx ), + obs); + } + + public boolean drawImage(Image image, int x, int y, Color bgcolor, + ImageObserver observer) + { + if (image instanceof QtImage) + return ((QtImage)image).drawImage (this, x, y, bgcolor, observer); + return (new QtImage(image.getSource())).drawImage (this, x, y, + bgcolor, observer); + } + + 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) + { + if (image instanceof QtImage) + return ((QtImage)image).drawImage(this, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, bgcolor, observer); + + return (new QtImage(image.getSource())).drawImage(this, dx1, dy1, + dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer); + } + + public boolean drawImage(Image image, int x, int y, + int width, int height, Color bgcolor, + ImageObserver observer) + { + if (image instanceof QtImage) + return ((QtImage)image).drawImage (this, x, y, width, height, + bgcolor, observer); + return (new QtImage(image.getSource())).drawImage (this, x, y, + width, height, + bgcolor, observer); + } + + public boolean drawImage(Image image, int x, int y, int width, int height, + ImageObserver observer) + { + return drawImage(image, x, y, width, height, null, observer); + } + + public boolean drawImage(Image image, int x, int y, ImageObserver observer) + { + return drawImage(image, x, y, null, observer); + } + + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) + { + return drawImage(image, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, null, observer); + } + + // *********************** Transform methods ************************* + public AffineTransform getTransform() + { + return new AffineTransform( xform ); + } + + public void setTransform(AffineTransform Tx) + { + xform = new AffineTransform( Tx ); + setQtTransform( new QMatrix( xform ) ); + } + + public void rotate(double theta) + { + xform.rotate( theta ); + setQtTransform( new QMatrix( xform ) ); + } + + public void rotate(double theta, double x, double y) + { + xform.rotate(theta, x, y); + setQtTransform( new QMatrix( xform ) ); + } + + public void scale(double sx, double sy) + { + xform.scale(sx, sy); + setQtTransform( new QMatrix( xform ) ); + } + + public void shear(double shx, double shy) + { + xform.shear(shx, shy); + setQtTransform( new QMatrix( xform ) ); + } + + public void transform(AffineTransform Tx) + { + xform.concatenate( Tx ); + setQtTransform( new QMatrix( xform ) ); + } + + public void translate(double tx, double ty) + { + xform.translate( tx, ty ); + setQtTransform( new QMatrix( xform ) ); + } + + public void translate(int x, int y) + { + translate((double)x, (double)y); + } + + // *************** Stroking, Filling, Compositing ***************** + public void setStroke(Stroke s) + { + try // ..to convert the stroke into a native one. + { + QPen pen = new QPen( s ); + nativeStroking = true; + setNativeStroke( pen ); + setColor( color ); + } + catch (IllegalArgumentException e) + { + nativeStroking = false; + } + currentStroke = s; + } + + public Stroke getStroke() + { // FIXME: return copy? + return currentStroke; + } + + public void setComposite(Composite comp) + { + if( comp == null) + { + setNativeComposite( AlphaComposite.SRC_OVER ); + return; + } + + if( comp instanceof AlphaComposite ) + { + if( ((AlphaComposite)comp).getRule() != AlphaComposite.XOR ) + setAlpha( ((AlphaComposite)comp).getAlpha() ); + setNativeComposite( ((AlphaComposite)comp).getRule() ); + composite = comp; + } + else + { + // FIXME: this check is only required "if this Graphics2D + // context is drawing to a Component on the display screen". + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new AWTPermission("readDisplayPixels")); + + throw new UnsupportedOperationException("We don't support custom"+ + " composites yet."); + } + } + + public Composite getComposite() + { + return composite; + } + + public void setPaint(Paint p) + { + if( p == null ) + return; + + // FIXME + currentPaint = p; + if( p instanceof GradientPaint ) + { + GradientPaint lg = (GradientPaint)p; + setLinearGradient(lg.getColor1().getRed(), lg.getColor1().getGreen(), + lg.getColor1().getBlue(), lg.getColor2().getRed(), + lg.getColor2().getGreen(), lg.getColor2().getBlue(), + lg.getPoint1().getX(), lg.getPoint1().getY(), + lg.getPoint2().getX(), lg.getPoint2().getY(), + lg.isCyclic() ); + return; + } + if( p instanceof Color ) + { + setColor((Color) p); + return; + } + throw new UnsupportedOperationException("We don't support custom"+ + " paints yet."); + } + + public Paint getPaint() + { + // FIXME + return currentPaint; + } + + // ********************** Rendering Hints ************************* + + public void addRenderingHints(Map hints) + { + renderingHints.putAll( hints ); + } + + public Object getRenderingHint(RenderingHints.Key hintKey) + { + return renderingHints.get( hintKey ); + } + + public RenderingHints getRenderingHints() + { + return (RenderingHints) renderingHints.clone(); + } + + public void setRenderingHints(Map<?,?> hints) + { + renderingHints = new RenderingHints( null ); + renderingHints.putAll(hints); + updateRenderingHints(); + } + + public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) + { + renderingHints.put( hintKey, hintValue ); + updateRenderingHints(); + } + + private void updateRenderingHints() + { + // FIXME - update native settings. + } + + ////////////////////////////// unimplemented ///////////////////// + + public FontRenderContext getFontRenderContext() + { + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void drawRenderableImage(RenderableImage image, AffineTransform xform) + { + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void drawRenderedImage(RenderedImage image, AffineTransform xform) + { + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y) + { + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtGraphicsEnvironment.java b/libjava/classpath/gnu/java/awt/peer/qt/QtGraphicsEnvironment.java new file mode 100644 index 000000000..dec4db2c8 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtGraphicsEnvironment.java @@ -0,0 +1,105 @@ +/* QtGraphicsEnvironment.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.util.Locale; + +public class QtGraphicsEnvironment extends GraphicsEnvironment +{ + QtToolkit toolkit; + GraphicsDevice[] screens; + + public QtGraphicsEnvironment (QtToolkit tk) + { + super(); + toolkit = tk; + // Get the number of screens from Qt. + int n = toolkit.numScreens(); + + /** + * Create the screen device objects + */ + screens = new GraphicsDevice[ n ]; + for(int i = 0; i < n; i++) + screens[ i ] = new QtScreenDevice( i ); + } + + public Font[] getAllFonts () + { + String[] fonts = getAvailableFontFamilyNames(); + Font[] fontObjs = new Font[fonts.length]; + for( int i = 0; i < fonts.length; i++) + fontObjs[i] = new Font(fonts[i], Font.PLAIN, 12); + return fontObjs; + } + + public String[] getAvailableFontFamilyNames() + { + return toolkit.getFontList(); + } + + public String[] getAvailableFontFamilyNames(Locale l) + { + return getAvailableFontFamilyNames(); + } + + public GraphicsDevice getDefaultScreenDevice () + { + return screens[ toolkit.defaultScreen() ]; + } + + public Graphics2D createGraphics (BufferedImage image) + { + return (Graphics2D)image.getGraphics(); + } + + public GraphicsDevice[] getScreenDevices() + { + return screens; + } + + public QtToolkit getToolkit() + { + return toolkit; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtImage.java b/libjava/classpath/gnu/java/awt/peer/qt/QtImage.java new file mode 100644 index 000000000..b7e50ea25 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtImage.java @@ -0,0 +1,641 @@ +/* QtImage.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Graphics; +import java.awt.Color; +import java.awt.Image; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.MemoryImageSource; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.io.File; +import java.io.IOException; +import java.io.ByteArrayOutputStream; +import java.io.BufferedInputStream; +import java.net.URL; +import java.util.Hashtable; +import java.util.WeakHashMap; +import java.util.Vector; + +/** + * QtImage - wraps a QImage + * + */ +public class QtImage extends Image +{ + int width = -1, height = -1; + + /** + * Properties. + */ + Hashtable props; + + /** + * Loaded or not flag, for asynchronous compatibility. + */ + boolean isLoaded; + + /** + * Pointer to the QImage + */ + long nativeObject; + + /** + * Observer queue. + */ + Vector observers; + + /** + * Error flag for loading. + */ + boolean errorLoading; + + /** + * Original source, if created from an ImageProducer. + */ + ImageProducer source; + + /* + * The 32-bit AARRGGBB format the uses. + */ + static ColorModel nativeModel = new DirectColorModel(32, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + /** + * HashMap of Graphics objects painting on this Image. + */ + WeakHashMap painters; + + /** + * Flags if this image is to be destroyed. + */ + boolean killFlag; + + /** + * Clears the image to RGBA 0 + */ + public native void clear(); + + /** + * Returns a copy of the pixel data as a java array. + */ + private native int[] getPixels(); + + /** + * Sets the pixel data from a java array. + */ + private native void setPixels(int[] pixels); + + /** + * Loads an image + */ + private native boolean loadImage(String name); + + /** + * Loads an image from data. + */ + private native boolean loadImageFromData(byte[] data); + + /** + * Allocates a QImage + */ + private native void createImage(); + + /** + * Frees the above. + */ + private synchronized native void freeImage(); + + /** + * Sets the image to scaled copy of src image. hints are rendering hints. + */ + private native void createScaledImage(QtImage src, int hints); + + /** + * Draws the image optionally composited. + */ + native void drawPixels (QtGraphics gc, + int bg_red, int bg_green, int bg_blue, + int x, int y, + boolean composite); + /** + * Draws the image, optionally scaled and composited. + */ + private native void drawPixelsScaled (QtGraphics gc, + int bg_red, int bg_green, int bg_blue, + int x, int y, int width, int height, + boolean composite); + + /** + * Draws the image transformed. + */ + private native void drawPixelsTransformed (QtGraphics gc, QMatrix transform); + + /** + * Draws the image scaled flipped and optionally composited. + */ + native void drawPixelsScaledFlipped (QtGraphics 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); + + /** + * Creates the image from an ImageProducer. May result in an error image. + */ + public QtImage (ImageProducer producer) + { + killFlag = false; + isLoaded = false; + observers = new Vector(); + source = producer; + errorLoading = false; + if( producer != null ) + source.startProduction(new QtImageConsumer(this, source)); + } + + /** + * Creates the image from a URL. May result in an error image. + */ + public QtImage (URL url) + { + killFlag = false; + isLoaded = false; + observers = new Vector(); + errorLoading = false; + if( url == null) + return; + ByteArrayOutputStream baos = new ByteArrayOutputStream( 5000 ); + try + { + BufferedInputStream bis = new BufferedInputStream(url.openStream()); + + byte[] buf = new byte[5000]; + int n = 0; + + while ( (n = bis.read( buf )) != -1 ) + baos.write(buf, 0, n); + bis.close(); + } + catch(IOException e) + { + throw new IllegalArgumentException("Couldn't load image."); + } + if ( loadImageFromData( baos.toByteArray() ) != true ) + throw new IllegalArgumentException("Couldn't load image."); + + isLoaded = true; + observers = null; + props = new Hashtable(); + } + + /** + * Constructs a QtImage by loading a given file. + * + * @throws IllegalArgumentException if the image could not be loaded. + */ + public QtImage (String filename) + { + killFlag = false; + File f = new File(filename); + observers = null; + props = new Hashtable(); + try + { + String fn = f.getCanonicalPath(); + if (loadImage( fn ) != true) + { + errorLoading = true; + isLoaded = false; + return; + } + } + catch(IOException e) + { + errorLoading = true; + isLoaded = false; + return; + } + errorLoading = false; + isLoaded = true; + } + + /** + * Constructs a QtImage from a byte array of an image file. + * + * @throws IllegalArgumentException if the image could not be loaded. + */ + public QtImage (byte[] data) + { + if (loadImageFromData(data) != true) + throw new IllegalArgumentException("Couldn't load image."); + + killFlag = false; + isLoaded = true; + observers = null; + errorLoading = false; + props = new Hashtable(); + } + + /** + * Constructs an empty QtImage. + */ + public QtImage (int width, int height) + { + this.width = width; + this.height = height; + props = new Hashtable(); + isLoaded = true; + killFlag = false; + observers = null; + errorLoading = false; + createImage(); + clear(); + } + + /** + * Constructs a scaled version of the src bitmap, using Qt + */ + private QtImage (QtImage src, int width, int height, int hints) + { + this.width = width; + this.height = height; + props = new Hashtable(); + isLoaded = true; + killFlag = false; + observers = null; + errorLoading = false; + + createScaledImage(src, hints); + } + + /** + * Callback from the image consumer. + */ + public void setImage(int width, int height, + int[] pixels, Hashtable properties) + { + this.width = width; + this.height = height; + props = (properties != null) ? properties : new Hashtable(); + + if (width <= 0 || height <= 0 || pixels == null) + { + errorLoading = true; + return; + } + + isLoaded = true; + deliver(); + createImage(); + setPixels(pixels); + } + + // java.awt.Image methods //////////////////////////////////////////////// + + public int getWidth (ImageObserver observer) + { + if (addObserver(observer)) + return -1; + + return width; + } + + public int getHeight (ImageObserver observer) + { + if (addObserver(observer)) + return -1; + + return height; + } + + public Object getProperty (String name, ImageObserver observer) + { + if (addObserver(observer)) + return UndefinedProperty; + + Object value = props.get (name); + return (value == null) ? UndefinedProperty : value; + } + + /** + * Returns the source of this image. + */ + public ImageProducer getSource () + { + if (!isLoaded) + return null; + return new MemoryImageSource(width, height, nativeModel, getPixels(), + 0, width); + } + + void putPainter(QtImageGraphics g) + { + if( painters == null ) + painters = new WeakHashMap(); + painters.put( g, "dummy" ); + } + + void removePainter(QtImageGraphics g) + { + painters.remove( g ); + if( killFlag && painters.isEmpty() ) + freeImage(); + } + + /** + * Creates a Graphics context for this image. + */ + public Graphics getGraphics () + { + if (!isLoaded || killFlag) + return null; + + return new QtImageGraphics(this); + } + + /** + * Creates a Graphics context for this image. + */ + Graphics getDirectGraphics(QtComponentPeer peer) + { + if (!isLoaded) + return null; + + return new QtImageDirectGraphics(this, peer); + } + + /** + * Returns a scaled instance of this image. + */ + public Image getScaledInstance(int width, + int height, + int hints) + { + if (width <= 0 || height <= 0) + throw new IllegalArgumentException("Width and height of scaled bitmap"+ + "must be >= 0"); + + return new QtImage(this, width, height, hints); + } + + /** + * If the image is loaded and comes from an ImageProducer, + * regenerate the image from there. + * + * I have no idea if this is ever actually used. Since QtImage can't be + * instantiated directly, how is the user to know if it was created from + * an ImageProducer or not? + */ + public synchronized void flush () + { + if (isLoaded && source != null) + { + observers = new Vector(); + isLoaded = false; + freeImage(); + source.startProduction(new QtImageConsumer(this, source)); + } + } + + public void finalize() + { + dispose(); + } + + public void dispose() + { + if (isLoaded) + { + if( painters == null || painters.isEmpty() ) + freeImage(); + else + killFlag = true; // can't destroy image yet. + // Do so when all painters are gone. + } + } + + /** + * Returns the image status, used by QtToolkit + */ + public int checkImage (ImageObserver observer) + { + if (addObserver(observer)) + { + if (errorLoading == true) + return ImageObserver.ERROR; + else + return 0; + } + + return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT; + } + + // Drawing methods //////////////////////////////////////////////// + + /** + * Draws an image with eventual scaling/transforming. + */ + public boolean drawImage (QtGraphics g, QMatrix matrix, + ImageObserver observer) + { + if (addObserver(observer)) + return false; + + drawPixelsTransformed (g, matrix); + + return true; + } + + /** + * Draws an image to the QtGraphics context, at (x,y) with optional + * compositing with a background color. + */ + public boolean drawImage (QtGraphics g, int x, int y, + Color bgcolor, ImageObserver observer) + { + if (addObserver(observer)) + return false; + + if(bgcolor != null) + drawPixels(g, bgcolor.getRed (), bgcolor.getGreen (), + bgcolor.getBlue (), x, y, true); + else + drawPixels(g, 0, 0, 0, x, y, false); + + return true; + } + + /** + * Draws an image to the QtGraphics context, at (x,y) scaled to + * width and height, with optional compositing with a background color. + */ + public boolean drawImage (QtGraphics g, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) + { + if (addObserver(observer)) + return false; + + 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; + } + + /** + * Draws an image with eventual scaling/transforming. + */ + public boolean drawImage (QtGraphics 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 ( srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0) + return true; + + 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; + } + + public native void copyArea(int x, int y, int width, int height, + int dx, int dy); + + // Private methods //////////////////////////////////////////////// + + /** + * Delivers notifications to all queued observers. + */ + private void deliver() + { + int flags = ImageObserver.HEIGHT | + ImageObserver.WIDTH | + ImageObserver.PROPERTIES | + ImageObserver.ALLBITS; + + if (observers != null) + for(int i=0; i < observers.size(); i++) + ((ImageObserver)observers.elementAt(i)). + imageUpdate(this, flags, 0, 0, width, height); + + observers = null; + } + + /** + * Adds an observer, if we need to. + * @return true if an observer was added. + */ + private boolean addObserver(ImageObserver observer) + { + if (!isLoaded) + { + if(observer != null) + if (!observers.contains (observer)) + observers.addElement (observer); + return true; + } + return false; + } + + public String toString() + { + return "QtImage [isLoaded="+isLoaded+", width="+width+", height="+height + +"]"; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtImageConsumer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtImageConsumer.java new file mode 100644 index 000000000..9883475f9 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtImageConsumer.java @@ -0,0 +1,147 @@ +/* QtImageConsumer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.util.Hashtable; + +/** + * Helper class to QtImage. Sits and gathers pixels for a QtImage and then + * calls QtImage.setImage(). + * + * @author Sven de Marothy + */ +public class QtImageConsumer implements ImageConsumer +{ + private QtImage target; + private int width, height; + private Hashtable properties; + private int[] pixelCache = null; + private ImageProducer source; + + public QtImageConsumer(QtImage target, ImageProducer source) + { + this.target = target; + this.source = source; + } + + public synchronized void imageComplete (int status) + { + source.removeConsumer(this); + target.setImage(width, height, pixelCache, properties); + } + + public synchronized void setColorModel (ColorModel model) + { + // This method is to inform on what the most used color model + // in the image is, for optimization reasons. We ignore this + // information. + } + + public synchronized void setDimensions (int width, int height) + { + pixelCache = new int[width*height]; + + this.width = width; + this.height = height; + } + + public synchronized void setHints (int flags) + { + // This method informs us in which order the pixels are + // delivered, for progressive-loading support, etc. + // Since we wait until it's all loaded, we can ignore the hints. + } + + public synchronized void setPixels (int x, int y, int width, int height, + ColorModel cm, byte[] pixels, + int offset, int scansize) + { + setPixels (x, y, width, height, cm, convertPixels (pixels), offset, + scansize); + } + + public synchronized void setPixels (int x, int y, int width, int height, + ColorModel cm, int[] pixels, + int offset, int scansize) + { + if (pixelCache == null) + return; // Not sure this should ever happen. + + if (cm.equals(QtImage.nativeModel)) + for (int i = 0; i < height; i++) + System.arraycopy (pixels, offset + (i * scansize), + pixelCache, (y + i) * this.width + x, + width); + else + { + for (int i = 0; i < height; i++) + for (int j = 0; j < width; j++) + { + // get in AARRGGBB and convert to AABBGGRR + int pix = cm.getRGB(pixels[offset + (i * scansize) + x + j]); + byte b = (byte)(pix & 0xFF); + byte r = (byte)(((pix & 0x00FF0000) >> 16) & 0xFF); + pix &= 0xFF00FF00; + pix |= ((b & 0xFF) << 16); + pix |= (r & 0xFF); + pixelCache[(y + i) * this.width + x + j] = pix; + } + } + } + + /** + * This is an old method, no idea if it's correct. + */ + private int[] convertPixels (byte[] pixels) + { + int ret[] = new int[pixels.length]; + + for (int i = 0; i < pixels.length; i++) + ret[i] = pixels[i] & 0xFF; + + return ret; + } + + public synchronized void setProperties (Hashtable props) + { + this.properties = props; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtImageDirectGraphics.java b/libjava/classpath/gnu/java/awt/peer/qt/QtImageDirectGraphics.java new file mode 100644 index 000000000..daa174ad4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtImageDirectGraphics.java @@ -0,0 +1,145 @@ +/* QtImageDirectGraphics.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Color; +import java.awt.Image; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.image.ImageObserver; + +/** + * A QtImagePainter that does an update after every drawing op. + */ +public class QtImageDirectGraphics extends QtImageGraphics +{ + private QtComponentPeer peer; + private boolean modified; + + public QtImageDirectGraphics(QtImage image, QtComponentPeer peer) + { + super( image ); + this.peer = peer; + modified = false; + } + + public QtImageDirectGraphics(QtImageGraphics g) + { + super( g ); + } + + private void scheduleUpdate() + { + } + + public void dispose() + { + super.dispose(); + peer.toolkit.sync(); + peer.QtUpdate(); + } + + public void draw(Shape s) + { + super.draw(s); + scheduleUpdate(); + } + + public void fill(Shape s) + { + super.fill(s); + scheduleUpdate(); + } + + public void drawString(String string, int x, int y) + { + super.drawString( string, x, y ); + scheduleUpdate(); + } + + public void drawString(String string, float x, float y) + { + super.drawString( string, x, y ); + scheduleUpdate(); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + super.drawLine(x1, y1, x2, y2); + scheduleUpdate(); + } + + public boolean drawImage(Image image, + AffineTransform Tx, + ImageObserver obs) + { + boolean r = super.drawImage(image, Tx, obs); + scheduleUpdate(); + return r; + } + + public boolean drawImage(Image image, int x, int y, Color bgcolor, + ImageObserver observer) + { + boolean r = super.drawImage(image, x, y, bgcolor, observer); + scheduleUpdate(); + return r; + } + + 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) + { + boolean r = super.drawImage( image, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer); + scheduleUpdate(); + return r; + } + + public boolean drawImage(Image image, int x, int y, + int width, int height, Color bgcolor, + ImageObserver observer) + { + boolean r = super.drawImage(image, x, y, width, height, bgcolor, + observer); + scheduleUpdate(); + return r; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtImageGraphics.java b/libjava/classpath/gnu/java/awt/peer/qt/QtImageGraphics.java new file mode 100644 index 000000000..bba25e068 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtImageGraphics.java @@ -0,0 +1,139 @@ +/* QtImageGraphics.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Color; +import java.awt.GraphicsConfiguration; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Rectangle; +import java.util.Stack; + +/** + * QtComponentPainter is a Graphics2D context for painting to QtImage and + * QtVolatileImages. + */ +public class QtImageGraphics extends QtGraphics +{ + Image parentImage; + Stack owners; + QtImageGraphics topParent; + + public QtImageGraphics(Image image) + { + if(!( image instanceof QtVolatileImage || image instanceof QtImage)) + throw new IllegalArgumentException("Cannot create QtImageGraphics for a non-QImage context."); + + owners = new Stack(); + owners.push(this); + topParent = null; + int w, h; + if(image instanceof QtImage) + { + w = ((QtImage)image).width; + h = ((QtImage)image).height; + initImage((QtImage) image ); + ((QtImage)image).putPainter( this ); + } + else + { + w = ((QtVolatileImage)image).width; + h = ((QtVolatileImage)image).height; + initVolatileImage((QtVolatileImage) image ); + ((QtVolatileImage)image).putPainter( this ); + } + + parentImage = image; + initialClip = new Rectangle( 0, 0, w, h ); + setClip( initialClip ); + setBackground(Color.white); // fixme + currentAlpha = 1.0; + setColor(Color.black); + setup(); + } + + /** + * Copying constructor + */ + QtImageGraphics( QtImageGraphics g ) + { + super( g ); + parentImage = g.parentImage; + if(parentImage instanceof QtImage) + ((QtImage)parentImage).putPainter( this ); + else + ((QtVolatileImage)parentImage).putPainter( this ); + } + + public void dispose() + { + delete(); + if( parentImage instanceof QtImage ) + ((QtImage)parentImage).removePainter( this ); + else + ((QtVolatileImage)parentImage).removePainter( this ); + } + + /** + * Create a copy of this context. + */ + public Graphics create() + { + return new QtImageGraphics( this ); + } + + /** + * Copy an area. + */ + public void copyArea(int x, int y, int width, int height, + int dx, int dy) + { + if(parentImage instanceof QtImage) + ((QtImage)parentImage).copyArea(x, y, width, height, dx, dy); + else + ((QtVolatileImage)parentImage).copyArea(x, y, width, height, dx, dy); + } + + /** + * Returns the GraphicsConfiguration of the context component. + */ + public GraphicsConfiguration getDeviceConfiguration() + { + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtLabelPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtLabelPeer.java new file mode 100644 index 000000000..80acd491d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtLabelPeer.java @@ -0,0 +1,62 @@ +/* QtLabelPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Label; +import java.awt.peer.LabelPeer; + +public class QtLabelPeer extends QtComponentPeer implements LabelPeer +{ + public QtLabelPeer( QtToolkit kit, Label owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setText( ((Label)owner).getText() ); + setAlignment( ((Label)owner).getAlignment() ); + } + + public native void setAlignment( int alignment ); + + public native void setText( String label ); +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtListPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtListPeer.java new file mode 100644 index 000000000..14ae2a0cd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtListPeer.java @@ -0,0 +1,188 @@ +/* QtListPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Dimension; +import java.awt.List; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.peer.ListPeer; + +public class QtListPeer extends QtComponentPeer implements ListPeer +{ + public QtListPeer( QtToolkit kit, List owner ) + { + super( kit, owner ); + } + + public native void init(); + + protected void setup() + { + super.setup(); + List o = (List)owner; + // Multiple selection + setMultipleMode(o.isMultipleMode()); + // Add initial list items. + String[] items = o.getItems(); + for (int i = 0; i < items.length; i++) + add(items[i], i); + + // Initial selections. + int[] selected = o.getSelectedIndexes(); + for (int i = 0; i < selected.length; i++) + select(selected[i]); + + // If no initial selection, use 0. + if(selected.length == 0 && items.length > 0) + select( 0 ); + } + + private boolean ignoreNextSelect = false; + + /** + * Called back when a row is selected. -1 if no row is selected. + */ + private void fireChoice( int index ) + { + ignoreNextSelect = true; + if( index == -1) + ((List)owner).deselect( ((List)owner).getSelectedIndex() ); + else + { + ((List)owner).select( index ); + ItemEvent e = new ItemEvent((List)owner, + ItemEvent.ITEM_STATE_CHANGED, + ""+index, + ItemEvent.SELECTED); + QtToolkit.eventQueue.postEvent(e); + } + } + + /** + * Called back when an item is double-clicked. + */ + private void itemDoubleClicked( int index, int modifiers ) + { + ActionEvent e = new ActionEvent(owner, + ActionEvent.ACTION_PERFORMED, + ((List)owner).getItem( index ), + System.currentTimeMillis(), + modifiers); + QtToolkit.eventQueue.postEvent(e); + } + + private native void select(int index, boolean selected); + + // ************ Public methods ********************* + + public native void add(String item, int index); + + public void addItem(String item, int index) + { + add(item, index); + } + + public void clear() + { + removeAll(); + } + + /** + * Deletes items from the starting index to the ending index (inclusive). + */ + public native void delItems(int start_index, int end_index); + + public void deselect(int index) + { + if( ignoreNextSelect == true ) + ignoreNextSelect = false; + else + select(index, false); + } + + public native int[] getSelectedIndexes(); + + public native void makeVisible(int index); + + public Dimension minimumSize(int s) + { + return getMinimumSize(s); + } + + public Dimension preferredSize(int s) + { + return getPreferredSize(s); + } + + public void removeAll() + { + delItems(0, ((List)owner).getItemCount() - 1); + } + + public void select(int index) + { + if( ignoreNextSelect == true ) + ignoreNextSelect = false; + else + select(index, true); + } + + /** + * Sets multiple-selection mode. + * Note there's a bug in multiple selection in Qt 4.0.0, use 4.0.1. + */ + public native void setMultipleMode(boolean multi); + + public void setMultipleSelections(boolean multi) + { + setMultipleMode(multi); + } + + public Dimension getPreferredSize(int s) + { + // FIXME + return getPreferredSize(); + } + + public Dimension getMinimumSize(int s) + { + // FIXME + return getMinimumSize(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtMenuBarPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuBarPeer.java new file mode 100644 index 000000000..962d76d41 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuBarPeer.java @@ -0,0 +1,101 @@ +/* QtMenuBarPeer.java -- Qt peer for a menu bar. + Copyright (C) 2005, 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.qt; + +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.peer.MenuBarPeer; + +public class QtMenuBarPeer extends QtMenuComponentPeer implements MenuBarPeer +{ + public QtMenuBarPeer( QtToolkit kit, MenuBar owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + } + + /** + * Recurses the menubar adding menus (and menu items), + * called from the Frame peer. + */ + void addMenus() + { + MenuBar o = (MenuBar)owner; + int help = (o.getHelpMenu() != null) ? 1 : 0; + for (int i = 0; i < o.getMenuCount() - help; i++) + addMenu( o.getMenu(i) ); + if(o.getHelpMenu() != null) + addHelpMenu( o.getHelpMenu() ); + } + + private native void addMenu( QtMenuPeer mp ); + + private native void addHelpMenu( QtMenuPeer mp ); + + private native void delMenu( QtMenuPeer mp ); + + // ************ Public methods ********************* + + public void addMenu( Menu m ) + { + if (m.getPeer() == null) + m.addNotify(); + ((QtMenuPeer)m.getPeer()).addItems(); + addMenu( (QtMenuPeer)m.getPeer() ); + } + + public void addHelpMenu( Menu m ) + { + if (m.getPeer() == null) + m.addNotify(); + ((QtMenuPeer)m.getPeer()).addItems(); + addHelpMenu( (QtMenuPeer)m.getPeer() ); + } + + public void delMenu( int index ) + { + Menu m = ((MenuBar)owner).getMenu( index ); + if(m != null) + delMenu( (QtMenuPeer)m.getPeer() ); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtMenuComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuComponentPeer.java new file mode 100644 index 000000000..2050bef06 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuComponentPeer.java @@ -0,0 +1,94 @@ +/* QtMenuComponentPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Font; +import java.awt.MenuComponent; +import java.awt.peer.MenuComponentPeer; + +public class QtMenuComponentPeer extends NativeWrapper + implements MenuComponentPeer +{ + protected QtToolkit toolkit; + protected MenuComponent owner; + + public QtMenuComponentPeer( QtToolkit kit, MenuComponent owner ) + { + this.toolkit = kit; + this.owner = owner; + nativeObject = 0; + synchronized(this) + { + callInit(); // Calls the init method FROM THE MAIN THREAD. + try + { + wait(); // Wait for the thing to be created. + } + catch(InterruptedException e) + { + } + } + setup(); + } + + protected native void callInit(); + + protected void init() + { + } + + protected void setup() + { + } + + public void finalize() + { + dispose(); + } + + // ************ Public methods ********************* + + public native void dispose(); + + public void setFont(Font font) + { + // TODO Auto-generated method stub + + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtMenuItemPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuItemPeer.java new file mode 100644 index 000000000..2b77540a5 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuItemPeer.java @@ -0,0 +1,100 @@ +/* QtMenuItemPeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.MenuItem; +import java.awt.CheckboxMenuItem; +import java.awt.event.ActionEvent; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.CheckboxMenuItemPeer; + +public class QtMenuItemPeer extends QtMenuComponentPeer + implements MenuItemPeer, CheckboxMenuItemPeer +{ + public QtMenuItemPeer( QtToolkit toolkit, MenuItem owner ) + { + super(toolkit, owner); + } + + protected void init() + { + String label = ((MenuItem)owner).getLabel(); + create(label, label.equals("-"), (owner instanceof CheckboxMenuItem)); + } + + protected void setup() + { + } + + private native void create(String label, boolean isSeperator, boolean isCheckable); + + public void finalize() + { + dispose(); + } + + public native void dispose(); + + private void fireClick(int modifiers) + { + ActionEvent e = new ActionEvent(owner, + ActionEvent.ACTION_PERFORMED, + ((MenuItem)owner).getActionCommand(), + System.currentTimeMillis(), + (modifiers & 0x2FF)); + QtToolkit.eventQueue.postEvent(e); + } + + // ************ Public methods ********************* + + public void disable() + { + setEnabled(false); + } + + public void enable() + { + setEnabled(true); + } + + public native void setEnabled(boolean b); + + public native void setLabel(String label); + + public native void setState(boolean state); +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtMenuPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuPeer.java new file mode 100644 index 000000000..0f65fecbd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtMenuPeer.java @@ -0,0 +1,149 @@ +/* QtMenuPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Menu; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.event.ActionEvent; +import java.awt.peer.MenuPeer; +import java.util.Vector; + +public class QtMenuPeer extends QtMenuComponentPeer implements MenuPeer +{ + Vector items; + boolean itemsAdded; + + public QtMenuPeer( QtToolkit kit, Menu owner ) + { + super( kit, owner ); + itemsAdded = false; + } + + protected native void init(); + + protected void setup() + { + items = new Vector(); + setLabel( ((Menu)owner).getLabel() ); + if( ((Menu)owner).isTearOff() ) + allowTearOff(); + } + + // Recurse the menu tree adding items, + // called from the MenuBar addMenus() method, called from the Frame peer. + void addItems() + { + if(!itemsAdded) + { + Menu o = (Menu)owner; + for( int i=0; i < o.getItemCount(); i++ ) + { + MenuItem ci = o.getItem(i); + if (ci instanceof Menu && ci.getPeer() != null) + ((QtMenuPeer)ci.getPeer()).addItems(); + addItem( ci ); + } + itemsAdded = true; + } + } + + private void fireClick() + { + ActionEvent e = new ActionEvent(owner, + ActionEvent.ACTION_PERFORMED, + ((Menu)owner).getActionCommand()); + QtToolkit.eventQueue.postEvent(e); + } + + private native void allowTearOff(); + + private native void insertSeperator(); + + private native void insertItem(QtMenuItemPeer p); + + private native void insertMenu(QtMenuPeer menu); + + private native void delItem(long ptr); + + private void add(long ptr) + { + items.add(new Long(ptr)); + } + + // ************ Public methods ********************* + + public void addItem( MenuItem item ) + { + if( item instanceof Menu || item instanceof PopupMenu) + insertMenu((QtMenuPeer)item.getPeer()); + else + { + QtMenuItemPeer p = (QtMenuItemPeer)item.getPeer(); + insertItem(p); + } + } + + public void addSeparator() + { + insertSeperator(); + } + + public void delItem( int index ) + { + long ptr = ((Long)items.elementAt(index)).longValue(); + delItem(ptr); + items.removeElementAt(index); + } + + // Inherited methods.. + + public void disable() + { + setEnabled(false); + } + + public void enable() + { + setEnabled(true); + } + + public native void setEnabled(boolean enabled); + + public native void setLabel(String text); +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtPanelPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtPanelPeer.java new file mode 100644 index 000000000..1ac0ca9a6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtPanelPeer.java @@ -0,0 +1,56 @@ +/* QtPanelPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Component; +import java.awt.peer.PanelPeer; + +public class QtPanelPeer extends QtContainerPeer implements PanelPeer +{ + public QtPanelPeer( QtToolkit kit, Component owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtPopupMenuPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtPopupMenuPeer.java new file mode 100644 index 000000000..eb4dae404 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtPopupMenuPeer.java @@ -0,0 +1,76 @@ +/* QtPopupMenuPeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Component; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Event; +import java.awt.peer.PopupMenuPeer; + +public class QtPopupMenuPeer extends QtMenuPeer implements PopupMenuPeer +{ + public QtPopupMenuPeer( QtToolkit kit, PopupMenu owner ) + { + super( kit, owner ); + } + + private native void showNative(int x, int y); + + // ************ Public methods ********************* + + /** + * Part of the older API, replaced by event version instead. + */ + public void show (Component origin, int x, int y) + { + if( origin == null ) + throw new NullPointerException("Null parent component."); + addItems(); + + Point p = origin.getLocationOnScreen(); + showNative( (int)p.getX() + x, (int)p.getY() + y ); + } + + public void show (Event e) + { + if (!(e.target instanceof Component)) + throw new IllegalArgumentException("Expecting a component Event target!"); + show((Component)e.target, e.x, e.y); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtRepaintThread.java b/libjava/classpath/gnu/java/awt/peer/qt/QtRepaintThread.java new file mode 100644 index 000000000..6861be8fc --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtRepaintThread.java @@ -0,0 +1,156 @@ +/* QtRepaintThread.java -- Repaint thread implementation + Copyright (C) 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.qt; + +/** + * This class does repainting of Component back-buffers. It is undesirable to + * do this directly from the paint callback in QtComponentPeer, because that + * is executed from the main thread. Thus, if a call is made at the same time + * which requires execution by the main thread, and this is sharing a lock with + * paint(), then a deadlock will occur, which must be avoided. In general, + * the main Qt thread should avoid calling into java code as far as possible. + * + */ +public class QtRepaintThread extends Thread +{ + static class RepaintComponent + { + public QtComponentPeer curr; + public RepaintComponent next; + public boolean paintAll; + public int x, y, w, h; + + public RepaintComponent(QtComponentPeer p) + { + curr = p; + next = null; + paintAll = true; + } + + public RepaintComponent(QtComponentPeer p, int x, int y, int w, int h) + { + this(p); + paintAll = false; + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } + } + + RepaintComponent component; + boolean busy; + + public QtRepaintThread() + { + component = null; + } + + public void run() + { + while( true ) + { + try + { + busy = false; + // Wait for a repaint + sleep(100); + busy = true; + } + catch (InterruptedException ie) + { + while( component != null ) + { + try + { + if( component.paintAll ) + { + // update the back-buffer. + component.curr.paintBackBuffer(); + component.curr.QtUpdate(); // trigger a native repaint event + } + else + { + component.curr.paintBackBuffer(component.x, component.y, + component.w, component.h); + component.curr.QtUpdateArea(component.x, component.y, + component.w, component.h); + } + } + catch (InterruptedException e) + { + } + component = component.next; + } + } + } + } + + /** + * Enqueue a component for repainting. + */ + public synchronized void queueComponent(QtComponentPeer p) + { + if( component == null ) + component = new RepaintComponent(p); + else + { + RepaintComponent r = component; + while( r.next != null ) r = r.next; + r.next = new RepaintComponent(p); + } + interrupt(); + } + + /** + * Enqueue a component for repainting. + */ + public synchronized void queueComponent(QtComponentPeer p, int x, int y, + int w, int h) + { + if( component == null ) + component = new RepaintComponent(p, x, y, w, h); + else + { + RepaintComponent r = component; + while( r.next != null ) r = r.next; + r.next = new RepaintComponent(p, x, y, w, h); + } + interrupt(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtScreenDevice.java b/libjava/classpath/gnu/java/awt/peer/qt/QtScreenDevice.java new file mode 100644 index 000000000..c7d8a4784 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtScreenDevice.java @@ -0,0 +1,115 @@ +/* QtScreenDevice.java -- Wrapper on a Qt screen Widget + Copyright (C) 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.qt; + +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.DisplayMode; +import java.awt.GraphicsConfigTemplate; +import java.awt.Rectangle; +import java.awt.Window; + +public class QtScreenDevice extends GraphicsDevice +{ + private long nativeObject; + private int id; + private String IDstring; + QtScreenDeviceConfiguration config; + + public QtScreenDevice(int id) + { + this.id = id; + IDstring = "QtScreen" + id; + init( id ); + config = new QtScreenDeviceConfiguration(this); + } + + public native void init( int id ); + public native void dispose(); + + // Package-private methods used by QtScreenDeviceConfiguration + native Rectangle getBounds(); + native int getDpiX(); + native int getDpiY(); + native int depth(); + + // ****************** Public methods *********************** + + public GraphicsConfiguration getBestConfiguration(GraphicsConfigTemplate gct) + { + return config; + } + + public GraphicsConfiguration[] getConfigurations() + { + return new GraphicsConfiguration[]{ config }; + } + + public GraphicsConfiguration getDefaultConfiguration() + { + return config; + } + + public String getIDstring() + { + return IDstring; + } + + public int getType() + { + return TYPE_RASTER_SCREEN; + } + + public boolean isDisplayChangeSupported() + { + return false; + } + + public boolean isFullScreenSupported() + { + return false; + } + + public void setDisplayMode(DisplayMode dm) + { + } + + public void setFullScreenWindow(Window w) + { + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtScreenDeviceConfiguration.java b/libjava/classpath/gnu/java/awt/peer/qt/QtScreenDeviceConfiguration.java new file mode 100644 index 000000000..34de36c09 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtScreenDeviceConfiguration.java @@ -0,0 +1,145 @@ +/* QtScreenDeviceConfiguration.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.ImageCapabilities; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.VolatileImage; +import java.awt.geom.AffineTransform; + +public class QtScreenDeviceConfiguration extends GraphicsConfiguration { + + private QtScreenDevice owner; + private Rectangle bounds; + private double dpiX, dpiY; + private int depth; + + public QtScreenDeviceConfiguration(QtScreenDevice owner) + { + this.owner = owner; + bounds = owner.getBounds(); + dpiX = owner.getDpiX(); + dpiY = owner.getDpiY(); + depth = owner.depth(); + } + + public BufferedImage createCompatibleImage(int width, int height) + { + switch( depth ) + { + case 24: + return new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + case 16: + return new BufferedImage(width, height, + BufferedImage.TYPE_USHORT_565_RGB); + case 8: + return new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED); + default: + case 32: + return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + } + } + + public BufferedImage createCompatibleImage(int width, int height, int transparency) + { + // FIXME: Take the transpareny flag into account? + // For now, ignore it and just use an alpha channel. + if(depth == 32) + return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + return createCompatibleImage(width, height); + } + + public VolatileImage createCompatibleVolatileImage(int width, int height) + { + return new QtVolatileImage( width, height ); + } + + public VolatileImage createCompatibleVolatileImage(int width, int height, + ImageCapabilities caps) + { + return createCompatibleVolatileImage( width, height ); + } + + public Rectangle getBounds() + { + return bounds; + } + + public ColorModel getColorModel() + { + // FIXME? + return QtToolkit.getDefaultToolkit().getColorModel(); + } + + public ColorModel getColorModel(int transparency) + { + // FIXME? + return QtToolkit.getDefaultToolkit().getColorModel(); + } + + public AffineTransform getDefaultTransform() + { + return new AffineTransform(); + } + + public GraphicsDevice getDevice() + { + return owner; + } + + /** + * Returns the transform which transforms from this display's resolution + * to a 72 DPI resolution. + */ + public AffineTransform getNormalizingTransform() + { + AffineTransform nTrans = new AffineTransform(); + nTrans.scale( 72.0 / dpiX, 72.0 / dpiY ); + return nTrans; + } + + public VolatileImage createCompatibleVolatileImage(int width, int height, + int transparency) + { + return createCompatibleVolatileImage(width, height); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtScrollPanePeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtScrollPanePeer.java new file mode 100644 index 000000000..079d06de7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtScrollPanePeer.java @@ -0,0 +1,90 @@ +/* QtScrollPanePeer.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Adjustable; +import java.awt.Insets; +import java.awt.ScrollPane; +import java.awt.peer.ScrollPanePeer; + +public class QtScrollPanePeer extends QtContainerPeer implements ScrollPanePeer +{ + public QtScrollPanePeer( QtToolkit kit, ScrollPane owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setPolicy( ((ScrollPane)owner).getScrollbarDisplayPolicy() ); + } + + private native void setPolicy(int policy); + + // ************ Public methods ********************* + + public native void childResized(int width, int height); + + public native int getHScrollbarHeight(); + + public native int getVScrollbarWidth(); + + public native void setScrollPosition(int x, int y); + + public Insets getInsets() + { + // FIXME : more accurate? + return new Insets(5 + getHScrollbarHeight(), // Top + 5 + getVScrollbarWidth(), // Left + 5, // Bottom + 5); // Right + } + + public void setUnitIncrement(Adjustable item, int inc) + { + // FIXME + } + + public void setValue(Adjustable item, int value) + { + // FIXME + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtScrollbarPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtScrollbarPeer.java new file mode 100644 index 000000000..694287131 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtScrollbarPeer.java @@ -0,0 +1,80 @@ +/* QtScrollbarPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Scrollbar; +import java.awt.event.AdjustmentEvent; +import java.awt.peer.ScrollbarPeer; + +public class QtScrollbarPeer extends QtComponentPeer implements ScrollbarPeer +{ + public QtScrollbarPeer( QtToolkit kit, Scrollbar owner ) + { + super( kit, owner ); + } + + public native void init(); + + protected void setup() + { + super.setup(); + Scrollbar o = (Scrollbar)owner; + setValues(o.getValue(), o.getVisible(), o.getMinimum(), o.getMaximum()); + setOrientation(o.getOrientation()); + setLineIncrement(o.getLineIncrement()); + setPageIncrement(o.getPageIncrement()); + } + + private native void setOrientation(int orientation); + + private void fireMoved(int type, int value) + { + AdjustmentEvent e = new AdjustmentEvent((Scrollbar)owner, + AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED, + type, value); + QtToolkit.eventQueue.postEvent(e); + } + + // ************ Public methods ********************* + + public native void setLineIncrement(int inc); + + public native void setPageIncrement(int inc); + + public native void setValues(int value, int visible, int min, int max); +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtTextAreaPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtTextAreaPeer.java new file mode 100644 index 000000000..a5aff58ef --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtTextAreaPeer.java @@ -0,0 +1,179 @@ +/* QtTextAreaPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.TextArea; +import java.awt.event.TextEvent; +import java.awt.im.InputMethodRequests; +import java.awt.peer.TextAreaPeer; + +public class QtTextAreaPeer extends QtComponentPeer implements TextAreaPeer +{ + public QtTextAreaPeer( QtToolkit kit, TextArea owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setText(((TextArea)owner).getText()); + setEditable(((TextArea)owner).isEditable()); + } + + /** + * Returns the start (start = true) or end (start = false) of the selection. + */ + private native int getSelection(boolean start); + + /** + * Called back on a text edit. + */ + private void textChanged() + { + TextEvent e = new TextEvent(owner, TextEvent.TEXT_VALUE_CHANGED); + QtToolkit.eventQueue.postEvent(e); + } + + // ************ Public methods ********************* + + public long filterEvents(long filter) + { + return filter; + } + + public native int getCaretPosition(); + + public Rectangle getCharacterBounds(int pos) + { + // FIXME + return new Rectangle(0,0,0,0); + } + + /** + * Implemented, but who uses it? + */ + public native int getIndexAtPoint(int x, int y); + +// public void reshape(int x, int y, +// int width, int height) +// { +// if(width != 0 || height != 0) +// super.reshape(x, y, width, height); +// else +// super.reshape(x, y, 10, 10); +// } + + public Dimension getMinimumSize(int rows, int cols) + { + // FIXME + return getMinimumSize(); + } + + public Dimension getPreferredSize(int rows, int cols) + { + // FIXME + return getPreferredSize(); + } + + public int getSelectionEnd() + { + return getSelection(false); + } + + public int getSelectionStart() + { + return getSelection(true); + } + + public native String getText(); + + public void insert(String text, int pos) + { + // Not very efficient, no. + String s = getText(); + setText(s.substring(0, pos) + text + s.substring(pos)); + } + + public void insertText(String text, int pos) + { + insert(text, pos); + } + + public Dimension minimumSize(int rows, int cols) + { + return getMinimumSize(rows, cols); + } + + public Dimension preferredSize(int rows, int cols) + { + return getPreferredSize(rows, cols); + } + + public void replaceRange(String insert, int start_pos, int end_pos) + { + // Not very efficient, no. + String text = getText(); + String right = text.substring(0, start_pos); + String left = text.substring(end_pos); + setText(right + insert + left); + } + + public void replaceText(String text, int start_pos, int end_pos) + { + replaceRange(text, start_pos, end_pos); + } + + public native void setText(String text); + + public native void select(int start_pos, int end_pos); + + public native void setEditable(boolean editable); + + public native void setCaretPosition(int pos); + + public InputMethodRequests getInputMethodRequests() + { + // TODO Auto-generated method stub + return null; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtTextFieldPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtTextFieldPeer.java new file mode 100644 index 000000000..f92943202 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtTextFieldPeer.java @@ -0,0 +1,159 @@ +/* QtTextFieldPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.TextField; +import java.awt.event.TextEvent; +import java.awt.im.InputMethodRequests; +import java.awt.peer.TextFieldPeer; + +public class QtTextFieldPeer extends QtComponentPeer implements TextFieldPeer +{ + public QtTextFieldPeer( QtToolkit kit, TextField owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + setText(((TextField)owner).getText()); + setEditable(((TextField)owner).isEditable()); + } + + /** + * Called back on a text edit. + */ + private void textChanged() + { + TextEvent e = new TextEvent(owner, TextEvent.TEXT_VALUE_CHANGED); + QtToolkit.eventQueue.postEvent(e); + } + + /** + * Returns the start (start = true) or end (start = false) of the selection. + */ + private native int getSelection(boolean start); + + private native Dimension getMinimumSizeNative(int columns); + + private native Dimension getPreferredSizeNative(int columns); + + // ************ Public methods ********************* + + public long filterEvents(long e) + { + return e; + } + + public native int getCaretPosition(); + + public Rectangle getCharacterBounds(int i) + { + return new Rectangle(0,0,0,0); + } + + public int getIndexAtPoint(int x, int y) + { + // FIXME + return 0; + } + + public Dimension getMinimumSize(int columns) + { + Dimension d = getMinimumSizeNative( columns ); + if ( d == null ) + return new Dimension(10, 10); + return d; + } + + public Dimension getPreferredSize(int columns) + { + Dimension d = getPreferredSizeNative( columns ); + if ( d == null ) + return owner.getSize(); + return d; + } + + public int getSelectionEnd() + { + return getSelection(false); + } + + public int getSelectionStart() + { + return getSelection(true); + } + + public native String getText(); + + public Dimension minimumSize(int cols) + { + return getMinimumSize(cols); + } + + public Dimension preferredSize(int cols) + { + return getPreferredSize(cols); + } + + public native void select(int selStart, int selEnd); + + public native void setCaretPosition(int pos); + + public void setEchoCharacter(char c) + { + setEchoChar(c); + } + + public native void setEchoChar(char echoChar); + + public native void setEditable(boolean editable); + + public native void setText(String l); + + public InputMethodRequests getInputMethodRequests() + { + // TODO Auto-generated method stub + return null; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtToolkit.java b/libjava/classpath/gnu/java/awt/peer/qt/QtToolkit.java new file mode 100644 index 000000000..9f8a691c6 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtToolkit.java @@ -0,0 +1,470 @@ +/* QtToolkit.java -- + Copyright (C) 2005, 2006, 2007 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.qt; + +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.EmbeddedWindow; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.peer.EmbeddedWindowPeer; + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.PrintJob; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.event.AWTEventListener; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.CheckboxMenuItemPeer; +import java.awt.peer.CheckboxPeer; +import java.awt.peer.ChoicePeer; +import java.awt.peer.DialogPeer; +import java.awt.peer.FileDialogPeer; +import java.awt.peer.FontPeer; +import java.awt.peer.FramePeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.ListPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.PopupMenuPeer; +import java.awt.peer.RobotPeer; +import java.awt.peer.ScrollPanePeer; +import java.awt.peer.ScrollbarPeer; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.WindowPeer; +import java.io.InputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class QtToolkit extends ClasspathToolkit +{ + public static EventQueue eventQueue = null; // the native event queue + public static QtRepaintThread repaintThread = null; + public static MainQtThread guiThread = null; + public static QtGraphicsEnvironment graphicsEnv = null; + + private static void initToolkit() + { + eventQueue = new EventQueue(); + repaintThread = new QtRepaintThread(); + System.loadLibrary("qtpeer"); + + String theme = null; + try + { + String style = System.getProperty("qtoptions.style"); + if(style != null) + theme = style; + } + catch(SecurityException e) + { + } + catch(IllegalArgumentException e) + { + } + + boolean doublebuffer = true; + try + { + String style = System.getProperty("qtoptions.nodoublebuffer"); + if(style != null) + doublebuffer = false; + } + catch(SecurityException e) + { + } + catch(IllegalArgumentException e) + { + } + + guiThread = new MainQtThread( theme, doublebuffer ); + guiThread.start(); + repaintThread.start(); + } + + /** + * Construct the toolkit! + */ + public QtToolkit() + { + if( guiThread == null ) + initToolkit(); + + // make sure the GUI thread has started. + while (!guiThread.isRunning()) + ; + + if( graphicsEnv == null ) + graphicsEnv = new QtGraphicsEnvironment( this ); + } + + native String[] nativeFontFamilies(); + + native int numScreens(); + + native int defaultScreen(); + + // ************ Public methods ********************* + + public synchronized native void beep(); + + public int checkImage(Image image, int w, int h, ImageObserver observer) + { + if(image instanceof QtImage) + return ((QtImage)image).checkImage(observer); + + return ImageObserver.ERROR; // FIXME + } + + protected ButtonPeer createButton( Button target ) + { + return new QtButtonPeer( this, target ); + } + + protected CanvasPeer createCanvas(Canvas target) + { + return new QtCanvasPeer( this, target ); + } + + protected CheckboxPeer createCheckbox(Checkbox target) + { + return new QtCheckboxPeer( this, target ); + } + + protected ChoicePeer createChoice(Choice target) + { + return new QtChoicePeer( this, target ); + } + + protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) + { + return new QtMenuItemPeer( this, target ); + } + + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) + { + throw new RuntimeException("Not implemented"); + } + + protected FramePeer createFrame(Frame target) + { + return new QtFramePeer( this, target ); + } + + protected FileDialogPeer createFileDialog(FileDialog target) + { + return new QtFileDialogPeer( this, target ); + } + + public Image createImage(ImageProducer producer) + { + return new QtImage( producer ); + } + + public Image createImage(byte[] imageData, + int imageOffset, + int imageLength) + { + byte[] dataCopy = new byte[imageLength]; + System.arraycopy(imageData, imageOffset, dataCopy, 0, imageLength); + return new QtImage( dataCopy ); + } + + public Image createImage(String filename) + { + return new QtImage( filename ); + } + + public Image createImage(URL url) + { + return new QtImage( url ); + } + + protected TextFieldPeer createTextField(TextField target) + { + return new QtTextFieldPeer(this,target); + } + + protected LabelPeer createLabel(Label target) + { + return new QtLabelPeer( this, target ); + } + + protected ListPeer createList(List target) + { + return new QtListPeer( this, target ); + } + + protected ScrollbarPeer createScrollbar(Scrollbar target) + { + return new QtScrollbarPeer( this, target ); + } + + protected ScrollPanePeer createScrollPane(ScrollPane target) + { + return new QtScrollPanePeer( this, target ); + } + + protected TextAreaPeer createTextArea(TextArea target) + { + return new QtTextAreaPeer( this, target ); + } + + protected PanelPeer createPanel(Panel target) + { + return new QtPanelPeer( this, target); + } + + protected WindowPeer createWindow(Window target) + { + return new QtWindowPeer( this, target ); + } + + protected DialogPeer createDialog(Dialog target) + { + return new QtDialogPeer( this, target ); + } + + protected MenuBarPeer createMenuBar(MenuBar target) + { + return new QtMenuBarPeer( this, target ); + } + + protected MenuPeer createMenu(Menu target) + { + return new QtMenuPeer( this, target ); + } + + protected PopupMenuPeer createPopupMenu(PopupMenu target) + { + return new QtPopupMenuPeer( this, target ); + } + + protected MenuItemPeer createMenuItem(MenuItem target) + { + return new QtMenuItemPeer( this, target ); + } + + /** + * @since 1.4 + */ + public AWTEventListener[] getAWTEventListeners() + { + return null; // FIXME + } + + /** + * @since 1.4 + */ + public AWTEventListener[] getAWTEventListeners(long mask) + { + return null; // FIXME + } + + public ColorModel getColorModel() + { + return new DirectColorModel(32, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + } + + /** + * Just return the defaults. + */ + public String[] getFontList() + { + String[] builtIn = new String[] { "Dialog", + "DialogInput", + "Monospaced", + "Serif", + "SansSerif" }; + String[] nat = nativeFontFamilies(); + String[] allFonts = new String[ nat.length + 5 ]; + System.arraycopy(builtIn, 0, allFonts, 0, 5); + System.arraycopy(nat, 0, allFonts, 5, nat.length); + return allFonts; + } + + public FontMetrics getFontMetrics(Font font) + { + return new QtFontMetrics(font); + } + + protected FontPeer getFontPeer(String name, + int style) + { + Map attrs = new HashMap (); + ClasspathFontPeer.copyStyleToAttrs(style, attrs); + ClasspathFontPeer.copySizeToAttrs(12, attrs); // Default size is 12. + return getClasspathFontPeer (name, attrs); + } + + public Image getImage(String filename) + { + return new QtImage(filename); + } + + public Image getImage(URL url) + { + return createImage( url ); + } + + public PrintJob getPrintJob(Frame frame, + String jobtitle, + Properties props) + { + SecurityManager sm; + sm = System.getSecurityManager(); + if (sm != null) + sm.checkPrintJobAccess(); + + throw new RuntimeException("Not implemented"); + } + + public Clipboard getSystemClipboard() + { + throw new RuntimeException("Not implemented"); + } + + protected EventQueue getSystemEventQueueImpl() + { + return eventQueue; + } + + public native Dimension getScreenSize(); + + public native int getScreenResolution(); + + public Map mapInputMethodHighlight(InputMethodHighlight highlight) + { + return null; // FIXME + } + + public boolean prepareImage(Image image, int w, int h, ImageObserver observer) + { + if(image instanceof QtImage) + return true; + return false; // FIXME? + } + + public native void sync(); + + // ********************** ClasspathToolkit methods + + public GraphicsEnvironment getLocalGraphicsEnvironment() + { + return graphicsEnv; + } + + public ClasspathFontPeer getClasspathFontPeer (String name, Map attrs) + { + return new QtFontPeer (name, attrs); + } + + // FIXME + public Font createFont(int format, InputStream stream) + { + throw new UnsupportedOperationException(); + } + + // FIXME + public RobotPeer createRobot (GraphicsDevice screen) throws AWTException + { + throw new UnsupportedOperationException(); + } + + public EmbeddedWindowPeer createEmbeddedWindow(EmbeddedWindow w) + { + // return new QtEmbeddedWindowPeer( this, w ); + return null; + } + + @Override + public boolean isModalExclusionTypeSupported + (Dialog.ModalExclusionType modalExclusionType) + { + return false; + } + + @Override + public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) + { + return false; + } + + +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtVolatileImage.java b/libjava/classpath/gnu/java/awt/peer/qt/QtVolatileImage.java new file mode 100644 index 000000000..a203de0d0 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtVolatileImage.java @@ -0,0 +1,434 @@ +/* QtVolatileImage.java -- + Copyright (C) 2005, 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.qt; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Image; +import java.awt.ImageCapabilities; +import java.awt.GraphicsConfiguration; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.MemoryImageSource; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; +import java.util.Hashtable; +import java.util.WeakHashMap; + +/** + * QtVolatileImage - wraps a QImage + * + */ +public class QtVolatileImage extends VolatileImage +{ + int width = -1, height = -1; + + /** + * Properties. + */ + Hashtable props; + + /** + * Pointer to the QImage + */ + long nativeObject; + + /* + * The 32-bit AARRGGBB format the uses. + */ + static ColorModel nativeModel = new DirectColorModel(32, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + + /** + * Clears the image to RGBA 0 + */ + public native void clear(); + + /** + * Returns a copy of the pixel data as a java array. + */ + private native int[] getPixels(); + + /** + * Allocates a QImage + */ + private native void createImage(); + + /** + * HashMap of Graphics objects painting on this Image. + */ + WeakHashMap painters; + + /** + * Flags if this image is to be destroyed. + */ + boolean killFlag; + + /** + * Frees the above. + */ + private native void freeImage(); + + /** + * Blit a QImage + */ + public native void blit(QtImage i); + public native void blit(QtImage i, int x, int y, int w, int h); + + /** + * Sets the image to scaled copy of src image. hints are rendering hints. + */ + private native void createScaledImage(QtVolatileImage src, int hints); + + /** + * Draws the image optionally composited. + */ + private native void drawPixels (QtGraphics gc, + int bg_red, int bg_green, int bg_blue, + int x, int y, + boolean composite); + /** + * Draws the image, optionally scaled and composited. + */ + private native void drawPixelsScaled (QtGraphics gc, + int bg_red, int bg_green, int bg_blue, + int x, int y, int width, int height, + boolean composite); + + /** + * Draws the image transformed. + */ + private native void drawPixelsTransformed (QtGraphics gc, QMatrix transform); + + /** + * Draws the image scaled flipped and optionally composited. + */ + native void drawPixelsScaledFlipped (QtGraphics 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); + + /** + * Constructs an empty QtVolatileImage. + */ + public QtVolatileImage (int width, int height) + { + this.width = width; + this.height = height; + props = new Hashtable(); + createImage(); + clear(); + } + + /** + * Constructs a scaled version of the src bitmap, using Qt + */ + private QtVolatileImage (QtVolatileImage src, int width, int height, + int hints) + { + this.width = width; + this.height = height; + props = new Hashtable(); + + createScaledImage(src, hints); + } + + + public void finalize() + { + dispose(); + } + + public void dispose() + { + if( painters == null || painters.isEmpty() ) + freeImage(); + else + killFlag = true; // can't destroy image yet. + // Do so when all painters are gone. + } + + // java.awt.Image methods //////////////////////////////////////////////// + + public int getWidth (ImageObserver observer) + { + return getWidth(); + } + + public int getHeight (ImageObserver observer) + { + return getHeight(); + } + + public Object getProperty (String name, ImageObserver observer) + { + Object value = props.get (name); + return (value == null) ? UndefinedProperty : value; + } + + /** + * Returns the source of this image. + */ + public ImageProducer getSource () + { + return new MemoryImageSource(width, height, nativeModel, getPixels(), + 0, width); + } + + void putPainter(QtImageGraphics g) + { + if( painters == null ) + painters = new WeakHashMap(); + painters.put( g, "dummy" ); + } + + void removePainter(QtImageGraphics g) + { + painters.remove( g ); + if( killFlag && painters.isEmpty() ) + freeImage(); + } + + /** + * Creates a Graphics context for this image. + */ + public Graphics getGraphics () + { + QtImageGraphics g = new QtImageGraphics( this ); + putPainter( g ); + return g; + } + + /** + * Returns a scaled instance of this image. + */ + public Image getScaledInstance(int width, + int height, + int hints) + { + if (width <= 0 || height <= 0) + throw new IllegalArgumentException("Width and height of scaled bitmap"+ + "must be >= 0"); + + return new QtVolatileImage(this, width, height, hints); + } + + /** + */ + public void flush () + { + // FIXME ? + } + + /** + * Returns the image status, used by QtToolkit + */ + public int checkImage (ImageObserver observer) + { + return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT; + } + + // Drawing methods //////////////////////////////////////////////// + + /** + * Draws an image with eventual scaling/transforming. + */ + public boolean drawImage (QtGraphics g, QMatrix matrix, + ImageObserver observer) + { + drawPixelsTransformed (g, matrix); + return true; + } + + /** + * Draws an image to the QtGraphics context, at (x,y) with optional + * compositing with a background color. + */ + public boolean drawImage (QtGraphics g, int x, int y, + Color bgcolor, ImageObserver observer) + { + if(bgcolor != null) + drawPixels(g, bgcolor.getRed (), bgcolor.getGreen (), + bgcolor.getBlue (), x, y, true); + else + drawPixels(g, 0, 0, 0, x, y, false); + + return true; + } + + /** + * Draws an image to the QtGraphics context, at (x,y) scaled to + * width and height, with optional compositing with a background color. + */ + public boolean drawImage (QtGraphics g, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) + { + 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; + } + + /** + * Draws an image with eventual scaling/transforming. + */ + public boolean drawImage (QtGraphics g, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, ImageObserver observer) + { + 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 ( srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0) + return true; + + 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; + } + + public native void copyArea(int x, int y, int width, int height, + int dx, int dy); + + //******************** VolatileImage stuff ******************** + + public boolean contentsLost() + { + return false; + } + + public Graphics2D createGraphics() + { + QtImageGraphics g = new QtImageGraphics(this); + putPainter( g ); + return g; + } + + public ImageCapabilities getCapabilities() + { + return new ImageCapabilities(false) + { + public boolean isTrueVolatile() + { + return false; + } + }; + } + + public int getHeight() + { + return height; + } + + public BufferedImage getSnapshot() + { + BufferedImage bi = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB_PRE); + bi.setRGB( 0, 0, width, height, getPixels(), 0, width); + return bi; + } + + public int getWidth() + { + return width; + } + + public int validate(GraphicsConfiguration gc) + { + return IMAGE_OK; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/qt/QtWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/qt/QtWindowPeer.java new file mode 100644 index 000000000..2dfe2ec5a --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/qt/QtWindowPeer.java @@ -0,0 +1,105 @@ +/* QtWindowPeer.java -- + Copyright (C) 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.qt; + +import java.awt.Component; +import java.awt.peer.WindowPeer; + +public class QtWindowPeer extends QtContainerPeer implements WindowPeer +{ + public QtWindowPeer( QtToolkit kit, Component owner ) + { + super( kit, owner ); + } + + protected native void init(); + + protected void setup() + { + super.setup(); + } + + // ************ Public methods ********************* + + public native void toBack(); + + public native void toFront(); + + /* + * Belongs to Frame and Dialog, but no sense in duplicating code. + */ + public native void setTitle(String title); + + public void updateAlwaysOnTop() + { + // TODO Auto-generated method stub + + } + + public boolean requestWindowFocus() + { + // TODO Auto-generated method stub + return false; + } + + public void updateIconImages() + { + // TODO: Implement properly. + } + + public void updateMinimumSize() + { + // TODO: Implement properly. + } + + public void setModalBlocked(java.awt.Dialog d, boolean b) + { + // TODO: Implement properly. + } + + public void updateFocusableWindowState() + { + // TODO: Implement properly. + } + + public void setAlwaysOnTop(boolean b) + { + // TODO: Implement properly. + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingButtonPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingButtonPeer.java new file mode 100644 index 000000000..1a42fc953 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingButtonPeer.java @@ -0,0 +1,261 @@ +/* SwingButtonPeer.java -- A Swing based peer for AWT buttons + Copyright (C) 2006, 2007 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.swing; + +import java.awt.Button; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.ButtonPeer; + +import javax.swing.JButton; +import javax.swing.JComponent; + +/** + * A Swing based peer for the AWT button. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingButtonPeer + extends SwingComponentPeer + implements ButtonPeer +{ + + /** + * A specialized Swing button to be used as AWT button. + * + * @author Roman Kennke (kennke@aicas.com) + */ + class SwingButton + extends JButton + implements SwingComponent + { + Button button; + + SwingButton(Button button) + { + this.button = button; + } + + /** + * Overridden so that this method returns the correct value even without a + * peer. + * + * @return the screen location of the button + */ + public Point getLocationOnScreen() + { + return SwingButtonPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value for the + * swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (button != null) + retVal = button.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingButtonPeer.this.createImage(w, h); + } + + /** + * Overridden, so that the Swing button can create a Graphics without its + * own peer. + * + * @return a graphics instance for the button + */ + public Graphics getGraphics() + { + return SwingButtonPeer.this.getGraphics(); + } + + /** + * Returns this button. + * + * @return this button + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code> after having retargetted it to this + * button. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseEvent(ev); + } + + /** + * Handles mouse motion events by forwarding it to + * <code>processMouseMotionEvent()</code> after having retargetted it to + * this button. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to + * <code>processKeyEvent()</code> after having retargetted it to this + * button. + * + * @param ev the mouse event + */ + public void handleKeyEvent(KeyEvent ev) + { + ev.setSource(this); + processKeyEvent(ev); + } + + public Container getParent() + { + Container par = null; + if (button != null) + par = button.getParent(); + return par; + } + + /** + * Handles focus events by forwarding it to + * <code>processFocusEvent()</code>. + * + * @param ev the Focus event + */ + public void handleFocusEvent(FocusEvent ev) + { + processFocusEvent(ev); + } + + public void requestFocus() { + SwingButtonPeer.this.requestFocus(awtComponent, false, true, 0); + } + + public boolean requestFocus(boolean temporary) { + return SwingButtonPeer.this.requestFocus(awtComponent, temporary, + true, 0); + } + } + + /** + * Listens for ActionEvents on the Swing button and triggers corresponding + * ActionEvents on the AWT button. + * + * @author Roman Kennke (kennke@aicas.com) + */ + class SwingButtonListener implements ActionListener + { + + /** + * Receives notification when an action was performend on the button. + * + * @param event the action event + */ + public void actionPerformed(ActionEvent event) + { + Button b = (Button) SwingButtonPeer.this.awtComponent; + ActionListener[] l = b.getActionListeners(); + if (l.length == 0) + return; + ActionEvent ev = new ActionEvent(b, ActionEvent.ACTION_PERFORMED, + b.getActionCommand()); + for (int i = 0; i < l.length; ++i) + l[i].actionPerformed(ev); + } + + } + + /** + * Constructs a new SwingButtonPeer. + * + * @param theButton the AWT button for this peer + */ + public SwingButtonPeer(Button theButton) + { + SwingButton button = new SwingButton(theButton); + button.setText(theButton.getLabel()); + button.addActionListener(new SwingButtonListener()); + init(theButton, button); + } + + /** + * Sets the label of the button. This call is forwarded to the setText method + * of the managed Swing button. + * + * @param label the label to set + */ + public void setLabel(String label) + { + ((SwingButton) swingComponent).setText(label); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingCanvasPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingCanvasPeer.java new file mode 100644 index 000000000..abef9ef12 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingCanvasPeer.java @@ -0,0 +1,64 @@ +/* SwingCanvasPeer.java -- A canvas peer based on Swing + 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.swing; + +import java.awt.Canvas; +import java.awt.peer.CanvasPeer; +import java.awt.peer.LightweightPeer; + +/** + * A CanvasPeer to be used together with the Swing peers. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingCanvasPeer + extends SwingComponentPeer + implements LightweightPeer, CanvasPeer +{ + + /** + * Creates a new <code>SwingCanvasPeer</code> for the specified Canvas. + * + * @param canvas the canvas. + */ + public SwingCanvasPeer(Canvas canvas) + { + init(canvas, null); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingCheckboxPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingCheckboxPeer.java new file mode 100755 index 000000000..7080831a2 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingCheckboxPeer.java @@ -0,0 +1,256 @@ +/* SwingCheckboxPeer.java -- A Swing based peer for AWT checkboxes + Copyright (C) 2007 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.swing; + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; +import java.awt.event.FocusEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.CheckboxPeer; + +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JToggleButton; + +/** + * A CheckboxPeer implementation that is backed by the Swing JCheckBox. + */ +public class SwingCheckboxPeer extends SwingComponentPeer implements + CheckboxPeer { + + /** + * A spezialized Swing checkbox used to paint the checkbox for the + * AWT checkbox. + */ + private class SwingCheckbox + extends JCheckBox + implements SwingComponent + { + Checkbox checkbox; + + SwingCheckbox(Checkbox checkbox) + { + this.checkbox = checkbox; + } + + /** + * Returns this checkbox. + * + * @return <code>this</code> + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseEvent(ev); + } + + /** + * Handles mouse motion events by forwarding it to + * <code>processMouseMotionEvent()</code>. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to <code>processKeyEvent()</code>. + * + * @param ev the mouse event + */ + public void handleKeyEvent(KeyEvent ev) + { + ev.setSource(this); + processKeyEvent(ev); + } + + /** + * Handles focus events by forwarding it to + * <code>processFocusEvent()</code>. + * + * @param ev the Focus event + */ + public void handleFocusEvent(FocusEvent ev) + { + processFocusEvent(ev); + } + + /** + * Overridden so that this method returns the correct value even without a + * peer. + * + * @return the screen location of the button + */ + public Point getLocationOnScreen() + { + return SwingCheckboxPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value + * for the swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (checkbox != null) + retVal = checkbox.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingCheckboxPeer.this.createImage(w, h); + } + + public Graphics getGraphics() + { + return SwingCheckboxPeer.this.getGraphics(); + } + + public Container getParent() + { + Container par = null; + if (checkbox != null) + par = checkbox.getParent(); + return par; + } + + public void requestFocus() { + SwingCheckboxPeer.this.requestFocus(awtComponent, false, true, 0); + } + + public boolean requestFocus(boolean temporary) { + return SwingCheckboxPeer.this.requestFocus(awtComponent, temporary, + true, 0); + } + } + + /** + * Listens for ActionEvents on the Swing button and triggers corresponding + * ActionEvents on the AWT button. + */ + class SwingCheckboxListener implements ItemListener + { + Checkbox awtCheckbox; + + SwingCheckboxListener(Checkbox checkbox) + { + awtCheckbox = checkbox; + } + + /** + * Receives notification when an action was performend on the button. + * + * @param event the action event + */ + public void itemStateChanged(ItemEvent event) + { + awtCheckbox.setState(event.getStateChange()==ItemEvent.SELECTED); + ItemListener[] l = awtCheckbox.getItemListeners(); + if (l.length == 0) + return; + ItemEvent ev = new ItemEvent(awtCheckbox, ItemEvent.ITEM_STATE_CHANGED, + awtCheckbox, event.getStateChange()); + for (int i = 0; i < l.length; ++i) + l[i].itemStateChanged(ev); + } + } + + /** + * Creates a new SwingCheckboxPeer instance. + */ + public SwingCheckboxPeer(Checkbox checkbox) + { + SwingCheckbox swingCheckbox = new SwingCheckbox(checkbox); + swingCheckbox.addItemListener(new SwingCheckboxListener(checkbox)); + + init(checkbox, swingCheckbox); + setLabel(checkbox.getLabel()); + setState(checkbox.getState()); + } + + public void setCheckboxGroup(CheckboxGroup group) + { + // TODO: Implement this. + } + + public void setLabel(String label) + { + ((JToggleButton) swingComponent).setText(label); + } + + public void setState(boolean state) + { + ((JToggleButton) swingComponent).setSelected(state); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java new file mode 100644 index 000000000..ca42fb748 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponent.java @@ -0,0 +1,99 @@ +/* SwingComponent.java -- An interface that defines a Swing component for peers + 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.swing; + +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; + +import javax.swing.JComponent; + +/** + * Defines some additional methods that the Swing components must implement + * in order to work with the Swing peers. This is usually achieved by + * subclassing a Swing component and forwarding the method calls to some + * protected JComponent method. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public interface SwingComponent +{ + + /** + * Returns the actual swing compenent. + * + * @return the actual swing compenent + */ + JComponent getJComponent(); + + /** + * Handles a mouse event. This is usually forwarded to + * {@link Component#processMouseMotionEvent(MouseEvent)} of the swing + * component. + * + * @param ev the mouse event + */ + void handleMouseEvent(MouseEvent ev); + + /** + * Handles a mouse motion event. This is usually forwarded to + * {@link Component#processMouseEvent(MouseEvent)} of the swing + * component. + * + * @param ev the mouse motion event + */ + void handleMouseMotionEvent(MouseEvent ev); + + /** + * Handles a key event. This is usually forwarded to + * {@link Component#processKeyEvent(KeyEvent)} of the swing + * component. + * + * @param ev the key event + */ + void handleKeyEvent(KeyEvent ev); + + /** + * Handles a focus event. This is usually forwarded to + * {@link Component#processFocusEvent(FocusEvent)} of the swing + * component. + * + * @param ev the focus event + */ + void handleFocusEvent(FocusEvent ev); +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java new file mode 100644 index 000000000..8be95dcb7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java @@ -0,0 +1,1136 @@ +/* SwingComponentPeer.java -- An abstract base class for Swing based peers + Copyright (C) 2006, 2007 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.swing; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.BufferCapabilities; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.BufferCapabilities.FlipContents; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.PaintEvent; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; +import java.awt.peer.ComponentPeer; +import java.awt.peer.ContainerPeer; +import java.awt.peer.LightweightPeer; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; + +/** + * The base class for Swing based component peers. This provides the basic + * functionality needed for Swing based component peers. Many methods are + * implemented to forward to the Swing component. Others however forward + * to the component's parent and expect the toplevel component peer to provide + * a real implementation of it. These are for example the key methods + * {@link #getGraphics()} and {@link #createImage(int, int)}, as well as + * {@link #getLocationOnScreen()}. + * + * This class also provides the necesary hooks into the Swing painting and + * event handling system. In order to achieve this, it traps paint, mouse and + * key events in {@link #handleEvent(AWTEvent)} and calls some special methods + * ({@link #peerPaint(Graphics)}, {@link #handleKeyEvent(KeyEvent)}, + * {@link #handleMouseEvent(MouseEvent)} and + * {@link #handleMouseMotionEvent(MouseEvent)}) that call the corresponding + * Swing methods. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingComponentPeer + implements ComponentPeer +{ + + /** + * The AWT component for this peer. + */ + protected Component awtComponent; + + /** + * The Swing component for this peer. + */ + protected SwingComponent swingComponent; + + /** + * The font that is set for this peer. + */ + protected Font peerFont; + + /** + * The current repaint area. + */ + protected Rectangle paintArea; + + /** + * 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. + */ + protected SwingComponentPeer() + { + // Nothing to do here. + } + + /** + * Initializes the AWT and Swing component for this peer. It is expected that + * subclasses call this from within their constructor. + * + * @param awtComp the AWT component for this peer + * @param swingComp the Swing component for this peer + */ + protected void init(Component awtComp, SwingComponent swingComp) + { + awtComponent = awtComp; + swingComponent = swingComp; + if (swingComponent != null) + { + JComponent c = swingComponent.getJComponent(); + if (c != null) + { + c.addNotify(); + RepaintManager.currentManager(c).setDoubleBufferingEnabled(false); + System.setProperty("gnu.awt.swing.doublebuffering", "true"); + } + } + + // Register this heavyweight component with the nearest heavyweight + // container, so we get peerPaint() triggered by that container. + if (! (this instanceof LightweightPeer)) + { + Component comp = awtComponent; + Container parent = comp.getParent(); + while (parent != null && + ! (parent.getPeer() instanceof SwingContainerPeer)) + { + comp = parent; + parent = comp.getParent(); + } + + // At this point we have the ancestor with a SwingContainerPeer + // (or null peer). + if (parent != null && parent.getPeer() instanceof SwingContainerPeer) + { + SwingContainerPeer p = (SwingContainerPeer) parent.getPeer(); + p.addHeavyweightDescendent(awtComponent); + } + } + } + + /** + * Returns the construction status of the specified image. This is called + * by {@link Component#checkImage(Image, int, int, ImageObserver)}. + * + * @param img the image + * @param width the width of the image + * @param height the height of the image + * @param ob the image observer to be notified of updates of the status + * + * @return a bitwise ORed set of ImageObserver flags + */ + public int checkImage(Image img, int width, int height, ImageObserver ob) + { + return Toolkit.getDefaultToolkit().checkImage(img, width, height, ob); + } + + /** + * Creates an image by starting the specified image producer. This is called + * by {@link Component#createImage(ImageProducer)}. + * + * @param prod the image producer to be used to create the image + * + * @return the created image + */ + public Image createImage(ImageProducer prod) + { + Image image = Toolkit.getDefaultToolkit().createImage(prod); + return image; + } + + /** + * Creates an empty image with the specified <code>width</code> and + * <code>height</code>. + * + * This is implemented to let the parent component create the image. This + * eventually goes up to the top-level component peer, which is then expected + * to deliver the image. + * + * @param width the width of the image to be created + * @param height the height of the image to be created + * + * @return the created image + */ + public Image createImage(int width, int height) + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + return parentPeer.createImage(width, height); + } + + /** + * Disables the component. This is called by {@link Component#disable()}. + */ + public void disable() + { + if (swingComponent != null) + swingComponent.getJComponent().setEnabled(false); + } + + /** + * Disposes the component peer. This should release all resources held by the + * peer. This is called when the component is no longer in use. + */ + public void dispose() + { + // Unregister this heavyweight component from the nearest heavyweight + // container. + if (! (this instanceof LightweightPeer)) + { + Component comp = awtComponent; + Container parent = comp.getParent(); + while (parent != null && + ! (parent.getPeer() instanceof SwingContainerPeer)) + { + comp = parent; + parent = comp.getParent(); + } + + // At this point we have the ancestor with a SwingContainerPeer + // (or null peer). + if (parent != null && parent.getPeer() instanceof SwingContainerPeer) + { + SwingContainerPeer p = (SwingContainerPeer) parent.getPeer(); + p.removeHeavyweightDescendent(awtComponent); + } + } + + awtComponent = null; + swingComponent = null; + } + + /** + * Enables the component. This is called by {@link Component#enable()}. + */ + public void enable() + { + if (swingComponent != null) + swingComponent.getJComponent().setEnabled(true); + } + + /** + * Returns the color model of the component. This is currently not used. + * + * @return the color model of the component + */ + public ColorModel getColorModel() + { + // FIXME: When this peer method will be used, we need to provide an + // implementation of this, probably forwarding to the toplevel peer, like + // in the other methods. + return null; + } + + /** + * Returns the font metrics for the specified font. This is called by + * {@link Component#getFontMetrics(Font)}. + * + * This is implemented to query the font metrics from the parent component. + * This will eventually call the top-level component peer, which is then + * expected to deliver a font metrics object. + * + * @param f the font for which to query the font metrics + * + * @return the font metrics for the specified font + */ + public FontMetrics getFontMetrics(Font f) + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + return parentPeer.getFontMetrics(f); + } + + /** + * Returns a {@link Graphics} object suitable for drawing on this component. + * This is called by {@link Component#getGraphics()}. + * + * This is implemented to query the graphics from the parent component and + * adjust the clip and translation to match this component. + * This will eventually call the top-level component peer, which is then + * expected to deliver a graphics object. + * + * @return a graphics object suitable for drawing on this component + */ + public Graphics getGraphics() + { + Component parent = awtComponent.getParent(); + Graphics g = parent.getGraphics(); + g.translate(awtComponent.getX(), awtComponent.getY()); + g.setClip(0, 0, awtComponent.getWidth(), awtComponent.getHeight()); + return g; + } + + /** + * Returns the location of this component in screen coordinates. This is + * called by {@link Component#getLocationOnScreen()}. + * + * This is implemented to query the parent component peer for its screen + * location and adds the offset of this component to it. This will eventually + * call the top-level component's peer, which is then expected to provide + * it's screen location. + * + * @return the location of this component in screen coordinates + */ + public Point getLocationOnScreen() + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + Point location = parentPeer.getLocationOnScreen(); + location.x += awtComponent.getX(); + location.y += awtComponent.getY(); + return location; + } + + /** + * Returns the minimum size for the component. This is called by + * {@link Component#getMinimumSize()}. + * + * This is implemented to return the Swing component's minimum size. + * + * @return the minimum size for the component + */ + public Dimension getMinimumSize() + { + return minimumSize(); + } + + /** + * Returns the preferred size for the component. This is called by + * {@link Component#getPreferredSize()}. + * + * This is implemented to return the Swing component's preferred size. + * + * @return the preferred size for the component + */ + public Dimension getPreferredSize() + { + return preferredSize(); + } + + /** + * Returns the toolkit that created this peer. + * + * @return the toolkit that created this peer + */ + public Toolkit getToolkit() + { + return Toolkit.getDefaultToolkit(); + } + + /** + * Handles the given event. This is called from + * {@link Component#dispatchEvent(AWTEvent)} to give the peer a chance to + * react to events for the component. + * + * @param e the event + */ + public void handleEvent(AWTEvent e) + { + switch (e.getID()) + { + case PaintEvent.UPDATE: + case PaintEvent.PAINT: + if (awtComponent.isShowing()) + { + Rectangle clip ; + synchronized (this) + { + coalescePaintEvent((PaintEvent) e); + assert paintArea != null; + clip = paintArea; + paintArea = null; + } + Graphics g = awtComponent.getGraphics(); + try + { + g.clipRect(clip.x, clip.y, clip.width, clip.height); + peerPaint(g, e.getID() == PaintEvent.UPDATE); + } + finally + { + g.dispose(); + } + } + break; + case MouseEvent.MOUSE_PRESSED: + case MouseEvent.MOUSE_RELEASED: + case MouseEvent.MOUSE_CLICKED: + case MouseEvent.MOUSE_ENTERED: + case MouseEvent.MOUSE_EXITED: + handleMouseEvent((MouseEvent) e); + break; + case MouseEvent.MOUSE_MOVED: + case MouseEvent.MOUSE_DRAGGED: + handleMouseMotionEvent((MouseEvent) e); + break; + case KeyEvent.KEY_PRESSED: + case KeyEvent.KEY_RELEASED: + case KeyEvent.KEY_TYPED: + handleKeyEvent((KeyEvent) e); + break; + case FocusEvent.FOCUS_GAINED: + case FocusEvent.FOCUS_LOST: + handleFocusEvent((FocusEvent)e); + break; + default: + // Other event types are not handled here. + break; + } + } + + /** + * Makes the component invisible. This is called from + * {@link Component#hide()}. + * + * This is implemented to call setVisible(false) on the Swing component. + */ + public void hide() + { + if (swingComponent != null) + swingComponent.getJComponent().setVisible(false); + + Component parent = awtComponent.getParent(); + if (parent != null) + parent.repaint(awtComponent.getX(), awtComponent.getY(), + awtComponent.getWidth(), awtComponent.getHeight()); + } + + /** + * Returns <code>true</code> if the component can receive keyboard input + * focus. This is called from {@link Component#isFocusTraversable()}. + * + * This is implemented to return isFocusable() from the Swing component. + * + * @specnote Part of the earlier 1.1 API, replaced by isFocusable(). + */ + public boolean isFocusTraversable() + { + return swingComponent != null ? + swingComponent.getJComponent().isFocusable() : false; + } + + /** + * Returns <code>true</code> if the component can receive keyboard input + * focus. This is called from {@link Component#isFocusable()}. + * + * This is implemented to return isFocusable() from the Swing component. + */ + public boolean isFocusable() + { + return swingComponent != null ? + swingComponent.getJComponent().isFocusable() : false; + } + + /** + * Returns the minimum size for the component. This is called by + * {@link Component#minimumSize()}. + * + * This is implemented to return the Swing component's minimum size. + * + * @return the minimum size for the component + */ + public Dimension minimumSize() + { + Dimension retVal; + if (swingComponent != null) + retVal = swingComponent.getJComponent().getMinimumSize(); + else + retVal = new Dimension(0, 0); + return retVal; + } + + /** + * Returns the preferred size for the component. This is called by + * {@link Component#getPreferredSize()}. + * + * This is implemented to return the Swing component's preferred size. + * + * @return the preferred size for the component + */ + public Dimension preferredSize() + { + Dimension retVal; + if (swingComponent != null) + retVal = swingComponent.getJComponent().getPreferredSize(); + else + retVal = new Dimension(0, 0); + return retVal; + } + + /** + * Paints the component. This is triggered by + * {@link Component#paintAll(Graphics)}. + * + * @param graphics the graphics to paint with + */ + public void paint(Graphics graphics) + { + peerPaint(graphics, false); + } + + /** + * 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 boolean prepareImage(Image img, int width, int height, ImageObserver ob) + { + Component parent = awtComponent.getParent(); + if(parent != null) + { + ComponentPeer parentPeer = parent.getPeer(); + return parentPeer.prepareImage(img, width, height, ob); + } + else + { + return Toolkit.getDefaultToolkit().prepareImage(img, width, height, ob); + } + } + + public void print(Graphics graphics) + { + // FIXME: I don't know what this method is supposed to do. + } + + /** + * Repaints the specified rectangle of this component. This is called from + * {@link Component#repaint(long, int, int, int, int)}. + * + * This is implemented to call repaint() on the Swing component. + * + * @param tm number of milliseconds to wait with repainting + * @param x the X coordinate of the upper left corner of the damaged + * rectangle + * @param y the Y coordinate of the upper left corner of the damaged + * rectangle + * @param width the width of the damaged rectangle + * @param height the height of the damaged rectangle + */ + public void repaint(long tm, int x, int y, int width, int height) + { + // NOTE: This is never called by AWT but is mandated by the peer interface. + if (swingComponent != null) + swingComponent.getJComponent().repaint(tm, x, y, width, height); + else + { + PaintEvent ev = new PaintEvent(awtComponent, PaintEvent.UPDATE, + new Rectangle(x, y, width, height)); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ev); + } + } + + /** + * Requests that this component receives the focus. This is called from + * {@link Component#requestFocus()}. + * + * This calls requestFocus() on the Swing component. + * + * @specnote Part of the earlier 1.1 API, apparently replaced by argument + * form of the same method. + */ + public void requestFocus() + { + // NOTE: This is never called by AWT but is mandated by the peer interface. + Toolkit tk = Toolkit.getDefaultToolkit(); + EventQueue q = tk.getSystemEventQueue(); + q.postEvent(new FocusEvent(awtComponent, FocusEvent.FOCUS_GAINED, false)); + } + + /** + * Requests that this component receives the focus. This is called from + * {@link Component#requestFocus()}. + * + * This calls requestFocus() on the Swing component. + * + * @param source the actual component that requests focus (may be a + * lightweight descendant of the heavyweight container) + * @param tmp true when the change is temporary + * @param allowWindowFocus + * @param tm the timestamp of the focus change + * + * @return true when the focus change is guaranteed to be granted, false + * otherwise + */ + public boolean requestFocus(Component source, boolean tmp, + boolean allowWindowFocus, long tm) + { + Toolkit tk = Toolkit.getDefaultToolkit(); + EventQueue q = tk.getSystemEventQueue(); + q.postEvent(new FocusEvent(source, FocusEvent.FOCUS_GAINED, tmp)); + return true; + } + + /** + * Notifies the peer that the bounds of this component have changed. This + * is called by {@link Component#reshape(int, int, int, int)}. + * + * This is implemented to call setBounds() on the Swing component. + * + * @param x the X coordinate of the upper left corner of the component + * @param y the Y coordinate of the upper left corner of the component + * @param width the width of the component + * @param height the height of the component + */ + public void reshape(int x, int y, int width, int height) + { + if (swingComponent != null) + swingComponent.getJComponent().setBounds(x, y, width, height); + } + + /** + * Sets the background color of the component. This is called by + * {@link Component#setBackground(Color)}. + * + * This is implemented to call setBackground() on the Swing component. + * + * @param color the background color to set + */ + public void setBackground(Color color) + { + if (swingComponent != null) + swingComponent.getJComponent().setBackground(color); + } + + /** + * Notifies the peer that the bounds of this component have changed. This + * is called by {@link Component#setBounds(int, int, int, int)}. + * + * This is implemented to call setBounds() on the Swing component. + * + * @param x the X coordinate of the upper left corner of the component + * @param y the Y coordinate of the upper left corner of the component + * @param width the width of the component + * @param height the height of the component + */ + public void setBounds(int x, int y, int width, int height) + { + reshape(x, y, width, height); + } + + /** + * Sets the cursor of the component. This is called by + * {@link Component#setCursor(Cursor)}. + * + * This is implemented to call setCursor() on the Swing component. + * + * @specnote Part of the earlier 1.1 API, apparently no longer needed. + */ + public void setCursor(Cursor cursor) + { + if (swingComponent != null) + swingComponent.getJComponent().setCursor(cursor); + } + + /** + * Sets the enabled/disabled state of this component. This is called by + * {@link Component#setEnabled(boolean)}. + * + * This is implemented to call setEnabled() on the Swing component. + * + * @param enabled <code>true</code> to enable the component, + * <code>false</code> to disable it + */ + public void setEnabled(boolean enabled) + { + if (swingComponent != null) + swingComponent.getJComponent().setEnabled(enabled); + } + + /** + * Sets the font of the component. This is called by + * {@link Component#setFont(Font)}. + * + * This is implemented to call setFont() on the Swing component. + * + * @param font the font to set + */ + public void setFont(Font font) + { + peerFont = font; + if (swingComponent != null) + swingComponent.getJComponent().setFont(font); + } + + /** + * Sets the foreground color of the component. This is called by + * {@link Component#setForeground(Color)}. + * + * This is implemented to call setForeground() on the Swing component. + * + * @param color the foreground color to set + */ + public void setForeground(Color color) + { + if (swingComponent != null) + swingComponent.getJComponent().setForeground(color); + } + + /** + * Sets the visibility state of the component. This is called by + * {@link Component#setVisible(boolean)}. + * + * This is implemented to call setVisible() on the Swing component. + * + * @param visible <code>true</code> to make the component visible, + * <code>false</code> to make it invisible + */ + public void setVisible(boolean visible) + { + if (visible) + show(); + else + hide(); + } + + /** + * Makes the component visible. This is called by {@link Component#show()}. + * + * This is implemented to call setVisible(true) on the Swing component. + */ + public void show() + { + if (swingComponent != null) + swingComponent.getJComponent().setVisible(true); + } + + /** + * Get the graphics configuration of the component. The color model + * of the component can be derived from the configuration. + * + * This is implemented to return the GraphicsConfiguration of the parent + * component. This will eventually call the toplevel component peer, which + * is expected to provide a real implementation. + * + * @return the graphics configuration of the component + */ + public GraphicsConfiguration getGraphicsConfiguration() + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + return parentPeer.getGraphicsConfiguration(); + } + + /** + * Part of an older API, no longer needed. + */ + public void setEventMask(long mask) + { + // Nothing to do here. + } + + /** + * Returns <code>true</code> if this component has been obscured, + * <code>false</code> otherwise. This will only work if + * {@link #canDetermineObscurity()} also returns <code>true</code>. + * + * This is not yet implemented. + * + * @return <code>true</code> if this component has been obscured, + * <code>false</code> otherwise. + */ + public boolean isObscured() + { + return false; + } + + /** + * Returns <code>true</code> if this component peer can determine if the + * component has been obscured, <code>false</code> otherwise. + * + * This is not yet implemented. + * + * @return <code>true</code> if this component peer can determine if the + * component has been obscured, <code>false</code> otherwise + */ + public boolean canDetermineObscurity() + { + return false; + } + + /** + * Coalesces the specified paint event. + * + * @param e the paint event + */ + public void coalescePaintEvent(PaintEvent e) + { + synchronized (this) + { + Rectangle newRect = e.getUpdateRect(); + if (paintArea == null) + paintArea = newRect; + else + Rectangle.union(paintArea, newRect, paintArea); + } + } + + /** + * Updates the cursor. This is not yet implemented. + */ + public void updateCursorImmediately() + { + // Nothing to do here yet. + } + + /** + * Returns true, if this component can handle wheel scrolling, + * <code>false</code> otherwise. + * + * This is not yet implemented and returns <code>false</code>. + * + * @return true, if this component can handle wheel scrolling, + * <code>false</code> otherwise + */ + public boolean handlesWheelScrolling() + { + return false; + } + + /** + * A convenience method that creates a volatile image. The volatile + * image is created on the screen device on which this component is + * displayed, in the device's current graphics configuration. + * + * This is implemented to let the parent component peer create an image. + * This eventually ends up in the toplevel component peer, which is then + * responsible for creating the real image. + * + * @param width width of the image + * @param height height of the image + * + * @see VolatileImage + * + * @since 1.2 + */ + public VolatileImage createVolatileImage(int width, int height) + { + Component parent = awtComponent.getParent(); + VolatileImage im = null; + if (parent != null) + { + ComponentPeer parentPeer = parent.getPeer(); + im = parentPeer.createVolatileImage(width, height); + } + return im; + } + + /** + * Create a number of image buffers that implement a buffering + * strategy according to the given capabilities. + * + * This is implemented to forward to the parent component peer. Eventually + * this ends up in the top level component peer, which is then responsible + * for doing the real work. + * + * @param numBuffers the number of buffers + * @param caps the buffering capabilities + * + * @throws AWTException if the specified buffering strategy is not + * implemented + * + * @since 1.2 + */ + public void createBuffers(int numBuffers, BufferCapabilities caps) throws AWTException + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + parentPeer.createBuffers(numBuffers, caps); + } + + /** + * Return the back buffer of this component. + * + * This is implemented to forward to the parent. Eventually this ends + * up in the toplevel component, which is then responsible for providing + * a back buffer. + * + * @return the back buffer of this component. + * + * @since 1.2 + */ + public Image getBackBuffer() + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + return parentPeer.getBackBuffer(); + } + + /** + * Perform a page flip, leaving the contents of the back buffer in + * the specified state. + * + * This is implemented to forward to the parent. Eventually this ends + * up in the toplevel component, which is then responsible for doing the real + * work. + * + * @param contents the state in which to leave the back buffer + * + * @since 1.2 + */ + public void flip(FlipContents contents) + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + parentPeer.flip(contents); + } + + /** + * Destroy the resources created by createBuffers. + * + * This is implemented to forward to the parent component peer. Eventually + * this ends up in the top level component peer, which is then responsible + * for doing the real work. + * + * @since 1.2 + */ + public void destroyBuffers() + { + Component parent = awtComponent.getParent(); + ComponentPeer parentPeer = parent.getPeer(); + parentPeer.destroyBuffers(); + } + + /** + * Get the bounds of this component peer. + * + * This is implemented to forward to the Swing component. + * + * @return component peer bounds + * @since 1.5 + */ + public Rectangle getBounds() + { + Rectangle retVal; + if (swingComponent != null) + retVal = swingComponent.getJComponent().getBounds(); + else + retVal = new Rectangle(); + return retVal; + } + + /** + * Reparent this component under another container. + * + * @param parent + * @since 1.5 + */ + public void reparent(ContainerPeer parent) + { + // Nothing to do here. + } + + /** + * Set the bounds of this component peer. + * + * This is implemented to forward to the swing component. + * + * @param x the new x co-ordinate + * @param y the new y co-ordinate + * @param width the new width + * @param height the new height + * @param z the new stacking level + * @since 1.5 + */ + public void setBounds(int x, int y, int width, int height, int z) + { + if (swingComponent != null) + swingComponent.getJComponent().setBounds(x, y, width, height); + // FIXME: Somehow handle the Z order. + } + + /** + * Check if this component supports being reparented. + * + * @return true if this component can be reparented, + * false otherwise. + * @since 1.5 + */ + public boolean isReparentSupported() + { + return true; + } + + + /** + * Layout this component peer. + * + * @since 1.5 + */ + public void layout() + { + if (swingComponent != null) + swingComponent.getJComponent().doLayout(); + } + + /** + * Triggers 'heavyweight' painting of the components. This usually calls + * paint() on the Swing component. + * + * @param g the graphics context to use for painting + * @param update wether we need to call update or paint on the AWT component + */ + protected void peerPaint(Graphics g, boolean update) + { + peerPaintComponent(g); + + Graphics userGraphics = g.create(); + try{ + if (update) + awtComponent.update(userGraphics); + else + awtComponent.paint(userGraphics); + } finally { + userGraphics.dispose(); + } + + } + + /** + * Paints the actual 'heavyweight' swing component, if there is one + * associated to this peer. + * + * @param g the graphics to paint the component with + */ + protected void peerPaintComponent(Graphics g) + { + // Paint the actual Swing component if this peer has one. + if (swingComponent != null) + swingComponent.getJComponent().paint(g); + } + + /** + * Handles mouse events on the component. This is usually forwarded to the + * SwingComponent's processMouseEvent() method. + * + * @param e the mouse event + */ + protected void handleMouseEvent(MouseEvent e) + { + if (swingComponent != null) + swingComponent.handleMouseEvent(e); + } + + /** + * Handles mouse motion events on the component. This is usually forwarded + * to the SwingComponent's processMouseMotionEvent() method. + * + * @param e the mouse motion event + */ + protected void handleMouseMotionEvent(MouseEvent e) + { + if (swingComponent != null) + swingComponent.handleMouseMotionEvent(e); + } + + /** + * Handles key events on the component. This is usually forwarded to the + * SwingComponent's processKeyEvent() method. + * + * @param e the key event + */ + protected void handleKeyEvent(KeyEvent e) + { + if (swingComponent != null) + swingComponent.handleKeyEvent(e); + } + + /** + * Handles focus events on the component. This is usually forwarded to the + * SwingComponent's processFocusEvent() method. + * + * @param e the key event + */ + protected void handleFocusEvent(FocusEvent e) + { + if (swingComponent != null) + swingComponent.handleFocusEvent(e); + } + + + /** + * Returns the AWT component for this peer. + * + * @return the AWT component for this peer + */ + public Component getComponent() + { + return awtComponent; + } + + public boolean requestFocus(Component lightweightChild, boolean temporary, + boolean focusedWindowChangeAllowed, + long time, sun.awt.CausedFocusEvent.Cause cause) + { + return true; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java new file mode 100644 index 000000000..ca3adc4c7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingContainerPeer.java @@ -0,0 +1,378 @@ +/* SwingContainerPeer.java -- A Swing based peer for AWT containers + Copyright (C) 2006, 2007 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.swing; + +import gnu.classpath.SystemProperties; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.ContainerPeer; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * A peer for Container to be used with the Swing based AWT peers. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingContainerPeer + extends SwingComponentPeer + implements ContainerPeer +{ + + /** + * Stores all heavyweight descendents of the container. This is used + * in {@link #peerPaintChildren(Graphics)}. + */ + private LinkedList heavyweightDescendents; + + /** + * The backbuffer used for painting UPDATE events. + */ + private Image backbuffer; + + /** + * Creates a new SwingContainerPeer. + * + * @param awtCont + */ + public SwingContainerPeer(Container awtCont) + { + heavyweightDescendents = new LinkedList(); + } + + /** + * Registers a heavyweight descendent. This is then painted by + * {@link #peerPaintChildren(Graphics)}. + * + * @param comp the descendent to register + * + * @see #peerPaintChildren(Graphics) + * @see #removeHeavyweightDescendent(Component) + */ + protected synchronized void addHeavyweightDescendent(Component comp) + { + heavyweightDescendents.add(comp); + focusOwner = null; + } + + /** + * Unregisters a heavyweight descendent. + * + * @param comp the descendent to unregister + * + * @see #peerPaintChildren(Graphics) + * @see #addHeavyweightDescendent(Component) + */ + protected synchronized void removeHeavyweightDescendent(Component comp) + { + heavyweightDescendents.remove(comp); + focusOwner = null; + } + + /** + * Returns an array of all registered heavyweight descendents. + * + * @return all registered heavyweight descendents + */ + protected Component[] getHeavyweightDescendents() + { + Component[] heavyweights = new Component[heavyweightDescendents.size()]; + heavyweights = (Component[]) heavyweightDescendents.toArray(heavyweights); + return heavyweights; + } + + /** + * Returns the insets of the container. + * + * This is implemented to return the insets of the Swing container. + * + * @return the insets of the container + */ + public Insets insets() + { + Insets retVal; + if (swingComponent != null) + retVal = swingComponent.getJComponent().getInsets(); + else + retVal = new Insets(0, 0, 0, 0); + return retVal; + } + + /** + * Returns the insets of the container. + * + * This is implemented to return the insets of the Swing container. + * + * @return the insets of the container + */ + public Insets getInsets() + { + return insets(); + } + + /** + * Called before the validation of this containers begins. + */ + public void beginValidate() + { + // Nothing to do here. + } + + /** + * Called after the validation of this containers ended. + */ + public void endValidate() + { + // Nothing to do here. + } + + /** + * Called before the layout of this containers begins. + */ + public void beginLayout() + { + // Nothing to do here. + } + + /** + * Called after the layout of this containers ended. + */ + public void endLayout() + { + // Nothing to do here. + } + + /** + * Returns <code>false</code> unconditionally. This method is not used at + * the moment. + * + * @return <code>false</code> + */ + public boolean isPaintPending() + { + return false; + } + + /** + * Returns <code>false</code> unconditionally. This method is not used at + * the moment. + * + * @return <code>false</code> + */ + public boolean isRestackSupported() + { + return false; + } + + /** + * This method is not used at the moment. + */ + public void cancelPendingPaint(int x, int y, int width, int height) + { + // Nothing to do here. + } + + /** + * This method is not used at the moment. + */ + public void restack() + { + // Nothing to do here. + } + + /** + * Performs the super behaviour (call peerPaintComponent() and + * awtComponent.paint()), and forwards the paint request to the heavyweight + * descendents of the container. + */ + protected void peerPaint(Graphics g, boolean update) + { + if (isDoubleBuffering()) + { + int width = awtComponent.getWidth(); + int height = awtComponent.getHeight(); + if (backbuffer == null + || backbuffer.getWidth(awtComponent) < width + || backbuffer.getHeight(awtComponent) < height) + backbuffer = awtComponent.createImage(width, height); + Graphics g2 = backbuffer.getGraphics(); + Rectangle clip = g.getClipRect(); + try + { + g2.setClip(clip); + super.peerPaint(g2, update); + peerPaintChildren(g2); + } + finally + { + g2.dispose(); + } + g.drawImage(backbuffer, 0, 0, awtComponent); + } + else + { + super.peerPaint(g, update); + peerPaintChildren(g); + } + } + + /** + * Determines if we should do double buffering or not. + * + * @return if we should do double buffering or not + */ + private boolean isDoubleBuffering() + { + Object prop = + SystemProperties.getProperty("gnu.awt.swing.doublebuffering", "false"); + return prop.equals("true"); + } + + /** + * Paints any heavyweight child components. + * + * @param g the graphics to use for painting + */ + protected synchronized void peerPaintChildren(Graphics g) + { + // TODO: Is this the right painting order? + for (Iterator i = heavyweightDescendents.iterator(); i.hasNext();) + { + Component child = (Component) i.next(); + ComponentPeer peer = child.getPeer(); + + if (peer instanceof SwingComponentPeer && child.isVisible()) + { + // TODO: The translation here doesn't work for deeper + // nested children. Fix this! + Graphics g2 = g.create(child.getX(), child.getY(), + child.getWidth(), child.getHeight()); + try + { + // update() is only called for the topmost component if + // necessary, all other components only get paint() called. + ((SwingComponentPeer) peer).peerPaint(g2, false); + } + finally + { + g2.dispose(); + } + } + } + } + + /** + * Handles mouse events by dispatching it to the correct component. + * + * @param ev the mouse event + */ + protected void handleMouseEvent(MouseEvent ev) + { + Component comp = awtComponent.getComponentAt(ev.getPoint()); + if(comp == null) comp = awtComponent; + ComponentPeer peer = comp.getPeer(); + if (awtComponent != comp && !comp.isLightweight() && peer instanceof SwingComponentPeer) + { + ev.translatePoint(comp.getX(), comp.getY()); + ev.setSource(comp); + ((SwingComponentPeer) peer).handleMouseEvent(ev); + } + } + + /** + * Handles mouse events by dispatching it to the correct component. + * + * @param ev the mouse event + */ + protected void handleMouseMotionEvent(MouseEvent ev) + { + Component comp = awtComponent.getComponentAt(ev.getPoint()); + if (comp != null) + { + ComponentPeer peer = comp.getPeer(); + if (awtComponent != comp && !comp.isLightweight() && peer instanceof SwingComponentPeer) + { + ev.translatePoint(comp.getX(), comp.getY()); + ((SwingComponentPeer) peer).handleMouseMotionEvent(ev); + } + } + } + + /** + * Handles key events on the component. This is usually forwarded to the + * SwingComponent's processKeyEvent() method. + * + * @param e the key event + */ + protected void handleKeyEvent(KeyEvent e) + { + Component owner = getFocusOwner(); + if(owner != null) + owner.getPeer().handleEvent(e); + else + super.handleKeyEvent(e); + } + + private Component focusOwner = null; + + private Component getFocusOwner() + { + if(focusOwner == null) + { + for(Iterator iter=heavyweightDescendents.iterator(); iter.hasNext();) + { + Component child = (Component) iter.next(); + if(child.isFocusable()) + { + focusOwner = child; + break; + } + } + } + return focusOwner; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java new file mode 100644 index 000000000..56c7417cd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingFramePeer.java @@ -0,0 +1,197 @@ +/* SwingFramePeer.java -- An abstract Swing based peer for AWT frames + 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.swing; + +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.MenuBar; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.FramePeer; + +/** + * An abstract base class for FramePeer implementations based on Swing. + * This class provides the ability to display and handle AWT MenuBars that + * are based on Swing. + * + * 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> + * </ul> + * + * @author Roman Kennke (kennke@aicas.com) + */ +public abstract class SwingFramePeer + extends SwingWindowPeer + implements FramePeer +{ + /** + * The menu bar to display. + */ + SwingMenuBarPeer menuBar = null; + + /** + * Creates a new SwingFramePeer. + * + * @param frame the frame + */ + public SwingFramePeer(Frame frame) + { + super(frame); + } + + /** + * Sets the menu bar to display in this frame. + * + * @param mb the menu bar to set + */ + public void setMenuBar(MenuBar mb) + { + menuBar = (SwingMenuBarPeer) mb.getPeer(); + menuBar.setFramePeer(this); + menuBar.setWidth(awtComponent.getWidth()); + } + + /** + * Triggers 'heavyweight' painting of the frame. This will paint a menu bar + * if present as well as the child components of this frame. + * + * @param g the graphics context to use for painting + */ + protected void peerPaintComponent(Graphics g) + { + super.peerPaintComponent(g); + if (menuBar != null) + menuBar.peerPaint(g); + } + + /** + * Sets the size and location of this frame. This resizes the menubar to fit + * within the frame. + * + * @param x the X coordinate of the screen location + * @param y the Y coordinate of the screen location + * @param w the width of the frame + * @param h the height of the frame + */ + public void setBounds(int x, int y, int w, int h) + { + super.setBounds(x, y, w, h); + if (menuBar != null) + menuBar.setWidth(w); + } + + /** + * Calculates the insets of this frame peer. This fetches the insets + * from the superclass and adds the insets of the menubar if one is present. + * + * @return the insets of the frame + */ + public Insets getInsets() + { + Insets insets = super.getInsets(); + if (menuBar != null) + insets.top += menuBar.getHeight(); + return insets; + } + + /** + * Returns the location of the menu on the screen. This is needed internally + * by the {@link SwingMenuBarPeer} in order to determine its screen location. + * + * @return the location of the menu on the screen + */ + public Point getMenuLocationOnScreen() + { + Insets i = super.getInsets(); + return new Point(i.top, i.left); + } + + /** + * Overridden to provide the ability to handle menus. + * + * @param ev the mouse event + */ + protected void handleMouseEvent(MouseEvent ev) + { + Point p = ev.getPoint(); + Insets i = super.getInsets(); + if (menuBar != null) + { + int menuHeight = menuBar.getHeight(); + if (p.y >= i.top && p.y <= i.top + menuHeight) + menuBar.handleMouseEvent(ev); + else + { + ev.translatePoint(0, -menuHeight); + super.handleMouseMotionEvent(ev); + } + } + + super.handleMouseEvent(ev); + } + + /** + * Overridden to provide the ability to handle menus. + * + * @param ev the mouse event + */ + protected void handleMouseMotionEvent(MouseEvent ev) + { + Point p = ev.getPoint(); + Insets i = super.getInsets(); + if (menuBar != null) + { + int menuHeight = menuBar.getHeight(); + if (p.y >= i.top && p.y <= i.top + menuHeight) + menuBar.handleMouseMotionEvent(ev); + else + { + ev.translatePoint(0, -menuHeight); + super.handleMouseMotionEvent(ev); + } + } + + super.handleMouseMotionEvent(ev); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingLabelPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingLabelPeer.java new file mode 100644 index 000000000..5c979d613 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingLabelPeer.java @@ -0,0 +1,242 @@ +/* SwingLabelPeer.java -- A Swing based peer for AWT labels + Copyright (C) 2006, 2007 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.swing; + +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Label; +import java.awt.Point; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.LabelPeer; + +import javax.swing.JComponent; +import javax.swing.JLabel; + + +/** + * A Label peer based on {@link JLabel}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingLabelPeer + extends SwingComponentPeer + implements LabelPeer +{ + + /** + * A spezialized Swing label used to paint the label for the AWT Label. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingLabel + extends JLabel + implements SwingComponent + { + Label label; + + + SwingLabel(Label label) + { + this.label = label; + } + + /** + * Returns this label. + * + * @return <code>this</code> + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + processMouseEvent(ev); + } + + /** + * Handles mouse motion events by forwarding it to + * <code>processMouseMotionEvent()</code>. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to <code>processKeyEvent()</code>. + * + * @param ev the mouse event + */ + public void handleKeyEvent(KeyEvent ev) + { + processKeyEvent(ev); + } + + /** + * Handles focus events by forwarding it to + * <code>processFocusEvent()</code>. + * + * @param ev the Focus event + */ + public void handleFocusEvent(FocusEvent ev) + { + processFocusEvent(ev); + } + + /** + * Overridden so that this method returns the correct value even without a + * peer. + * + * @return the screen location of the button + */ + public Point getLocationOnScreen() + { + return SwingLabelPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value for the + * swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (label != null) + retVal = label.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingLabelPeer.this.createImage(w, h); + } + + public Graphics getGraphics() + { + return SwingLabelPeer.this.getGraphics(); + } + + public Container getParent() + { + Container par = null; + if (label != null) + par = label.getParent(); + return par; + } + } + + /** + * Creates a new <code>SwingLabelPeer</code> for the specified AWT label. + * + * @param label the AWT label + */ + public SwingLabelPeer(Label label) + { + super(); + SwingLabel swingLabel = new SwingLabel(label); + swingLabel.setText(label.getText()); + swingLabel.setOpaque(true); + init(label, swingLabel); + setAlignment(label.getAlignment()); + } + + /** + * Sets the text of the label. This is implemented to set the text on the + * Swing label. + * + * @param text the text to be set + */ + public void setText(String text) + { + ((JLabel) swingComponent.getJComponent()).setText(text); + } + + /** + * Sets the horizontal alignment of the label. This is implemented to + * set the alignment on the Swing label. + * + * @param alignment the horizontal alignment + * + * @see Label#LEFT + * @see Label#RIGHT + * @see Label#CENTER + */ + public void setAlignment(int alignment) + { + JLabel swingLabel = (JLabel) swingComponent.getJComponent(); + switch (alignment) + { + case Label.RIGHT: + swingLabel.setHorizontalAlignment(JLabel.RIGHT); + break; + case Label.CENTER: + swingLabel.setHorizontalAlignment(JLabel.CENTER); + break; + case Label.LEFT: + default: + swingLabel.setHorizontalAlignment(JLabel.LEFT); + break; + } + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingListPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingListPeer.java new file mode 100644 index 000000000..cf766fd4f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingListPeer.java @@ -0,0 +1,364 @@ +/* SwingListPeer.java -- A Swing based peer for AWT lists + Copyright (C) 2006, 2007 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.swing; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.List; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.ListPeer; + +import javax.swing.DefaultListModel; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; + +public class SwingListPeer + extends SwingComponentPeer + implements ListPeer +{ + + /** + * A spezialized Swing scroller used to hold the list. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingList + extends JScrollPane + implements SwingComponent + { + + SwingList(Component comp) + { + super(comp, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + } + + /** + * Returns this label. + * + * @return <code>this</code> + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + dispatchEvent(ev); + } + + /** + * Force lightweight mouse dispatching. + */ + public boolean isLightweight() + { + return false; + } + + /** + * Handles mouse motion events by forwarding it to + * <code>processMouseMotionEvent()</code>. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to <code>processKeyEvent()</code>. + * + * @param ev the mouse event + */ + public void handleKeyEvent(KeyEvent ev) + { + processKeyEvent(ev); + } + + /** + * Handles focus events by forwarding it to <code>processFocusEvent()</code>. + * + * @param ev the Focus event + */ + public void handleFocusEvent(FocusEvent ev) + { + processFocusEvent(ev); + } + + + /** + * Overridden so that this method returns the correct value even without a + * peer. + * + * @return the screen location of the button + */ + public Point getLocationOnScreen() + { + return SwingListPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value for the + * swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (SwingListPeer.this.awtComponent != null) + retVal = SwingListPeer.this.awtComponent.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingListPeer.this.createImage(w, h); + } + + public Graphics getGraphics() + { + return SwingListPeer.this.getGraphics(); + } + + public Container getParent() + { + Container par = null; + if (SwingListPeer.this.awtComponent != null) + par = SwingListPeer.this.awtComponent.getParent(); + return par; + } + } + + /** + * The actual Swing JList. + */ + private JList jList; + + private DefaultListModel listModel; + + public SwingListPeer(List list) + { + super(); + listModel = new DefaultListModel(); + jList = new JList(listModel); + SwingList swingList = new SwingList(jList); + init(list, swingList); + + // Pull over the items from the list. + String[] items = list.getItems(); + for (int i = 0 ; i < items.length; i++) + addItem(items[i], i); + } + + public void add(String item, int index) + { + if (listModel != null) + listModel.add(index, item); + } + + public void addItem(String item, int index) + { + if (listModel != null) + listModel.add(index, item); + } + + public void clear() + { + if (listModel != null) + listModel.clear(); + } + + public void delItems(int startIndex, int endIndex) + { + if (listModel != null) + listModel.removeRange(startIndex, endIndex); + } + + public void deselect(int index) + { + if (jList != null) + { + jList.getSelectionModel().removeSelectionInterval(index, index); + } + } + + public Dimension getMinimumSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getMinimumSize(); + } + return d; + } + + public Dimension getPreferredSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getPreferredSize(); + } + return d; + } + + public int[] getSelectedIndexes() + { + int[] sel = null; + if (jList != null) + { + sel = jList.getSelectedIndices(); + } + return sel; + } + + public void makeVisible(int index) + { + if (jList != null) + { + Component comp = jList.getComponent(index); + jList.scrollRectToVisible(comp.getBounds()); + } + } + + public Dimension minimumSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getMinimumSize(); + } + return d; + } + + public Dimension preferredSize(int s) + { + Dimension d = null; + if (jList != null) + { + d = jList.getComponent(s).getPreferredSize(); + } + return d; + } + + public void removeAll() + { + if (jList != null) + { + jList.removeAll(); + } + } + + public void select(int index) + { + if (jList != null) + { + jList.setSelectedIndex(index); + } + } + + public void setMultipleMode(boolean multi) + { + if (jList != null) + { + jList.setSelectionMode(multi + ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION + : ListSelectionModel.SINGLE_SELECTION); + } + } + + public void setMultipleSelections(boolean multi) + { + if (jList != null) + { + jList.setSelectionMode(multi + ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION + : ListSelectionModel.SINGLE_SELECTION); + } + } + + public void reshape(int x, int y, int width, int height) + { + if (swingComponent != null) + { + swingComponent.getJComponent().setBounds(x, y, width, height); + swingComponent.getJComponent().validate(); + } + } + + protected void peerPaint(Graphics g, boolean update) + { + super.peerPaint(g, update); + jList.doLayout(); + jList.list(); + + Rectangle r = getBounds(); + g.setColor(Color.RED); + g.drawRect(r.x, r.y, r.width, r.height); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java new file mode 100644 index 000000000..0033efb02 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuBarPeer.java @@ -0,0 +1,295 @@ +/* SwingMenuBarPeer.java -- A Swing based peer for AWT menu bars + 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.swing; + +import java.awt.Container; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.peer.MenuBarPeer; + +import javax.swing.JMenuBar; + +/** + * A Swing based peer for the AWT menu bar. This is a little bit different from + * the other peers, since the AWT MenuBar is not derived from the AWT + * component. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingMenuBarPeer + implements MenuBarPeer +{ + + /** + * The AWT menu bar. + */ + MenuBar awtMenuBar; + + /** + * The Swing menu bar. + */ + SwingMenuBar menuBar; + + /** + * The peer of the frame that contains this menu bar. + */ + SwingFramePeer framePeer; + + /** + * A specialized JMenuBar that can be used as 'backend' for AWT MenuBars. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingMenuBar + extends JMenuBar + { + /** + * Overridden in order to provide a parent frame for this menu bar. The + * menu bar still is not inside the component hierarchy, we are faking + * here. + */ + public Container getParent() + { + Container result = null; + if (framePeer != null) + result = (Container) framePeer.awtComponent; + return result; + } + + /** + * Unconditionally returns <code>true</code>, since we assume that when the + * menubar has a peer, it must be showing. + * + * @return <code>true</code> + */ + public boolean isShowing() + { + // FIXME: This might be wrong. Maybe find a better way to do that. + return true; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseEvent(ev); + } + + /** + * Determines the menubar's screen location by asking the SwingFramePeer + * for it. + * + * @return the screen location of the menu bar + */ + public Point getLocationOnScreen() + { + return framePeer.getMenuLocationOnScreen(); + } + } + + /** + * Creates a new <code>SwingMenuBarPeer</code> instance. + * + * @param awtMenuBar the AWT menu bar + */ + public SwingMenuBarPeer(MenuBar awtMenuBar) + { + this.awtMenuBar = awtMenuBar; + menuBar = new SwingMenuBar(); + menuBar.setDoubleBuffered(false); + // Add all the menus that are already in the MenuBar. + for (int i = 0; i < awtMenuBar.getMenuCount(); i++) + { + Menu menu = awtMenuBar.getMenu(i); + menu.addNotify(); + addMenu(awtMenuBar.getMenu(i)); + } + } + + /** + * Sets the <code>SwingFramePeer</code> of the frame that holds this menu. + * + * @param peer the <code>SwingFramePeer</code> to set + */ + public void setFramePeer(SwingFramePeer peer) + { + framePeer = peer; + } + + /** + * Adds a menu to the menu bar. + * + * @param m the menu to add + */ + public void addMenu(Menu m) + { + SwingMenuPeer menuPeer = (SwingMenuPeer) m.getPeer(); + menuBar.add(menuPeer.menu); + } + + /** + * Adds a help menu to the menu bar. + * + * @param menu the menu to add + */ + public void addHelpMenu(Menu menu) + { + // FIXME: We should manage the help menu differently, so that it always + // appears at the rightmost position. + SwingMenuPeer menuPeer = (SwingMenuPeer) menu.getPeer(); + menuBar.add(menuPeer.menu); + } + + /** + * Removes the menu with the specified index. + * + * @param index the index of the menu to remove + */ + public void delMenu(int index) + { + menuBar.remove(index); + } + + /** + * Disposes this peer. This releases any reference to the AWT and Swing + * components. + */ + public void dispose() + { + menuBar = null; + awtMenuBar = null; + } + + /** + * Sets a font for the menu bar. + * + * @param font the font to set + */ + public void setFont(Font font) + { + menuBar.setFont(font); + } + + /** + * Sets the width of the menu bar. This is called from the top level + * component peers to adjust the width of the menubar when their sizes + * change. + * + * @param w the width to set + */ + public void setWidth(int w) + { + menuBar.setSize(w, menuBar.getPreferredSize().height); + menuBar.doLayout(); + } + + /** + * Paints the menu bar. + * + * @param g the graphics context to use for painting + */ + public void peerPaint(Graphics g) + { + menuBar.paint(g); + } + + /** + * Determines the height of the menubar. + * + * @return the height of the menu bar + */ + public int getHeight() + { + return menuBar.getPreferredSize().height; + } + + /** + * Handles mouse events. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + Point point = ev.getPoint(); + for (int i = 0; i < awtMenuBar.getMenuCount(); i++) + { + Menu menu = awtMenuBar.getMenu(i); + SwingMenuPeer peer = (SwingMenuPeer) menu.getPeer(); + int x1 = peer.getX(); + int x2 = x1 + peer.getWidth(); + if (point.x >= x1 && point.x <= x2) + { + ev.translatePoint(peer.getX(), peer.getY()); + peer.handleMouseEvent(ev); + break; + } + } + } + + /** + * Handles mouse motion events. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + Point point = ev.getPoint(); + for (int i = 0; i < awtMenuBar.getMenuCount(); i++) + { + Menu menu = awtMenuBar.getMenu(i); + SwingMenuPeer peer = (SwingMenuPeer) menu.getPeer(); + int x1 = peer.getX(); + int x2 = x1 + peer.getWidth(); + if (point.x >= x1 && point.x <= x2) + { + ev.translatePoint(peer.getX(), peer.getY()); + peer.handleMouseMotionEvent(ev); + break; + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuItemPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuItemPeer.java new file mode 100644 index 000000000..721b3349f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuItemPeer.java @@ -0,0 +1,157 @@ +/* SwingMenuItemPeer.java -- A Swing based peer for AWT menu items + 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.swing; + +import java.awt.Font; +import java.awt.MenuItem; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.MenuItemPeer; + +import javax.swing.JMenuItem; + +/** + * A Swing based peer for the AWT MenuItem. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingMenuItemPeer + implements MenuItemPeer +{ + /** + * The AWT menu item. + */ + MenuItem awtMenuItem; + + /** + * The Swing menu item. + */ + JMenuItem menuItem; + + /** + * Receives ActionEvents from the Swing menu item and forwards them + * to the ActionListeners of the AWT MenuItem. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingMenuItemListener implements ActionListener + { + + /** + * Receives notification when the action has been performed. + * + * @param event the action event + */ + public void actionPerformed(ActionEvent event) + { + event.setSource(awtMenuItem); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event); + } + + } + + /** + * Creates a new instance of <code>SwingMenuItemPeer</code>. + * + * @param awtMenuItem the AWT menu item + */ + public SwingMenuItemPeer(MenuItem awtMenuItem) + { + this.awtMenuItem = awtMenuItem; + menuItem = new JMenuItem(awtMenuItem.getLabel()); + menuItem.addActionListener(new SwingMenuItemListener()); + } + + /** + * Disables the menu item. + */ + public void disable() + { + menuItem.setEnabled(false); + } + + /** + * Enables the menu item. + */ + public void enable() + { + menuItem.setEnabled(true); + } + + /** + * Sets the enabled state to <code>enabled</code>. + * + * @param enabled if the menu item should be enabled or not + */ + public void setEnabled(boolean enabled) + { + menuItem.setEnabled(enabled); + } + + /** + * Sets the label for the menu item. + * + * @param text the label to set + */ + public void setLabel(String text) + { + menuItem.setText(text); + } + + /** + * Disposes the menu item. This releases any reference to the Swing and AWT + * menu item. + */ + public void dispose() + { + menuItem = null; + awtMenuItem = null; + } + + /** + * Sets the font for this menu item. + * + * @param font the font to set + */ + public void setFont(Font font) + { + menuItem.setFont(font); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuPeer.java new file mode 100644 index 000000000..afe20616d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingMenuPeer.java @@ -0,0 +1,284 @@ +/* SwingMenuPeer.java -- A Swing based peer for AWT menus + 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.swing; + +import java.awt.Font; +import java.awt.Menu; +import java.awt.MenuItem; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.peer.MenuPeer; + +import javax.swing.JMenu; + +/** + * A Swing based peer for the AWT menu. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingMenuPeer + implements MenuPeer +{ + + /** + * The AWT menu. + */ + Menu awtMenu; + + /** + * The Swing menu. + */ + SwingMenu menu; + + /** + * A specialized JMenu that can be used as 'backend' for an AWT menu. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingMenu + extends JMenu + { + + /** + * Unconditionally returns <code>true</code>, since we assume that when the + * menu has a peer, it must be showing. + * + * @return <code>true</code> + */ + public boolean isShowing() + { + // FIXME: This might be wrong. Maybe find a better way to do that. + return true; + } + + /** + * Overridden so that we can provide a location even without a real peer + * attached. + * + * @return the screen location of this menu + */ + public Point getLocationOnScreen() + { + Point parentLoc = getParent().getLocationOnScreen(); + parentLoc.x += getX(); + parentLoc.y += getY(); + return parentLoc; + } + + /** + * Handles mouse events by forwarding them to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseEvent(ev); + } + + /** + * Handles mouse events by forwarding them to + * <code>processMouseMotionEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseMotionEvent(ev); + } + } + + /** + * Creates a new <code>SwingMenuPeer</code> instance. + * + * @param awtMenu the AWT menu + */ + public SwingMenuPeer(Menu awtMenu) + { + this.awtMenu = awtMenu; + menu = new SwingMenu(); + menu.setDoubleBuffered(false); + menu.setText(awtMenu.getLabel()); + for (int i = 0; i < awtMenu.getItemCount(); i++) + { + MenuItem item = awtMenu.getItem(i); + item.addNotify(); + SwingMenuItemPeer peer = (SwingMenuItemPeer) item.getPeer(); + menu.add(peer.menuItem); + } + } + + /** + * Adds a menu item to this menu. + * + * @param item the menu item to add + */ + public void addItem(MenuItem item) + { + SwingMenuItemPeer menuItemPeer = (SwingMenuItemPeer) item.getPeer(); + menu.add(menuItemPeer.menuItem); + } + + /** + * Adds a separator to the menu. + */ + public void addSeparator() + { + menu.addSeparator(); + } + + /** + * Removes a menu item from the menu. + * + * @param index the index of the menu item to remove + */ + public void delItem(int index) + { + menu.remove(index); + } + + /** + * Disables the menu. + */ + public void disable() + { + menu.setEnabled(false); + } + + /** + * Enables the menu. + */ + public void enable() + { + menu.setEnabled(true); + } + + /** + * Sets the enabled state of the menu to <code>enabled</code>. + * + * @param enabled if the menu should be enabled or not + */ + public void setEnabled(boolean enabled) + { + menu.setEnabled(enabled); + } + + /** + * Sets the label of the menu. + * + * @param text the label to set + */ + public void setLabel(String text) + { + menu.setText(text); + } + + /** + * Releases any reference to the AWT and Swing menu instances. + */ + public void dispose() + { + menu = null; + awtMenu = null; + } + + /** + * Sets the font for the menu. + * + * @param font the font to set + */ + public void setFont(Font font) + { + menu.setFont(font); + } + + /** + * Handles mouse events by forwarding them to the Swing menu. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + menu.handleMouseEvent(ev); + } + + /** + * Handles mouse motion events by forwarding them to the Swing menu. + * + * @param ev the mouse event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + menu.handleMouseMotionEvent(ev); + } + + /** + * Returns the X coordinate of the upper left corner of the menu. This is + * used internally by the SwingMenuBarPeer. + * + * @return the X coordinate of the upper left corner of the menu + */ + int getX() + { + return menu.getX(); + } + + /** + * Returns the width of the menu. This is used internally by the + * SwingMenuBarPeer. + * + * @return the X coordinate of the upper left corner of the menu + */ + int getWidth() + { + return menu.getWidth(); + } + + /** + * Returns the Y coordinate of the upper left corner of the menu. This is + * used internally by the SwingMenuBarPeer. + * + * @return the X coordinate of the upper left corner of the menu + */ + public int getY() + { + return menu.getY(); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingPanelPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingPanelPeer.java new file mode 100644 index 000000000..37c6dbc7a --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingPanelPeer.java @@ -0,0 +1,67 @@ +/* SwingPanelPeer.java -- A PanelPeer based on Swing + Copyright (C) 2006, 2007 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.swing; + +import java.awt.Panel; +import java.awt.peer.PanelPeer; + +/** + * A panel peer based on Swing. + * + * @author Roman Kennke (kennke@aicas.com) + */ +// TODO: Maybe base implementation on JPanel. However, this doesn't seem +// necessary, but might be good for more consistent Look. +public class SwingPanelPeer + extends SwingContainerPeer + implements PanelPeer +{ + + /** + * Creates a new instance of <code>SwingPanelPeer</code> for the specified + * AWT panel. + * + * @param panel the AWT panel + */ + public SwingPanelPeer(Panel panel) + { + super(panel); + init(panel, null); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingTextAreaPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextAreaPeer.java new file mode 100644 index 000000000..d56e950ec --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextAreaPeer.java @@ -0,0 +1,487 @@ +/* SwingTextAreaPeer.java -- A Swing based peer for AWT textareas + Copyright (C) 2006, 2007 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.swing; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.TextArea; +import java.awt.event.ComponentEvent; +import java.awt.event.FocusEvent; +import java.awt.event.HierarchyEvent; +import java.awt.event.InputMethodEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.im.InputMethodRequests; +import java.awt.peer.TextAreaPeer; + +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JViewport; +import javax.swing.text.BadLocationException; + +public class SwingTextAreaPeer + extends SwingComponentPeer + implements TextAreaPeer +{ + + /** + * A spezialized Swing scroller used to hold the textarea. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingScrollPane + extends JScrollPane + implements SwingComponent + { + + SwingTextArea textArea; + + SwingScrollPane(SwingTextArea textArea) + { + super(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + + this.textArea = textArea; + } + + /** + * Returns this label. + * + * @return <code>this</code> + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to + * <code>processMouseEvent()</code>. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + JViewport viewPort = getViewport(); + if(viewPort.contains(ev.getPoint())) + { + ev.setSource(textArea); + textArea.dispatchEvent(ev); + } + else + { + ev.setSource(this); + this.dispatchEvent(ev); + } + } + + /** + * Force lightweight mouse dispatching. + */ + public boolean isLightweight() + { + return false; + } + + /** + * Handles mouse motion events by forwarding it to + * <code>processMouseMotionEvent()</code>. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + textArea.processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to <code>processKeyEvent()</code>. + * + * @param ev the mouse event + */ + public void handleKeyEvent(KeyEvent ev) + { + textArea.processKeyEvent(ev); + } + + /** + * Handles focus events by forwarding it to + * <code>processFocusEvent()</code>. + * + * @param ev the Focus event + */ + public void handleFocusEvent(FocusEvent ev) + { + textArea.processFocusEvent(ev); + } + + /** + * Overridden so that this method returns the correct value even without a + * peer. + * + * @return the screen location of the button + */ + public Point getLocationOnScreen() + { + return SwingTextAreaPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value for the + * swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (SwingTextAreaPeer.this.awtComponent != null) + retVal = SwingTextAreaPeer.this.awtComponent.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingTextAreaPeer.this.createImage(w, h); + } + + public Graphics getGraphics() + { + return SwingTextAreaPeer.this.getGraphics(); + } + + public Container getParent() + { + Container par = null; + if (SwingTextAreaPeer.this.awtComponent != null) + par = SwingTextAreaPeer.this.awtComponent.getParent(); + return par; + } + + public void requestFocus() { + SwingTextAreaPeer.this.requestFocus(awtComponent, false, true, 0); + } + + public boolean requestFocus(boolean temporary) { + return SwingTextAreaPeer.this.requestFocus(awtComponent, temporary, + true, 0); + } + + } + + private class SwingTextArea extends JTextArea + { + /** + * Make this method accessible in this Package. + */ + protected final void processComponentKeyEvent(KeyEvent e) + { + super.processComponentKeyEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processMouseMotionEvent(MouseEvent ev) + { + super.processMouseMotionEvent(ev); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processComponentEvent(ComponentEvent e) + { + super.processComponentEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processFocusEvent(FocusEvent e) + { + super.processFocusEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processHierarchyBoundsEvent(HierarchyEvent e) + { + super.processHierarchyBoundsEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processHierarchyEvent(HierarchyEvent e) + { + super.processHierarchyEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processInputMethodEvent(InputMethodEvent e) + { + super.processInputMethodEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processMouseEvent(MouseEvent e) + { + super.processMouseEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processMouseWheelEvent(MouseWheelEvent e) + { + super.processMouseWheelEvent(e); + } + + /** + * Make this method accessible in this Package. + */ + protected final void processKeyEvent(KeyEvent e) + { + super.processKeyEvent(e); + } + + public void requestFocus() { + SwingTextAreaPeer.this.requestFocus(awtComponent, false, true, 0); + } + + public boolean requestFocus(boolean temporary) { + return SwingTextAreaPeer.this.requestFocus(awtComponent, temporary, + true, 0); + } + } + + /** + * The actual JTextArea. + */ + private SwingTextArea jTextArea; + + public SwingTextAreaPeer(TextArea textArea) + { + super(); + jTextArea = new SwingTextArea(); + SwingScrollPane swingArea = new SwingScrollPane(jTextArea); + init(textArea, swingArea); + + JViewport viewport = new JViewport() + { + public Image createImage(int width, int height) + { + return awtComponent.createImage(width, height); + } + }; + + viewport.setView(jTextArea); + swingArea.setViewport(viewport); + // Pull over the text from the text area. + setText(textArea.getText()); + + // Pull over the number of rows and columns + // if non were set use default values + int columns = textArea.getColumns(); + int rows = textArea.getRows(); + + if(columns == 0 && rows == 0) + { + columns = 25; + textArea.setColumns(columns); + rows = 5; + textArea.setRows(rows); + } + + jTextArea.setColumns(columns); + jTextArea.setRows(rows); + } + + public Dimension getMinimumSize(int rows, int cols) + { + return jTextArea.getMinimumSize(); + } + + public Dimension getPreferredSize(int rows, int cols) + { + return jTextArea.getPreferredSize(); + } + + public void insert(String text, int pos) + { + jTextArea.insert(text, pos); + } + + public void insertText(String text, int pos) + { + jTextArea.insert(text, pos); + } + + public Dimension minimumSize() + { + return jTextArea.getMinimumSize(); + } + + public Dimension preferredSize() + { + return jTextArea.getPreferredSize(); + } + + public Dimension minimumSize(int rows, int cols) + { + return jTextArea.getMinimumSize(); + } + + public Dimension preferredSize(int rows, int cols) + { + return jTextArea.getPreferredSize(); + } + + public void replaceRange(String text, int start, int end) + { + jTextArea.replaceRange(text, start, end); + } + + public void replaceText(String text, int start, int end) + { + jTextArea.replaceRange(text, start, end); + } + + public long filterEvents(long filter) + { + // TODO Auto-generated method stub + return 0; + } + + public int getCaretPosition() + { + return jTextArea.getCaretPosition(); + } + + public Rectangle getCharacterBounds(int pos) + { + Rectangle r; + try + { + return jTextArea.modelToView(pos); + } + catch (BadLocationException ex) + { + r = null; + } + return r; + } + + public int getIndexAtPoint(int x, int y) + { + return jTextArea.viewToModel(new Point(x, y)); + } + + public InputMethodRequests getInputMethodRequests() + { + // TODO Auto-generated method stub + return null; + } + + public int getSelectionEnd() + { + return jTextArea.getSelectionEnd(); + } + + public int getSelectionStart() + { + return jTextArea.getSelectionStart(); + } + + public String getText() + { + return jTextArea.getText(); + } + + public void select(int start, int end) + { + jTextArea.select(start, end); + } + + public void setCaretPosition(int pos) + { + jTextArea.setCaretPosition(pos); + } + + public void setEditable(boolean editable) + { + jTextArea.setEditable(editable); + } + + public void setText(String text) + { + jTextArea.setText(text); + } + + public void reshape(int x, int y, int width, int height) + { + if (swingComponent != null) + { + swingComponent.getJComponent().setBounds(x, y, width, height); + swingComponent.getJComponent().validate(); + } + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java new file mode 100644 index 000000000..9750c9bf7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingTextFieldPeer.java @@ -0,0 +1,411 @@ +/* SwingTextFieldPeer.java -- A Swing based peer for AWT textfields + Copyright (C) 2006, 2007 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.swing; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.TextField; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.im.InputMethodRequests; +import java.awt.peer.TextFieldPeer; + +import javax.swing.JComponent; +import javax.swing.JTextField; + +/** + * A TextFieldPeer based on Swing JTextField. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class SwingTextFieldPeer + extends SwingComponentPeer + implements TextFieldPeer +{ + + /** + * A specialized Swing textfield for use in the peer. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class SwingTextField + extends JTextField + implements SwingComponent + { + + TextField textField; + + SwingTextField(TextField textField) + { + this.textField = textField; + } + + /** + * Overridden to provide normal behaviour even without a real peer + * attached. + * + * @return the location of the textfield on screen + */ + public Point getLocationOnScreen() + { + return SwingTextFieldPeer.this.getLocationOnScreen(); + } + + /** + * Overridden so that the isShowing method returns the correct value + * for the swing button, even if it has no peer on its own. + * + * @return <code>true</code> if the button is currently showing, + * <code>false</code> otherwise + */ + public boolean isShowing() + { + boolean retVal = false; + if (textField != null) + retVal = textField.isShowing(); + return retVal; + } + + /** + * Overridden, so that the Swing button can create an Image without its + * own peer. + * + * @param w the width of the image + * @param h the height of the image + * + * @return an image + */ + public Image createImage(int w, int h) + { + return SwingTextFieldPeer.this.createImage(w, h); + } + + /** + * Returns this textfield. + * + * @return <code>this</code> + */ + public JComponent getJComponent() + { + return this; + } + + /** + * Handles mouse events by forwarding it to the swing textfield. + * + * @param ev the mouse event + */ + public void handleMouseEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseEvent(ev); + } + + /** + * Handles mouse motion events by forwarding it to the swing textfield. + * + * @param ev the mouse motion event + */ + public void handleMouseMotionEvent(MouseEvent ev) + { + ev.setSource(this); + processMouseMotionEvent(ev); + } + + /** + * Handles key events by forwarding it to the swing textfield. + * + * @param ev the key event + */ + public void handleKeyEvent(KeyEvent ev) + { + ev.setSource(this); + processKeyEvent(ev); + } + + /** + * Handles focus events by forwarding it to + * <code>processFocusEvent()</code>. + * + * @param ev the Focus event + */ + public void handleFocusEvent(FocusEvent ev) + { + processFocusEvent(ev); + } + + + public Container getParent() + { + Container par = null; + if (textField != null) + par = textField.getParent(); + return par; + } + + public Graphics getGraphics() + { + return SwingTextFieldPeer.this.getGraphics(); + } + + public void requestFocus() { + SwingTextFieldPeer.this.requestFocus(awtComponent, false, true, 0); + } + + public boolean requestFocus(boolean temporary) { + return SwingTextFieldPeer.this.requestFocus(awtComponent, temporary, + true, 0); + } + + } + + /** + * Creates a new <code>SwingTextFieldPeer</code> instance for the specified + * AWT textfield. + * + * @param textField the AWT textfield + */ + public SwingTextFieldPeer(TextField textField) + { + SwingTextField swingTextField = new SwingTextField(textField); + swingTextField.setText(textField.getText()); + init(textField, swingTextField); + } + + /** + * Returns the minimum size of the textfield. + * + * @param len not used here + * + * @return the minimum size of the textfield + */ + public Dimension minimumSize(int len) + { + return swingComponent.getJComponent().getMinimumSize(); + } + + /** + * Returns the preferred size of the textfield. + * + * @param len not used here + * + * @return the preferred size of the textfield + */ + public Dimension preferredSize(int len) + { + return swingComponent.getJComponent().getPreferredSize(); + } + + /** + * Returns the minimum size of the textfield. + * + * @param len not used here + * + * @return the minimum size of the textfield + */ + public Dimension getMinimumSize(int len) + { + return swingComponent.getJComponent().getMinimumSize(); + } + + /** + * Returns the preferred size of the textfield. + * + * @param len not used here + * + * @return the preferred size of the textfield + */ + public Dimension getPreferredSize(int len) + { + return swingComponent.getJComponent().getPreferredSize(); + } + + /** + * Sets the echo character. + * + * @param echoChar the echo character to be set + */ + public void setEchoChar(char echoChar) + { + // TODO: Must be implemented. + } + + /** + * Sets the echo character. + * + * @param echoChar the echo character to be set + */ + public void setEchoCharacter(char echoChar) + { + // TODO: Must be implemented. + } + + /** + * Returns the end index of the current selection. + * + * @return the end index of the current selection + */ + public int getSelectionEnd() + { + // TODO: Must be implemented. + return 0; + } + + /** + * Returns the start index of the current selection. + * + * @return the start index of the current selection + */ + public int getSelectionStart() + { + // TODO: Must be implemented. + return 0; + } + + /** + * Returns the current content of the textfield. + * + * @return the current content of the textfield + */ + public String getText() + { + return ((JTextField) swingComponent.getJComponent()).getText(); + } + + /** + * Sets the content of the textfield. + * + * @param text the text to set + */ + public void setText(String text) + { + ((JTextField) swingComponent.getJComponent()).setText(text); + } + + /** + * Sets the current selection. + * + * @param startPos the start index of the selection + * @param endPos the start index of the selection + */ + public void select(int startPos, int endPos) + { + // TODO: Must be implemented. + } + + /** + * Sets the editable flag of the text field. + * + * @param editable <code>true</code> to make the textfield editable, + * <code>false</code> to make it uneditable + */ + public void setEditable(boolean editable) + { + ((JTextField) swingComponent.getJComponent()).setEditable(editable); + } + + /** + * Returns the current caret position. + * + * @return the current caret position + */ + public int getCaretPosition() + { + return ((JTextField) swingComponent.getJComponent()).getCaret().getDot(); + } + + /** + * Sets the current caret position. + * + * @param pos the caret position to set + */ + public void setCaretPosition(int pos) + { + ((JTextField) swingComponent.getJComponent()).getCaret().setDot(pos); + } + + /** + * Returns the index of the character at the specified location. + * + * @param x the X coordinate of the point to query + * @param y the Y coordinate of the point to query + * + * @return the index of the character at the specified location + */ + public int getIndexAtPoint(int x, int y) + { + // TODO: Must be implemented. + return 0; + } + + /** + * Returns the bounds of the character at the specified index. + * + * @param pos the index of the character + * + * @return the bounds of the character at the specified index + */ + public Rectangle getCharacterBounds(int pos) + { + // TODO: Must be implemented. + return null; + } + + /** + * Not used. + */ + public long filterEvents(long filter) + { + // TODO: Must be implemented. + return 0; + } + + /** + * Not used. + */ + public InputMethodRequests getInputMethodRequests() + { + // TODO: Must be implemented. + return null; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingToolkit.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingToolkit.java new file mode 100644 index 000000000..63414050b --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingToolkit.java @@ -0,0 +1,181 @@ +/* SwingToolkit.java -- A base toolkit for Swing peers + 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.swing; + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Dialog; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.TextField; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.TextFieldPeer; + +import gnu.java.awt.ClasspathToolkit; + +/** + * A base implementation for {@link java.awt.Toolkit} that provides the + * Swing based widgets. Concrete implementations still must provide the + * remaining abstract methods. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public abstract class SwingToolkit extends ClasspathToolkit +{ + + /** + * Creates a SwingButtonPeer. + * + * @param button the AWT button + * + * @return the Swing button peer + */ + protected ButtonPeer createButton(Button button) + { + return new SwingButtonPeer(button); + } + + /** + * Creates a SwingCanvasPeer. + * + * @param canvas the AWT canvas + * + * @return the Swing canvas peer + */ + protected CanvasPeer createCanvas(Canvas canvas) + { + return new SwingCanvasPeer(canvas); + } + + /** + * Creates a SwingLabelPeer. + * + * @param label the AWT label + * + * @return the Swing label peer + */ + protected LabelPeer createLabel(Label label) + { + return new SwingLabelPeer(label); + } + + /** + * Creates a SwingMenuPeer. + * + * @param menu the AWT menu + * + * @return the Swing menu peer + */ + protected MenuPeer createMenu(Menu menu) + { + return new SwingMenuPeer(menu); + } + + /** + * Creates a SwingMenuBarPeer. + * + * @param menuBar the AWT menubar + * + * @return the Swing menu bar peer + */ + protected MenuBarPeer createMenuBar(MenuBar menuBar) + { + return new SwingMenuBarPeer(menuBar); + } + + /** + * Creates a SwingMenuItemPeer. + * + * @param menuItem the AWT menu item + * + * @return the Swing menu item peer + */ + protected MenuItemPeer createMenuItem(MenuItem menuItem) + { + return new SwingMenuItemPeer(menuItem); + } + + /** + * Creates a SwingPanelPeer. + * + * @param panel the AWT panel + * + * @return the Swing panel peer + */ + protected PanelPeer createPanel(Panel panel) + { + return new SwingPanelPeer(panel); + } + + /** + * Creates a SwingTextFieldPeer. + * + * @param textField the AWT text field + * + * @return the Swing text field peer + */ + protected TextFieldPeer createTextField(TextField textField) + { + return new SwingTextFieldPeer(textField); + } + + @Override + public boolean isModalExclusionTypeSupported + (Dialog.ModalExclusionType modalExclusionType) + { + return false; + } + + @Override + public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) + { + return false; + } + + +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java new file mode 100644 index 000000000..bdc494e95 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/SwingWindowPeer.java @@ -0,0 +1,99 @@ +/* SwingWindowPeer.java -- An abstract base for Swing based window peers + 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.swing; + +import java.awt.Window; +import java.awt.peer.ComponentPeer; +import java.awt.peer.WindowPeer; + +/** + * An abstract base class for Swing based WindowPeer implementation. Concrete + * implementations of WindowPeers should subclass this class in order to get + * the correct behaviour. + * + * 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> + * </ul> + * + * @author Roman Kennke (kennke@aicas.com) + */ +public abstract class SwingWindowPeer + extends SwingContainerPeer + implements WindowPeer +{ + + /** + * Creates a new instance of WindowPeer. + * + * @param window the AWT window + */ + public SwingWindowPeer(Window window) + { + super(window); + init(window, null); + } + + public void updateIconImages() + { + // TODO: Implement properly. + } + + public void updateMinimumSize() + { + // TODO: Implement properly. + } + + public void setModalBlocked(java.awt.Dialog d, boolean b) + { + // TODO: Implement properly. + } + + public void updateFocusableWindowState() + { + // TODO: Implement properly. + } + + public void setAlwaysOnTop(boolean b) + { + // TODO: Implement properly. + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/swing/package.html b/libjava/classpath/gnu/java/awt/peer/swing/package.html new file mode 100644 index 000000000..506eda883 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/swing/package.html @@ -0,0 +1,71 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.awt.peer.swing package. + 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. --> + +<html> + <head> + <title>Swing based AWT peers</title> + </head> + <body> + <h1>Swing based AWT peers.</h1> + <p>This package defines an abstract set of AWT peers that is based on Swing + widgets. This can be used as an implementation base for peer implementors + who don't have access to native widgets or who want to build a quick + prototype of a peer set without implementing all of the AWT widgets. + </p> + <p>An actual implementation would have to provide the following: + <ul> + <li>A concrete implementation of {@link java.awt.Toolkit}, possibly based + on {@link SwingToolkit}. This implementation must provide all the missing + methods of the <code>SwingToolkit</code>.</li> + <li>Concrete implementations of {@link java.awt.peer.DialogPeer}, + {@link java.awt.peer.FramePeer} and {@link java.awt.peer.WindowPeer}, + ideally based on their <code>SwingXXXPeer</code> counterparts. + Some methods must be specially + overridden in those peers to provide useful functionality, like + <code>getLocationOnScreen()</code>. See the API documentation for more + details</li> + <li>An implementation of {@link java.awt.Image}. These must be provided by + the toplevel component peers.</li> + <li>An implementation of {@link java.awt.Graphics}. This must also be + provided by the toplevel peers.</li> + <li>An implementation of {@link java.awt.Font}. This must be + provided by the toolkit.</li> + </ul> + </p> + </body> +</html> diff --git a/libjava/classpath/gnu/java/awt/peer/x/GLGraphics.java b/libjava/classpath/gnu/java/awt/peer/x/GLGraphics.java new file mode 100644 index 000000000..3cf3797ab --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/GLGraphics.java @@ -0,0 +1,134 @@ +/* GLGraphics.java -- Graphics2D impl on top of GLX + 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.x; + +import java.awt.Color; +import java.awt.GraphicsConfiguration; +import java.awt.Rectangle; +import java.awt.image.ColorModel; +import java.util.Map; + +import gnu.java.awt.java2d.AbstractGraphics2D; +import gnu.x11.extension.glx.GL; + +/** + * An implementation of Graphics2D on top of the GLX extension of X. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class GLGraphics extends AbstractGraphics2D +{ + + /** + * The rendering context. + */ + private GL gl; + + /** + * Creates a new GLGraphics that paints on the specified GL context. + * + * @param g the GL context to paint to + */ + GLGraphics(GL g) + { + gl = g; + } + + public void setBackground(Color b) + { + super.setBackground(b); + + gl.clearColor(b.getRed() / 255.F, b.getGreen() / 255.F, + b.getBlue() / 255.F, b.getAlpha() / 255.F); + } + + public void clearRect(int x, int y, int w, int h) + { + // TODO: Maybe use fillRect(). + gl.clear(GL.COLOR_BUFFER_BIT); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + gl.begin(GL.LINES); + gl.vertex2i(x1, y1); + gl.vertex2i(x2, y2); + gl.end(); + // TODO: Maybe do: + // gl.flush(); + } + + public void drawRect(int x, int y, int w, int h) + { + gl.polygon_mode(GL.FRONT_AND_BACK, GL.LINE); + gl.begin(GL.POLYGON); + gl.recti(x, y, x + w, y + h); + gl.end(); + // TODO: Maybe do: + // gl.flush(); + } + + public void fillRect(int x, int y, int w, int h) + { + gl.polygon_mode(GL.FRONT_AND_BACK, GL.FILL); + gl.recti(x, y, x + w, y + h); + // TODO: Maybe do: + // gl.flush(); + } + + protected ColorModel getColorModel() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + @Override + protected Rectangle getDeviceBounds() + { + // FIXME: not sure it's correct + return new Rectangle(0, 0, + gl.display.default_screen.width, + gl.display.default_screen.height); + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/KeyboardMapping.java b/libjava/classpath/gnu/java/awt/peer/x/KeyboardMapping.java new file mode 100644 index 000000000..c982a30d5 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/KeyboardMapping.java @@ -0,0 +1,419 @@ +/* KeyboardMapping.java -- Maps X keysyms to Java keyCode and keyChar + 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.x; + +import gnu.x11.Input; +import gnu.x11.keysym.Latin1; +import gnu.x11.keysym.Misc; + +import java.awt.event.KeyEvent; + +/** + * Defines the keyboard mapping from X keysyms to Java + * keycodes and keychars. + * + * @author Roman Kennke (kennke@aicas.com) + */ +final class KeyboardMapping +{ + + /** + * Maps X keycodes to AWT keycodes. + * + * @param xInput the X input event + * @param xKeyCode the X keycode to map + * @param xMods the X modifiers + * + * @return the AWT keycode and keychar + */ + static int mapToKeyCode(gnu.x11.Input xInput, int xKeyCode, int xMods) + { + int mapped = KeyEvent.VK_UNDEFINED; + int keysym = xInput.keycode_to_keysym(xKeyCode, xMods, true); + + // Special keys. + if (keysym >= 255 << 8) + { + // FIXME: Add missing mappings. + switch (keysym) + { + case Misc.BACKSPACE: + mapped = KeyEvent.VK_BACK_SPACE; + break; + case Misc.TAB: + mapped = KeyEvent.VK_TAB; + break; + case Misc.CLEAR: + mapped = KeyEvent.VK_CLEAR; + break; + case Misc.RETURN: + mapped = KeyEvent.VK_ENTER; + break; + case Misc.PAUSE: + mapped = KeyEvent.VK_PAUSE; + break; + case Misc.SCROLL_LOCK: + mapped = KeyEvent.VK_SCROLL_LOCK; + break; + case Misc.ESCAPE: + mapped = KeyEvent.VK_ESCAPE; + break; + case Misc.HOME: + mapped = KeyEvent.VK_HOME; + break; + case Misc.LEFT: + mapped = KeyEvent.VK_LEFT; + break; + case Misc.UP: + mapped = KeyEvent.VK_UP; + break; + case Misc.RIGHT: + mapped = KeyEvent.VK_RIGHT; + break; + case Misc.DOWN: + mapped = KeyEvent.VK_DOWN; + break; + case Misc.PAGE_UP: + mapped = KeyEvent.VK_PAGE_UP; + break; + case Misc.PAGE_DOWN: + mapped = KeyEvent.VK_PAGE_DOWN; + break; + case Misc.END: + mapped = KeyEvent.VK_END; + break; + case Misc.BEGIN: + mapped = KeyEvent.VK_BEGIN; + break; + case Misc.INSERT: + mapped = KeyEvent.VK_INSERT; + break; + case Misc.UNDO: + mapped = KeyEvent.VK_UNDO; + break; + case Misc.FIND: + mapped = KeyEvent.VK_FIND; + break; + case Misc.CANCEL: + mapped = KeyEvent.VK_CANCEL; + break; + case Misc.HELP: + mapped = KeyEvent.VK_HELP; + break; + case Misc.MODE_SWITCH: + mapped = KeyEvent.VK_MODECHANGE; + break; + case Misc.NUM_LOCK: + mapped = KeyEvent.VK_NUM_LOCK; + break; + case Misc.KP_LEFT: + mapped = KeyEvent.VK_KP_LEFT; + break; + case Misc.KP_UP: + mapped = KeyEvent.VK_KP_UP; + break; + case Misc.KP_RIGHT: + mapped = KeyEvent.VK_KP_RIGHT; + break; + case Misc.KP_DOWN: + mapped = KeyEvent.VK_KP_DOWN; + break; + case Misc.F1: + mapped = KeyEvent.VK_F1; + break; + case Misc.F2: + mapped = KeyEvent.VK_F2; + break; + case Misc.F3: + mapped = KeyEvent.VK_F3; + break; + case Misc.F4: + mapped = KeyEvent.VK_F4; + break; + case Misc.F5: + mapped = KeyEvent.VK_F5; + break; + case Misc.F6: + mapped = KeyEvent.VK_F6; + break; + case Misc.F7: + mapped = KeyEvent.VK_F7; + break; + case Misc.F8: + mapped = KeyEvent.VK_F8; + break; + case Misc.F9: + mapped = KeyEvent.VK_F9; + break; + case Misc.F10: + mapped = KeyEvent.VK_F10; + break; + case Misc.F11: + mapped = KeyEvent.VK_F11; + break; + case Misc.F12: + mapped = KeyEvent.VK_F12; + break; + case Misc.F13: + mapped = KeyEvent.VK_F13; + break; + case Misc.F14: + mapped = KeyEvent.VK_F14; + break; + case Misc.F15: + mapped = KeyEvent.VK_F15; + break; + case Misc.F16: + mapped = KeyEvent.VK_F16; + break; + case Misc.F17: + mapped = KeyEvent.VK_F17; + break; + case Misc.F18: + mapped = KeyEvent.VK_F18; + break; + case Misc.F19: + mapped = KeyEvent.VK_F19; + break; + case Misc.F20: + mapped = KeyEvent.VK_F20; + break; + case Misc.F21: + mapped = KeyEvent.VK_F21; + break; + case Misc.F22: + mapped = KeyEvent.VK_F22; + break; + case Misc.F23: + mapped = KeyEvent.VK_F23; + break; + case Misc.F24: + mapped = KeyEvent.VK_F24; + break; + case Misc.SHIFT_L: + case Misc.SHIFT_R: + mapped = KeyEvent.VK_SHIFT; + break; + case Misc.CONTROL_L: + case Misc.CONTROL_R: + mapped = KeyEvent.VK_CONTROL; + break; + case Misc.CAPS_LOCK: + case Misc.SHIFT_LOCK: + mapped = KeyEvent.VK_CAPS_LOCK; + break; + case Misc.META_L: + case Misc.META_R: + mapped = KeyEvent.VK_META; + break; + case Misc.ALT_L: + case Misc.ALT_R: + mapped = KeyEvent.VK_ALT; + break; + case Misc.DELETE: + mapped = KeyEvent.VK_DELETE; + break; + default: + mapped = KeyEvent.VK_UNDEFINED; + } + } + // Map Latin1 characters. + else if (keysym < 256) + { + // TODO: Add missing mappings, if any. + // Lowercase characters are mapped to + // their corresponding upper case pendants. + if (keysym >= Latin1.A_SMALL && keysym <= Latin1.Z_SMALL) + mapped = keysym - 0x20; + // Uppercase characters are mapped 1:1. + else if (keysym >= Latin1.A && keysym <= Latin1.Z) + mapped = keysym; + // Digits are mapped 1:1. + else if (keysym >= Latin1.NUM_0 && keysym <= Latin1.NUM_9) + mapped = keysym; + else + { + switch (keysym) + { + case Latin1.SPACE: + mapped = KeyEvent.VK_SPACE; + break; + case Latin1.EXCLAM: + mapped = KeyEvent.VK_EXCLAMATION_MARK; + break; + case Latin1.QUOTE_DBL: + mapped = KeyEvent.VK_QUOTEDBL; + break; + case Latin1.NUMBER_SIGN: + mapped = KeyEvent.VK_NUMBER_SIGN; + break; + case Latin1.DOLLAR: + mapped = KeyEvent.VK_DOLLAR; + break; + case Latin1.AMPERSAND: + mapped = KeyEvent.VK_AMPERSAND; + break; + case Latin1.APOSTROPHE: + mapped = KeyEvent.VK_QUOTE; + break; + case Latin1.PAREN_LEFT: + mapped = KeyEvent.VK_LEFT_PARENTHESIS; + break; + case Latin1.PAREN_RIGHT: + mapped = KeyEvent.VK_RIGHT_PARENTHESIS; + break; + case Latin1.ASTERISK: + mapped = KeyEvent.VK_ASTERISK; + break; + case Latin1.PLUS: + mapped = KeyEvent.VK_PLUS; + break; + case Latin1.COMMA: + mapped = KeyEvent.VK_COMMA; + break; + case Latin1.MINUS: + mapped = KeyEvent.VK_MINUS; + break; + case Latin1.PERIOD: + mapped = KeyEvent.VK_PERIOD; + break; + case Latin1.SLASH: + mapped = KeyEvent.VK_SLASH; + break; + case Latin1.COLON: + mapped = KeyEvent.VK_COLON; + break; + case Latin1.SEMICOLON: + mapped = KeyEvent.VK_SEMICOLON; + break; + case Latin1.LESS: + mapped = KeyEvent.VK_LESS; + break; + case Latin1.EQUAL: + mapped = KeyEvent.VK_EQUALS; + break; + case Latin1.GREATER: + mapped = KeyEvent.VK_GREATER; + break; + case Latin1.AT: + mapped = KeyEvent.VK_AT; + break; + case Latin1.BRACKET_LEFT: + mapped = KeyEvent.VK_OPEN_BRACKET; + break; + case Latin1.BACKSLASH: + mapped = KeyEvent.VK_BACK_SLASH; + break; + case Latin1.BRACKET_RIGHT: + mapped = KeyEvent.VK_CLOSE_BRACKET; + break; + case Latin1.ASCII_CIRCUM: + mapped = KeyEvent.VK_CIRCUMFLEX; + break; + case Latin1.UNDERSCORE: + mapped = KeyEvent.VK_UNDERSCORE; + break; + case Latin1.GRAVE: + mapped = KeyEvent.VK_DEAD_GRAVE; + break; + case Latin1.BRACE_LEFT: + mapped = KeyEvent.VK_BRACELEFT; + break; + case Latin1.BRACE_RIGHT: + mapped = KeyEvent.VK_BRACERIGHT; + break; + case Latin1.ASCII_TILDE: + mapped = KeyEvent.VK_DEAD_TILDE; + break; + case Latin1.EXCLAM_DOWN: + mapped = KeyEvent.VK_INVERTED_EXCLAMATION_MARK; + break; + default: + mapped = KeyEvent.VK_UNDEFINED; + } + } + } + return mapped; + } + + /** + * Maps X keycodes+modifiers to Java keychars. + * + * @param xInput The X Input to use for mapping + * @param xKeyCode the X keycode + * @param xMods the X key modifiers + * + * @return the Java keychar + */ + static char mapToKeyChar(gnu.x11.Input xInput, int xKeyCode, int xMods) + { + char mapped = KeyEvent.CHAR_UNDEFINED; + char keysym = (char) xInput.keycode_to_keysym(xKeyCode, xMods, false); + // FIXME: Map other encodings properly. + if (keysym < 256) // Latin1. + { + mapped = keysym; + } + return mapped; + } + + /** + * Maps X modifier masks to AWT modifier masks. + * + * @param xMods the X modifiers + * + * @return the AWT modifiers + */ + static int mapModifiers(int xMods) + { + int mods = 0; + + if ((xMods & Input.SHIFT_MASK) != 0) + mods |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK; + if ((xMods & Input.META_MASK) != 0) + mods |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK; + if ((xMods & Input.ALT_MASK) != 0) + mods |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK; + if ((xMods & Input.MOD5_MASK) != 0) + mods |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK; + if ((xMods & Input.CONTROL_MASK) != 0) + mods |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK; + + return mods; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/PixmapVolatileImage.java b/libjava/classpath/gnu/java/awt/peer/x/PixmapVolatileImage.java new file mode 100644 index 000000000..131647fab --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/PixmapVolatileImage.java @@ -0,0 +1,185 @@ +/* PixmapVolatileImage.java -- VolatileImage implementation around a Pixmap + Copyright (C) 2007 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.x; + +import gnu.x11.GC; +import gnu.x11.Pixmap; +import gnu.x11.image.Image; +import gnu.x11.image.ZPixmap; + +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.ImageCapabilities; +import java.awt.Point; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.ImageObserver; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.VolatileImage; +import java.awt.image.WritableRaster; + +/** + * A {@link VolatileImage} implementation that wraps an X Pixmap. + */ +class PixmapVolatileImage + extends VolatileImage +{ + + /** + * The shared capabilities instance. + */ + private static final ImageCapabilities caps = new ImageCapabilities(true); + + /** + * The underlying pixmap. + */ + private Pixmap pixmap; + + /** + * Creates a new PixmapVolatileImage. + * + * @param w the width of the image + * @param h the height of the image + */ + public PixmapVolatileImage(int w, int h) + { + GraphicsEnvironment env = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + XGraphicsDevice dev = (XGraphicsDevice) env.getDefaultScreenDevice(); + pixmap = new Pixmap(dev.getDisplay(), w, h); + + // Clear pixmap. + GC gc = new GC(pixmap); + gc.set_foreground(0xffffffff); + pixmap.fill_rectangle(gc, 0, 0, w, h); + + } + + @Override + public boolean contentsLost() + { + return false; + } + + @Override + public Graphics2D createGraphics() + { + return new XGraphics2D(pixmap); + } + + @Override + public ImageCapabilities getCapabilities() + { + return caps; + } + + @Override + public int getHeight() + { + return pixmap.height; + } + + @Override + public BufferedImage getSnapshot() + { + // TODO: Support non-24-bit resolutions. + int w = pixmap.width; + int h = pixmap.height; + ZPixmap zpixmap = (ZPixmap) pixmap.image(0, 0, w, h, 0xffffffff, + Image.Format.ZPIXMAP); + DataBuffer buffer = new ZPixmapDataBuffer(zpixmap); + SampleModel sm = new ComponentSampleModel(DataBuffer.TYPE_BYTE, w, h, 4, + w * 4, + new int[]{0, 1, 2, 3 }); + ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); + ColorModel cm = new ComponentColorModel(cs, true, false, + Transparency.OPAQUE, + DataBuffer.TYPE_BYTE); + WritableRaster raster = Raster.createWritableRaster(sm, buffer, + new Point(0, 0)); + return new BufferedImage(cm, raster, false, null); + } + + @Override + public int getWidth() + { + return pixmap.width; + } + + @Override + public int validate(GraphicsConfiguration gc) + { + // TODO: Check compatibility with gc. + return IMAGE_OK; + } + + @Override + public int getHeight(ImageObserver observer) + { + return getHeight(); + } + + @Override + public Object getProperty(String name, ImageObserver observer) + { + return null; + } + + @Override + public int getWidth(ImageObserver observer) + { + return getWidth(); + } + + /** + * Returns the underlying X pixmap. This is used for the graphics code. + * + * @return the underlying X pixmap + */ + Pixmap getPixmap() + { + return pixmap; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XDialogPeer.java b/libjava/classpath/gnu/java/awt/peer/x/XDialogPeer.java new file mode 100644 index 000000000..45ad24d67 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XDialogPeer.java @@ -0,0 +1,61 @@ +/* XDialogPeer.java -- The peer for AWT dialogs + 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.x; + +import java.awt.Dialog; +import java.awt.peer.DialogPeer; + +public class XDialogPeer + extends XWindowPeer + implements DialogPeer +{ + + XDialogPeer(Dialog target) + { + super(target); + } + + public void setResizable(boolean resizeable) + { + } + + public void setTitle(String title) + { + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XEventPump.java b/libjava/classpath/gnu/java/awt/peer/x/XEventPump.java new file mode 100644 index 000000000..8e80b97a3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XEventPump.java @@ -0,0 +1,486 @@ +/* XEventPump.java -- Pumps events from X to AWT + 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.x; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; +import java.util.HashMap; + +import gnu.java.awt.ComponentReshapeEvent; +import gnu.x11.Atom; +import gnu.x11.Display; +import gnu.x11.event.ButtonPress; +import gnu.x11.event.ButtonRelease; +import gnu.x11.event.ClientMessage; +import gnu.x11.event.ConfigureNotify; +import gnu.x11.event.DestroyNotify; +import gnu.x11.event.Event; +import gnu.x11.event.Expose; +import gnu.x11.event.Input; +import gnu.x11.event.KeyPress; +import gnu.x11.event.KeyRelease; +import gnu.x11.event.MotionNotify; +import gnu.x11.event.PropertyNotify; +import gnu.x11.event.ResizeRequest; +import gnu.x11.event.UnmapNotify; + +/** + * Fetches events from X, translates them to AWT events and pumps them up + * into the AWT event queue. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class XEventPump + implements Runnable +{ + + /** + * The X Display from which we fetch and pump up events. + */ + private Display display; + + /** + * Maps X Windows to AWT Windows to be able to correctly determine the + * event targets. + */ + private HashMap windows; + + /** + * Indicates if we are currently inside a drag operation. This is + * set to the button ID when a button is pressed and to -1 (indicating + * that no drag is active) when the mouse is released. + */ + private int drag; + + /** + * Creates a new XEventPump for the specified X Display. + * + * @param d the X Display + */ + XEventPump(Display d) + { + display = d; + windows = new HashMap(); + drag = -1; + Thread thread = new Thread(this, "X Event Pump"); + thread.setDaemon(true); + thread.start(); + } + + /** + * The main event pump loop. This basically fetches events from the + * X Display and pumps them into the system event queue. + */ + public void run() + { + while (display.connected) + { + try + { + Event xEvent = display.next_event(); + handleEvent(xEvent); + } + catch (ThreadDeath death) + { + // If someone wants to kill us, let them. + return; + } + catch (Throwable x) + { + System.err.println("Exception during event dispatch:"); + x.printStackTrace(System.err); + } + } + } + + /** + * Adds an X Window to AWT Window mapping. This is required so that the + * event pump can correctly determine the event targets. + * + * @param xWindow the X Window + * @param awtWindow the AWT Window + */ + void registerWindow(gnu.x11.Window xWindow, Window awtWindow) + { + if (XToolkit.DEBUG) + System.err.println("registering window id: " + xWindow.id); + windows.put(new Integer(xWindow.id), awtWindow); + } + + void unregisterWindow(gnu.x11.Window xWindow) + { + windows.remove(new Integer(xWindow.id)); + } + + private void handleButtonPress(ButtonPress event) + { + Integer key = new Integer(event.getEventWindowID()); + Window awtWindow = (Window) windows.get(key); + + // Create and post the mouse event. + int button = event.detail(); + + // AWT cannot handle more than 3 buttons and expects 0 instead. + if (button >= gnu.x11.Input.BUTTON3) + button = 0; + drag = button; + + Component target = + findMouseEventTarget(awtWindow, event.getEventX(), event.getEventY()); + if(target == null) + { + target = awtWindow; + } + + MouseEvent mp = new MouseEvent(target, MouseEvent.MOUSE_PRESSED, + System.currentTimeMillis(), + KeyboardMapping.mapModifiers(event.getState()) + | buttonToModifier(button), + event.getEventX(), event.getEventY(), + 1, false, button); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mp); + } + + private void handleButtonRelease(ButtonRelease event) + { + Integer key = new Integer(event.getEventWindowID()); + Window awtWindow = (Window) windows.get(key); + + int button = event.detail(); + + // AWT cannot handle more than 3 buttons and expects 0 instead. + if (button >= gnu.x11.Input.BUTTON3) + button = 0; + drag = -1; + + Component target = + findMouseEventTarget(awtWindow, event.getEventX(), event.getEventY()); + if(target == null) + { + target = awtWindow; + } + + MouseEvent mr = new MouseEvent(target, MouseEvent.MOUSE_RELEASED, + System.currentTimeMillis(), + KeyboardMapping.mapModifiers(event.getState()) + | buttonToModifier(button), + event.getEventX(), event.getEventY(), + 1, false, button); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mr); + } + + + private void handleMotionNotify(MotionNotify event) + { + Integer key = new Integer(event.getEventWindowID()); + Window awtWindow = (Window) windows.get(key); + + int button = event.detail(); + + // AWT cannot handle more than 3 buttons and expects 0 instead. + if (button >= gnu.x11.Input.BUTTON3) + button = 0; + + MouseEvent mm = null; + if (drag == -1) + { + mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_MOVED, + System.currentTimeMillis(), + KeyboardMapping.mapModifiers(event.getState()) + | buttonToModifier(button), + event.getEventX(), event.getEventY(), + 1, false); + + } + else + { + mm = new MouseEvent(awtWindow, MouseEvent.MOUSE_DRAGGED, + System.currentTimeMillis(), + KeyboardMapping.mapModifiers(event.getState()) + | buttonToModifier(drag), + event.getEventX(), event.getEventY(), + 1, false); + } + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(mm); + } + + // FIME: refactor and make faster, maybe caching the event and handle + // and/or check timing (timing is generated for PropertyChange)? + private void handleExpose(Expose event) + { + Integer key = new Integer(event.window_id); + Window awtWindow = (Window) windows.get(key); + + if (XToolkit.DEBUG) + System.err.println("expose request for window id: " + key); + + Rectangle r = new Rectangle(event.x(), event.y(), event.width(), + event.height()); + // We need to clear the background of the exposed rectangle. + assert awtWindow != null : "awtWindow == null for window ID: " + key; + + Graphics g = awtWindow.getGraphics(); + g.clearRect(r.x, r.y, r.width, r.height); + g.dispose(); + + XWindowPeer xwindow = (XWindowPeer) awtWindow.getPeer(); + Insets i = xwindow.insets(); + if (event.width() != awtWindow.getWidth() - i.left - i.right + || event.height() != awtWindow.getHeight() - i.top - i.bottom) + { + int w = event.width(); + int h = event.height(); + int x = xwindow.xwindow.x; + int y = xwindow.xwindow.y; + + if (XToolkit.DEBUG) + System.err.println("Setting size on AWT window: " + w + + ", " + h + ", " + awtWindow.getWidth() + + ", " + awtWindow.getHeight()); + + // new width and height + xwindow.xwindow.width = w; + xwindow.xwindow.height = h; + + // reshape the window + ComponentReshapeEvent cre = + new ComponentReshapeEvent(awtWindow, x, y, w, h); + awtWindow.dispatchEvent(cre); + } + + ComponentEvent ce = + new ComponentEvent(awtWindow, ComponentEvent.COMPONENT_RESIZED); + awtWindow.dispatchEvent(ce); + + PaintEvent pev = new PaintEvent(awtWindow, PaintEvent.UPDATE, r); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(pev); + } + + private void handleDestroyNotify(DestroyNotify destroyNotify) + { + if (XToolkit.DEBUG) + System.err.println("DestroyNotify event: " + destroyNotify); + + Integer key = new Integer(destroyNotify.event_window_id); + Window awtWindow = (Window) windows.get(key); + + AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_CLOSED); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event); + } + + private void handleClientMessage(ClientMessage clientMessage) + { + if (XToolkit.DEBUG) + System.err.println("ClientMessage event: " + clientMessage); + + if (clientMessage.delete_window()) + { + if (XToolkit.DEBUG) + System.err.println("ClientMessage is a delete_window event"); + + Integer key = new Integer(clientMessage.window_id); + Window awtWindow = (Window) windows.get(key); + + AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_CLOSING); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event); + } + } + + private void handleEvent(Event xEvent) + { + if (XToolkit.DEBUG) + System.err.println("fetched event: " + xEvent); + + switch (xEvent.code() & 0x7f) + { + case ButtonPress.CODE: + this.handleButtonPress((ButtonPress) xEvent); + break; + case ButtonRelease.CODE: + this.handleButtonRelease((ButtonRelease) xEvent); + break; + case MotionNotify.CODE: + this.handleMotionNotify((MotionNotify) xEvent); + break; + case Expose.CODE: + this.handleExpose((Expose) xEvent); + break; + case KeyPress.CODE: + case KeyRelease.CODE: + Integer key = new Integer(((Input) xEvent).getEventWindowID()); + Window awtWindow = (Window) windows.get(key); + handleKeyEvent(xEvent, awtWindow); + break; + case DestroyNotify.CODE: + this.handleDestroyNotify((DestroyNotify) xEvent); + break; + case ClientMessage.CODE: + this.handleClientMessage((ClientMessage) xEvent); + break; + case PropertyNotify.CODE: + key = new Integer (((PropertyNotify) xEvent).getWindowID()); + awtWindow = (Window) windows.get(key); + AWTEvent event = new WindowEvent(awtWindow, WindowEvent.WINDOW_STATE_CHANGED); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event); + break; + default: + if (XToolkit.DEBUG) + System.err.println("Unhandled X event: " + xEvent); + } + } + + /** + * Handles key events from X. + * + * @param xEvent the X event + * @param awtWindow the AWT window to which the event gets posted + */ + private void handleKeyEvent(Event xEvent, Window awtWindow) + { + Input keyEvent = (Input) xEvent; + int xKeyCode = keyEvent.detail(); + int xMods = keyEvent.getState(); + int keyCode = KeyboardMapping.mapToKeyCode(xEvent.display.input, xKeyCode, + xMods); + char keyChar = KeyboardMapping.mapToKeyChar(xEvent.display.input, xKeyCode, + xMods); + if (XToolkit.DEBUG) + System.err.println("XEventPump.handleKeyEvent: " + xKeyCode + ", " + + xMods + ": " + ((int) keyChar) + ", " + keyCode); + int awtMods = KeyboardMapping.mapModifiers(xMods); + long when = System.currentTimeMillis(); + KeyEvent ke; + if (keyEvent.code() == KeyPress.CODE) + { + ke = new KeyEvent(awtWindow, KeyEvent.KEY_PRESSED, when, + awtMods, keyCode, + KeyEvent.CHAR_UNDEFINED); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke); + if (keyChar != KeyEvent.CHAR_UNDEFINED) + { + ke = new KeyEvent(awtWindow, KeyEvent.KEY_TYPED, when, + awtMods, KeyEvent.VK_UNDEFINED, + keyChar); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke); + } + + } + else + { + ke = new KeyEvent(awtWindow, KeyEvent.KEY_RELEASED, when, + awtMods, keyCode, + KeyEvent.CHAR_UNDEFINED); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ke); + } + + } + + /** Translates an X button identifier to the AWT's MouseEvent modifier + * mask. As the AWT cannot handle more than 3 buttons those return + * <code>0</code>. + */ + static int buttonToModifier(int button) + { + switch (button) + { + case gnu.x11.Input.BUTTON1: + return MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON1_MASK; + case gnu.x11.Input.BUTTON2: + return MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON2_MASK; + case gnu.x11.Input.BUTTON3: + return MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON3_MASK; + } + + return 0; + } + + /** + * Finds the heavyweight mouse event target. + * + * @param src the original source of the event + * + * @param pt the event coordinates + * + * @return the real mouse event target + */ + private Component findMouseEventTarget(Component src, int x, int y) + { + Component found = null; + if (src instanceof Container) + { + Container cont = (Container) src; + int numChildren = cont.getComponentCount(); + for (int i = 0; i < numChildren && found == null; i++) + { + Component child = cont.getComponent(i); + if (child != null && child.isVisible() + && child.contains(x - child.getX(), y - child.getY())) + { + if (child instanceof Container) + { + Component deeper = findMouseEventTarget(child, + x - child.getX(), + y - child.getY()); + if (deeper != null) + found = deeper; + } + else if (! child.isLightweight()) + found = child; + } + } + } + + // Consider the source itself. + if (found == null && src.contains(x, y) && ! src.isLightweight()) + found = src; + + return found; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java b/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java new file mode 100644 index 000000000..190209014 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XFontPeer.java @@ -0,0 +1,770 @@ +/* XFontPeer.java -- The font peer for X + 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.x; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.AWTError; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.InputStream; +import java.text.CharacterIterator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.x11.Display; +import gnu.x11.Fontable; + +/** + * The bridge from AWT to X fonts. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class XFontPeer + extends ClasspathFontPeer +{ + + /** + * The font mapping as specified in the file fonts.properties. + */ + private static Properties fontProperties; + static + { + fontProperties = new Properties(); + InputStream in = XFontPeer.class.getResourceAsStream("xfonts.properties"); + try + { + fontProperties.load(in); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * The FontMetrics implementation for XFontPeer. + */ + private class XFontMetrics + extends FontMetrics + { + /** + * The ascent of the font. + */ + int ascent; + + /** + * The descent of the font. + */ + int descent; + + /** + * The maximum of the character advances. + */ + private int maxAdvance; + + /** + * The internal leading. + */ + int leading; + + /** + * Cached string metrics. This caches string metrics locally so that the + * server doesn't have to be asked each time. + */ + private HashMap metricsCache; + + /** + * The widths of the characters indexed by the characters themselves. + */ + private int[] charWidths; + + /** + * Creates a new XFontMetrics for the specified font. + * + * @param font the font + */ + protected XFontMetrics(Font font) + { + super(font); + metricsCache = new HashMap(); + Fontable.FontInfo info = getXFont().info(); + ascent = info.font_ascent(); + descent = info.font_descent(); + maxAdvance = info.max_bounds().character_width(); + leading = 0; // TODO: Not provided by X. Possible not needed. + + if (info.min_byte1() == 0 && info.max_byte1() == 0) + readCharWidthsLinear(info); + else + readCharWidthsNonLinear(info); + } + + /** + * Reads the character widths when specified in a linear fashion. That is + * when the min-byte1 and max-byte2 fields are both zero in the X protocol. + * + * @param info the font info reply + */ + private void readCharWidthsLinear(Fontable.FontInfo info) + { + int startIndex = info.min_char_or_byte2(); + int endIndex = info.max_char_or_byte2(); + charWidths = new int[endIndex + 1]; + // All the characters before startIndex are zero width. + for (int i = 0; i < startIndex; i++) + { + charWidths[i] = 0; + } + // All the other character info is fetched from the font info. + int index = startIndex; + Fontable.FontInfo.CharInfo[] charInfos = info.char_infos(); + for (Fontable.FontInfo.CharInfo charInfo : charInfos) + { + charWidths[index] = charInfo.character_width(); + index++; + } + } + + private void readCharWidthsNonLinear(Fontable.FontInfo info) + { + // TODO: Implement. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Returns the ascent of the font. + * + * @return the ascent of the font + */ + public int getAscent() + { + return ascent; + } + + /** + * Returns the descent of the font. + * + * @return the descent of the font + */ + public int getDescent() + { + return descent; + } + + /** + * Returns the overall height of the font. This is the distance from + * baseline to baseline (usually ascent + descent + leading). + * + * @return the overall height of the font + */ + public int getHeight() + { + return ascent + descent; + } + + /** + * Returns the leading of the font. + * + * @return the leading of the font + */ + public int getLeading() + { + return leading; + } + + /** + * Returns the maximum advance for this font. + * + * @return the maximum advance for this font + */ + public int getMaxAdvance() + { + return maxAdvance; + } + + /** + * Determines the width of the specified character <code>c</code>. + * + * @param c the character + * + * @return the width of the character + */ + public int charWidth(char c) + { + int width; + if (c > charWidths.length) + width = charWidths['?']; + else + width = charWidths[c]; + return width; + } + + /** + * Determines the overall width of the specified string. + * + * @param c the char buffer holding the string + * @param offset the starting offset of the string in the buffer + * @param length the number of characters in the string buffer + * + * @return the overall width of the specified string + */ + public int charsWidth(char[] c, int offset, int length) + { + int width = 0; + if (c.length > 0 && length > 0) + { + String s = new String(c, offset, length); + width = stringWidth(s); + } + return width; + } + + /** + * Determines the overall width of the specified string. + * + * @param s the string + * + * @return the overall width of the specified string + */ + public int stringWidth(String s) + { + int width = 0; + if (s.length() > 0) + { + if (metricsCache.containsKey(s)) + { + width = ((Integer) metricsCache.get(s)).intValue(); + } + else + { + Fontable.TextExtentInfo extents = getXFont().text_extent(s); + /* + System.err.println("string: '" + s + "' : "); + System.err.println("ascent: " + extents.getAscent()); + System.err.println("descent: " + extents.getDescent()); + System.err.println("overall ascent: " + extents.getOverallAscent()); + System.err.println("overall descent: " + extents.getOverallDescent()); + System.err.println("overall width: " + extents.getOverallWidth()); + System.err.println("overall left: " + extents.getOverallLeft()); + System.err.println("overall right: " + extents.getOverallRight()); + */ + width = extents.overall_width(); // + extents.overall_left(); + //System.err.println("String: " + s + ", width: " + width); + metricsCache.put(s, new Integer(width)); + } + } + //System.err.print("stringWidth: '" + s + "': "); + //System.err.println(width); + return width; + } + } + + /** + * The LineMetrics implementation for the XFontPeer. + */ + private class XLineMetrics + extends LineMetrics + { + + /** + * Returns the ascent of the font. + * + * @return the ascent of the font + */ + public float getAscent() + { + return fontMetrics.ascent; + } + + public int getBaselineIndex() + { + // FIXME: Implement this. + throw new UnsupportedOperationException(); + } + + public float[] getBaselineOffsets() + { + // FIXME: Implement this. + throw new UnsupportedOperationException(); + } + + /** + * Returns the descent of the font. + * + * @return the descent of the font + */ + public float getDescent() + { + return fontMetrics.descent; + } + + /** + * Returns the overall height of the font. This is the distance from + * baseline to baseline (usually ascent + descent + leading). + * + * @return the overall height of the font + */ + public float getHeight() + { + return fontMetrics.ascent + fontMetrics.descent; + } + + /** + * Returns the leading of the font. + * + * @return the leading of the font + */ + public float getLeading() + { + return fontMetrics.leading; + } + + public int getNumChars() + { + // FIXME: Implement this. + throw new UnsupportedOperationException(); + } + + public float getStrikethroughOffset() + { + return 0.F; // TODO: Provided by X?? + } + + public float getStrikethroughThickness() + { + return 1.F; // TODO: Provided by X?? + } + + public float getUnderlineOffset() + { + return 0.F; // TODO: Provided by X?? + } + + public float getUnderlineThickness() + { + return 1.F; // TODO: Provided by X?? + } + + } + + /** + * The X font. + */ + private gnu.x11.Font xfont; + + private String name; + + private int style; + + private int size; + + /** + * The font metrics for this font. + */ + XFontMetrics fontMetrics; + + /** + * Creates a new XFontPeer for the specified font name, style and size. + * + * @param name the font name + * @param style the font style (bold / italic / normal) + * @param size the size of the font + */ + public XFontPeer(String name, int style, int size) + { + super(name, style, size); + this.name = name; + this.style = style; + this.size = size; + } + + /** + * Creates a new XFontPeer for the specified font name and style + * attributes. + * + * @param name the font name + * @param atts the font attributes + */ + public XFontPeer(String name, Map atts) + { + super(name, atts); + String family = name; + if (family == null || family.equals("")) + family = (String) atts.get(TextAttribute.FAMILY); + if (family == null) + family = "SansSerif"; + + int size = 12; + Float sizeFl = (Float) atts.get(TextAttribute.SIZE); + if (sizeFl != null) + size = sizeFl.intValue(); + + int style = 0; + // Detect italic attribute. + Float posture = (Float) atts.get(TextAttribute.POSTURE); + if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR)) + style |= Font.ITALIC; + + // Detect bold attribute. + Float weight = (Float) atts.get(TextAttribute.WEIGHT); + if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0) + style |= Font.BOLD; + + this.name = name; + this.style = style; + this.size = size; + } + + /** + * Initializes the font peer with the specified attributes. This method is + * called from both constructors. + * + * @param name the font name + * @param style the font style + * @param size the font size + */ + private void init(String name, int style, int size) + { + if (name == null) + { + name = "SansSerif"; + } + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice dev = env.getDefaultScreenDevice(); + if (dev instanceof XGraphicsDevice) + { + Display display = ((XGraphicsDevice) dev).getDisplay(); + String fontDescr = encodeFont(name, style, size); + if (XToolkit.DEBUG) + System.err.println("XLFD font description: " + fontDescr); + xfont = new gnu.x11.Font(display, fontDescr); + } + else + { + throw new AWTError("Local GraphicsEnvironment is not XWindowGraphicsEnvironment"); + } + } + + public boolean canDisplay(Font font, int c) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public int canDisplayUpTo(Font font, CharacterIterator i, int start, int limit) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public String getSubFamilyName(Font font, Locale locale) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public String getPostScriptName(Font font) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public int getNumGlyphs(Font font) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public int getMissingGlyphCode(Font font) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public byte getBaselineFor(Font font, char c) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public String getGlyphName(Font font, int glyphIndex) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public GlyphVector createGlyphVector(Font font, FontRenderContext frc, + CharacterIterator ci) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public GlyphVector createGlyphVector(Font font, FontRenderContext ctx, + int[] glyphCodes) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public GlyphVector layoutGlyphVector(Font font, FontRenderContext frc, + char[] chars, int start, int limit, + int flags) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Returns the font metrics for the specified font. + * + * @param font the font for which to fetch the font metrics + * + * @return the font metrics for the specified font + */ + public FontMetrics getFontMetrics(Font font) + { + if (font.getPeer() != this) + throw new AWTError("The specified font has a different peer than this"); + + if (fontMetrics == null) + fontMetrics = new XFontMetrics(font); + return fontMetrics; + } + + /** + * Frees the font in the X server. + */ + protected void finalize() + { + if (xfont != null) + xfont.close(); + } + + public boolean hasUniformLineMetrics(Font font) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Returns the line metrics for this font and the specified string and + * font render context. + */ + public LineMetrics getLineMetrics(Font font, CharacterIterator ci, int begin, + int limit, FontRenderContext rc) + { + return new XLineMetrics(); + } + + public Rectangle2D getMaxCharBounds(Font font, FontRenderContext rc) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public Rectangle2D getStringBounds(Font font, CharacterIterator ci, + int begin, int limit, FontRenderContext frc) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Encodes a font name + style + size specification into a X logical font + * description (XLFD) as described here: + * + * http://www.meretrx.com/e93/docs/xlfd.html + * + * This is implemented to look up the font description in the + * fonts.properties of this package. + * + * @param name the font name + * @param atts the text attributes + * + * @return the encoded font description + */ + static String encodeFont(String name, Map atts) + { + String family = name; + if (family == null || family.equals("")) + family = (String) atts.get(TextAttribute.FAMILY); + if (family == null) + family = "SansSerif"; + + int size = 12; + Float sizeFl = (Float) atts.get(TextAttribute.SIZE); + if (sizeFl != null) + size = sizeFl.intValue(); + + int style = 0; + // Detect italic attribute. + Float posture = (Float) atts.get(TextAttribute.POSTURE); + if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR)) + style |= Font.ITALIC; + + // Detect bold attribute. + Float weight = (Float) atts.get(TextAttribute.WEIGHT); + if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0) + style |= Font.BOLD; + + return encodeFont(family, style, size); + } + + /** + * Encodes a font name + style + size specification into a X logical font + * description (XLFD) as described here: + * + * http://www.meretrx.com/e93/docs/xlfd.html + * + * This is implemented to look up the font description in the + * fonts.properties of this package. + * + * @param name the font name + * @param style the font style + * @param size the font size + * + * @return the encoded font description + */ + static String encodeFont(String name, int style, int size) + { + CPStringBuilder key = new CPStringBuilder(); + key.append(validName(name)); + key.append('.'); + switch (style) + { + case Font.BOLD: + key.append("bold"); + break; + case Font.ITALIC: + key.append("italic"); + break; + case (Font.BOLD | Font.ITALIC): + key.append("bolditalic"); + break; + case Font.PLAIN: + default: + key.append("plain"); + + } + + String protoType = fontProperties.getProperty(key.toString()); + int s = validSize(size); + return protoType.replaceFirst("%d", String.valueOf(s)); + } + + /** + * Checks the specified font name for a valid font name. If the font name + * is not known, then this returns 'sansserif' as fallback. + * + * @param name the font name to check + * + * @return a valid font name + */ + static String validName(String name) + { + String retVal; + if (name.equalsIgnoreCase("sansserif") + || name.equalsIgnoreCase("serif") + || name.equalsIgnoreCase("monospaced") + || name.equalsIgnoreCase("dialog") + || name.equalsIgnoreCase("dialoginput")) + { + retVal = name.toLowerCase(); + } + else + { + retVal = "sansserif"; + } + return retVal; + } + + /** + * Translates an arbitrary point size to a size that is typically available + * on an X server. These are the sizes 8, 10, 12, 14, 18 and 24. + * + * @param size the queried size + * @return the real available size + */ + private static final int validSize(int size) + { + int val; + if (size <= 9) + val = 8; + else if (size <= 11) + val = 10; + else if (size <= 13) + val = 12; + else if (size <= 17) + val = 14; + else if (size <= 23) + val = 18; + else + val = 24; + return val; + } + + /** + * Returns the X Font reference. This lazily loads the font when first + * requested. + * + * @return the X Font reference + */ + gnu.x11.Font getXFont() + { + if (xfont == null) + { + init(name, style, size); + } + return xfont; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XFramePeer.java b/libjava/classpath/gnu/java/awt/peer/x/XFramePeer.java new file mode 100644 index 000000000..cde67778d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XFramePeer.java @@ -0,0 +1,145 @@ +/* XFramePeer.java -- The X FramePeer implementation + 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.x; + +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.MenuBar; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; +import java.awt.peer.FramePeer; + +import gnu.java.awt.peer.swing.SwingFramePeer; +import gnu.x11.Window; +import gnu.x11.event.Event; + +public class XFramePeer + extends XWindowPeer + implements FramePeer +{ + + XFramePeer(Frame f) + { + super(f); + setTitle(f.getTitle()); + } + + public void setIconImage(Image image) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public void setMenuBar(MenuBar mb) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public void setResizable(boolean resizable) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public void setTitle(String title) + { + xwindow.set_wm_name (title); + } + + public int getState() + { + return 0; + } + + public void setState(int state) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public void setMaximizedBounds(Rectangle r) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Check if this frame peer supports being restacked. + * + * @return true if this frame peer can be restacked, + * false otherwise + * @since 1.5 + */ + public boolean isRestackSupported() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Sets the bounds of this frame peer. + * + * @param x the new x co-ordinate + * @param y the new y co-ordinate + * @param width the new width + * @param height the new height + * @since 1.5 + */ + public void setBoundsPrivate(int x, int y, int width, int height) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public Rectangle getBoundsPrivate() + { + // TODO: Implement this properly. + throw new InternalError("Not yet implemented"); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XGraphics2D.java b/libjava/classpath/gnu/java/awt/peer/x/XGraphics2D.java new file mode 100644 index 000000000..1fce2dcf7 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XGraphics2D.java @@ -0,0 +1,508 @@ +/* XGraphics2D.java -- A Java based Graphics2D impl for X + 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.x; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.Shape; +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.DataBuffer; +import java.awt.image.ImageObserver; +import java.awt.image.Raster; +import java.awt.peer.FontPeer; +import java.util.HashMap; +import java.util.WeakHashMap; + +import gnu.java.awt.image.AsyncImage; +import gnu.java.awt.java2d.AbstractGraphics2D; +import gnu.java.awt.java2d.ScanlineCoverage; +import gnu.x11.Colormap; +import gnu.x11.Drawable; +import gnu.x11.GC; +import gnu.x11.image.ZPixmap; + +public class XGraphics2D + extends AbstractGraphics2D +{ + + /** + * When this property is set to true, then images are always rendered as + * opaque images, ignoring their translucence. This is intended for + * debugging and demonstration purposes. + */ + private static final boolean RENDER_OPAQUE = + Boolean.getBoolean("escherpeer.renderopaque"); + + /** + * The X Drawable to draw on. + */ + private Drawable xdrawable; + + /** + * The X graphics context (GC). + */ + private GC xgc; + + /** + * Indicates if this graphics has already been disposed. + */ + private boolean disposed; + + /** + * The current foreground color, possibly null. + */ + private Color foreground; + + XGraphics2D(Drawable d) + { + super(); + xdrawable = d; + xgc = new GC(d); + init(); + disposed = false; + //setClip(new Rectangle(0, 0, xdrawable.width, xdrawable.height)); + } + + @Override + protected void rawDrawLine(int x0, int y0, int x1, int y1) + { + xdrawable.segment(xgc, x0, y0, x1, y1); + } + + @Override + protected void rawDrawRect(int x, int y, int w, int h) + { + xdrawable.rectangle(xgc, x, y, w, h, false); + } + + @Override + protected void rawFillRect(int x, int y, int w, int h) + { + xdrawable.rectangle(xgc, x, y, w, h, true); + } + + /** + * Returns the color model of this Graphics object. + * + * @return the color model of this Graphics object + */ + protected ColorModel getColorModel() + { + return Toolkit.getDefaultToolkit().getColorModel(); + } + + /** + * Returns the color model of the target device. + * + * @return the color model of the target device + */ + protected ColorModel getDestinationColorModel() + { + return Toolkit.getDefaultToolkit().getColorModel(); + } + + /** + * Returns the bounds of the target. + * + * @return the bounds of the target + */ + protected Rectangle getDeviceBounds() + { + return new Rectangle(0, 0, xdrawable.width, xdrawable.height); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public void dispose() + { + if (!disposed) + { + xgc.free(); + xdrawable.display.flush(); + disposed = true; + } + } + + public Graphics create() + { + // super.create() returns a copy created by clone(), so it should + // be a XGraphics2D. + XGraphics2D copy = (XGraphics2D) super.create(); + copy.xgc = xgc.copy(); + return copy; + } + + public void setClip(Shape c) + { + super.setClip(c); + if (c instanceof Rectangle) + { + Rectangle r = (Rectangle) c; + AffineTransform t = getTransform(); + int translateX = (int) t.getTranslateX(); + //System.err.println("translateX: " + translateX); + int translateY = (int) t.getTranslateY(); + //System.err.println("translateY: " + translateY); + //System.err.println("clip: " + c); + gnu.x11.Rectangle clip = new gnu.x11.Rectangle(r.x, r.y, r.width, + r.height); + xgc.set_clip_rectangles(translateX, translateY, + new gnu.x11.Rectangle[]{clip}, GC.UN_SORTED); + } + } + + /** + * Notifies the backend that the raster has changed in the specified + * rectangular area. The raster that is provided in this method is always + * the same as the one returned in {@link #getDestinationRaster}. + * Backends that reflect changes to this raster directly don't need to do + * anything here. + * + * @param raster the updated raster, identical to the raster returned + * by {@link #getDestinationRaster()} + * @param x the upper left corner of the updated region, X coordinate + * @param y the upper lef corner of the updated region, Y coordinate + * @param w the width of the updated region + * @param h the height of the updated region + */ + protected void updateRaster(Raster raster, int x, int y, int w, int h) + { + if (w > 0 && h > 0) + { + ZPixmap zPixmap = new ZPixmap(xdrawable.display, w, h, + xdrawable.display.default_pixmap_format); + int[] pixel = null; + int x1 = x + w; + int y1 = y + h; + for (int tx = x; tx < x1; tx++) + { + for (int ty = y; ty < y1; ty++) + { + pixel = raster.getPixel(tx, ty, pixel); + //System.err.println("tx: " + tx + ", ty: " + ty + ", pixel: " + pixel[0] + ", " + pixel[1] + ", " + pixel[2]); +// System.err.print("r: " + pixel[0]); +// System.err.print(", g: " + pixel[1]); +// System.err.println(", b: " + pixel[2]); + zPixmap.set_red(tx - x, ty - y, pixel[0]); + zPixmap.set_green(tx - x, ty - y, pixel[1]); + zPixmap.set_blue(tx - x, ty - y, pixel[2]); + } + } + xdrawable.put_image(xgc, zPixmap, x, y); + } + } + + @Override + public void renderScanline(int y, ScanlineCoverage c) + { + if (y >= xdrawable.height) + return; + + // TODO: Handle Composite and Paint. + ScanlineCoverage.Iterator iter = c.iterate(); + int coverageAlpha = 0; + int maxCoverage = c.getMaxCoverage(); + while (iter.hasNext()) + { + ScanlineCoverage.Range range = iter.next(); + + coverageAlpha = range.getCoverage(); + int x0 = range.getXPos(); + int l = range.getLength(); + if (coverageAlpha == c.getMaxCoverage()) + { + // Simply paint the current color over the existing pixels. + xdrawable.fill_rectangle(xgc, x0, y, l, 1); + } + else if (coverageAlpha > 0) + { + // Composite the current color with the existing pixels. + int x1 = x0 + l; + x0 = Math.min(Math.max(0, x0), xdrawable.width - 1); + x1 = Math.min(Math.max(0, x1), xdrawable.width - 1); + if ((x1 - x0) < 1) + continue; + l = x1 - x0; + gnu.x11.image.ZPixmap existing = (ZPixmap) + xdrawable.image(x0, y, l, 1, 0xFFFFFFFF, + gnu.x11.image.Image.Format.ZPIXMAP); + for (int x = 0; x < l; x++) + { + Color col = getColor(); + if (col == null) + { + col = Color.BLACK; + } + int red = col.getRed(); + int green = col.getGreen(); + int blue = col.getBlue(); + int redOut = existing.get_red(x, 0); + int greenOut = existing.get_green(x, 0); + int blueOut = existing.get_blue(x, 0); + int outAlpha = maxCoverage - coverageAlpha; + redOut = redOut * outAlpha + red * coverageAlpha; + redOut = redOut / maxCoverage; + greenOut = greenOut * outAlpha + green * coverageAlpha; + greenOut = greenOut / maxCoverage; + blueOut = blueOut * outAlpha + blue * coverageAlpha; + blueOut = blueOut / maxCoverage; + existing.set(x, 0, redOut, greenOut, blueOut); + } + xdrawable.put_image(xgc, existing, x0, y); + } + } + } + + protected void init() + { + super.init(); + } + + public void setPaint(Paint p) + { + super.setPaint(p); + if (p instanceof Color) + { + // TODO: Optimize for different standard bit-depths. + Color c = (Color) p; + /* XToolkit tk = (XToolkit) Toolkit.getDefaultToolkit(); + HashMap colorMap = tk.colorMap; + gnu.x11.Color col = (gnu.x11.Color) colorMap.get(c); + if (col == null) + { + Colormap map = xdrawable.display.default_colormap; + col = map.alloc_color (c.getRed() * 256, + c.getGreen() * 256, + c.getBlue() * 256); + colorMap.put(c, col); + }*/ + //xgc.set_foreground(col); + + xgc.set_foreground(c.getRGB()); + foreground = c; + } + } + + protected void fillShape(Shape s, boolean isFont) + { + synchronized (xdrawable.display) { + super.fillShape(s, isFont); + } + } + + private static WeakHashMap<Image,ZPixmap> imageCache = new WeakHashMap<Image,ZPixmap>(); + + protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs) + { + image = unwrap(image); + boolean ret; + if (image instanceof XImage) + { + XImage xImage = (XImage) image; + xdrawable.copy_area(xImage.pixmap, xgc, 0, 0, xImage.getWidth(obs), + xImage.getHeight(obs), x, y); + ret = true; + } + else if (image instanceof PixmapVolatileImage) + { + PixmapVolatileImage pvi = (PixmapVolatileImage) image; + xdrawable.copy_area(pvi.getPixmap(), xgc, 0, 0, pvi.getWidth(obs), + pvi.getHeight(obs), x, y); + ret = true; + } + else if (image instanceof BufferedImage) + { + BufferedImage bi = (BufferedImage) image; + DataBuffer db = bi.getRaster().getDataBuffer(); + if (db instanceof ZPixmapDataBuffer) + { + ZPixmapDataBuffer zpmdb = (ZPixmapDataBuffer) db; + ZPixmap zpixmap = zpmdb.getZPixmap(); + xdrawable.put_image(xgc, zpixmap, x, y); + ret = true; + } + else + { + int transparency = bi.getTransparency(); + int w = bi.getWidth(); + int h = bi.getHeight(); + if (imageCache.containsKey(image)) + { + ZPixmap zpixmap = imageCache.get(image); + xdrawable.put_image(xgc, zpixmap, x, y); + } + else if (transparency == Transparency.OPAQUE || RENDER_OPAQUE) + { + XGraphicsDevice gd = XToolkit.getDefaultDevice(); + ZPixmap zpixmap = new ZPixmap(gd.getDisplay(), w, h); + for (int yy = 0; yy < h; yy++) + { + for (int xx = 0; xx < w; xx++) + { + int rgb = bi.getRGB(xx, yy); + zpixmap.set(xx, yy, rgb); + } + } + xdrawable.put_image(xgc, zpixmap, x, y); + imageCache.put(image, zpixmap); + } else { + + // TODO optimize reusing the rectangles + Rectangle source = + new Rectangle(0, 0, xdrawable.width, xdrawable.height); + Rectangle target = new Rectangle(x, y, w, h); + + Rectangle destination = source.intersection(target); + + x = destination.x; + y = destination.y; + w = destination.width; + h = destination.height; + + ZPixmap zpixmap = + (ZPixmap) xdrawable.image(x, y, w, h, + 0xffffffff, + gnu.x11.image.Image.Format.ZPIXMAP); + for (int yy = 0; yy < h; yy++) + { + for (int xx = 0; xx < w; xx++) + { + int rgb = bi.getRGB(xx, yy); + int alpha = 0xff & (rgb >> 24); + if (alpha == 0) + { + // Completely translucent. + rgb = zpixmap.get_red(xx, yy) << 16 + | zpixmap.get_green(xx, yy) << 8 + | zpixmap.get_blue(xx, yy); + } + else if (alpha < 255) + { + // Composite pixels. + int red = 0xff & (rgb >> 16); + red = red * alpha + + (255 - alpha) * zpixmap.get_red(xx, yy); + red = red / 255; + int green = 0xff & (rgb >> 8); + green = green * alpha + + (255 - alpha) * zpixmap.get_green(xx, yy); + green = green / 255; + int blue = 0xff & rgb; + blue = blue * alpha + + (255 - alpha) * zpixmap.get_blue(xx, yy); + blue = blue / 255; + rgb = red << 16 | green << 8 | blue; + } + // else keep rgb value from source image. + + zpixmap.set(xx, yy, rgb); + } + } + xdrawable.put_image(xgc, zpixmap, x, y); + // We can't cache prerendered translucent images, because + // we never know how the background changes. + } + ret = true; + } + } + else + { + ret = super.rawDrawImage(image, x, y, obs); + } + return ret; + } + + public void setFont(Font f) + { + super.setFont(f); + FontPeer p = getFont().getPeer(); + if (p instanceof XFontPeer) + { + XFontPeer xFontPeer = (XFontPeer) p; + xgc.set_font(xFontPeer.getXFont()); + } + } + + public void drawString(String s, int x, int y) + { + FontPeer p = getFont().getPeer(); + if (p instanceof XFontPeer) + { + int tx = (int) transform.getTranslateX(); + int ty = (int) transform.getTranslateY(); + xdrawable.text(xgc, x + tx, y + ty, s); + } + else + { + super.drawString(s, x, y); + } + } + + /** + * Extracts an image instance out of an AsyncImage. If the image isn't + * an AsyncImage, then the original instance is returned. + * + * @param im the image + * + * @return the image to render + */ + private Image unwrap(Image im) + { + Image image = im; + if (image instanceof AsyncImage) + { + AsyncImage aIm = (AsyncImage) image; + image = aIm.getRealImage(); + } + return image; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XGraphicsConfiguration.java b/libjava/classpath/gnu/java/awt/peer/x/XGraphicsConfiguration.java new file mode 100644 index 000000000..aed11a3af --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XGraphicsConfiguration.java @@ -0,0 +1,200 @@ +/* XGraphicsConfiguration.java -- GraphicsConfiguration for X + 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.x; + +import gnu.x11.Display; +import gnu.x11.Screen; + +import java.awt.Dimension; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.VolatileImage; +import java.awt.image.WritableRaster; + +public class XGraphicsConfiguration + extends GraphicsConfiguration +{ + + XGraphicsDevice device; + + XGraphicsConfiguration(XGraphicsDevice dev) + { + device = dev; + } + + public GraphicsDevice getDevice() + { + return device; + } + + public BufferedImage createCompatibleImage(int w, int h) + { + return createCompatibleImage(w, h, Transparency.OPAQUE); + } + + public BufferedImage createCompatibleImage(int w, int h, int transparency) + { + BufferedImage bi; + switch (transparency) + { + case Transparency.OPAQUE: + DataBuffer buffer = new ZPixmapDataBuffer(w, h); + SampleModel sm = new ComponentSampleModel(DataBuffer.TYPE_BYTE, w, h, + 4, w * 4, + new int[]{0, 1, 2, 3 }); + ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); + ColorModel cm = new ComponentColorModel(cs, true, false, + Transparency.OPAQUE, + DataBuffer.TYPE_BYTE); + WritableRaster raster = Raster.createWritableRaster(sm, buffer, + new Point(0, 0)); + bi = new BufferedImage(cm, raster, false, null); + break; + case Transparency.BITMASK: + case Transparency.TRANSLUCENT: + bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + break; + default: + throw new IllegalArgumentException("Illegal transparency: " + + transparency); + } + return bi; + } + + public VolatileImage createCompatibleVolatileImage(int w, int h) + { + return createCompatibleVolatileImage(w, h, Transparency.OPAQUE); + } + + public VolatileImage createCompatibleVolatileImage(int width, int height, + int transparency) + { + VolatileImage im; + switch (transparency) + { + case Transparency.OPAQUE: + im = new PixmapVolatileImage(width, height); + break; + case Transparency.BITMASK: + case Transparency.TRANSLUCENT: + throw new UnsupportedOperationException("Not yet implemented"); + default: + throw new IllegalArgumentException("Unknown transparency type: " + + transparency); + } + return im; + } + + public ColorModel getColorModel() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public ColorModel getColorModel(int transparency) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public AffineTransform getDefaultTransform() + { + return new AffineTransform(); + } + + public AffineTransform getNormalizingTransform() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public Rectangle getBounds() + { + Display d = device.getDisplay(); + Screen screen = d.default_screen; + + return new Rectangle(0, 0, screen.width, screen.height); + } + + /** + * Determines the size of the primary screen. + * + * @return the size of the primary screen + */ + Dimension getSize() + { + // TODO: A GraphicsConfiguration should correspond to a Screen instance. + Display d = device.getDisplay(); + Screen screen = d.default_screen; + int w = screen.width; + int h = screen.height; + return new Dimension(w, h); + } + + /** + * Determines the resolution of the primary screen in pixel-per-inch. + * + * @returnthe resolution of the primary screen in pixel-per-inch + */ + int getResolution() + { + Display d = device.getDisplay(); + Screen screen = d.default_screen; + int w = screen.width * 254; + int h = screen.height * 254; + int wmm = screen.width_in_mm * 10; + int hmm = screen.height_in_mm * 10; + int xdpi = w / wmm; + int ydpi = h / hmm; + int dpi = (xdpi + ydpi) / 2; + return dpi; + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XGraphicsDevice.java b/libjava/classpath/gnu/java/awt/peer/x/XGraphicsDevice.java new file mode 100644 index 000000000..6b65e14ed --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XGraphicsDevice.java @@ -0,0 +1,200 @@ +/* XGraphicsDevice.java -- GraphicsDevice for X + 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.x; + +import gnu.classpath.SystemProperties; +import gnu.x11.Display; +import gnu.x11.EscherServerConnectionException; + +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.lang.reflect.Constructor; +import java.net.Socket; + +/** + * This class represents an X Display. The actual connection is established + * lazily when it is first needed. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class XGraphicsDevice + extends GraphicsDevice +{ + + private XGraphicsConfiguration defaultConfiguration; + + /** + * The X display associated with the XGraphicsDevice. This is established + * when {@link #getDisplay} is first called. + */ + private Display display; + + /** + * The display name from which the display will be initialized. + */ + private Display.Name displayName; + + /** + * The event pump for this X Display. + */ + private XEventPump eventPump; + + /** + * Creates a new XGraphicsDevice. + */ + XGraphicsDevice(Display.Name dn) + { + displayName = dn; + } + + public int getType() + { + return TYPE_RASTER_SCREEN; + } + + public String getIDstring() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public GraphicsConfiguration[] getConfigurations() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public GraphicsConfiguration getDefaultConfiguration() + { + if (defaultConfiguration == null) + defaultConfiguration = new XGraphicsConfiguration(this); + return defaultConfiguration; + } + + /** + * Returns the X Display associated with this XGraphicsDevice. + * This establishes the connection to the X server on the first invocation. + * + * @return the X Display associated with this XGraphicsDevice + */ + Display getDisplay() + { + if (display == null) + { + if (displayName.hostname.equals("")) + displayName.hostname = "localhost"; + if (XToolkit.DEBUG) + System.err.println("connecting to : " + displayName); + // Try to connect via unix domain sockets when host == localhost. + if ((displayName.hostname.equals("localhost") + || displayName.hostname.equals("")) + && SystemProperties.getProperty("gnu.xawt.no_local_sockets") == null) + { + Socket socket = createLocalSocket(); + if (socket != null) + { + try + { + display = new Display(socket, "localhost", + displayName.display_no, + displayName.screen_no); + } + catch (EscherServerConnectionException e) + { + throw new RuntimeException(e.getCause()); + } + } + } + + // The following happens when we are configured to use plain sockets, + // when the connection is probably remote or when we couldn't load + // the LocalSocket class stuff. + if (display == null) + { + try + { + display = new Display(displayName); + } + catch (EscherServerConnectionException e) + { + throw new RuntimeException(e.getCause()); + } + } + + eventPump = new XEventPump(display); + } + return display; + } + + XEventPump getEventPump() + { + return eventPump; + } + + /** + * Tries to load the LocalSocket class and initiate a connection to the + * local X server. + */ + private Socket createLocalSocket() + { + Socket socket = null; + try + { + // TODO: Is this 100% ok? + String sockPath = "/tmp/.X11-unix/X" + displayName.display_no; + Class localSocketAddressClass = + Class.forName("gnu.java.net.local.LocalSocketAddress"); + Constructor localSocketAddressConstr = + localSocketAddressClass.getConstructor(new Class[]{ String.class }); + Object addr = + localSocketAddressConstr.newInstance(new Object[]{ sockPath }); + Class localSocketClass = + Class.forName("gnu.java.net.local.LocalSocket"); + Constructor localSocketConstructor = + localSocketClass.getConstructor(new Class[]{localSocketAddressClass}); + Object localSocket = + localSocketConstructor.newInstance(new Object[]{ addr }); + socket = (Socket) localSocket; + } + catch (Exception ex) + { + // Whatever goes wrong here, we return null. + } + return socket; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XGraphicsEnvironment.java b/libjava/classpath/gnu/java/awt/peer/x/XGraphicsEnvironment.java new file mode 100644 index 000000000..7b1d82fee --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XGraphicsEnvironment.java @@ -0,0 +1,203 @@ +/* XGraphicsEnvironment.java -- Represents the X environment + 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.x; + +import gnu.java.awt.font.OpenTypeFontPeer; +import gnu.java.awt.java2d.RasterGraphics; +import gnu.x11.Display; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Properties; + +/** + * Represents the X environment for AWT. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class XGraphicsEnvironment + extends GraphicsEnvironment +{ + + /** + * The default graphics device. This is normally the local main X + * Display, but can be configured to be any X connection. + */ + private XGraphicsDevice defaultDevice; + + /** + * All configured devices. + */ + private XGraphicsDevice[] devices; + + /** + * Creates a new XGraphicsEnvironment. This loads the configuration if + * there is one present and initializes the XGraphicsDevices in the + * environment. If there is no configuration, then there is one + * default device initialized with the local main X device. + */ + public XGraphicsEnvironment() + { + // Initiliaze the devices. + Properties props = new Properties(); + File config = new File(System.getProperty("user.home"), + ".xawt.properties"); + + try + { + FileInputStream configIn = new FileInputStream(config); + props.load(configIn); + int dev = 1; + ArrayList deviceList = new ArrayList(); + while (true) + { + String propName = "display." + dev; + String propValue = props.getProperty(propName); + if (propValue != null) + { + Display.Name displayName = new Display.Name(propValue); + XGraphicsDevice device = new XGraphicsDevice(displayName); + if (dev == 1) + defaultDevice = device; + deviceList.add(device); + dev++; + } + else + { + if (dev == 1) + { + defaultDevice = initDefaultDevice(); + deviceList.add(defaultDevice); + } + break; + } + } + devices = (XGraphicsDevice[]) deviceList.toArray + (new XGraphicsDevice[deviceList.size()]); + } + catch (FileNotFoundException ex) + { + defaultDevice = initDefaultDevice(); + devices = new XGraphicsDevice[]{ defaultDevice }; + } + catch (IOException ex) + { + defaultDevice = initDefaultDevice(); + devices = new XGraphicsDevice[]{ defaultDevice }; + } + + } + + /** + * Helper method that initializes the default device in the case when there + * is no configuration for the default. + */ + private XGraphicsDevice initDefaultDevice() + { + String display = System.getenv("DISPLAY"); + if (display == null) + display = ":0.0"; + Display.Name displayName = new Display.Name(display); + return new XGraphicsDevice(displayName); + } + + /** + * Returns all configured screen devices. + * + * @return all configured screen devices + */ + public GraphicsDevice[] getScreenDevices() + { + // We return a copy so that nobody can fiddle with our devices. + XGraphicsDevice[] copy = new XGraphicsDevice[devices.length]; + System.arraycopy(devices, 0, copy, 0, devices.length); + return copy; + } + + /** + * Returns the default screen device. + * + * @return the default screen device + */ + public GraphicsDevice getDefaultScreenDevice() + { + return defaultDevice; + } + + /** + * Returns a Graphics instance suitable for drawing on top of the + * BufferedImage. + * + * @param image the buffered image to create a graphics for + * + * @return a Graphics2D instance for drawing on the BufferedImage + */ + public Graphics2D createGraphics(BufferedImage image) + { + return new RasterGraphics(image.getRaster(), image.getColorModel()); + } + + public Font[] getAllFonts() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public String[] getAvailableFontFamilyNames() + { + return getAvailableFontFamilyNames(Locale.getDefault()); + } + + public String[] getAvailableFontFamilyNames(Locale l) + { + // TODO: This doesn't work when we are using X fonts. + // Fix this. + return OpenTypeFontPeer.getAvailableFontFamilyNames(l); + } + +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XImage.java b/libjava/classpath/gnu/java/awt/peer/x/XImage.java new file mode 100644 index 000000000..f3df89f4d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XImage.java @@ -0,0 +1,178 @@ +/* XImage.java -- Image impl for X Pixmaps + 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.x; + +import gnu.x11.Pixmap; +import gnu.x11.image.ZPixmap; + +import java.awt.Graphics; +import java.awt.GraphicsEnvironment; +import java.awt.Image; + +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; + +import java.util.Hashtable; +import java.util.Vector; + +public class XImage + extends Image +{ + + Pixmap pixmap; + + private Hashtable properties; + + XImage(int w, int h) + { + GraphicsEnvironment env = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + XGraphicsDevice dev = (XGraphicsDevice) env.getDefaultScreenDevice(); + pixmap = new Pixmap(dev.getDisplay(), w, h); + } + + public int getWidth(ImageObserver observer) + { + return pixmap.width; + } + + public int getHeight(ImageObserver observer) + { + return pixmap.height; + } + + public ImageProducer getSource() + { + return new XImageProducer(); + } + + /** + * Creates an XGraphics for drawing on this XImage. + * + * @return an XGraphics for drawing on this XImage + */ + public Graphics getGraphics() + { + XGraphics2D g = new XGraphics2D(pixmap); + return g; + } + + public Object getProperty(String name, ImageObserver observer) + { + Object val = null; + if (properties != null) + val = properties.get(val); + return val; + } + + public void flush() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected void finalize() + { + pixmap.free(); + } + + protected class XImageProducer implements ImageProducer + { + private Vector<ImageConsumer> consumers = new Vector<ImageConsumer>(); + + public void addConsumer(ImageConsumer ic) + { + if (ic != null && !isConsumer(ic)) + this.consumers.add(ic); + } + + public boolean isConsumer(ImageConsumer ic) + { + return this.consumers.contains(ic); + } + + public void removeConsumer(ImageConsumer ic) + { + if (ic != null) + this.consumers.remove(ic); + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + /* just ignore the call */ + } + + public void startProduction(ImageConsumer ic) + { + this.addConsumer(ic); + + for (ImageConsumer consumer : this.consumers) + { + int width = XImage.this.getWidth(null); + int height = XImage.this.getHeight(null); + + XGraphics2D graphics = (XGraphics2D) getGraphics(); + ColorModel model = graphics.getColorModel(); + graphics.dispose(); + + ZPixmap zpixmap = (ZPixmap) + XImage.this.pixmap.image(0, 0, width, height, + 0xffffffff, + gnu.x11.image.Image.Format.ZPIXMAP); + + int size = zpixmap.get_data_length(); + System.out.println("size: " + size + ", w = " + width + ", h = " + height); + + int [] pixel = new int[size]; + for (int i = 0; i < size; i++) + pixel[i] = zpixmap.get_data_element(i); + + consumer.setHints(ImageConsumer.SINGLEPASS); + + consumer.setDimensions(width, height); + consumer.setPixels(0, 0, width, height, model, pixel, 0, width); + consumer.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + + System.out.println("done!"); + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XToolkit.java b/libjava/classpath/gnu/java/awt/peer/x/XToolkit.java new file mode 100644 index 000000000..a3eeb0f53 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XToolkit.java @@ -0,0 +1,667 @@ +/* XToolkit.java -- The central AWT Toolkit for the X peers + 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.x; + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.PrintJob; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Transparency; +import java.awt.Window; +import java.awt.Dialog.ModalExclusionType; +import java.awt.Dialog.ModalityType; +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.im.InputMethodHighlight; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.CheckboxMenuItemPeer; +import java.awt.peer.CheckboxPeer; +import java.awt.peer.ChoicePeer; +import java.awt.peer.DialogPeer; +import java.awt.peer.FileDialogPeer; +import java.awt.peer.FontPeer; +import java.awt.peer.FramePeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.ListPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.PopupMenuPeer; +import java.awt.peer.RobotPeer; +import java.awt.peer.ScrollPanePeer; +import java.awt.peer.ScrollbarPeer; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.WindowPeer; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.WeakHashMap; + +import javax.imageio.ImageIO; + +import gnu.classpath.SystemProperties; +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.EmbeddedWindow; +import gnu.java.awt.font.OpenTypeFontPeer; +import gnu.java.awt.image.ImageConverter; +import gnu.java.awt.java2d.AbstractGraphics2D; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.peer.EmbeddedWindowPeer; +import gnu.java.awt.peer.swing.SwingButtonPeer; +import gnu.java.awt.peer.swing.SwingCanvasPeer; +import gnu.java.awt.peer.swing.SwingCheckboxPeer; +import gnu.java.awt.peer.swing.SwingLabelPeer; +import gnu.java.awt.peer.swing.SwingPanelPeer; +import gnu.java.awt.peer.swing.SwingTextAreaPeer; +import gnu.java.awt.peer.swing.SwingTextFieldPeer; + +public class XToolkit + extends ClasspathToolkit +{ + + /** + * Set to true to enable debug output. + */ + static boolean DEBUG = false; + + /** + * Maps AWT colors to X colors. + */ + HashMap colorMap = new HashMap(); + + /** + * The system event queue. + */ + private EventQueue eventQueue; + + /** + * The default color model of this toolkit. + */ + private ColorModel colorModel; + + /** + * Maps image URLs to Image instances. + */ + private HashMap imageCache = new HashMap(); + + /** + * The cached fonts. + */ + private WeakHashMap<String,ClasspathFontPeer> fontCache = + new WeakHashMap<String,ClasspathFontPeer>(); + + public XToolkit() + { + SystemProperties.setProperty("gnu.javax.swing.noGraphics2D", "true"); + SystemProperties.setProperty("java.awt.graphicsenv", + "gnu.java.awt.peer.x.XGraphicsEnvironment"); + } + + public GraphicsEnvironment getLocalGraphicsEnvironment() + { + return new XGraphicsEnvironment(); + } + + /** + * Returns the font peer for a font with the specified name and attributes. + * + * @param name the font name + * @param attrs the font attributes + * + * @return the font peer for a font with the specified name and attributes + */ + public ClasspathFontPeer getClasspathFontPeer(String name, Map attrs) + { + ClasspathFontPeer font; + if ("true".equals(System.getProperty("escherpeer.usexfonts"))) + { + String canonical = XFontPeer.encodeFont(name, attrs); + if (!fontCache.containsKey(canonical)) + { + font = new XFontPeer(name, attrs); + fontCache.put(canonical, font); + } + else + { + font = fontCache.get(canonical); + } + } + else + { + String canonical = OpenTypeFontPeer.encodeFont(name, attrs); + if (!fontCache.containsKey(canonical)) + { + font = new OpenTypeFontPeer(name, attrs); + fontCache.put(canonical, font); + } + else + { + font = fontCache.get(canonical); + } + } + return font; + } + + public Font createFont(int format, InputStream stream) + { + return null; + } + + public RobotPeer createRobot(GraphicsDevice screen) throws AWTException + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public EmbeddedWindowPeer createEmbeddedWindow(EmbeddedWindow w) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected ButtonPeer createButton(Button target) + { + checkHeadLess("No ButtonPeer can be created in an headless" + + "graphics environment."); + + return new SwingButtonPeer(target); + } + + protected TextFieldPeer createTextField(TextField target) + { + checkHeadLess("No TextFieldPeer can be created in an headless " + + "graphics environment."); + + return new SwingTextFieldPeer(target); + } + + protected LabelPeer createLabel(Label target) + { + checkHeadLess("No LabelPeer can be created in an headless graphics " + + "environment."); + return new SwingLabelPeer(target); + } + + protected ListPeer createList(List target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected CheckboxPeer createCheckbox(Checkbox target) + { + checkHeadLess("No CheckboxPeer can be created in an headless graphics " + + "environment."); + + return new SwingCheckboxPeer(target); + } + + protected ScrollbarPeer createScrollbar(Scrollbar target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected ScrollPanePeer createScrollPane(ScrollPane target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected TextAreaPeer createTextArea(TextArea target) + { + checkHeadLess("No TextAreaPeer can be created in an headless graphics " + + "environment."); + + return new SwingTextAreaPeer(target); + } + + protected ChoicePeer createChoice(Choice target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected FramePeer createFrame(Frame target) + { + XFramePeer frame = new XFramePeer(target); + return frame; + } + + protected CanvasPeer createCanvas(Canvas target) + { + return new SwingCanvasPeer(target); + } + + protected PanelPeer createPanel(Panel target) + { + return new SwingPanelPeer(target); + } + + protected WindowPeer createWindow(Window target) + { + return new XWindowPeer(target); + } + + protected DialogPeer createDialog(Dialog target) + { + return new XDialogPeer(target); + } + + protected MenuBarPeer createMenuBar(MenuBar target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected MenuPeer createMenu(Menu target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected PopupMenuPeer createPopupMenu(PopupMenu target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected MenuItemPeer createMenuItem(MenuItem target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected FileDialogPeer createFileDialog(FileDialog target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + protected FontPeer getFontPeer(String name, int style) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public Dimension getScreenSize() + { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + XGraphicsConfiguration xgc = (XGraphicsConfiguration) gc; + + return xgc.getSize(); + } + + public int getScreenResolution() + { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + XGraphicsConfiguration xgc = (XGraphicsConfiguration) gc; + + return xgc.getResolution(); + } + + /** + * Returns the color model used by this toolkit. + * + * @return the color model used by this toolkit + */ + public ColorModel getColorModel() + { + // TODO: I assume 24 bit depth here, we can do this better. + if (colorModel == null) + colorModel = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF); + return colorModel; + } + + public String[] getFontList() + { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + return ge.getAvailableFontFamilyNames(); + } + + public FontMetrics getFontMetrics(Font name) + { + ClasspathFontPeer peer = (ClasspathFontPeer) name.getPeer(); + return peer.getFontMetrics(name); + } + + public void sync() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Returns an image that has its pixel data loaded from a file with the + * specified name. If that file doesn't exist, an empty or error image + * is returned instead. + * + * @param name the filename of the file that contains the pixel data + * + * @return the image + */ + public Image getImage(String name) + { + Image image; + try + { + File file = new File(name); + image = getImage(file.toURL()); + } + catch (MalformedURLException ex) + { + // TODO: Replace by a more meaningful error image instead. + image = null; + } + return image; + } + + /** + * Returns an image that has its pixel data loaded from the specified URL. + * If the image cannot be loaded for some reason, an empty or error image + * is returned instead. + * + * @param url the URL to the image data + * + * @return the image + */ + public Image getImage(URL url) + { + Image image; + if (imageCache.containsKey(url)) + { + image = (Image) imageCache.get(url); + } + else + { + image = createImage(url); + imageCache.put(url, image); + } + return image; + } + + /** + * Returns an image that has its pixel data loaded from a file with the + * specified name. If that file doesn't exist, an empty or error image + * is returned instead. + * + * @param filename the filename of the file that contains the pixel data + * + * @return the image + */ + public Image createImage(String filename) + { + Image im; + try + { + File file = new File(filename); + URL url = file.toURL(); + im = createImage(url); + } + catch (MalformedURLException ex) + { + im = createErrorImage(); + } + return im; + } + + /** + * Returns an image that has its pixel data loaded from the specified URL. + * If the image cannot be loaded for some reason, an empty or error image + * is returned instead. + * + * @param url the URL to the image data + * + * @return the image + */ + public Image createImage(URL url) + { + Image image; + try + { + image = createImage(url.openStream()); + } + catch (IOException ex) + { + image = createErrorImage(); + } + return image; + } + + /** + * Creates an image that is returned when calls to createImage() yields an + * error. + * + * @return an image that is returned when calls to createImage() yields an + * error + */ + private Image createErrorImage() + { + // TODO: Create better error image. + return new XImage(1, 1); + } + + public boolean prepareImage(Image image, int width, int height, ImageObserver observer) + { + Image scaled = AbstractGraphics2D.prepareImage(image, width, height); + return checkImage(image, width, height, observer) == ImageObserver.ALLBITS; + } + + public int checkImage(Image image, int width, int height, ImageObserver observer) + { + // Images are loaded synchronously, so we don't bother and return true. + return ImageObserver.ALLBITS; + } + + public Image createImage(ImageProducer producer) + { + ImageConverter conv = new ImageConverter(); + producer.startProduction(conv); + Image image = conv.getImage(); + return image; + } + + public Image createImage(byte[] data, int offset, int len) + { + Image image; + try + { + ByteArrayInputStream i = new ByteArrayInputStream(data, offset, len); + image = createImage(i); + } + catch (IOException ex) + { + image = createErrorImage(); + } + return image; + } + + private Image createImage(InputStream i) + throws IOException + { + Image image; + BufferedImage buffered = ImageIO.read(i); + // If the bufferedimage is opaque, then we can copy it over to an + // X Pixmap for faster drawing. + if (buffered != null && buffered.getTransparency() == Transparency.OPAQUE) + { + ImageProducer source = buffered.getSource(); + image = createImage(source); + } + else if (buffered != null) + { + image = buffered; + } + else + { + image = createErrorImage(); + } + return image; + } + + public PrintJob getPrintJob(Frame frame, String title, Properties props) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public void beep() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public Clipboard getSystemClipboard() + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Returns the eventqueue used by the XLib peers. + * + * @return the eventqueue used by the XLib peers + */ + protected EventQueue getSystemEventQueueImpl() + { + if (eventQueue == null) + eventQueue = new EventQueue(); + return eventQueue; + } + + public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + public Map mapInputMethodHighlight(InputMethodHighlight highlight) + { + // TODO: Implement this. + throw new UnsupportedOperationException("Not yet implemented."); + } + + /** + * Helper method to quickly fetch the default device (X Display). + * + * @return the default XGraphicsDevice + */ + static XGraphicsDevice getDefaultDevice() + { + XGraphicsEnvironment env = (XGraphicsEnvironment) + XGraphicsEnvironment.getLocalGraphicsEnvironment(); + return (XGraphicsDevice) env.getDefaultScreenDevice(); + } + + @Override + public boolean isModalExclusionTypeSupported(ModalExclusionType modalExclusionType) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isModalityTypeSupported(ModalityType modalityType) + { + // TODO Auto-generated method stub + return false; + } + + private void checkHeadLess(String message) throws HeadlessException + { + if(GraphicsEnvironment.isHeadless()) + { + if(message == null) + message = "This method cannot be called in headless mode."; + + throw new HeadlessException(message); + } + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/XWindowPeer.java b/libjava/classpath/gnu/java/awt/peer/x/XWindowPeer.java new file mode 100644 index 000000000..541eb74fa --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/XWindowPeer.java @@ -0,0 +1,303 @@ +/* XWindowPeer.java -- Window peer for X + 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.x; + +import java.awt.Component; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; +import java.awt.image.VolatileImage; + +import gnu.x11.Atom; +import gnu.x11.Window; +import gnu.x11.event.Event; + +import gnu.java.awt.font.OpenTypeFontPeer; +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.awt.peer.swing.SwingWindowPeer; + +public class XWindowPeer + extends SwingWindowPeer +{ + + private static int standardSelect = Event.BUTTON_PRESS_MASK + | Event.BUTTON_RELEASE_MASK + | Event.POINTER_MOTION_MASK + // | Event.RESIZE_REDIRECT_MASK // + | Event.EXPOSURE_MASK + | Event.PROPERTY_CHANGE_MASK + //| Event.STRUCTURE_NOTIFY_MASK + //| Event.SUBSTRUCTURE_NOTIFY_MASK + | Event.KEY_PRESS_MASK + | Event.KEY_RELEASE_MASK + //| Event.VISIBILITY_CHANGE_MASK // + ; + + /** + * The X window. + */ + protected Window xwindow; + + /** + * The frame insets. These get updated in {@link #show()}. + */ + private Insets insets; + + XWindowPeer(java.awt.Window window) + { + super(window); + XGraphicsDevice dev = XToolkit.getDefaultDevice(); + + // TODO: Maybe initialize lazily in show(). + Window.Attributes atts = new Window.Attributes(); + // FIXME: Howto generate a Window without decorations? + int x = Math.max(window.getX(), 0); + int y = Math.max(window.getY(), 0); + int w = Math.max(window.getWidth(), 1); + int h = Math.max(window.getHeight(), 1); + xwindow = new Window(dev.getDisplay().default_root, x, y, w, h, 0, atts); + xwindow.select_input(standardSelect); + + dev.getEventPump().registerWindow(xwindow, window); + xwindow.set_wm_delete_window(); + + boolean undecorated; + if (awtComponent instanceof Frame) + { + Frame f = (Frame) awtComponent; + undecorated = f.isUndecorated(); + } + else if (awtComponent instanceof Dialog) + { + Dialog d = (Dialog) awtComponent; + undecorated = d.isUndecorated(); + } + else + { + undecorated = true; + } + if (undecorated) + { + // First try the Motif implementation of undecorated frames. This + // is semantically closest and supported by all major window + // managers. + // TODO: At the time of writing this, there's no freedesktop.org + // standard extension that matches the required semantic. Maybe + // undecorated frames are added in the future, if so, then use these. + Atom at = Atom.intern(dev.getDisplay(), "_MOTIF_WM_HINTS"); + if (at != null) + { + xwindow.change_property(Window.REPLACE, at, at, 32, + new int[]{1 << 1, 0, 0, 0, 0}, 0, 5); + } + } + insets = new Insets(0, 0, 0, 0); + } + + public void toBack() + { + // TODO Auto-generated method stub + + } + + public void toFront() + { + // TODO Auto-generated method stub + + } + + public void updateAlwaysOnTop() + { + // TODO Auto-generated method stub + + } + + public boolean requestWindowFocus() + { + // TODO Auto-generated method stub + return false; + } + + public Point getLocationOnScreen() + { + return new Point(xwindow.x, xwindow.y); + } + + /** + * Returns a XGraphics suitable for drawing on this frame. + * + * @return a XGraphics suitable for drawing on this frame + */ + public Graphics getGraphics() + { + XGraphics2D xg2d = new XGraphics2D(xwindow); + xg2d.setColor(awtComponent.getForeground()); + xg2d.setBackground(awtComponent.getBackground()); + xg2d.setFont(awtComponent.getFont()); + return xg2d; + } + + public Image createImage(int w, int h) + { + // FIXME: Should return a buffered image. + return createVolatileImage(w, h); + } + + @Override + public VolatileImage createVolatileImage(int width, int height) + { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + return gc.createCompatibleVolatileImage(width, height); + } + + /** + * Makes the component visible. This is called by {@link Component#show()}. + * + * This is implemented to call setVisible(true) on the Swing component. + */ + public void show() + { + // Prevent ResizeRedirect events. + //xwindow.select_input(Event.NO_EVENT_MASK); + //xwindow.select_input(noResizeRedirectSelect); + + XGraphicsDevice dev = XToolkit.getDefaultDevice(); + xwindow.map(); + EventQueue eq = XToolkit.getDefaultToolkit().getSystemEventQueue(); + java.awt.Window w = (java.awt.Window) super.awtComponent; + eq.postEvent(new WindowEvent(w, WindowEvent.WINDOW_OPENED)); + eq.postEvent(new PaintEvent(w, PaintEvent.PAINT, + new Rectangle(0, 0, w.getWidth(), + w.getHeight()))); + + Graphics g = getGraphics(); + g.clearRect(0, 0, awtComponent.getWidth(), awtComponent.getHeight()); + g.dispose(); +// // Reset input selection. +// atts.set_override_redirect(false); +// xwindow.change_attributes(atts); + + // Determine the frame insets. + Atom atom = (Atom) Atom.intern(dev.getDisplay(), "_NET_FRAME_EXTENTS"); + Window.Property p = xwindow.get_property(false, atom, Atom.CARDINAL, 0, + Window.MAX_WM_LENGTH); + if (p.format() != 0) + { + insets = new Insets(p.value(0), p.value(1), p.value(2), p.value(3)); + Window.Changes ch = new Window.Changes(); + ch.width(awtComponent.getWidth() - insets.left - insets.top); + ch.height(awtComponent.getHeight() - insets.top - insets.bottom); + xwindow.configure(ch); + } + + } + + /** + * Makes the component invisible. This is called from + * {@link Component#hide()}. + * + * This is implemented to call setVisible(false) on the Swing component. + */ + public void hide() + { + xwindow.unmap(); + } + + /** + * Notifies the peer that the bounds of this component have changed. This + * is called by {@link Component#reshape(int, int, int, int)}. + * + * This is implemented to call setBounds() on the Swing component. + * + * @param x the X coordinate of the upper left corner of the component + * @param y the Y coordinate of the upper left corner of the component + * @param width the width of the component + * @param height the height of the component + */ + public void reshape(int x, int y, int width, int height) + { + Insets i = insets; + xwindow.move_resize(x - i.left, y - i.right, width - i.left - i.right, + height - i.top - i.bottom); + } + + public Insets insets() + { + return (Insets) insets.clone(); + } + + /** + * Returns the font metrics for the specified font. + * + * @return the font metrics for the specified font + */ + public FontMetrics getFontMetrics(Font font) + { + ClasspathFontPeer fontPeer = (ClasspathFontPeer) font.getPeer(); + return fontPeer.getFontMetrics(font); + } + + /** + * Unregisters the window in the event pump when it is closed. + */ + protected void finalize() + { + XGraphicsDevice dev = XToolkit.getDefaultDevice(); + dev.getEventPump().unregisterWindow(xwindow); + } + + public Window getXwindow() + { + return xwindow; + } +} diff --git a/libjava/classpath/gnu/java/awt/peer/x/ZPixmapDataBuffer.java b/libjava/classpath/gnu/java/awt/peer/x/ZPixmapDataBuffer.java new file mode 100644 index 000000000..cf40f4d69 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/peer/x/ZPixmapDataBuffer.java @@ -0,0 +1,67 @@ +package gnu.java.awt.peer.x; + +import gnu.x11.Display; +import gnu.x11.image.ZPixmap; + +import java.awt.GraphicsEnvironment; +import java.awt.image.DataBuffer; + +/** + * A DataBuffer implementation that is based on a ZPixmap. This is used + * as backing store for BufferedImages. + */ +class ZPixmapDataBuffer + extends DataBuffer +{ + + /** + * The backing ZPixmap. + */ + private ZPixmap zpixmap; + + /** + * Creates a new ZPixmapDataBuffer with a specified width and height. + * + * @param d the X display + * @param w the width + * @param h the height + */ + ZPixmapDataBuffer(int w, int h) + { + super(TYPE_BYTE, w * h * 3); // TODO: Support non-24-bit-resolutions. + GraphicsEnvironment env = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + XGraphicsDevice dev = (XGraphicsDevice) env.getDefaultScreenDevice(); + Display d = dev.getDisplay(); + zpixmap = new ZPixmap(d, w, h, d.default_pixmap_format); + } + + /** + * Creates a ZPixmapDataBuffer from an existing ZPixmap. + * + * @param zpixmap the ZPixmap to wrap + */ + ZPixmapDataBuffer(ZPixmap zpixmap) + { + super(TYPE_BYTE, zpixmap.get_data_length()); + this.zpixmap = zpixmap; + } + + @Override + public int getElem(int bank, int i) + { + return 0xff & zpixmap.get_data_element(i); + } + + @Override + public void setElem(int bank, int i, int val) + { + zpixmap.set_data_element(i, (byte) val); + } + + ZPixmap getZPixmap() + { + return zpixmap; + } + +} 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 000000000..64d197cd0 --- /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 000000000..295d231cb --- /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 000000000..10fc25c2f --- /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.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(null); + 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(null); + 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 000000000..54819984f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/print/SpooledDocument.java @@ -0,0 +1,90 @@ +/* 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.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; + } +} |