summaryrefslogtreecommitdiffstats
path: root/awt/org/apache/harmony/awt
diff options
context:
space:
mode:
Diffstat (limited to 'awt/org/apache/harmony/awt')
-rw-r--r--awt/org/apache/harmony/awt/ChoiceStyle.java33
-rw-r--r--awt/org/apache/harmony/awt/ClipRegion.java84
-rw-r--r--awt/org/apache/harmony/awt/ComponentInternals.java212
-rw-r--r--awt/org/apache/harmony/awt/ContextStorage.java154
-rw-r--r--awt/org/apache/harmony/awt/ContextThreadGroup.java34
-rw-r--r--awt/org/apache/harmony/awt/ListenerList.java194
-rw-r--r--awt/org/apache/harmony/awt/ReadOnlyIterator.java53
-rw-r--r--awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java65
-rw-r--r--awt/org/apache/harmony/awt/gl/CommonGraphics2D.java1132
-rw-r--r--awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java78
-rw-r--r--awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java67
-rw-r--r--awt/org/apache/harmony/awt/gl/Crossing.java889
-rw-r--r--awt/org/apache/harmony/awt/gl/GLVolatileImage.java30
-rw-r--r--awt/org/apache/harmony/awt/gl/ICompositeContext.java90
-rw-r--r--awt/org/apache/harmony/awt/gl/ImageSurface.java323
-rw-r--r--awt/org/apache/harmony/awt/gl/MultiRectArea.java836
-rw-r--r--awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java837
-rw-r--r--awt/org/apache/harmony/awt/gl/Surface.java309
-rw-r--r--awt/org/apache/harmony/awt/gl/TextRenderer.java59
-rw-r--r--awt/org/apache/harmony/awt/gl/XORComposite.java48
-rw-r--r--awt/org/apache/harmony/awt/gl/color/ColorConverter.java257
-rw-r--r--awt/org/apache/harmony/awt/gl/color/ColorScaler.java355
-rw-r--r--awt/org/apache/harmony/awt/gl/color/ICC_ProfileHelper.java82
-rw-r--r--awt/org/apache/harmony/awt/gl/color/ICC_Transform.java156
-rw-r--r--awt/org/apache/harmony/awt/gl/color/LUTColorConverter.java148
-rw-r--r--awt/org/apache/harmony/awt/gl/color/NativeCMM.java92
-rw-r--r--awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java642
-rw-r--r--awt/org/apache/harmony/awt/gl/font/AndroidFont.java254
-rw-r--r--awt/org/apache/harmony/awt/gl/font/AndroidFontManager.java277
-rw-r--r--awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java81
-rw-r--r--awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java219
-rw-r--r--awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java120
-rw-r--r--awt/org/apache/harmony/awt/gl/font/BasicMetrics.java134
-rw-r--r--awt/org/apache/harmony/awt/gl/font/CaretManager.java530
-rw-r--r--awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java954
-rw-r--r--awt/org/apache/harmony/awt/gl/font/CompositeFont.java486
-rw-r--r--awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java145
-rw-r--r--awt/org/apache/harmony/awt/gl/font/FontFinder.java121
-rw-r--r--awt/org/apache/harmony/awt/gl/font/FontManager.java819
-rw-r--r--awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java282
-rw-r--r--awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java499
-rw-r--r--awt/org/apache/harmony/awt/gl/font/FontProperty.java106
-rw-r--r--awt/org/apache/harmony/awt/gl/font/Glyph.java236
-rw-r--r--awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java412
-rw-r--r--awt/org/apache/harmony/awt/gl/font/TextDecorator.java433
-rw-r--r--awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java209
-rw-r--r--awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java861
-rw-r--r--awt/org/apache/harmony/awt/gl/font/TextRunSegment.java165
-rw-r--r--awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java979
-rw-r--r--awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java79
-rw-r--r--awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java136
-rw-r--r--awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java62
-rw-r--r--awt/org/apache/harmony/awt/gl/image/DataBufferListener.java31
-rw-r--r--awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java261
-rw-r--r--awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java68
-rw-r--r--awt/org/apache/harmony/awt/gl/image/GifDecoder.java692
-rw-r--r--awt/org/apache/harmony/awt/gl/image/ImageDecoder.java258
-rw-r--r--awt/org/apache/harmony/awt/gl/image/ImageLoader.java208
-rw-r--r--awt/org/apache/harmony/awt/gl/image/JpegDecoder.java231
-rw-r--r--awt/org/apache/harmony/awt/gl/image/OffscreenImage.java532
-rw-r--r--awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java153
-rw-r--r--awt/org/apache/harmony/awt/gl/image/PngDecoder.java270
-rw-r--r--awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java282
-rw-r--r--awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java77
-rw-r--r--awt/org/apache/harmony/awt/gl/render/Blitter.java53
-rw-r--r--awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java502
-rw-r--r--awt/org/apache/harmony/awt/gl/render/JavaBlitter.java611
-rw-r--r--awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java760
-rw-r--r--awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java475
-rw-r--r--awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java263
-rw-r--r--awt/org/apache/harmony/awt/gl/render/NativeImageBlitter.java218
-rw-r--r--awt/org/apache/harmony/awt/gl/render/NullBlitter.java56
-rw-r--r--awt/org/apache/harmony/awt/im/InputMethodContext.java563
-rw-r--r--awt/org/apache/harmony/awt/internal/nls/Messages.java143
-rw-r--r--awt/org/apache/harmony/awt/internal/nls/MsgHelp.java86
-rw-r--r--awt/org/apache/harmony/awt/state/MenuItemState.java51
-rw-r--r--awt/org/apache/harmony/awt/state/MenuState.java46
-rw-r--r--awt/org/apache/harmony/awt/state/State.java55
-rw-r--r--awt/org/apache/harmony/awt/wtk/CreationParams.java133
-rw-r--r--awt/org/apache/harmony/awt/wtk/CursorFactory.java85
-rw-r--r--awt/org/apache/harmony/awt/wtk/GraphicsFactory.java82
-rw-r--r--awt/org/apache/harmony/awt/wtk/KeyInfo.java53
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeCursor.java45
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeEvent.java268
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeEventQueue.java117
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeEventThread.java78
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeIM.java130
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java42
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeRobot.java75
-rw-r--r--awt/org/apache/harmony/awt/wtk/NativeWindow.java220
-rw-r--r--awt/org/apache/harmony/awt/wtk/ShutdownThread.java83
-rw-r--r--awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java86
-rw-r--r--awt/org/apache/harmony/awt/wtk/Synchronizer.java200
-rw-r--r--awt/org/apache/harmony/awt/wtk/SystemProperties.java59
-rw-r--r--awt/org/apache/harmony/awt/wtk/WTK.java61
-rw-r--r--awt/org/apache/harmony/awt/wtk/WindowFactory.java85
96 files changed, 24705 insertions, 0 deletions
diff --git a/awt/org/apache/harmony/awt/ChoiceStyle.java b/awt/org/apache/harmony/awt/ChoiceStyle.java
new file mode 100644
index 0000000..93b7aad
--- /dev/null
+++ b/awt/org/apache/harmony/awt/ChoiceStyle.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt;
+
+/**
+ * ChoiceStyle.
+ * Is used to define custom choice properties:
+ * width and x screen coordinate of the list popup window.
+ */
+public interface ChoiceStyle {
+
+ int getPopupX(int x, int width, int choiceWidth, int screenWidth);
+ int getPopupWidth(int choiceWidth);
+
+}
diff --git a/awt/org/apache/harmony/awt/ClipRegion.java b/awt/org/apache/harmony/awt/ClipRegion.java
new file mode 100644
index 0000000..c89a81d
--- /dev/null
+++ b/awt/org/apache/harmony/awt/ClipRegion.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov, Anton Avtamonov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt;
+
+import java.awt.Component;
+import java.awt.Rectangle;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+public class ClipRegion extends Rectangle {
+ private final MultiRectArea clip;
+
+ public ClipRegion(final MultiRectArea clip) {
+ this.clip = new MultiRectArea(clip);
+ setBounds(clip.getBounds());
+ }
+
+ public MultiRectArea getClip() {
+ return clip;
+ }
+
+ @Override
+ public String toString() {
+ String str = clip.toString();
+ int i = str.indexOf('[');
+ str = str.substring(i);
+ if (clip.getRectCount() == 1) {
+ str = str.substring(1, str.length() - 1);
+ }
+ return getClass().getName() + str;
+ }
+
+
+ public void convertRegion(final Component child, final Component parent) {
+ convertRegion(child, clip, parent);
+ }
+
+ public void intersect(final Rectangle rect) {
+ clip.intersect(rect);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return clip.isEmpty();
+ }
+
+ public static void convertRegion(final Component child,
+ final MultiRectArea region,
+ final Component parent) {
+ int x = 0, y = 0;
+ Component c = child;
+ //???AWT
+ /*
+ for (; c != null && c != parent; c = c.getParent()) {
+ x += c.getX();
+ y += c.getY();
+ }
+ */
+ if (c == null) {
+ // awt.51=Component expected to be a parent
+ throw new IllegalArgumentException(Messages.getString("awt.51")); //$NON-NLS-1$
+ }
+ region.translate(x, y);
+ }
+}
diff --git a/awt/org/apache/harmony/awt/ComponentInternals.java b/awt/org/apache/harmony/awt/ComponentInternals.java
new file mode 100644
index 0000000..c359784
--- /dev/null
+++ b/awt/org/apache/harmony/awt/ComponentInternals.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt;
+
+//???AWT
+//import java.awt.Component;
+//import java.awt.Container;
+//import java.awt.Dialog;
+import java.awt.Dimension;
+//import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+//import java.awt.Window;
+//import java.awt.Choice;
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+//import org.apache.harmony.awt.text.TextFieldKit;
+//import org.apache.harmony.awt.text.TextKit;
+//import org.apache.harmony.awt.wtk.NativeWindow;
+
+import org.apache.harmony.luni.util.NotImplementedException;
+
+/**
+ * The accessor to AWT private API
+ */
+public abstract class ComponentInternals {
+
+ /**
+ * @return the ComponentInternals instance to serve the requests
+ */
+ public static ComponentInternals getComponentInternals() {
+ return ContextStorage.getComponentInternals();
+ }
+
+ /**
+ * This method must be called by AWT to establish the connection
+ * @param internals - implementation of ComponentInternals created by AWT
+ */
+ public static void setComponentInternals(ComponentInternals internals) {
+ ContextStorage.setComponentInternals(internals);
+ }
+
+ /**
+ * The accessor to native resource connected to a component.
+ * It returns non-<code>null</code> value only if component
+ * already has the native resource
+ */
+ //public abstract NativeWindow getNativeWindow(Component component);
+
+ /**
+ * Connect Window object to existing native resource
+ * @param nativeWindowId - id of native window to attach
+ * @return Window object with special behaviour that
+ * restricts manupulation with that window
+ */
+ //public abstract Window attachNativeWindow(long nativeWindowId);
+
+ /**
+ * Start mouse grab in "client" mode.
+ * All mouse events in AWT components will be reported as usual,
+ * mouse events that occured outside of AWT components will be sent to
+ * the window passed as grabWindow parameter. When mouse grab is canceled
+ * (because of click in non-AWT window or by task switching)
+ * the whenCanceled callback is called
+ *
+ * @param grabWindow - window that will own the grab
+ * @param whenCanceled - callback called when grab is canceled by user's action
+ */
+ //public abstract void startMouseGrab(Window grabWindow, Runnable whenCanceled);
+
+ /**
+ * End mouse grab and resume normal processing of mouse events
+ */
+ //public abstract void endMouseGrab();
+
+ /**
+ * Set the <code>popup</code> flag of the window to true.
+ * This window won't be controlled by window manager on Linux.
+ * Call this method before the window is shown first time
+ * @param window - the window that should become popup one
+ */
+ //public abstract void makePopup(Window window);
+
+ /**
+ * This method must be called by Graphics at the beginning of drawImage()
+ * to store image drawing parameters (defined by application developer) in component
+ *
+ * @param comp - component that draws the image
+ * @param image - image to be drawn
+ * @param destLocation - location of the image upon the component's surface. Never null.
+ * @param destSize - size of the component's area to be filled with the image.
+ * Equals to null if size parameters omitted in drawImage.
+ * @param source - area of the image to be drawn on the component.
+ * Equals to null if src parameters omitted in drawImage.
+ */
+ /*
+ public abstract void onDrawImage(Component comp, Image image, Point destLocation,
+ Dimension destSize, Rectangle source);
+*/
+ /**
+ * Sets system's caret position.
+ * This method should be called by text component to synchronize our caret position
+ * with system's caret position.
+ * @param x
+ * @param y
+ */
+ //public abstract void setCaretPos(Component c, int x, int y);
+
+ /**
+ * NEVER USE IT. FORGET IT. IT DOES NOT EXIST.
+ * See Toolkit.unsafeInvokeAndWait(Runnable).
+ *
+ * Accessor for Toolkit.unsafeInvokeAndWait(Runnable) method.
+ * For use in exceptional cases only.
+ * Read comments for Toolkit.unsafeInvokeAndWait(Runnable) before use.
+ */
+ /*
+ public abstract void unsafeInvokeAndWait(Runnable runnable)
+ throws InterruptedException, InvocationTargetException;
+
+ public abstract TextKit getTextKit(Component comp);
+
+ public abstract void setTextKit(Component comp, TextKit kit);
+
+ public abstract TextFieldKit getTextFieldKit(Component comp);
+
+ public abstract void setTextFieldKit(Component comp, TextFieldKit kit);
+*/
+ /**
+ * Terminate event dispatch thread, completely destroy AWT context.<br>
+ * Intended for multi-context mode, in single-context mode does nothing.
+ *
+ */
+ public abstract void shutdown();
+
+ /**
+ * Sets mouse events preprocessor for event queue
+ */
+ //public abstract void setMouseEventPreprocessor(MouseEventPreprocessor preprocessor);
+
+ /**
+ * Create customized Choice using style
+ */
+ //public abstract Choice createCustomChoice(ChoiceStyle style);
+
+ //public abstract Insets getNativeInsets(Window w);
+
+ /**
+ * Region to be repainted (could be null). Use this in overridden repaint()
+ */
+ //public abstract MultiRectArea getRepaintRegion(Component c);
+
+ //public abstract MultiRectArea subtractPendingRepaintRegion(Component c, MultiRectArea mra);
+
+ /**
+ * Returns true if the window was at least once painted due to native paint events
+ */
+ //public abstract boolean wasPainted(Window w);
+
+ /**
+ * The component's region hidden behind top-level windows
+ * (belonging to both this Java app and all other apps), and behind
+ * heavyweight components overlapping with passed component
+ */
+ //public abstract MultiRectArea getObscuredRegion(Component c);
+
+ /**
+ * An accessor to Container.addObscuredRegions() method
+ * @see java.awt.Container#addObscuredRegions(MultiRectArea, Component)
+ */
+ //public abstract void addObscuredRegions(MultiRectArea mra, Component c, Container container);
+
+ /**
+ * Makes it possible to call protected Toolkit.setDesktopProperty()
+ * method from any class outside of java.awt package
+ */
+ public abstract void setDesktopProperty(String name, Object value);
+
+ /**
+ * Makes it possible to start/stop dialog modal loop
+ * from anywhere outside of java.awt package
+ */
+ //public abstract void runModalLoop(Dialog dlg);
+ //public abstract void endModalLoop(Dialog dlg);
+
+ /**
+ * Sets component's visible flag only
+ * (the component is not actually shown/hidden)
+ */
+ //public abstract void setVisibleFlag(Component comp, boolean visible);
+
+}
diff --git a/awt/org/apache/harmony/awt/ContextStorage.java b/awt/org/apache/harmony/awt/ContextStorage.java
new file mode 100644
index 0000000..d44648a
--- /dev/null
+++ b/awt/org/apache/harmony/awt/ContextStorage.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt;
+
+import java.awt.*;
+
+//???AWT
+//import org.apache.harmony.awt.datatransfer.*;
+import org.apache.harmony.awt.internal.nls.Messages;
+import org.apache.harmony.awt.wtk.*;
+
+
+public final class ContextStorage {
+
+ private static volatile boolean multiContextMode = false;
+ private volatile boolean shutdownPending = false;
+
+ private static final ContextStorage globalContext = new ContextStorage();
+
+ private Toolkit toolkit;
+ private ComponentInternals componentInternals;
+ //???AWT: private DTK dtk;
+ private WTK wtk;
+ private GraphicsEnvironment graphicsEnvironment;
+
+ private class ContextLock {}
+ private final Object contextLock = new ContextLock();
+ private final Synchronizer synchronizer = new Synchronizer();
+
+ public static void activateMultiContextMode() {
+ // TODO: checkPermission
+ multiContextMode = true;
+ }
+
+ public static void setDefaultToolkit(Toolkit newToolkit) {
+ // TODO: checkPermission
+ getCurrentContext().toolkit = newToolkit;
+ }
+
+ public static Toolkit getDefaultToolkit() {
+ return getCurrentContext().toolkit;
+ }
+
+ //???AWT
+ /*
+ public static void setDTK(DTK dtk) {
+ // TODO: checkPermission
+ getCurrentContext().dtk = dtk;
+ }
+
+ public static DTK getDTK() {
+ return getCurrentContext().dtk;
+ }
+ */
+
+ public static Synchronizer getSynchronizer() {
+ return getCurrentContext().synchronizer;
+ }
+
+ public static ComponentInternals getComponentInternals() {
+ return getCurrentContext().componentInternals;
+ }
+
+ static void setComponentInternals(ComponentInternals internals) {
+ // TODO: checkPermission
+ getCurrentContext().componentInternals = internals;
+ }
+
+ public static Object getContextLock() {
+ return getCurrentContext().contextLock;
+ }
+
+ public static WindowFactory getWindowFactory() {
+ return getCurrentContext().wtk.getWindowFactory();
+ }
+
+ public static void setWTK(WTK wtk) {
+ getCurrentContext().wtk = wtk;
+ }
+
+ public static NativeIM getNativeIM() {
+ return getCurrentContext().wtk.getNativeIM();
+ }
+
+ public static NativeEventQueue getNativeEventQueue() {
+ return getCurrentContext().wtk.getNativeEventQueue();
+ }
+
+ public static GraphicsEnvironment getGraphicsEnvironment() {
+ return getCurrentContext().graphicsEnvironment;
+ }
+
+ public static void setGraphicsEnvironment(GraphicsEnvironment environment) {
+ getCurrentContext().graphicsEnvironment = environment;
+ }
+
+ private static ContextStorage getCurrentContext() {
+ return multiContextMode ? getContextThreadGroup().context : globalContext;
+ }
+
+ private static ContextThreadGroup getContextThreadGroup() {
+
+ Thread thread = Thread.currentThread();
+ ThreadGroup group = thread.getThreadGroup();
+ while (group != null) {
+ if (group instanceof ContextThreadGroup) {
+ return (ContextThreadGroup)group;
+ }
+ group = group.getParent();
+ }
+ // awt.59=Application has run out of context thread group
+ throw new RuntimeException(Messages.getString("awt.59")); //$NON-NLS-1$
+ }
+
+ public static boolean shutdownPending() {
+ return getCurrentContext().shutdownPending;
+ }
+
+ void shutdown() {
+ if (!multiContextMode) {
+ return;
+ }
+ shutdownPending = true;
+
+ //???AWT: componentInternals.shutdown();
+
+ synchronized(contextLock) {
+ toolkit = null;
+ componentInternals = null;
+ //???AWT: dtk = null;
+ wtk = null;
+ graphicsEnvironment = null;
+ }
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/ContextThreadGroup.java b/awt/org/apache/harmony/awt/ContextThreadGroup.java
new file mode 100644
index 0000000..4f0af52
--- /dev/null
+++ b/awt/org/apache/harmony/awt/ContextThreadGroup.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt;
+
+public class ContextThreadGroup extends ThreadGroup {
+
+ final ContextStorage context = new ContextStorage();
+
+ public ContextThreadGroup(String name) {
+ super(name);
+ }
+
+ public void dispose() {
+ context.shutdown();
+ }
+}
diff --git a/awt/org/apache/harmony/awt/ListenerList.java b/awt/org/apache/harmony/awt/ListenerList.java
new file mode 100644
index 0000000..f5c55f1
--- /dev/null
+++ b/awt/org/apache/harmony/awt/ListenerList.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.awt;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * List of AWT listeners. It is for 3 purposes.
+ * 1. To support list modification from listeners
+ * 2. To ensure call for all listeners as atomic operation
+ * 3. To support system listeners that are needed for built-in AWT components
+ */
+public class ListenerList<T extends EventListener> implements Serializable {
+ private static final long serialVersionUID = 9180703263299648154L;
+
+ private transient ArrayList<T> systemList;
+ private transient ArrayList<T> userList;
+
+ public ListenerList() {
+ super();
+ }
+
+ /**
+ * Adds system listener to this list.
+ *
+ * @param listener - listener to be added.
+ */
+ public void addSystemListener(T listener) {
+ if (systemList == null) {
+ systemList = new ArrayList<T>();
+ }
+ systemList.add(listener);
+ }
+
+ /**
+ * Adds user (public) listener to this list.
+ *
+ * @param listener - listener to be added.
+ */
+ public void addUserListener(T listener) {
+ if (listener == null) {
+ return;
+ }
+ // transactionally replace old list
+ synchronized (this) {
+ if (userList == null) {
+ userList = new ArrayList<T>();
+ userList.add(listener);
+ return;
+ }
+ ArrayList<T> newList = new ArrayList<T>(userList);
+ newList.add(listener);
+ userList = newList;
+ }
+ }
+
+ /**
+ * Removes user (public) listener to this list.
+ *
+ * @param listener - listener to be removed.
+ */
+ public void removeUserListener(Object listener) {
+ if (listener == null) {
+ return;
+ }
+ // transactionally replace old list
+ synchronized (this) {
+ if (userList == null || !userList.contains(listener)) {
+ return;
+ }
+ ArrayList<T> newList = new ArrayList<T>(userList);
+ newList.remove(listener);
+ userList = (newList.size() > 0 ? newList : null);
+ }
+ }
+
+ /**
+ * Gets all user (public) listeners in one array.
+ *
+ * @param emptyArray - empty array, it's for deriving particular listeners class.
+ * @return array of all user listeners.
+ */
+ public <AT> AT[] getUserListeners(AT[] emptyArray){
+ synchronized (this) {
+ return (userList != null ? userList.toArray(emptyArray) : emptyArray);
+
+ }
+ }
+
+ /**
+ * Gets all user (public) listeners in one list.
+ *
+ * @return list of all user listeners.
+ */
+ public List<T> getUserListeners() {
+ synchronized (this) {
+ if (userList == null || userList.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<T>(userList);
+ }
+ }
+
+ public List<T> getSystemListeners() {
+ synchronized (this) {
+ if (systemList == null || systemList.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<T>(systemList);
+ }
+ }
+
+ /**
+ * Gets iterator for user listeners.
+ *
+ * @return iterator for user listeners.
+ */
+ public Iterator<T> getUserIterator() {
+ synchronized (this) {
+ if (userList == null) {
+ List<T> emptyList = Collections.emptyList();
+ return emptyList.iterator();
+ }
+ return new ReadOnlyIterator<T>(userList.iterator());
+ }
+ }
+
+ /**
+ * Gets iterator for system listeners.
+ *
+ * @return iterator for system listeners.
+ */
+ public Iterator<T> getSystemIterator() {
+ return systemList.iterator();
+ }
+
+ private static ArrayList<?> getOnlySerializable(ArrayList<?> list) {
+ if (list == null) {
+ return null;
+ }
+
+ ArrayList<Object> result = new ArrayList<Object>();
+ for (Iterator<?> it = list.iterator(); it.hasNext();) {
+ Object obj = it.next();
+ if (obj instanceof Serializable) {
+ result.add(obj);
+ }
+ }
+
+ return (result.size() != 0) ? result : null;
+ }
+
+ private void writeObject(ObjectOutputStream stream) throws IOException {
+
+ stream.defaultWriteObject();
+
+ stream.writeObject(getOnlySerializable(systemList));
+ stream.writeObject(getOnlySerializable(userList));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException {
+
+ stream.defaultReadObject();
+
+ systemList = (ArrayList<T>)stream.readObject();
+ userList = (ArrayList<T>)stream.readObject();
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/ReadOnlyIterator.java b/awt/org/apache/harmony/awt/ReadOnlyIterator.java
new file mode 100644
index 0000000..671653f
--- /dev/null
+++ b/awt/org/apache/harmony/awt/ReadOnlyIterator.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt;
+
+import java.util.Iterator;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * ReadOnlyIterator
+ */
+public final class ReadOnlyIterator<E> implements Iterator<E> {
+
+ private final Iterator<E> it;
+
+ public ReadOnlyIterator(Iterator<E> it) {
+ if (it == null) {
+ throw new NullPointerException();
+ }
+ this.it = it;
+ }
+
+ public void remove() {
+ // awt.50=Iterator is read-only
+ throw new UnsupportedOperationException(Messages.getString("awt.50")); //$NON-NLS-1$
+ }
+
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ public E next() {
+ return it.next();
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java b/awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java
new file mode 100644
index 0000000..bd5f6c6
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 23.11.2005
+ *
+ */
+
+
+package org.apache.harmony.awt.gl;
+
+import java.awt.Image;
+import java.awt.image.DataBuffer;
+import java.awt.image.IndexColorModel;
+import java.awt.image.DataBufferInt;
+
+import org.apache.harmony.awt.gl.image.DataBufferListener;
+
+/**
+ * This class give an opportunity to get access to private data of
+ * some java.awt.image classes
+ * Implementation of this class placed in java.awt.image package
+ */
+
+public abstract class AwtImageBackdoorAccessor {
+
+ static protected AwtImageBackdoorAccessor inst;
+
+ public static AwtImageBackdoorAccessor getInstance(){
+ // First we need to run the static initializer in the DataBuffer class to resolve inst.
+ new DataBufferInt(0);
+ return inst;
+ }
+
+ public abstract Surface getImageSurface(Image image);
+ public abstract boolean isGrayPallete(IndexColorModel icm);
+
+ public abstract Object getData(DataBuffer db);
+ public abstract int[] getDataInt(DataBuffer db);
+ public abstract byte[] getDataByte(DataBuffer db);
+ public abstract short[] getDataShort(DataBuffer db);
+ public abstract short[] getDataUShort(DataBuffer db);
+ public abstract double[] getDataDouble(DataBuffer db);
+ public abstract float[] getDataFloat(DataBuffer db);
+ public abstract void releaseData(DataBuffer db);
+
+ public abstract void addDataBufferListener(DataBuffer db, DataBufferListener listener);
+ public abstract void removeDataBufferListener(DataBuffer db);
+ public abstract void validate(DataBuffer db);
+}
diff --git a/awt/org/apache/harmony/awt/gl/CommonGraphics2D.java b/awt/org/apache/harmony/awt/gl/CommonGraphics2D.java
new file mode 100644
index 0000000..a33c38b
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/CommonGraphics2D.java
@@ -0,0 +1,1132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Alexey A. Petrenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+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.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import java.awt.image.renderable.RenderableImage;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.RoundRectangle2D;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+
+import org.apache.harmony.awt.gl.Surface;
+import org.apache.harmony.awt.gl.image.OffscreenImage;
+import org.apache.harmony.awt.gl.render.Blitter;
+import org.apache.harmony.awt.gl.render.JavaArcRasterizer;
+import org.apache.harmony.awt.gl.render.JavaLineRasterizer;
+import org.apache.harmony.awt.gl.render.JavaShapeRasterizer;
+import org.apache.harmony.awt.gl.render.JavaTextRenderer;
+import org.apache.harmony.awt.gl.render.NullBlitter;
+
+/*
+ * List of abstract methods to implement in subclusses
+ * Graphics.copyArea(int x, int y, int width, int height, int dx, int dy)
+ * Graphics.create()
+ * Graphics2D.getDeviceConfiguration()
+ * CommonGraphics2D.fillMultiRectAreaColor(MultiRectArea mra);
+ * CommonGraphics2D.fillMultiRectAreaPaint(MultiRectArea mra);
+ */
+
+/**
+ * CommonGraphics2D class is a super class for all system-dependent
+ * implementations. It implements major part of Graphics and Graphics2D
+ * abstract methods.
+ * <h2>CommonGraphics2D Class Internals</h2>
+ * <h3>Line and Shape Rasterizers</h3>
+ * <p>
+ * The CommonGraphics2D class splits all shapes into a set of rectangles
+ * to unify the drawing process for different operating systems and architectures.
+ * For this purpose Java 2D* uses the JavaShapeRasterizer and the JavaLineRasterizer
+ * classes from the org.apache.harmony.awt.gl.render package. The JavaShapeRasterizer
+ * class splits an object implementing a Shape interface into a set of rectangles and
+ * produces a MultiRectArea object. The JavaLineRasterizer class makes line drawing
+ * more accurate and processes lines with strokes, which are instances of the BasicStroke
+ * class.
+ * </p>
+ * <p>
+ * To port the shape drawing to another platform you just need to override
+ * rectangle-drawing methods. However, if your operating system has functions to draw
+ * particular shapes, you can optimize your subclass of the CommonGraphics2D class by
+ * using this functionality in overridden methods.
+ * </p>
+
+ * <h3>Blitters</h3>
+ * <p>
+ * Blitter classes draw images on the display or buffered images. All blitters inherit
+ * the org.apache.harmony.awt.gl.render.Blitter interface.
+ * </p>
+ * <p>Blitters are divided into:
+ * <ul>
+ * <li>Native blitters for simple types of images, which the underlying native library
+ * can draw.</li>
+ * <li>Java* blitters for those types of images, which the underlying native library
+ * cannot handle.</li>
+ * </ul></p>
+ * <p>
+ * DRL Java 2D* also uses blitters to fill the shapes and the user-defined subclasses
+ * of the java.awt.Paint class with paints, which the system does not support.
+ * </p>
+ *
+ *<h3>Text Renderers</h3>
+ *<p>
+ *Text renderers draw strings and glyph vectors. All text renderers are subclasses
+ *of the org.apache.harmony.awt.gl.TextRenderer class.
+ *</p>
+ *
+ */
+public abstract class CommonGraphics2D extends Graphics2D {
+ protected Surface dstSurf = null;
+ protected Blitter blitter = NullBlitter.getInstance();
+ protected RenderingHints hints = new RenderingHints(null);
+
+ // Clipping things
+ protected MultiRectArea clip = null;
+
+ protected Paint paint = Color.WHITE;
+ protected Color fgColor = Color.WHITE;
+ protected Color bgColor = Color.BLACK;
+
+ protected Composite composite = AlphaComposite.SrcOver;
+
+ protected Stroke stroke = new BasicStroke();
+
+ //TODO: Think more about FontRenderContext
+ protected FontRenderContext frc = new FontRenderContext(null, false, false);
+
+ protected JavaShapeRasterizer jsr = new JavaShapeRasterizer();
+
+ protected Font font = new Font("Dialog", Font.PLAIN, 12);; //$NON-NLS-1$
+
+ protected TextRenderer jtr = JavaTextRenderer.inst;
+
+ // Current graphics transform
+ protected AffineTransform transform = new AffineTransform();
+ protected double[] matrix = new double[6];
+
+ // Original user->device translation as transform and point
+ //public AffineTransform origTransform = new AffineTransform();
+ public Point origPoint = new Point(0, 0);
+
+
+ // Print debug output or not
+ protected static final boolean debugOutput = "1".equals(System.getProperty("g2d.debug")); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Constructors
+ protected CommonGraphics2D() {
+ }
+
+ protected CommonGraphics2D(int tx, int ty) {
+ this(tx, ty, null);
+ }
+
+ protected CommonGraphics2D(int tx, int ty, MultiRectArea clip) {
+ setTransform(AffineTransform.getTranslateInstance(tx, ty));
+ //origTransform = AffineTransform.getTranslateInstance(tx, ty);
+ origPoint = new Point(tx, ty);
+ setClip(clip);
+ }
+
+ // Public methods
+ @Override
+ public void addRenderingHints(Map<?,?> hints) {
+ this.hints.putAll(hints);
+ }
+
+ @Override
+ public void clearRect(int x, int y, int width, int height) {
+ Color c = getColor();
+ Paint p = getPaint();
+ setColor(getBackground());
+ fillRect(x, y, width, height);
+ setColor(c);
+ setPaint(p);
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.clearRect("+x+", "+y+", "+width+", "+height+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ }
+ }
+
+ @Override
+ public void clipRect(int x, int y, int width, int height) {
+ clip(new Rectangle(x, y, width, height));
+ }
+
+
+ @Override
+ public void clip(Shape s) {
+ if (s == null) {
+ clip = null;
+ return;
+ }
+
+ MultiRectArea mra = null;
+ if (s instanceof MultiRectArea) {
+ mra = new MultiRectArea((MultiRectArea)s);
+ mra.translate((int)transform.getTranslateX(), (int)transform.getTranslateY());
+ } else {
+ int type = transform.getType();
+ if(s instanceof Rectangle && (type & (AffineTransform.TYPE_IDENTITY |
+ AffineTransform.TYPE_TRANSLATION)) != 0){
+ mra = new MultiRectArea((Rectangle)s);
+ if(type == AffineTransform.TYPE_TRANSLATION){
+ mra.translate((int)transform.getTranslateX(), (int)transform.getTranslateY());
+ }
+ } else {
+ s = transform.createTransformedShape(s);
+ mra = jsr.rasterize(s, 0.5);
+ }
+ }
+
+ if (clip == null) {
+ setTransformedClip(mra);
+ } else {
+ clip.intersect(mra);
+ setTransformedClip(clip);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ // Do nothing for Java only classes
+ }
+
+
+
+
+ /***************************************************************************
+ *
+ * Draw methods
+ *
+ ***************************************************************************/
+
+ @Override
+ public void draw(Shape s) {
+ if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1) {
+ //TODO: Think about drawing the shape in one fillMultiRectArea call
+ BasicStroke bstroke = (BasicStroke)stroke;
+ JavaLineRasterizer.LineDasher ld = (bstroke.getDashArray() == null)?null:new JavaLineRasterizer.LineDasher(bstroke.getDashArray(), bstroke.getDashPhase());
+ PathIterator pi = s.getPathIterator(transform, 0.5);
+ float []points = new float[6];
+ int x1 = Integer.MIN_VALUE;
+ int y1 = Integer.MIN_VALUE;
+ int cx1 = Integer.MIN_VALUE;
+ int cy1 = Integer.MIN_VALUE;
+ while (!pi.isDone()) {
+ switch (pi.currentSegment(points)) {
+ case PathIterator.SEG_MOVETO:
+ x1 = (int)Math.floor(points[0]);
+ y1 = (int)Math.floor(points[1]);
+ cx1 = x1;
+ cy1 = y1;
+ break;
+ case PathIterator.SEG_LINETO:
+ int x2 = (int)Math.floor(points[0]);
+ int y2 = (int)Math.floor(points[1]);
+ fillMultiRectArea(JavaLineRasterizer.rasterize(x1, y1, x2, y2, null, ld, false));
+ x1 = x2;
+ y1 = y2;
+ break;
+ case PathIterator.SEG_CLOSE:
+ x2 = cx1;
+ y2 = cy1;
+ fillMultiRectArea(JavaLineRasterizer.rasterize(x1, y1, x2, y2, null, ld, false));
+ x1 = x2;
+ y1 = y2;
+ break;
+ }
+ pi.next();
+ }
+ } else {
+ s = stroke.createStrokedShape(s);
+ s = transform.createTransformedShape(s);
+ fillMultiRectArea(jsr.rasterize(s, 0.5));
+ }
+ }
+
+ @Override
+ public void drawArc(int x, int y, int width, int height, int sa, int ea) {
+ if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1 &&
+ ((BasicStroke)stroke).getDashArray() == null &&
+ (transform.isIdentity() || transform.getType() == AffineTransform.TYPE_TRANSLATION)) {
+ Point p = new Point(x, y);
+ transform.transform(p, p);
+ MultiRectArea mra = JavaArcRasterizer.rasterize(x, y, width, height, sa, ea, clip);
+ fillMultiRectArea(mra);
+ return;
+ }
+ draw(new Arc2D.Float(x, y, width, height, sa, ea, Arc2D.OPEN));
+ }
+
+
+ @Override
+ public boolean drawImage(Image image, int x, int y, Color bgcolor,
+ ImageObserver imageObserver) {
+
+ if(image == null) {
+ return true;
+ }
+
+ boolean done = false;
+ boolean somebits = false;
+ Surface srcSurf = null;
+ if(image instanceof OffscreenImage){
+ OffscreenImage oi = (OffscreenImage) image;
+ if((oi.getState() & ImageObserver.ERROR) != 0) {
+ return false;
+ }
+ done = oi.prepareImage(imageObserver);
+ somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
+ srcSurf = oi.getImageSurface();
+ }else{
+ done = true;
+ srcSurf = Surface.getImageSurface(image);
+ }
+
+ if(done || somebits) {
+ int w = srcSurf.getWidth();
+ int h = srcSurf.getHeight();
+ blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(),
+ composite, bgcolor, clip);
+ }
+ return done;
+ }
+
+ @Override
+ public boolean drawImage(Image image, int x, int y, ImageObserver imageObserver) {
+ return drawImage(image, x, y, null, imageObserver);
+ }
+
+ @Override
+ public boolean drawImage(Image image, int x, int y, int width, int height,
+ Color bgcolor, ImageObserver imageObserver) {
+
+ if(image == null) {
+ return true;
+ }
+ if(width == 0 || height == 0) {
+ return true;
+ }
+
+ boolean done = false;
+ boolean somebits = false;
+ Surface srcSurf = null;
+
+ if(image instanceof OffscreenImage){
+ OffscreenImage oi = (OffscreenImage) image;
+ if((oi.getState() & ImageObserver.ERROR) != 0) {
+ return false;
+ }
+ done = oi.prepareImage(imageObserver);
+ somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
+ srcSurf = oi.getImageSurface();
+ }else{
+ done = true;
+ srcSurf = Surface.getImageSurface(image);
+ }
+
+ if(done || somebits) {
+ int w = srcSurf.getWidth();
+ int h = srcSurf.getHeight();
+ if(w == width && h == height){
+ blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
+ (AffineTransform) transform.clone(),
+ composite, bgcolor, clip);
+ }else{
+ AffineTransform xform = new AffineTransform();
+ xform.setToScale((float)width / w, (float)height / h);
+ blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
+ (AffineTransform) transform.clone(),
+ xform, composite, bgcolor, clip);
+ }
+ }
+ return done;
+ }
+
+ @Override
+ public boolean drawImage(Image image, int x, int y, int width, int height,
+ ImageObserver imageObserver) {
+ return drawImage(image, x, y, width, height, null, imageObserver);
+ }
+
+ @Override
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2, Color bgcolor,
+ ImageObserver imageObserver) {
+
+ if(image == null) {
+ return true;
+ }
+ if(dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) {
+ return true;
+ }
+
+ boolean done = false;
+ boolean somebits = false;
+ Surface srcSurf = null;
+ if(image instanceof OffscreenImage){
+ OffscreenImage oi = (OffscreenImage) image;
+ if((oi.getState() & ImageObserver.ERROR) != 0) {
+ return false;
+ }
+ done = oi.prepareImage(imageObserver);
+ somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
+ srcSurf = oi.getImageSurface();
+ }else{
+ done = true;
+ srcSurf = Surface.getImageSurface(image);
+ }
+
+ if(done || somebits) {
+
+ int dstX = dx1;
+ int dstY = dy1;
+ int srcX = sx1;
+ int srcY = sy1;
+
+ int dstW = dx2 - dx1;
+ int dstH = dy2 - dy1;
+ int srcW = sx2 - sx1;
+ int srcH = sy2 - sy1;
+
+ if(srcW == dstW && srcH == dstH){
+ blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH,
+ (AffineTransform) transform.clone(),
+ composite, bgcolor, clip);
+ }else{
+ AffineTransform xform = new AffineTransform();
+ xform.setToScale((float)dstW / srcW, (float)dstH / srcH);
+ blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH,
+ (AffineTransform) transform.clone(),
+ xform, composite, bgcolor, clip);
+ }
+ }
+ return done;
+ }
+
+ @Override
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2, ImageObserver imageObserver) {
+
+ return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
+ imageObserver);
+ }
+
+ @Override
+ public void drawImage(BufferedImage bufImage, BufferedImageOp op,
+ int x, int y) {
+
+ if(bufImage == null) {
+ return;
+ }
+
+ if(op == null) {
+ drawImage(bufImage, x, y, null);
+ } else if(op instanceof AffineTransformOp){
+ AffineTransformOp atop = (AffineTransformOp) op;
+ AffineTransform xform = atop.getTransform();
+ Surface srcSurf = Surface.getImageSurface(bufImage);
+ int w = srcSurf.getWidth();
+ int h = srcSurf.getHeight();
+ blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
+ (AffineTransform) transform.clone(), xform,
+ composite, null, clip);
+ } else {
+ bufImage = op.filter(bufImage, null);
+ Surface srcSurf = Surface.getImageSurface(bufImage);
+ int w = srcSurf.getWidth();
+ int h = srcSurf.getHeight();
+ blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
+ (AffineTransform) transform.clone(),
+ composite, null, clip);
+ }
+ }
+
+ @Override
+ public boolean drawImage(Image image, AffineTransform trans,
+ ImageObserver imageObserver) {
+
+ if(image == null) {
+ return true;
+ }
+ if(trans == null || trans.isIdentity()) {
+ return drawImage(image, 0, 0, imageObserver);
+ }
+
+ boolean done = false;
+ boolean somebits = false;
+ Surface srcSurf = null;
+ if(image instanceof OffscreenImage){
+ OffscreenImage oi = (OffscreenImage) image;
+ if((oi.getState() & ImageObserver.ERROR) != 0) {
+ return false;
+ }
+ done = oi.prepareImage(imageObserver);
+ somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
+ srcSurf = oi.getImageSurface();
+ }else{
+ done = true;
+ srcSurf = Surface.getImageSurface(image);
+ }
+
+ if(done || somebits) {
+ int w = srcSurf.getWidth();
+ int h = srcSurf.getHeight();
+ AffineTransform xform = (AffineTransform) transform.clone();
+ xform.concatenate(trans);
+ blitter.blit(0, 0, srcSurf, 0, 0, dstSurf, w, h, xform, composite,
+ null, clip);
+ }
+ return done;
+ }
+
+ @Override
+ public void drawLine(int x1, int y1, int x2, int y2) {
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.drawLine("+x1+", "+y1+", "+x2+", "+y2+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ }
+
+ if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1) {
+ BasicStroke bstroke = (BasicStroke)stroke;
+ Point p1 = new Point(x1, y1);
+ Point p2 = new Point(x2, y2);
+ transform.transform(p1, p1);
+ transform.transform(p2, p2);
+ JavaLineRasterizer.LineDasher ld = (bstroke.getDashArray() == null)?null:new JavaLineRasterizer.LineDasher(bstroke.getDashArray(), bstroke.getDashPhase());
+ MultiRectArea mra = JavaLineRasterizer.rasterize(p1.x, p1.y, p2.x, p2.y, null, ld, false);
+ fillMultiRectArea(mra);
+ return;
+ }
+ draw(new Line2D.Float(x1, y1, x2, y2));
+ }
+
+ @Override
+ public void drawOval(int x, int y, int width, int height) {
+ if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1 &&
+ ((BasicStroke)stroke).getDashArray() == null &&
+ (transform.isIdentity() || transform.getType() == AffineTransform.TYPE_TRANSLATION)) {
+ Point p = new Point(x, y);
+ transform.transform(p, p);
+ MultiRectArea mra = JavaArcRasterizer.rasterize(x, y, width, height, 0, 360, clip);
+ fillMultiRectArea(mra);
+ return;
+ }
+ draw(new Ellipse2D.Float(x, y, width, height));
+ }
+
+ @Override
+ public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) {
+ draw(new Polygon(xpoints, ypoints, npoints));
+ }
+
+ @Override
+ public void drawPolygon(Polygon polygon) {
+ draw(polygon);
+ }
+
+ @Override
+ 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]);
+ }
+ }
+
+ @Override
+ public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
+ if (img == null) {
+ return;
+ }
+
+ double scaleX = xform.getScaleX();
+ double scaleY = xform.getScaleY();
+ if (scaleX == 1 && scaleY == 1) {
+ drawRenderedImage(img.createDefaultRendering(), xform);
+ } else {
+ int width = (int)Math.round(img.getWidth()*scaleX);
+ int height = (int)Math.round(img.getHeight()*scaleY);
+ xform = (AffineTransform)xform.clone();
+ xform.scale(1, 1);
+ drawRenderedImage(img.createScaledRendering(width, height, null), xform);
+ }
+ }
+
+ @Override
+ public void drawRenderedImage(RenderedImage rimg, AffineTransform xform) {
+ if (rimg == null) {
+ return;
+ }
+
+ Image img = null;
+
+ if (rimg instanceof Image) {
+ img = (Image)rimg;
+ } else {
+ //TODO: Create new class to provide Image interface for RenderedImage or rewrite this method
+ img = new BufferedImage(rimg.getColorModel(), rimg.copyData(null), false, null);
+ }
+
+ drawImage(img, xform, null);
+ }
+
+ @Override
+ public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.drawRoundRect("+x+", "+y+", "+width+", "+height+","+arcWidth+", "+arcHeight+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+ }
+
+ draw(new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight));
+ }
+
+
+
+
+
+ /***************************************************************************
+ *
+ * String methods
+ *
+ ***************************************************************************/
+
+ @Override
+ public void drawString(AttributedCharacterIterator iterator, float x, float y) {
+ GlyphVector gv = font.createGlyphVector(frc, iterator);
+ drawGlyphVector(gv, x, y);
+ }
+
+ @Override
+ public void drawString(AttributedCharacterIterator iterator, int x, int y) {
+ drawString(iterator, (float)x, (float)y);
+ }
+
+ @Override
+ public void drawString(String str, int x, int y) {
+ drawString(str, (float)x, (float)y);
+ }
+
+ @Override
+ public void drawString(String str, float x, float y) {
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.drawString("+str+", "+x+", "+y+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+
+ AffineTransform at = (AffineTransform)this.getTransform().clone();
+ AffineTransform fontTransform = font.getTransform();
+ at.concatenate(fontTransform);
+
+ double[] matrix = new double[6];
+ if (!at.isIdentity()){
+
+ int atType = at.getType();
+ at.getMatrix(matrix);
+
+ // TYPE_TRANSLATION
+ if (atType == AffineTransform.TYPE_TRANSLATION){
+ jtr.drawString(this, str,
+ (float)(x+fontTransform.getTranslateX()),
+ (float)(y+fontTransform.getTranslateY()));
+ return;
+ }
+ // TODO: we use slow type of drawing strings when Font object
+ // in Graphics has transforms, we just fill outlines. New textrenderer
+ // is to be implemented.
+ Shape sh = font.createGlyphVector(this.getFontRenderContext(), str).getOutline(x, y);
+ this.fill(sh);
+
+ } else {
+ jtr.drawString(this, str, x, y);
+ }
+
+ }
+
+ @Override
+ public void drawGlyphVector(GlyphVector gv, float x, float y) {
+
+ AffineTransform at = gv.getFont().getTransform();
+
+ double[] matrix = new double[6];
+ if ((at != null) && (!at.isIdentity())){
+
+ int atType = at.getType();
+ at.getMatrix(matrix);
+
+ // TYPE_TRANSLATION
+ if ((atType == AffineTransform.TYPE_TRANSLATION) &&
+ ((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) == 0)){
+ jtr.drawGlyphVector(this, gv, (int)(x+matrix[4]), (int)(y+matrix[5]));
+ return;
+ }
+ } else {
+ if (((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) == 0)){
+ jtr.drawGlyphVector(this, gv, x, y);
+ return;
+ }
+ }
+
+ // TODO: we use slow type of drawing strings when Font object
+ // in Graphics has transforms, we just fill outlines. New textrenderer
+ // is to be implemented.
+
+ Shape sh = gv.getOutline(x, y);
+ this.fill(sh);
+
+ }
+
+
+
+
+ /***************************************************************************
+ *
+ * Fill methods
+ *
+ ***************************************************************************/
+
+ @Override
+ public void fill(Shape s) {
+ s = transform.createTransformedShape(s);
+ MultiRectArea mra = jsr.rasterize(s, 0.5);
+ fillMultiRectArea(mra);
+ }
+
+ @Override
+ public void fillArc(int x, int y, int width, int height, int sa, int ea) {
+ fill(new Arc2D.Float(x, y, width, height, sa, ea, Arc2D.PIE));
+ }
+
+ @Override
+ public void fillOval(int x, int y, int width, int height) {
+ fill(new Ellipse2D.Float(x, y, width, height));
+ }
+
+ @Override
+ public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) {
+ fill(new Polygon(xpoints, ypoints, npoints));
+ }
+
+ @Override
+ public void fillPolygon(Polygon polygon) {
+ fill(polygon);
+ }
+
+ @Override
+ public void fillRect(int x, int y, int width, int height) {
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.fillRect("+x+", "+y+", "+width+", "+height+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ }
+
+ fill(new Rectangle(x, y, width, height));
+ }
+
+ @Override
+ public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.fillRoundRect("+x+", "+y+", "+width+", "+height+","+arcWidth+", "+arcHeight+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+ }
+
+ fill(new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight));
+ }
+
+
+
+
+ /***************************************************************************
+ *
+ * Get methods
+ *
+ ***************************************************************************/
+
+ @Override
+ public Color getBackground() {
+ return bgColor;
+ }
+
+ @Override
+ public Shape getClip() {
+ if (clip == null) {
+ return null;
+ }
+
+ MultiRectArea res = new MultiRectArea(clip);
+ res.translate(-Math.round((float)transform.getTranslateX()), -Math.round((float)transform.getTranslateY()));
+ return res;
+ }
+
+ @Override
+ public Rectangle getClipBounds() {
+ if (clip == null) {
+ return null;
+ }
+
+ Rectangle res = (Rectangle) clip.getBounds().clone();
+ res.translate(-Math.round((float)transform.getTranslateX()), -Math.round((float)transform.getTranslateY()));
+ return res;
+ }
+
+ @Override
+ public Color getColor() {
+ return fgColor;
+ }
+
+ @Override
+ public Composite getComposite() {
+ return composite;
+ }
+
+ @Override
+ public Font getFont() {
+ return font;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public FontMetrics getFontMetrics(Font font) {
+ return Toolkit.getDefaultToolkit().getFontMetrics(font);
+ }
+
+ @Override
+ public FontRenderContext getFontRenderContext() {
+ return frc;
+ }
+
+ @Override
+ public Paint getPaint() {
+ return paint;
+ }
+
+ @Override
+ public Object getRenderingHint(RenderingHints.Key key) {
+ return hints.get(key);
+ }
+
+ @Override
+ public RenderingHints getRenderingHints() {
+ return hints;
+ }
+
+ @Override
+ public Stroke getStroke() {
+ return stroke;
+ }
+
+ @Override
+ public AffineTransform getTransform() {
+ return (AffineTransform)transform.clone();
+ }
+
+ @Override
+ public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
+ //TODO: Implement method....
+ return false;
+ }
+
+
+
+
+ /***************************************************************************
+ *
+ * Transformation methods
+ *
+ ***************************************************************************/
+
+ @Override
+ public void rotate(double theta) {
+ transform.rotate(theta);
+ transform.getMatrix(matrix);
+ }
+
+ @Override
+ public void rotate(double theta, double x, double y) {
+ transform.rotate(theta, x, y);
+ transform.getMatrix(matrix);
+ }
+
+ @Override
+ public void scale(double sx, double sy) {
+ transform.scale(sx, sy);
+ transform.getMatrix(matrix);
+ }
+
+ @Override
+ public void shear(double shx, double shy) {
+ transform.shear(shx, shy);
+ transform.getMatrix(matrix);
+ }
+
+ @Override
+ public void transform(AffineTransform at) {
+ transform.concatenate(at);
+ transform.getMatrix(matrix);
+ }
+
+ @Override
+ public void translate(double tx, double ty) {
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.translate("+tx+", "+ty+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ transform.translate(tx, ty);
+ transform.getMatrix(matrix);
+ }
+
+ @Override
+ public void translate(int tx, int ty) {
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.translate("+tx+", "+ty+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ transform.translate(tx, ty);
+ transform.getMatrix(matrix);
+ }
+
+
+
+
+ /***************************************************************************
+ *
+ * Set methods
+ *
+ ***************************************************************************/
+
+ @Override
+ public void setBackground(Color color) {
+ bgColor = color;
+ }
+
+ @Override
+ public void setClip(int x, int y, int width, int height) {
+ setClip(new Rectangle(x, y, width, height));
+ }
+
+ @Override
+ public void setClip(Shape s) {
+ if (s == null) {
+ setTransformedClip(null);
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.setClip(null)"); //$NON-NLS-1$
+ }
+ return;
+ }
+
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.setClip("+s.getBounds()+")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ if (s instanceof MultiRectArea) {
+ MultiRectArea nclip = new MultiRectArea((MultiRectArea)s);
+ nclip.translate(Math.round((float)transform.getTranslateX()), Math.round((float)transform.getTranslateY()));
+ setTransformedClip(nclip);
+ } else {
+ int type = transform.getType();
+ if(s instanceof Rectangle && (type & (AffineTransform.TYPE_IDENTITY |
+ AffineTransform.TYPE_TRANSLATION)) != 0){
+ MultiRectArea nclip = new MultiRectArea((Rectangle)s);
+ if(type == AffineTransform.TYPE_TRANSLATION){
+ nclip.translate((int)transform.getTranslateX(), (int)transform.getTranslateY());
+ }
+ setTransformedClip(nclip);
+ } else {
+ s = transform.createTransformedShape(s);
+ setTransformedClip(jsr.rasterize(s, 0.5));
+ }
+ }
+ }
+
+ @Override
+ public void setColor(Color color) {
+ if (color != null) {
+ fgColor = color;
+ paint = color;
+ }
+ }
+
+ @Override
+ public void setComposite(Composite composite) {
+ this.composite = composite;
+ }
+
+ @Override
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ @Override
+ public void setPaint(Paint paint) {
+ if (paint == null)
+ return;
+
+ this.paint = paint;
+ if (paint instanceof Color) {
+ fgColor = (Color)paint;
+ }
+ }
+
+ @Override
+ public void setPaintMode() {
+ composite = AlphaComposite.SrcOver;
+ }
+
+ @Override
+ public void setRenderingHint(RenderingHints.Key key, Object value) {
+ hints.put(key, value);
+ }
+
+ @Override
+ public void setRenderingHints(Map<?,?> hints) {
+ this.hints.clear();
+ this.hints.putAll(hints);
+ }
+
+ @Override
+ public void setStroke(Stroke stroke) {
+ this.stroke = stroke;
+ }
+
+ @Override
+ public void setTransform(AffineTransform transform) {
+ this.transform = transform;
+
+ transform.getMatrix(matrix);
+ }
+
+ @Override
+ public void setXORMode(Color color) {
+ composite = new XORComposite(color);
+ }
+
+
+ // Protected methods
+ protected void setTransformedClip(MultiRectArea clip) {
+ this.clip = clip;
+ }
+
+ /**
+ * This method fills the given MultiRectArea with current paint.
+ * It calls fillMultiRectAreaColor and fillMultiRectAreaPaint
+ * methods depending on the type of current paint.
+ * @param mra MultiRectArea to fill
+ */
+ protected void fillMultiRectArea(MultiRectArea mra) {
+ if (clip != null) {
+ mra.intersect(clip);
+ }
+
+ // Return if all stuff is clipped
+ if (mra.rect[0] < 5) {
+ return;
+ }
+
+ if (debugOutput) {
+ System.err.println("CommonGraphics2D.fillMultiRectArea("+mra+")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ if (paint instanceof Color){
+ fillMultiRectAreaColor(mra);
+ }else{
+ fillMultiRectAreaPaint(mra);
+ }
+ }
+
+ /**
+ * This method fills the given MultiRectArea with solid color.
+ * @param mra MultiRectArea to fill
+ */
+ protected void fillMultiRectAreaColor(MultiRectArea mra) {
+ fillMultiRectAreaPaint(mra);
+ }
+
+ /**
+ * This method fills the given MultiRectArea with any paint.
+ * @param mra MultiRectArea to fill
+ */
+ protected void fillMultiRectAreaPaint(MultiRectArea mra) {
+ Rectangle rec = mra.getBounds();
+ int x = rec.x;
+ int y = rec.y;
+ int w = rec.width;
+ int h = rec.height;
+ if(w <= 0 || h <= 0) {
+ return;
+ }
+ PaintContext pc = paint.createContext(null, rec, rec, transform, hints);
+ Raster r = pc.getRaster(x, y, w, h);
+ WritableRaster wr;
+ if(r instanceof WritableRaster){
+ wr = (WritableRaster) r;
+ }else{
+ wr = r.createCompatibleWritableRaster();
+ wr.setRect(r);
+ }
+ Surface srcSurf = new ImageSurface(pc.getColorModel(), wr);
+ blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
+ composite, null, mra);
+ srcSurf.dispose();
+ }
+
+ /**
+ * Copies graphics class fields.
+ * Used in create method
+ *
+ * @param copy Graphics class to copy
+ */
+ protected void copyInternalFields(CommonGraphics2D copy) {
+ if (clip == null) {
+ copy.setTransformedClip(null);
+ } else {
+ copy.setTransformedClip(new MultiRectArea(clip));
+ }
+ copy.setBackground(bgColor);
+ copy.setColor(fgColor);
+ copy.setPaint(paint);
+ copy.setComposite(composite);
+ copy.setStroke(stroke);
+ copy.setFont(font);
+ copy.setTransform(new AffineTransform(transform));
+ //copy.origTransform = new AffineTransform(origTransform);
+ copy.origPoint = new Point(origPoint);
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java b/awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java
new file mode 100644
index 0000000..27e3ef0
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Alexey A. Petrenko, Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.peer.FontPeer;
+
+import org.apache.harmony.awt.gl.font.FontMetricsImpl;
+import org.apache.harmony.awt.wtk.GraphicsFactory;
+
+/**
+ * Common GraphicsFactory implementation
+ *
+ */
+public abstract class CommonGraphics2DFactory implements GraphicsFactory {
+
+ // static instance of CommonGraphics2DFactory
+ public static CommonGraphics2DFactory inst;
+
+ /**
+ * Returns FontMetrics object that keeps metrics of the specified font.
+ *
+ * @param font specified Font
+ * @return FontMetrics object corresponding to the specified Font object
+ */
+ public FontMetrics getFontMetrics(Font font) {
+ FontMetrics fm;
+ for (FontMetrics element : cacheFM) {
+ fm = element;
+ if (fm == null){
+ break;
+ }
+
+ if (fm.getFont().equals(font)){
+ return fm;
+ }
+ }
+ fm = new FontMetricsImpl(font);
+
+ System.arraycopy(cacheFM, 0, cacheFM, 1, cacheFM.length -1);
+ cacheFM[0] = fm;
+
+ return fm;
+ }
+ // Font methods
+
+ public FontPeer getFontPeer(Font font) {
+ return getFontManager().getFontPeer(font.getName(), font.getStyle(), font.getSize());
+ }
+
+ /**
+ * Embeds font from gile with specified path into the system.
+ *
+ * @param fontFilePath path to the font file
+ * @return Font object that was created from the file.
+ */
+ public abstract Font embedFont(String fontFilePath);
+
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java b/awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java
new file mode 100644
index 0000000..5c78e50
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Alexey A. Petrenko, Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import org.apache.harmony.awt.gl.image.BufferedImageGraphics2D;
+
+/**
+ * Common GraphicsEnvironment implementation
+ *
+ */
+public abstract class CommonGraphicsEnvironment extends GraphicsEnvironment {
+
+ @Override
+ public Graphics2D createGraphics(BufferedImage bufferedImage) {
+ return new BufferedImageGraphics2D(bufferedImage);
+ }
+
+ @Override
+ public String[] getAvailableFontFamilyNames(Locale locale) {
+ Font[] fonts = getAllFonts();
+ ArrayList<String> familyNames = new ArrayList<String>();
+
+ for (Font element : fonts) {
+ String name = element.getFamily(locale);
+ if (!familyNames.contains(name)) {
+ familyNames.add(name);
+ }
+ }
+
+ return familyNames.toArray(new String[familyNames.size()]);
+ }
+
+ @Override
+ public Font[] getAllFonts() {
+ return CommonGraphics2DFactory.inst.getFontManager().getAllFonts();
+ }
+
+ @Override
+ public String[] getAvailableFontFamilyNames() {
+ return CommonGraphics2DFactory.inst.getFontManager().getAllFamilies();
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/Crossing.java b/awt/org/apache/harmony/awt/gl/Crossing.java
new file mode 100644
index 0000000..ae7fb0e
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/Crossing.java
@@ -0,0 +1,889 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Shape;
+import java.awt.geom.PathIterator;
+
+public class Crossing {
+
+ /**
+ * Allowable tolerance for bounds comparison
+ */
+ static final double DELTA = 1E-5;
+
+ /**
+ * If roots have distance less then <code>ROOT_DELTA</code> they are double
+ */
+ static final double ROOT_DELTA = 1E-10;
+
+ /**
+ * Rectangle cross segment
+ */
+ public static final int CROSSING = 255;
+
+ /**
+ * Unknown crossing result
+ */
+ static final int UNKNOWN = 254;
+
+ /**
+ * Solves quadratic equation
+ * @param eqn - the coefficients of the equation
+ * @param res - the roots of the equation
+ * @return a number of roots
+ */
+ public static int solveQuad(double eqn[], double res[]) {
+ double a = eqn[2];
+ double b = eqn[1];
+ double c = eqn[0];
+ int rc = 0;
+ if (a == 0.0) {
+ if (b == 0.0) {
+ return -1;
+ }
+ res[rc++] = -c / b;
+ } else {
+ double d = b * b - 4.0 * a * c;
+ // d < 0.0
+ if (d < 0.0) {
+ return 0;
+ }
+ d = Math.sqrt(d);
+ res[rc++] = (- b + d) / (a * 2.0);
+ // d != 0.0
+ if (d != 0.0) {
+ res[rc++] = (- b - d) / (a * 2.0);
+ }
+ }
+ return fixRoots(res, rc);
+ }
+
+ /**
+ * Solves cubic equation
+ * @param eqn - the coefficients of the equation
+ * @param res - the roots of the equation
+ * @return a number of roots
+ */
+ public static int solveCubic(double eqn[], double res[]) {
+ double d = eqn[3];
+ if (d == 0) {
+ return solveQuad(eqn, res);
+ }
+ double a = eqn[2] / d;
+ double b = eqn[1] / d;
+ double c = eqn[0] / d;
+ int rc = 0;
+
+ double Q = (a * a - 3.0 * b) / 9.0;
+ double R = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0;
+ double Q3 = Q * Q * Q;
+ double R2 = R * R;
+ double n = - a / 3.0;
+
+ if (R2 < Q3) {
+ double t = Math.acos(R / Math.sqrt(Q3)) / 3.0;
+ double p = 2.0 * Math.PI / 3.0;
+ double m = -2.0 * Math.sqrt(Q);
+ res[rc++] = m * Math.cos(t) + n;
+ res[rc++] = m * Math.cos(t + p) + n;
+ res[rc++] = m * Math.cos(t - p) + n;
+ } else {
+// Debug.println("R2 >= Q3 (" + R2 + "/" + Q3 + ")");
+ double A = Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1.0 / 3.0);
+ if (R > 0.0) {
+ A = -A;
+ }
+// if (A == 0.0) {
+ if (-ROOT_DELTA < A && A < ROOT_DELTA) {
+ res[rc++] = n;
+ } else {
+ double B = Q / A;
+ res[rc++] = A + B + n;
+// if (R2 == Q3) {
+ double delta = R2 - Q3;
+ if (-ROOT_DELTA < delta && delta < ROOT_DELTA) {
+ res[rc++] = - (A + B) / 2.0 + n;
+ }
+ }
+
+ }
+ return fixRoots(res, rc);
+ }
+
+ /**
+ * Excludes double roots. Roots are double if they lies enough close with each other.
+ * @param res - the roots
+ * @param rc - the roots count
+ * @return new roots count
+ */
+ static int fixRoots(double res[], int rc) {
+ int tc = 0;
+ for(int i = 0; i < rc; i++) {
+ out: {
+ for(int j = i + 1; j < rc; j++) {
+ if (isZero(res[i] - res[j])) {
+ break out;
+ }
+ }
+ res[tc++] = res[i];
+ }
+ }
+ return tc;
+ }
+
+ /**
+ * QuadCurve class provides basic functionality to find curve crossing and calculating bounds
+ */
+ public static class QuadCurve {
+
+ double ax, ay, bx, by;
+ double Ax, Ay, Bx, By;
+
+ public QuadCurve(double x1, double y1, double cx, double cy, double x2, double y2) {
+ ax = x2 - x1;
+ ay = y2 - y1;
+ bx = cx - x1;
+ by = cy - y1;
+
+ Bx = bx + bx; // Bx = 2.0 * bx
+ Ax = ax - Bx; // Ax = ax - 2.0 * bx
+
+ By = by + by; // By = 2.0 * by
+ Ay = ay - By; // Ay = ay - 2.0 * by
+ }
+
+ int cross(double res[], int rc, double py1, double py2) {
+ int cross = 0;
+
+ for (int i = 0; i < rc; i++) {
+ double t = res[i];
+
+ // CURVE-OUTSIDE
+ if (t < -DELTA || t > 1 + DELTA) {
+ continue;
+ }
+ // CURVE-START
+ if (t < DELTA) {
+ if (py1 < 0.0 && (bx != 0.0 ? bx : ax - bx) < 0.0) {
+ cross--;
+ }
+ continue;
+ }
+ // CURVE-END
+ if (t > 1 - DELTA) {
+ if (py1 < ay && (ax != bx ? ax - bx : bx) > 0.0) {
+ cross++;
+ }
+ continue;
+ }
+ // CURVE-INSIDE
+ double ry = t * (t * Ay + By);
+ // ry = t * t * Ay + t * By
+ if (ry > py2) {
+ double rxt = t * Ax + bx;
+ // rxt = 2.0 * t * Ax + Bx = 2.0 * t * Ax + 2.0 * bx
+ if (rxt > -DELTA && rxt < DELTA) {
+ continue;
+ }
+ cross += rxt > 0.0 ? 1 : -1;
+ }
+ } // for
+
+ return cross;
+ }
+
+ int solvePoint(double res[], double px) {
+ double eqn[] = {-px, Bx, Ax};
+ return solveQuad(eqn, res);
+ }
+
+ int solveExtrem(double res[]) {
+ int rc = 0;
+ if (Ax != 0.0) {
+ res[rc++] = - Bx / (Ax + Ax);
+ }
+ if (Ay != 0.0) {
+ res[rc++] = - By / (Ay + Ay);
+ }
+ return rc;
+ }
+
+ int addBound(double bound[], int bc, double res[], int rc, double minX, double maxX, boolean changeId, int id) {
+ for(int i = 0; i < rc; i++) {
+ double t = res[i];
+ if (t > -DELTA && t < 1 + DELTA) {
+ double rx = t * (t * Ax + Bx);
+ if (minX <= rx && rx <= maxX) {
+ bound[bc++] = t;
+ bound[bc++] = rx;
+ bound[bc++] = t * (t * Ay + By);
+ bound[bc++] = id;
+ if (changeId) {
+ id++;
+ }
+ }
+ }
+ }
+ return bc;
+ }
+
+ }
+
+ /**
+ * CubicCurve class provides basic functionality to find curve crossing and calculating bounds
+ */
+ public static class CubicCurve {
+
+ double ax, ay, bx, by, cx, cy;
+ double Ax, Ay, Bx, By, Cx, Cy;
+ double Ax3, Bx2;
+
+ public CubicCurve(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2) {
+ ax = x2 - x1;
+ ay = y2 - y1;
+ bx = cx1 - x1;
+ by = cy1 - y1;
+ cx = cx2 - x1;
+ cy = cy2 - y1;
+
+ Cx = bx + bx + bx; // Cx = 3.0 * bx
+ Bx = cx + cx + cx - Cx - Cx; // Bx = 3.0 * cx - 6.0 * bx
+ Ax = ax - Bx - Cx; // Ax = ax - 3.0 * cx + 3.0 * bx
+
+ Cy = by + by + by; // Cy = 3.0 * by
+ By = cy + cy + cy - Cy - Cy; // By = 3.0 * cy - 6.0 * by
+ Ay = ay - By - Cy; // Ay = ay - 3.0 * cy + 3.0 * by
+
+ Ax3 = Ax + Ax + Ax;
+ Bx2 = Bx + Bx;
+ }
+
+ int cross(double res[], int rc, double py1, double py2) {
+ int cross = 0;
+ for (int i = 0; i < rc; i++) {
+ double t = res[i];
+
+ // CURVE-OUTSIDE
+ if (t < -DELTA || t > 1 + DELTA) {
+ continue;
+ }
+ // CURVE-START
+ if (t < DELTA) {
+ if (py1 < 0.0 && (bx != 0.0 ? bx : (cx != bx ? cx - bx : ax - cx)) < 0.0) {
+ cross--;
+ }
+ continue;
+ }
+ // CURVE-END
+ if (t > 1 - DELTA) {
+ if (py1 < ay && (ax != cx ? ax - cx : (cx != bx ? cx - bx : bx)) > 0.0) {
+ cross++;
+ }
+ continue;
+ }
+ // CURVE-INSIDE
+ double ry = t * (t * (t * Ay + By) + Cy);
+ // ry = t * t * t * Ay + t * t * By + t * Cy
+ if (ry > py2) {
+ double rxt = t * (t * Ax3 + Bx2) + Cx;
+ // rxt = 3.0 * t * t * Ax + 2.0 * t * Bx + Cx
+ if (rxt > -DELTA && rxt < DELTA) {
+ rxt = t * (Ax3 + Ax3) + Bx2;
+ // rxt = 6.0 * t * Ax + 2.0 * Bx
+ if (rxt < -DELTA || rxt > DELTA) {
+ // Inflection point
+ continue;
+ }
+ rxt = ax;
+ }
+ cross += rxt > 0.0 ? 1 : -1;
+ }
+ } //for
+
+ return cross;
+ }
+
+ int solvePoint(double res[], double px) {
+ double eqn[] = {-px, Cx, Bx, Ax};
+ return solveCubic(eqn, res);
+ }
+
+ int solveExtremX(double res[]) {
+ double eqn[] = {Cx, Bx2, Ax3};
+ return solveQuad(eqn, res);
+ }
+
+ int solveExtremY(double res[]) {
+ double eqn[] = {Cy, By + By, Ay + Ay + Ay};
+ return solveQuad(eqn, res);
+ }
+
+ int addBound(double bound[], int bc, double res[], int rc, double minX, double maxX, boolean changeId, int id) {
+ for(int i = 0; i < rc; i++) {
+ double t = res[i];
+ if (t > -DELTA && t < 1 + DELTA) {
+ double rx = t * (t * (t * Ax + Bx) + Cx);
+ if (minX <= rx && rx <= maxX) {
+ bound[bc++] = t;
+ bound[bc++] = rx;
+ bound[bc++] = t * (t * (t * Ay + By) + Cy);
+ bound[bc++] = id;
+ if (changeId) {
+ id++;
+ }
+ }
+ }
+ }
+ return bc;
+ }
+
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross line.
+ */
+ public static int crossLine(double x1, double y1, double x2, double y2, double x, double y) {
+
+ // LEFT/RIGHT/UP/EMPTY
+ if ((x < x1 && x < x2) ||
+ (x > x1 && x > x2) ||
+ (y > y1 && y > y2) ||
+ (x1 == x2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (y < y1 && y < y2) {
+ } else {
+ // INSIDE
+ if ((y2 - y1) * (x - x1) / (x2 - x1) <= y - y1) {
+ // INSIDE-UP
+ return 0;
+ }
+ }
+
+ // START
+ if (x == x1) {
+ return x1 < x2 ? 0 : -1;
+ }
+
+ // END
+ if (x == x2) {
+ return x1 < x2 ? 1 : 0;
+ }
+
+ // INSIDE-DOWN
+ return x1 < x2 ? 1 : -1;
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross quard curve
+ */
+ public static int crossQuad(double x1, double y1, double cx, double cy, double x2, double y2, double x, double y) {
+
+ // LEFT/RIGHT/UP/EMPTY
+ if ((x < x1 && x < cx && x < x2) ||
+ (x > x1 && x > cx && x > x2) ||
+ (y > y1 && y > cy && y > y2) ||
+ (x1 == cx && cx == x2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (y < y1 && y < cy && y < y2 && x != x1 && x != x2) {
+ if (x1 < x2) {
+ return x1 < x && x < x2 ? 1 : 0;
+ }
+ return x2 < x && x < x1 ? -1 : 0;
+ }
+
+ // INSIDE
+ QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2);
+ double px = x - x1;
+ double py = y - y1;
+ double res[] = new double[3];
+ int rc = c.solvePoint(res, px);
+
+ return c.cross(res, rc, py, py);
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross cubic curve
+ */
+ public static int crossCubic(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2, double x, double y) {
+
+ // LEFT/RIGHT/UP/EMPTY
+ if ((x < x1 && x < cx1 && x < cx2 && x < x2) ||
+ (x > x1 && x > cx1 && x > cx2 && x > x2) ||
+ (y > y1 && y > cy1 && y > cy2 && y > y2) ||
+ (x1 == cx1 && cx1 == cx2 && cx2 == x2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (y < y1 && y < cy1 && y < cy2 && y < y2 && x != x1 && x != x2) {
+ if (x1 < x2) {
+ return x1 < x && x < x2 ? 1 : 0;
+ }
+ return x2 < x && x < x1 ? -1 : 0;
+ }
+
+ // INSIDE
+ CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
+ double px = x - x1;
+ double py = y - y1;
+ double res[] = new double[3];
+ int rc = c.solvePoint(res, px);
+ return c.cross(res, rc, py, py);
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross path
+ */
+ public static int crossPath(PathIterator p, double x, double y) {
+ int cross = 0;
+ double mx, my, cx, cy;
+ mx = my = cx = cy = 0.0;
+ double coords[] = new double[6];
+
+ while (!p.isDone()) {
+ switch (p.currentSegment(coords)) {
+ case PathIterator.SEG_MOVETO:
+ if (cx != mx || cy != my) {
+ cross += crossLine(cx, cy, mx, my, x, y);
+ }
+ mx = cx = coords[0];
+ my = cy = coords[1];
+ break;
+ case PathIterator.SEG_LINETO:
+ cross += crossLine(cx, cy, cx = coords[0], cy = coords[1], x, y);
+ break;
+ case PathIterator.SEG_QUADTO:
+ cross += crossQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], x, y);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ cross += crossCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], x, y);
+ break;
+ case PathIterator.SEG_CLOSE:
+ if (cy != my || cx != mx) {
+ cross += crossLine(cx, cy, cx = mx, cy = my, x, y);
+ }
+ break;
+ }
+ p.next();
+ }
+ if (cy != my) {
+ cross += crossLine(cx, cy, mx, my, x, y);
+ }
+ return cross;
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross shape
+ */
+ public static int crossShape(Shape s, double x, double y) {
+ if (!s.getBounds2D().contains(x, y)) {
+ return 0;
+ }
+ return crossPath(s.getPathIterator(null), x, y);
+ }
+
+ /**
+ * Returns true if value enough small
+ */
+ public static boolean isZero(double val) {
+ return -DELTA < val && val < DELTA;
+ }
+
+ /**
+ * Sort bound array
+ */
+ static void sortBound(double bound[], int bc) {
+ for(int i = 0; i < bc - 4; i += 4) {
+ int k = i;
+ for(int j = i + 4; j < bc; j += 4) {
+ if (bound[k] > bound[j]) {
+ k = j;
+ }
+ }
+ if (k != i) {
+ double tmp = bound[i];
+ bound[i] = bound[k];
+ bound[k] = tmp;
+ tmp = bound[i + 1];
+ bound[i + 1] = bound[k + 1];
+ bound[k + 1] = tmp;
+ tmp = bound[i + 2];
+ bound[i + 2] = bound[k + 2];
+ bound[k + 2] = tmp;
+ tmp = bound[i + 3];
+ bound[i + 3] = bound[k + 3];
+ bound[k + 3] = tmp;
+ }
+ }
+ }
+
+ /**
+ * Returns are bounds intersect or not intersect rectangle
+ */
+ static int crossBound(double bound[], int bc, double py1, double py2) {
+
+ // LEFT/RIGHT
+ if (bc == 0) {
+ return 0;
+ }
+
+ // Check Y coordinate
+ int up = 0;
+ int down = 0;
+ for(int i = 2; i < bc; i += 4) {
+ if (bound[i] < py1) {
+ up++;
+ continue;
+ }
+ if (bound[i] > py2) {
+ down++;
+ continue;
+ }
+ return CROSSING;
+ }
+
+ // UP
+ if (down == 0) {
+ return 0;
+ }
+
+ if (up != 0) {
+ // bc >= 2
+ sortBound(bound, bc);
+ boolean sign = bound[2] > py2;
+ for(int i = 6; i < bc; i += 4) {
+ boolean sign2 = bound[i] > py2;
+ if (sign != sign2 && bound[i + 1] != bound[i - 3]) {
+ return CROSSING;
+ }
+ sign = sign2;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross line or the are intersect
+ */
+ public static int intersectLine(double x1, double y1, double x2, double y2, double rx1, double ry1, double rx2, double ry2) {
+
+ // LEFT/RIGHT/UP
+ if ((rx2 < x1 && rx2 < x2) ||
+ (rx1 > x1 && rx1 > x2) ||
+ (ry1 > y1 && ry1 > y2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (ry2 < y1 && ry2 < y2) {
+ } else {
+
+ // INSIDE
+ if (x1 == x2) {
+ return CROSSING;
+ }
+
+ // Build bound
+ double bx1, bx2;
+ if (x1 < x2) {
+ bx1 = x1 < rx1 ? rx1 : x1;
+ bx2 = x2 < rx2 ? x2 : rx2;
+ } else {
+ bx1 = x2 < rx1 ? rx1 : x2;
+ bx2 = x1 < rx2 ? x1 : rx2;
+ }
+ double k = (y2 - y1) / (x2 - x1);
+ double by1 = k * (bx1 - x1) + y1;
+ double by2 = k * (bx2 - x1) + y1;
+
+ // BOUND-UP
+ if (by1 < ry1 && by2 < ry1) {
+ return 0;
+ }
+
+ // BOUND-DOWN
+ if (by1 > ry2 && by2 > ry2) {
+ } else {
+ return CROSSING;
+ }
+ }
+
+ // EMPTY
+ if (x1 == x2) {
+ return 0;
+ }
+
+ // CURVE-START
+ if (rx1 == x1) {
+ return x1 < x2 ? 0 : -1;
+ }
+
+ // CURVE-END
+ if (rx1 == x2) {
+ return x1 < x2 ? 1 : 0;
+ }
+
+ if (x1 < x2) {
+ return x1 < rx1 && rx1 < x2 ? 1 : 0;
+ }
+ return x2 < rx1 && rx1 < x1 ? -1 : 0;
+
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross quad curve or the are intersect
+ */
+ public static int intersectQuad(double x1, double y1, double cx, double cy, double x2, double y2, double rx1, double ry1, double rx2, double ry2) {
+
+ // LEFT/RIGHT/UP ------------------------------------------------------
+ if ((rx2 < x1 && rx2 < cx && rx2 < x2) ||
+ (rx1 > x1 && rx1 > cx && rx1 > x2) ||
+ (ry1 > y1 && ry1 > cy && ry1 > y2))
+ {
+ return 0;
+ }
+
+ // DOWN ---------------------------------------------------------------
+ if (ry2 < y1 && ry2 < cy && ry2 < y2 && rx1 != x1 && rx1 != x2) {
+ if (x1 < x2) {
+ return x1 < rx1 && rx1 < x2 ? 1 : 0;
+ }
+ return x2 < rx1 && rx1 < x1 ? -1 : 0;
+ }
+
+ // INSIDE -------------------------------------------------------------
+ QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2);
+ double px1 = rx1 - x1;
+ double py1 = ry1 - y1;
+ double px2 = rx2 - x1;
+ double py2 = ry2 - y1;
+
+ double res1[] = new double[3];
+ double res2[] = new double[3];
+ int rc1 = c.solvePoint(res1, px1);
+ int rc2 = c.solvePoint(res2, px2);
+
+ // INSIDE-LEFT/RIGHT
+ if (rc1 == 0 && rc2 == 0) {
+ return 0;
+ }
+
+ // Build bound --------------------------------------------------------
+ double minX = px1 - DELTA;
+ double maxX = px2 + DELTA;
+ double bound[] = new double[28];
+ int bc = 0;
+ // Add roots
+ bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1);
+ // Add extremal points`
+ rc2 = c.solveExtrem(res2);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2);
+ // Add start and end
+ if (rx1 < x1 && x1 < rx2) {
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 4;
+ }
+ if (rx1 < x2 && x2 < rx2) {
+ bound[bc++] = 1.0;
+ bound[bc++] = c.ax;
+ bound[bc++] = c.ay;
+ bound[bc++] = 5;
+ }
+ // End build bound ----------------------------------------------------
+
+ int cross = crossBound(bound, bc, py1, py2);
+ if (cross != UNKNOWN) {
+ return cross;
+ }
+ return c.cross(res1, rc1, py1, py2);
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross cubic curve or the are intersect
+ */
+ public static int intersectCubic(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2, double rx1, double ry1, double rx2, double ry2) {
+
+ // LEFT/RIGHT/UP
+ if ((rx2 < x1 && rx2 < cx1 && rx2 < cx2 && rx2 < x2) ||
+ (rx1 > x1 && rx1 > cx1 && rx1 > cx2 && rx1 > x2) ||
+ (ry1 > y1 && ry1 > cy1 && ry1 > cy2 && ry1 > y2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (ry2 < y1 && ry2 < cy1 && ry2 < cy2 && ry2 < y2 && rx1 != x1 && rx1 != x2) {
+ if (x1 < x2) {
+ return x1 < rx1 && rx1 < x2 ? 1 : 0;
+ }
+ return x2 < rx1 && rx1 < x1 ? -1 : 0;
+ }
+
+ // INSIDE
+ CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
+ double px1 = rx1 - x1;
+ double py1 = ry1 - y1;
+ double px2 = rx2 - x1;
+ double py2 = ry2 - y1;
+
+ double res1[] = new double[3];
+ double res2[] = new double[3];
+ int rc1 = c.solvePoint(res1, px1);
+ int rc2 = c.solvePoint(res2, px2);
+
+ // LEFT/RIGHT
+ if (rc1 == 0 && rc2 == 0) {
+ return 0;
+ }
+
+ double minX = px1 - DELTA;
+ double maxX = px2 + DELTA;
+
+ // Build bound --------------------------------------------------------
+ double bound[] = new double[40];
+ int bc = 0;
+ // Add roots
+ bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1);
+ // Add extrimal points
+ rc2 = c.solveExtremX(res2);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2);
+ rc2 = c.solveExtremY(res2);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 4);
+ // Add start and end
+ if (rx1 < x1 && x1 < rx2) {
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 6;
+ }
+ if (rx1 < x2 && x2 < rx2) {
+ bound[bc++] = 1.0;
+ bound[bc++] = c.ax;
+ bound[bc++] = c.ay;
+ bound[bc++] = 7;
+ }
+ // End build bound ----------------------------------------------------
+
+ int cross = crossBound(bound, bc, py1, py2);
+ if (cross != UNKNOWN) {
+ return cross;
+ }
+ return c.cross(res1, rc1, py1, py2);
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross path or the are intersect
+ */
+ public static int intersectPath(PathIterator p, double x, double y, double w, double h) {
+
+ int cross = 0;
+ int count;
+ double mx, my, cx, cy;
+ mx = my = cx = cy = 0.0;
+ double coords[] = new double[6];
+
+ double rx1 = x;
+ double ry1 = y;
+ double rx2 = x + w;
+ double ry2 = y + h;
+
+ while (!p.isDone()) {
+ count = 0;
+ switch (p.currentSegment(coords)) {
+ case PathIterator.SEG_MOVETO:
+ if (cx != mx || cy != my) {
+ count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
+ }
+ mx = cx = coords[0];
+ my = cy = coords[1];
+ break;
+ case PathIterator.SEG_LINETO:
+ count = intersectLine(cx, cy, cx = coords[0], cy = coords[1], rx1, ry1, rx2, ry2);
+ break;
+ case PathIterator.SEG_QUADTO:
+ count = intersectQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], rx1, ry1, rx2, ry2);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ count = intersectCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], rx1, ry1, rx2, ry2);
+ break;
+ case PathIterator.SEG_CLOSE:
+ if (cy != my || cx != mx) {
+ count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
+ }
+ cx = mx;
+ cy = my;
+ break;
+ }
+ if (count == CROSSING) {
+ return CROSSING;
+ }
+ cross += count;
+ p.next();
+ }
+ if (cy != my) {
+ count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
+ if (count == CROSSING) {
+ return CROSSING;
+ }
+ cross += count;
+ }
+ return cross;
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross shape or the are intersect
+ */
+ public static int intersectShape(Shape s, double x, double y, double w, double h) {
+ if (!s.getBounds2D().intersects(x, y, w, h)) {
+ return 0;
+ }
+ return intersectPath(s.getPathIterator(null), x, y, w, h);
+ }
+
+ /**
+ * Returns true if cross count correspond inside location for non zero path rule
+ */
+ public static boolean isInsideNonZero(int cross) {
+ return cross != 0;
+ }
+
+ /**
+ * Returns true if cross count correspond inside location for even-odd path rule
+ */
+ public static boolean isInsideEvenOdd(int cross) {
+ return (cross & 1) != 0;
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/GLVolatileImage.java b/awt/org/apache/harmony/awt/gl/GLVolatileImage.java
new file mode 100644
index 0000000..177be23
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/GLVolatileImage.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.image.*;
+
+import org.apache.harmony.awt.gl.Surface;
+
+public abstract class GLVolatileImage extends VolatileImage {
+
+ public abstract Surface getImageSurface();
+}
diff --git a/awt/org/apache/harmony/awt/gl/ICompositeContext.java b/awt/org/apache/harmony/awt/gl/ICompositeContext.java
new file mode 100644
index 0000000..fc5631f
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/ICompositeContext.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Composite;
+import java.awt.CompositeContext;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+
+import org.apache.harmony.awt.gl.ImageSurface;
+import org.apache.harmony.awt.gl.render.NativeImageBlitter;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+
+/**
+ * This class represent implementation of the CompositeContext interface
+ */
+public class ICompositeContext implements CompositeContext {
+ Composite composite;
+ ColorModel srcCM, dstCM;
+ ImageSurface srcSurf, dstSurf;
+
+ public ICompositeContext(Composite comp, ColorModel src, ColorModel dst){
+ composite = comp;
+ srcCM = src;
+ dstCM = dst;
+ }
+
+ public void dispose() {
+ srcSurf.dispose();
+ dstSurf.dispose();
+ }
+
+ public void compose(Raster srcIn, Raster dstIn, WritableRaster dstOut) {
+
+ if(!srcCM.isCompatibleRaster(srcIn)) {
+ // awt.48=The srcIn raster is incompatible with src ColorModel
+ throw new IllegalArgumentException(Messages.getString("awt.48")); //$NON-NLS-1$
+ }
+
+ if(!dstCM.isCompatibleRaster(dstIn)) {
+ // awt.49=The dstIn raster is incompatible with dst ColorModel
+ throw new IllegalArgumentException(Messages.getString("awt.49")); //$NON-NLS-1$
+ }
+
+ if(dstIn != dstOut){
+ if(!dstCM.isCompatibleRaster(dstOut)) {
+ // awt.4A=The dstOut raster is incompatible with dst ColorModel
+ throw new IllegalArgumentException(Messages.getString("awt.4A")); //$NON-NLS-1$
+ }
+ dstOut.setDataElements(0, 0, dstIn);
+ }
+ WritableRaster src;
+ if(srcIn instanceof WritableRaster){
+ src = (WritableRaster) srcIn;
+ }else{
+ src = srcIn.createCompatibleWritableRaster();
+ src.setDataElements(0, 0, srcIn);
+ }
+ srcSurf = new ImageSurface(srcCM, src);
+ dstSurf = new ImageSurface(dstCM, dstOut);
+
+ int w = Math.min(srcIn.getWidth(), dstOut.getWidth());
+ int h = Math.min(srcIn.getHeight(), dstOut.getHeight());
+
+ NativeImageBlitter.getInstance().blit(0, 0, srcSurf, 0, 0, dstSurf,
+ w, h, composite, null, null);
+
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/ImageSurface.java b/awt/org/apache/harmony/awt/gl/ImageSurface.java
new file mode 100644
index 0000000..6368dd8
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/ImageSurface.java
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 10.11.2005
+ *
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.BandedSampleModel;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DirectColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.harmony.awt.gl.color.LUTColorConverter;
+import org.apache.harmony.awt.gl.image.DataBufferListener;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+
+/**
+ * This class represent Surface for different types of Images (BufferedImage,
+ * OffscreenImage and so on)
+ */
+public class ImageSurface extends Surface implements DataBufferListener {
+
+ boolean nativeDrawable = true;
+ int surfaceType;
+ int csType;
+ ColorModel cm;
+ WritableRaster raster;
+ Object data;
+
+ boolean needToRefresh = true;
+ boolean dataTaken = false;
+
+ private long cachedDataPtr; // Pointer for cached Image Data
+ private boolean alphaPre; // Cached Image Data alpha premultiplied
+
+ public ImageSurface(ColorModel cm, WritableRaster raster){
+ this(cm, raster, Surface.getType(cm, raster));
+ }
+
+ public ImageSurface(ColorModel cm, WritableRaster raster, int type){
+ if (!cm.isCompatibleRaster(raster)) {
+ // awt.4D=The raster is incompatible with this ColorModel
+ throw new IllegalArgumentException(Messages.getString("awt.4D")); //$NON-NLS-1$
+ }
+ this.cm = cm;
+ this.raster = raster;
+ surfaceType = type;
+
+ data = AwtImageBackdoorAccessor.getInstance().
+ getData(raster.getDataBuffer());
+ ColorSpace cs = cm.getColorSpace();
+ transparency = cm.getTransparency();
+ width = raster.getWidth();
+ height = raster.getHeight();
+
+ // For the moment we can build natively only images which have
+ // sRGB, Linear_RGB, Linear_Gray Color Space and type different
+ // from BufferedImage.TYPE_CUSTOM
+ if(cs == LUTColorConverter.sRGB_CS){
+ csType = sRGB_CS;
+ }else if(cs == LUTColorConverter.LINEAR_RGB_CS){
+ csType = Linear_RGB_CS;
+ }else if(cs == LUTColorConverter.LINEAR_GRAY_CS){
+ csType = Linear_Gray_CS;
+ }else{
+ csType = Custom_CS;
+ nativeDrawable = false;
+ }
+
+ if(type == BufferedImage.TYPE_CUSTOM){
+ nativeDrawable = false;
+ }
+ }
+
+ @Override
+ public ColorModel getColorModel() {
+ return cm;
+ }
+
+ @Override
+ public WritableRaster getRaster() {
+ return raster;
+ }
+
+ @Override
+ public long getSurfaceDataPtr() {
+ if(surfaceDataPtr == 0L && nativeDrawable){
+ createSufaceStructure();
+ }
+ return surfaceDataPtr;
+ }
+
+ @Override
+ public Object getData(){
+ return data;
+ }
+
+ @Override
+ public boolean isNativeDrawable(){
+ return nativeDrawable;
+ }
+
+ @Override
+ public int getSurfaceType() {
+ return surfaceType;
+ }
+
+ /**
+ * Creates native Surface structure which used for native blitting
+ */
+ private void createSufaceStructure(){
+ int cmType = 0;
+ int numComponents = cm.getNumComponents();
+ boolean hasAlpha = cm.hasAlpha();
+ boolean isAlphaPre = cm.isAlphaPremultiplied();
+ int transparency = cm.getTransparency();
+ int bits[] = cm.getComponentSize();
+ int pixelStride = cm.getPixelSize();
+ int masks[] = null;
+ int colorMap[] = null;
+ int colorMapSize = 0;
+ int transpPixel = -1;
+ boolean isGrayPallete = false;
+ SampleModel sm = raster.getSampleModel();
+ int smType = 0;
+ int dataType = sm.getDataType();
+ int scanlineStride = 0;
+ int bankIndeces[] = null;
+ int bandOffsets[] = null;
+ int offset = raster.getDataBuffer().getOffset();
+
+ if(cm instanceof DirectColorModel){
+ cmType = DCM;
+ DirectColorModel dcm = (DirectColorModel) cm;
+ masks = dcm.getMasks();
+ smType = SPPSM;
+ SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
+ scanlineStride = sppsm.getScanlineStride();
+
+ }else if(cm instanceof IndexColorModel){
+ cmType = ICM;
+ IndexColorModel icm = (IndexColorModel) cm;
+ colorMapSize = icm.getMapSize();
+ colorMap = new int[colorMapSize];
+ icm.getRGBs(colorMap);
+ transpPixel = icm.getTransparentPixel();
+ isGrayPallete = Surface.isGrayPallete(icm);
+
+ if(sm instanceof MultiPixelPackedSampleModel){
+ smType = MPPSM;
+ MultiPixelPackedSampleModel mppsm =
+ (MultiPixelPackedSampleModel) sm;
+ scanlineStride = mppsm.getScanlineStride();
+ }else if(sm instanceof ComponentSampleModel){
+ smType = CSM;
+ ComponentSampleModel csm =
+ (ComponentSampleModel) sm;
+ scanlineStride = csm.getScanlineStride();
+ }else{
+ // awt.4D=The raster is incompatible with this ColorModel
+ throw new IllegalArgumentException(Messages.getString("awt.4D")); //$NON-NLS-1$
+ }
+
+ }else if(cm instanceof ComponentColorModel){
+ cmType = CCM;
+ if(sm instanceof ComponentSampleModel){
+ ComponentSampleModel csm = (ComponentSampleModel) sm;
+ scanlineStride = csm.getScanlineStride();
+ bankIndeces = csm.getBankIndices();
+ bandOffsets = csm.getBandOffsets();
+ if(sm instanceof PixelInterleavedSampleModel){
+ smType = PISM;
+ }else if(sm instanceof BandedSampleModel){
+ smType = BSM;
+ }else{
+ smType = CSM;
+ }
+ }else{
+ // awt.4D=The raster is incompatible with this ColorModel
+ throw new IllegalArgumentException(Messages.getString("awt.4D")); //$NON-NLS-1$
+ }
+
+ }else{
+ surfaceDataPtr = 0L;
+ return;
+ }
+ surfaceDataPtr = createSurfStruct(surfaceType, width, height, cmType, csType, smType, dataType,
+ numComponents, pixelStride, scanlineStride, bits, masks, colorMapSize,
+ colorMap, transpPixel, isGrayPallete, bankIndeces, bandOffsets,
+ offset, hasAlpha, isAlphaPre, transparency);
+ }
+
+ @Override
+ public void dispose() {
+ if(surfaceDataPtr != 0L){
+ dispose(surfaceDataPtr);
+ surfaceDataPtr = 0L;
+ }
+ }
+
+ public long getCachedData(boolean alphaPre){
+ if(nativeDrawable){
+ if(cachedDataPtr == 0L || needToRefresh || this.alphaPre != alphaPre){
+ cachedDataPtr = updateCache(getSurfaceDataPtr(), data, alphaPre);
+ this.alphaPre = alphaPre;
+ validate();
+ }
+ }
+ return cachedDataPtr;
+ }
+
+ private native long createSurfStruct(int surfaceType, int width, int height,
+ int cmType, int csType, int smType, int dataType,
+ int numComponents, int pixelStride, int scanlineStride,
+ int bits[], int masks[], int colorMapSize, int colorMap[],
+ int transpPixel, boolean isGrayPalette, int bankIndeces[],
+ int bandOffsets[], int offset, boolean hasAlpha, boolean isAlphaPre,
+ int transparency);
+
+ private native void dispose(long structPtr);
+
+ private native void setImageSize(long structPtr, int width, int height);
+
+ private native long updateCache(long structPtr, Object data, boolean alphaPre);
+
+ /**
+ * Supposes that new raster is compatible with an old one
+ * @param r
+ */
+ public void setRaster(WritableRaster r) {
+ raster = r;
+ data = AwtImageBackdoorAccessor.getInstance().getData(r.getDataBuffer());
+ if (surfaceDataPtr != 0) {
+ setImageSize(surfaceDataPtr, r.getWidth(), r.getHeight());
+ }
+ this.width = r.getWidth();
+ this.height = r.getHeight();
+ }
+
+ @Override
+ public long lock() {
+ // TODO
+ return 0;
+ }
+
+ @Override
+ public void unlock() {
+ //TODO
+ }
+
+ @Override
+ public Surface getImageSurface() {
+ return this;
+ }
+
+ public void dataChanged() {
+ needToRefresh = true;
+ clearValidCaches();
+ }
+
+ public void dataTaken() {
+ dataTaken = true;
+ needToRefresh = true;
+ clearValidCaches();
+ }
+
+ public void dataReleased(){
+ dataTaken = false;
+ needToRefresh = true;
+ clearValidCaches();
+ }
+
+ @Override
+ public void invalidate(){
+ needToRefresh = true;
+ clearValidCaches();
+ }
+
+ @Override
+ public void validate(){
+ if(!needToRefresh) {
+ return;
+ }
+ if(!dataTaken){
+ needToRefresh = false;
+ AwtImageBackdoorAccessor ba = AwtImageBackdoorAccessor.getInstance();
+ ba.validate(raster.getDataBuffer());
+ }
+
+ }
+
+ @Override
+ public boolean invalidated(){
+ return needToRefresh;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/MultiRectArea.java b/awt/org/apache/harmony/awt/gl/MultiRectArea.java
new file mode 100644
index 0000000..c4267f3
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/MultiRectArea.java
@@ -0,0 +1,836 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+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;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+public class MultiRectArea implements Shape {
+
+ /**
+ * If CHECK is true validation check active
+ */
+ private static final boolean CHECK = false;
+
+ boolean sorted = true;
+
+ /**
+ * Rectangle buffer
+ */
+ public int[] rect;
+
+ /**
+ * Bounding box
+ */
+ Rectangle bounds;
+
+ /**
+ * Result rectangle array
+ */
+ Rectangle[] rectangles;
+
+ /**
+ * LineCash provides creating MultiRectArea line by line. Used in JavaShapeRasterizer.
+ */
+ public static class LineCash extends MultiRectArea {
+
+ int lineY;
+ int bottomCount;
+ int[] bottom;
+
+ public LineCash(int size) {
+ super();
+ bottom = new int[size];
+ bottomCount = 0;
+ }
+
+ public void setLine(int y) {
+ lineY = y;
+ }
+
+ public void skipLine() {
+ lineY++;
+ bottomCount = 0;
+ }
+
+ public void addLine(int[] points, int pointCount) {
+ int bottomIndex = 0;
+ int pointIndex = 0;
+ int rectIndex = 0;
+ int pointX1 = 0;
+ int pointX2 = 0;
+ int bottomX1 = 0;
+ int bottomX2 = 0;
+ boolean appendRect = false;
+ boolean deleteRect = false;
+ int lastCount = bottomCount;
+
+ while (bottomIndex < lastCount || pointIndex < pointCount) {
+
+ appendRect = false;
+ deleteRect = false;
+
+ if (bottomIndex < lastCount) {
+ rectIndex = bottom[bottomIndex];
+ bottomX1 = rect[rectIndex];
+ bottomX2 = rect[rectIndex + 2];
+ } else {
+ appendRect = true;
+ }
+
+ if (pointIndex < pointCount) {
+ pointX1 = points[pointIndex];
+ pointX2 = points[pointIndex + 1];
+ } else {
+ deleteRect = true;
+ }
+
+ if (!deleteRect && !appendRect) {
+ if (pointX1 == bottomX1 && pointX2 == bottomX2) {
+ rect[rectIndex + 3] = rect[rectIndex + 3] + 1;
+ pointIndex += 2;
+ bottomIndex++;
+ continue;
+ }
+ deleteRect = pointX2 >= bottomX1;
+ appendRect = pointX1 <= bottomX2;
+ }
+
+ if (deleteRect) {
+ if (bottomIndex < bottomCount - 1) {
+ System.arraycopy(bottom, bottomIndex + 1, bottom, bottomIndex, bottomCount - bottomIndex - 1);
+ rectIndex -= 4;
+ }
+ bottomCount--;
+ lastCount--;
+ }
+
+ if (appendRect) {
+ int i = rect[0];
+ bottom[bottomCount++] = i;
+ rect = MultiRectAreaOp.checkBufSize(rect, 4);
+ rect[i++] = pointX1;
+ rect[i++] = lineY;
+ rect[i++] = pointX2;
+ rect[i++] = lineY;
+ pointIndex += 2;
+ }
+ }
+ lineY++;
+
+ invalidate();
+ }
+
+ }
+
+ /**
+ * RectCash provides simple creating MultiRectArea
+ */
+ public static class RectCash extends MultiRectArea {
+
+ int[] cash;
+
+ public RectCash() {
+ super();
+ cash = new int[MultiRectAreaOp.RECT_CAPACITY];
+ cash[0] = 1;
+ }
+
+ public void addRectCashed(int x1, int y1, int x2, int y2) {
+ addRect(x1, y1, x2, y2);
+ invalidate();
+/*
+ // Exclude from cash unnecessary rectangles
+ int i = 1;
+ while(i < cash[0]) {
+ if (rect[cash[i] + 3] >= y1 - 1) {
+ if (i > 1) {
+ System.arraycopy(cash, i, cash, 1, cash[0] - i);
+ }
+ break;
+ }
+ i++;
+ }
+ cash[0] -= i - 1;
+
+ // Find in cash rectangle to concatinate
+ i = 1;
+ while(i < cash[0]) {
+ int index = cash[i];
+ if (rect[index + 3] != y1 - 1) {
+ break;
+ }
+ if (rect[index] == x1 && rect[index + 2] == x2) {
+ rect[index + 3] += y2 - y1 + 1;
+
+ int pos = i + 1;
+ while(pos < cash[0]) {
+ if (rect[index + 3] <= rect[cash[i] + 3]) {
+ System.arraycopy(cash, i + 1, cash, i, pos - i);
+ break;
+ }
+ i++;
+ }
+ cash[pos - 1] = index;
+
+ invalidate();
+ return;
+ }
+ i++;
+ }
+
+ // Add rectangle to buffer
+ int index = rect[0];
+ rect = MultiRectAreaOp.checkBufSize(rect, 4);
+ rect[index + 0] = x1;
+ rect[index + 1] = y1;
+ rect[index + 2] = x2;
+ rect[index + 3] = y2;
+
+ // Add rectangle to cash
+ int length = cash[0];
+ cash = MultiRectAreaOp.checkBufSize(cash, 1);
+ while(i < length) {
+ if (y2 <= rect[cash[i] + 3]) {
+ System.arraycopy(cash, i, cash, i + 1, length - i);
+ break;
+ }
+ i++;
+ }
+ cash[i] = index;
+ invalidate();
+*/
+ }
+
+ public void addRectCashed(int[] rect, int rectOff, int rectLength) {
+ for(int i = rectOff; i < rectOff + rectLength;) {
+ addRect(rect[i++], rect[i++], rect[i++], rect[i++]);
+// addRectCashed(rect[i++], rect[i++], rect[i++], rect[i++]);
+ }
+ }
+
+ }
+
+ /**
+ * MultiRectArea path iterator
+ */
+ class Iterator implements PathIterator {
+
+ int type;
+ int index;
+ int pos;
+
+ int[] rect;
+ AffineTransform t;
+
+ Iterator(MultiRectArea mra, AffineTransform t) {
+ rect = new int[mra.rect[0] - 1];
+ System.arraycopy(mra.rect, 1, rect, 0, rect.length);
+ this.t = t;
+ }
+
+ public int getWindingRule() {
+ return WIND_NON_ZERO;
+ }
+
+ public boolean isDone() {
+ return pos >= rect.length;
+ }
+
+ public void next() {
+ if (index == 4) {
+ pos += 4;
+ }
+ index = (index + 1) % 5;
+ }
+
+ public int currentSegment(double[] coords) {
+ if (isDone()) {
+ // awt.4B=Iiterator out of bounds
+ throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
+ }
+ int type = 0;
+
+ switch(index) {
+ case 0 :
+ type = SEG_MOVETO;
+ coords[0] = rect[pos + 0];
+ coords[1] = rect[pos + 1];
+ break;
+ case 1:
+ type = SEG_LINETO;
+ coords[0] = rect[pos + 2];
+ coords[1] = rect[pos + 1];
+ break;
+ case 2:
+ type = SEG_LINETO;
+ coords[0] = rect[pos + 2];
+ coords[1] = rect[pos + 3];
+ break;
+ case 3:
+ type = SEG_LINETO;
+ coords[0] = rect[pos + 0];
+ coords[1] = rect[pos + 3];
+ break;
+ case 4:
+ type = SEG_CLOSE;
+ break;
+ }
+
+ if (t != null) {
+ t.transform(coords, 0, coords, 0, 1);
+ }
+ return type;
+ }
+
+ public int currentSegment(float[] coords) {
+ if (isDone()) {
+ // awt.4B=Iiterator out of bounds
+ throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
+ }
+ int type = 0;
+
+ switch(index) {
+ case 0 :
+ type = SEG_MOVETO;
+ coords[0] = rect[pos + 0];
+ coords[1] = rect[pos + 1];
+ break;
+ case 1:
+ type = SEG_LINETO;
+ coords[0] = rect[pos + 2];
+ coords[1] = rect[pos + 1];
+ break;
+ case 2:
+ type = SEG_LINETO;
+ coords[0] = rect[pos + 2];
+ coords[1] = rect[pos + 3];
+ break;
+ case 3:
+ type = SEG_LINETO;
+ coords[0] = rect[pos + 0];
+ coords[1] = rect[pos + 3];
+ break;
+ case 4:
+ type = SEG_CLOSE;
+ break;
+ }
+
+ if (t != null) {
+ t.transform(coords, 0, coords, 0, 1);
+ }
+ return type;
+ }
+
+ }
+
+ /**
+ * Constructs a new empty MultiRectArea
+ */
+ public MultiRectArea() {
+ rect = MultiRectAreaOp.createBuf(0);
+ }
+
+ public MultiRectArea(boolean sorted) {
+ this();
+ this.sorted = sorted;
+ }
+
+ /**
+ * Constructs a new MultiRectArea as a copy of another one
+ */
+ public MultiRectArea(MultiRectArea mra) {
+ if (mra == null) {
+ rect = MultiRectAreaOp.createBuf(0);
+ } else {
+ rect = new int[mra.rect.length];
+ System.arraycopy(mra.rect, 0, rect, 0, mra.rect.length);
+ check(this, "MultiRectArea(MRA)"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Constructs a new MultiRectArea consists of single rectangle
+ */
+ public MultiRectArea(Rectangle r) {
+ rect = MultiRectAreaOp.createBuf(0);
+ if (r != null && !r.isEmpty()) {
+ rect[0] = 5;
+ rect[1] = r.x;
+ rect[2] = r.y;
+ rect[3] = r.x + r.width - 1;
+ rect[4] = r.y + r.height - 1;
+ }
+ check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$
+ }
+
+ /**
+ * Constructs a new MultiRectArea consists of single rectangle
+ */
+ public MultiRectArea(int x0, int y0, int x1, int y1) {
+ rect = MultiRectAreaOp.createBuf(0);
+ if (x1 >= x0 && y1 >= y0) {
+ rect[0] = 5;
+ rect[1] = x0;
+ rect[2] = y0;
+ rect[3] = x1;
+ rect[4] = y1;
+ }
+ check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$
+ }
+
+ /**
+ * Constructs a new MultiRectArea and append rectangle from buffer
+ */
+ public MultiRectArea(Rectangle[] buf) {
+ this();
+ for (Rectangle element : buf) {
+ add(element);
+ }
+ }
+
+ /**
+ * Constructs a new MultiRectArea and append rectangle from array
+ */
+ public MultiRectArea(ArrayList<Rectangle> buf) {
+ this();
+ for(int i = 0; i < buf.size(); i++) {
+ add(buf.get(i));
+ }
+ }
+
+ /**
+ * Sort rectangle buffer
+ */
+ void resort() {
+ int[] buf = new int[4];
+ for(int i = 1; i < rect[0]; i += 4) {
+ int k = i;
+ int x1 = rect[k];
+ int y1 = rect[k + 1];
+ for(int j = i + 4; j < rect[0]; j += 4) {
+ int x2 = rect[j];
+ int y2 = rect[j + 1];
+ if (y1 > y2 || (y1 == y2 && x1 > x2)) {
+ x1 = x2;
+ y1 = y2;
+ k = j;
+ }
+ }
+ if (k != i) {
+ System.arraycopy(rect, i, buf, 0, 4);
+ System.arraycopy(rect, k, rect, i, 4);
+ System.arraycopy(buf, 0, rect, k, 4);
+ }
+ }
+ invalidate();
+ }
+
+ /**
+ * Tests equals with another object
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof MultiRectArea) {
+ MultiRectArea mra = (MultiRectArea) obj;
+ for(int i = 0; i < rect[0]; i++) {
+ if (rect[i] != mra.rect[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks validation of MultiRectArea object
+ */
+ static MultiRectArea check(MultiRectArea mra, String msg) {
+ if (CHECK && mra != null) {
+ if (MultiRectArea.checkValidation(mra.getRectangles(), mra.sorted) != -1) {
+ // awt.4C=Invalid MultiRectArea in method {0}
+ new RuntimeException(Messages.getString("awt.4C", msg)); //$NON-NLS-1$
+ }
+ }
+ return mra;
+ }
+
+ /**
+ * Checks validation of MultiRectArea object
+ */
+ public static int checkValidation(Rectangle[] r, boolean sorted) {
+
+ // Check width and height
+ for(int i = 0; i < r.length; i++) {
+ if (r[i].width <= 0 || r[i].height <= 0) {
+ return i;
+ }
+ }
+
+ // Check order
+ if (sorted) {
+ for(int i = 1; i < r.length; i++) {
+ if (r[i - 1].y > r[i].y) {
+ return i;
+ }
+ if (r[i - 1].y == r[i].y) {
+ if (r[i - 1].x > r[i].x) {
+ return i;
+ }
+ }
+ }
+ }
+
+ // Check override
+ for(int i = 0; i < r.length; i++) {
+ for(int j = i + 1; j < r.length; j++) {
+ if (r[i].intersects(r[j])) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Assigns rectangle from another buffer
+ */
+ protected void setRect(int[] buf, boolean copy) {
+ if (copy) {
+ rect = new int[buf.length];
+ System.arraycopy(buf, 0, rect, 0, buf.length);
+ } else {
+ rect = buf;
+ }
+ invalidate();
+ }
+
+ /**
+ * Union with another MultiRectArea object
+ */
+ public void add(MultiRectArea mra) {
+ setRect(union(this, mra).rect, false);
+ invalidate();
+ }
+
+ /**
+ * Intersect with another MultiRectArea object
+ */
+ public void intersect(MultiRectArea mra) {
+ setRect(intersect(this, mra).rect, false);
+ invalidate();
+ }
+
+ /**
+ * Subtract another MultiRectArea object
+ */
+ public void substract(MultiRectArea mra) {
+ setRect(subtract(this, mra).rect, false);
+ invalidate();
+ }
+
+ /**
+ * Union with Rectangle object
+ */
+ public void add(Rectangle rect) {
+ setRect(union(this, new MultiRectArea(rect)).rect, false);
+ invalidate();
+ }
+
+ /**
+ * Intersect with Rectangle object
+ */
+ public void intersect(Rectangle rect) {
+ setRect(intersect(this, new MultiRectArea(rect)).rect, false);
+ invalidate();
+ }
+
+ /**
+ * Subtract rectangle object
+ */
+ public void substract(Rectangle rect) {
+ setRect(subtract(this, new MultiRectArea(rect)).rect, false);
+ }
+
+ /**
+ * Union two MutliRectareArea objects
+ */
+ public static MultiRectArea intersect(MultiRectArea src1, MultiRectArea src2) {
+ MultiRectArea res = check(MultiRectAreaOp.Intersection.getResult(src1, src2), "intersect(MRA,MRA)"); //$NON-NLS-1$
+ return res;
+ }
+
+ /**
+ * Intersect two MultiRectArea objects
+ */
+ public static MultiRectArea union(MultiRectArea src1, MultiRectArea src2) {
+ MultiRectArea res = check(new MultiRectAreaOp.Union().getResult(src1, src2), "union(MRA,MRA)"); //$NON-NLS-1$
+ return res;
+ }
+
+ /**
+ * Subtract two MultiRectArea objects
+ */
+ public static MultiRectArea subtract(MultiRectArea src1, MultiRectArea src2) {
+ MultiRectArea res = check(MultiRectAreaOp.Subtraction.getResult(src1, src2), "subtract(MRA,MRA)"); //$NON-NLS-1$
+ return res;
+ }
+
+ /**
+ * Print MultiRectArea object to output stream
+ */
+ public static void print(MultiRectArea mra, String msg) {
+ if (mra == null) {
+ System.out.println(msg + "=null"); //$NON-NLS-1$
+ } else {
+ Rectangle[] rects = mra.getRectangles();
+ System.out.println(msg + "(" + rects.length + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ for (Rectangle element : rects) {
+ System.out.println(
+ element.x + "," + //$NON-NLS-1$
+ element.y + "," + //$NON-NLS-1$
+ (element.x + element.width - 1) + "," + //$NON-NLS-1$
+ (element.y + element.height - 1));
+ }
+ }
+ }
+
+ /**
+ * Translate MultiRectArea object by (x, y)
+ */
+ public void translate(int x, int y) {
+ for(int i = 1; i < rect[0];) {
+ rect[i++] += x;
+ rect[i++] += y;
+ rect[i++] += x;
+ rect[i++] += y;
+ }
+
+ if (bounds != null && !bounds.isEmpty()) {
+ bounds.translate(x, y);
+ }
+
+ if (rectangles != null) {
+ for (Rectangle element : rectangles) {
+ element.translate(x, y);
+ }
+ }
+ }
+
+ /**
+ * Add rectangle to the buffer without any checking
+ */
+ public void addRect(int x1, int y1, int x2, int y2) {
+ int i = rect[0];
+ rect = MultiRectAreaOp.checkBufSize(rect, 4);
+ rect[i++] = x1;
+ rect[i++] = y1;
+ rect[i++] = x2;
+ rect[i++] = y2;
+ }
+
+ /**
+ * Tests is MultiRectArea empty
+ */
+ public boolean isEmpty() {
+ return rect[0] == 1;
+ }
+
+ void invalidate() {
+ bounds = null;
+ rectangles = null;
+ }
+
+ /**
+ * Returns bounds of MultiRectArea object
+ */
+ public Rectangle getBounds() {
+ if (bounds != null) {
+ return bounds;
+ }
+
+ if (isEmpty()) {
+ return bounds = new Rectangle();
+ }
+
+ int x1 = rect[1];
+ int y1 = rect[2];
+ int x2 = rect[3];
+ int y2 = rect[4];
+
+ for(int i = 5; i < rect[0]; i += 4) {
+ int rx1 = rect[i + 0];
+ int ry1 = rect[i + 1];
+ int rx2 = rect[i + 2];
+ int ry2 = rect[i + 3];
+ if (rx1 < x1) {
+ x1 = rx1;
+ }
+ if (rx2 > x2) {
+ x2 = rx2;
+ }
+ if (ry1 < y1) {
+ y1 = ry1;
+ }
+ if (ry2 > y2) {
+ y2 = ry2;
+ }
+ }
+
+ return bounds = new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ }
+
+ /**
+ * Recturn rectangle count in the buffer
+ */
+ public int getRectCount() {
+ return (rect[0] - 1) / 4;
+ }
+
+ /**
+ * Returns Rectangle array
+ */
+ public Rectangle[] getRectangles() {
+ if (rectangles != null) {
+ return rectangles;
+ }
+
+ rectangles = new Rectangle[(rect[0] - 1) / 4];
+ int j = 0;
+ for(int i = 1; i < rect[0]; i += 4) {
+ rectangles[j++] = new Rectangle(
+ rect[i],
+ rect[i + 1],
+ rect[i + 2] - rect[i] + 1,
+ rect[i + 3] - rect[i + 1] + 1);
+ }
+ return rectangles;
+ }
+
+ /**
+ * Returns Bounds2D
+ */
+ public Rectangle2D getBounds2D() {
+ return getBounds();
+ }
+
+ /**
+ * Tests does point lie inside MultiRectArea object
+ */
+ public boolean contains(double x, double y) {
+ for(int i = 1; i < rect[0]; i+= 4) {
+ if (rect[i] <= x && x <= rect[i + 2] && rect[i + 1] <= y && y <= rect[i + 3]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests does Point2D lie inside MultiRectArea object
+ */
+ public boolean contains(Point2D p) {
+ return contains(p.getX(), p.getY());
+ }
+
+ /**
+ * Tests does rectangle lie inside MultiRectArea object
+ */
+ public boolean contains(double x, double y, double w, double h) {
+ throw new RuntimeException("Not implemented"); //$NON-NLS-1$
+ }
+
+ /**
+ * Tests does Rectangle2D lie inside MultiRectArea object
+ */
+ public boolean contains(Rectangle2D r) {
+ throw new RuntimeException("Not implemented"); //$NON-NLS-1$
+ }
+
+ /**
+ * Tests does rectangle intersect MultiRectArea object
+ */
+ public boolean intersects(double x, double y, double w, double h) {
+ Rectangle r = new Rectangle();
+ r.setRect(x, y, w, h);
+ return intersects(r);
+ }
+
+ /**
+ * Tests does Rectangle2D intersect MultiRectArea object
+ */
+ public boolean intersects(Rectangle2D r) {
+ if (r == null || r.isEmpty()) {
+ return false;
+ }
+ for(int i = 1; i < rect[0]; i+= 4) {
+ if (r.intersects(rect[i], rect[i+1], rect[i + 2]-rect[i]+1, rect[i + 3]-rect[i + 1]+1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns path iterator
+ */
+ public PathIterator getPathIterator(AffineTransform t, double flatness) {
+ return new Iterator(this, t);
+ }
+
+ /**
+ * Returns path iterator
+ */
+ public PathIterator getPathIterator(AffineTransform t) {
+ return new Iterator(this, t);
+ }
+
+ /**
+ * Returns MultiRectArea object converted to string
+ */
+ @Override
+ public String toString() {
+ int cnt = getRectCount();
+ StringBuffer sb = new StringBuffer((cnt << 5) + 128);
+ sb.append(getClass().getName()).append(" ["); //$NON-NLS-1$
+ for(int i = 1; i < rect[0]; i += 4) {
+ sb.append(i > 1 ? ", [" : "[").append(rect[i]).append(", ").append(rect[i + 1]). //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ append(", ").append(rect[i + 2] - rect[i] + 1).append(", "). //$NON-NLS-1$ //$NON-NLS-2$
+ append(rect[i + 3] - rect[i + 1] + 1).append("]"); //$NON-NLS-1$
+ }
+ return sb.append("]").toString(); //$NON-NLS-1$
+ }
+
+}
+
diff --git a/awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java b/awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java
new file mode 100644
index 0000000..c75e203
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java
@@ -0,0 +1,837 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Rectangle;
+
+public class MultiRectAreaOp {
+
+ /**
+ * Rectangle buffer capacity
+ */
+ public static final int RECT_CAPACITY = 16;
+
+ /**
+ * If number of rectangle in MultiRectArea object less than MAX_SIMPLE simple algorithm applies
+ */
+ private static final int MAX_SIMPLE = 8;
+
+ /**
+ * Create buffer
+ */
+ public static int[] createBuf(int capacity) {
+ if (capacity == 0) {
+ capacity = RECT_CAPACITY;
+ }
+ int[] buf = new int[capacity];
+ buf[0] = 1;
+ return buf;
+ }
+
+ /**
+ * Checks buffer size and reallocate if necessary
+ */
+ public static int[] checkBufSize(int[] buf, int capacity) {
+ if (buf[0] + capacity >= buf.length) {
+ int length = buf[0] + (capacity > RECT_CAPACITY ? capacity : RECT_CAPACITY);
+ int[] tmp = new int[length];
+ System.arraycopy(buf, 0, tmp, 0, buf[0]);
+ buf = tmp;
+ }
+ buf[0] += capacity;
+ return buf;
+ }
+
+ /**
+ * Region class provides basic functionlity for MultiRectArea objects to make logical operations
+ */
+ static class Region {
+
+ int[] region;
+ int[] active;
+ int[] bottom;
+ int index;
+
+ public Region(int[] region) {
+ this.region = region;
+ active = new int[RECT_CAPACITY];
+ bottom = new int[RECT_CAPACITY];
+ active[0] = 1;
+ bottom[0] = 1;
+ index = 1;
+ }
+
+ void addActive(int index) {
+ int length = active[0];
+ active = checkBufSize(active, 4);
+ int i = 1;
+
+ while(i < length) {
+ if (region[index] < active[i]) {
+ // Insert
+ System.arraycopy(active, i, active, i + 4, length - i);
+ length = i;
+ break;
+ }
+ i += 4;
+ }
+ System.arraycopy(region, index, active, length, 4);
+
+ }
+
+ void findActive(int top, int bottom) {
+ while(index < region[0]) {
+ if (region[index + 1] > bottom) { // y1 > bottom
+ return;
+ }
+ if (region[index + 3] >= top) { // y2 >= top
+ addActive(index);
+ }
+ index += 4;
+ }
+ }
+
+ void deleteActive(int bottom) {
+ int length = active[0];
+ for(int i = 1; i < length;) {
+ if (active[i + 3] == bottom) {
+ length -= 4;
+ if (i < length) {
+ System.arraycopy(active, i + 4, active, i, length - i);
+ }
+ } else {
+ i += 4;
+ }
+ }
+ active[0] = length;
+ }
+
+ void deleteActive() {
+ int length = active[0];
+ for(int i = length - 4; i > 0; i -= 4) {
+ if (active[i + 1] > active[i + 3]) {
+ length -= 4;
+ if (i < length) {
+ System.arraycopy(active, i + 4, active, i, length - i);
+ }
+ }
+ }
+ active[0] = length;
+ }
+
+ void createLevel(int[] level) {
+ int levelCount = 1;
+ int topIndex = 1;
+ int i = 1;
+ while(i < region[0]) {
+
+ int top = region[i + 1];
+ int bottom = region[i + 3] + 1;
+ int j = topIndex;
+
+ addTop: {
+ while(j < levelCount) {
+ if (level[j] == top) {
+ break addTop;
+ }
+ if (level[j] > top) {
+ System.arraycopy(level, j, level, j + 1, levelCount - j);
+ break;
+ }
+ j++;
+ }
+
+ level[j] = top;
+ levelCount++;
+ topIndex = j;
+ }
+
+ addBottom: {
+ while(j < levelCount) {
+ if (level[j] == bottom) {
+ break addBottom;
+ }
+ if (level[j] > bottom) {
+ System.arraycopy(level, j, level, j + 1, levelCount - j);
+ break;
+ }
+ j++;
+ };
+
+ level[j] = bottom;
+ levelCount++;
+ }
+
+ i += 4;
+ }
+ level[0] = levelCount;
+ }
+
+ static void sortOrdered(int[] src1, int[] src2, int[] dst) {
+ int length1 = src1[0];
+ int length2 = src2[0];
+ int count = 1;
+ int i1 = 1;
+ int i2 = 1;
+ int v1 = src1[1];
+ int v2 = src2[1];
+ while(true) {
+
+ LEFT: {
+ while(i1 < length1) {
+ v1 = src1[i1];
+ if (v1 >= v2) {
+ break LEFT;
+ }
+ dst[count++] = v1;
+ i1++;
+ }
+ while(i2 < length2) {
+ dst[count++] = src2[i2++];
+ }
+ dst[0] = count;
+ return;
+ }
+
+ RIGHT: {
+ while(i2 < length2) {
+ v2 = src2[i2];
+ if (v2 >= v1) {
+ break RIGHT;
+ }
+ dst[count++] = v2;
+ i2++;
+ }
+ while(i1 < length1) {
+ dst[count++] = src1[i1++];
+ }
+ dst[0] = count;
+ return;
+ }
+
+ if (v1 == v2) {
+ dst[count++] = v1;
+ i1++;
+ i2++;
+ if (i1 < length1) {
+ v1 = src1[i1];
+ }
+ if (i2 < length2 - 1) {
+ v2 = src2[i2];
+ }
+ }
+ }
+ // UNREACHABLE
+ }
+
+ }
+
+ /**
+ * Intersection class provides intersection of two MultiRectAre aobjects
+ */
+ static class Intersection {
+
+ static void intersectRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) {
+
+ Region d1 = new Region(reg1);
+ Region d2 = new Region(reg2);
+
+ int[] level = new int[height1 + height2];
+ int[] level1 = new int[height1];
+ int[] level2 = new int[height2];
+ d1.createLevel(level1);
+ d2.createLevel(level2);
+ Region.sortOrdered(level1, level2, level);
+
+ int top;
+ int bottom = level[1] - 1;
+ for(int i = 2; i < level[0]; i++) {
+
+ top = bottom + 1;
+ bottom = level[i] - 1;
+
+ d1.findActive(top, bottom);
+ d2.findActive(top, bottom);
+
+ int i1 = 1;
+ int i2 = 1;
+
+ while(i1 < d1.active[0] && i2 < d2.active[0]) {
+
+ int x11 = d1.active[i1];
+ int x12 = d1.active[i1 + 2];
+ int x21 = d2.active[i2];
+ int x22 = d2.active[i2 + 2];
+
+ if (x11 <= x21) {
+ if (x12 >= x21) {
+ if (x12 <= x22) {
+ dst.addRectCashed(x21, top, x12, bottom);
+ i1 += 4;
+ } else {
+ dst.addRectCashed(x21, top, x22, bottom);
+ i2 += 4;
+ }
+ } else {
+ i1 += 4;
+ }
+ } else {
+ if (x22 >= x11) {
+ if (x22 <= x12) {
+ dst.addRectCashed(x11, top, x22, bottom);
+ i2 += 4;
+ } else {
+ dst.addRectCashed(x11, top, x12, bottom);
+ i1 += 4;
+ }
+ } else {
+ i2 += 4;
+ }
+ }
+ }
+
+ d1.deleteActive(bottom);
+ d2.deleteActive(bottom);
+ }
+ }
+
+ static int[] simpleIntersect(MultiRectArea src1, MultiRectArea src2) {
+ int[] rect1 = src1.rect;
+ int[] rect2 = src2.rect;
+ int[] rect = createBuf(0);
+
+ int k = 1;
+ for(int i = 1; i < rect1[0];) {
+
+ int x11 = rect1[i++];
+ int y11 = rect1[i++];
+ int x12 = rect1[i++];
+ int y12 = rect1[i++];
+
+ for(int j = 1; j < rect2[0];) {
+
+ int x21 = rect2[j++];
+ int y21 = rect2[j++];
+ int x22 = rect2[j++];
+ int y22 = rect2[j++];
+
+ if (x11 <= x22 && x12 >= x21 &&
+ y11 <= y22 && y12 >= y21)
+ {
+ rect = checkBufSize(rect, 4);
+ rect[k++] = x11 > x21 ? x11 : x21;
+ rect[k++] = y11 > y21 ? y11 : y21;
+ rect[k++] = x12 > x22 ? x22 : x12;
+ rect[k++] = y12 > y22 ? y22 : y12;
+ }
+ }
+ }
+
+ rect[0] = k;
+ return rect;
+ }
+
+ public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) {
+
+ if (src1 == null || src2 == null || src1.isEmpty() || src2.isEmpty()) {
+ return new MultiRectArea();
+ }
+
+ MultiRectArea.RectCash dst = new MultiRectArea.RectCash();
+
+ if (!src1.sorted || !src2.sorted ||
+ src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE)
+ {
+ dst.setRect(simpleIntersect(src1, src2), false);
+ } else {
+ Rectangle bounds1 = src1.getBounds();
+ Rectangle bounds2 = src2.getBounds();
+ Rectangle bounds3 = bounds1.intersection(bounds2);
+ if (bounds3.width > 0 && bounds3.height > 0) {
+ intersectRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2);
+ }
+ }
+
+ return dst;
+ }
+
+ }
+
+ /**
+ * Union class provides union of two MultiRectAre aobjects
+ */
+ static class Union {
+
+ int rx1, rx2;
+ int top, bottom;
+ MultiRectArea.RectCash dst;
+
+ boolean next(Region d, int index) {
+ int x1 = d.active[index];
+ int x2 = d.active[index + 2];
+ boolean res = false;
+
+ if (x2 < rx1 - 1) {
+ res = true;
+ dst.addRectCashed(x1, top, x2, bottom);
+ } else
+ if (x1 > rx2 + 1) {
+ res = false;
+ dst.addRectCashed(rx1, top, rx2, bottom);
+ rx1 = x1;
+ rx2 = x2;
+ } else {
+ res = x2 <= rx2;
+ rx1 = Math.min(x1, rx1);
+ rx2 = Math.max(x2, rx2);
+ }
+
+ // Top
+ if (d.active[index + 1] < top) {
+ dst.addRectCashed(x1, d.active[index + 1], x2, top - 1);
+ }
+ // Bottom
+ if (d.active[index + 3] > bottom) {
+ d.active[index + 1] = bottom + 1;
+ }
+ return res;
+ }
+
+ void check(Region d, int index, boolean t) {
+ int x1 = d.active[index];
+ int x2 = d.active[index + 2];
+ // Top
+ if (d.active[index + 1] < top) {
+ dst.addRectCashed(x1, d.active[index + 1], x2, top - 1);
+ }
+ if (t) {
+ dst.addRectCashed(x1, top, x2, bottom);
+ }
+ // Bottom
+ if (d.active[index + 3] > bottom) {
+ d.active[index + 1] = bottom + 1;
+ }
+ }
+
+ void unionRegions(int[] reg1, int[] reg2, int height1, int height2) {
+ Region d1 = new Region(reg1);
+ Region d2 = new Region(reg2);
+
+ int[] level = new int[height1 + height2];
+ int[] level1 = new int[height1];
+ int[] level2 = new int[height2];
+ d1.createLevel(level1);
+ d2.createLevel(level2);
+ Region.sortOrdered(level1, level2, level);
+
+ bottom = level[1] - 1;
+ for(int i = 2; i < level[0]; i++) {
+
+ top = bottom + 1;
+ bottom = level[i] - 1;
+
+ d1.findActive(top, bottom);
+ d2.findActive(top, bottom);
+
+ int i1 = 1;
+ int i2 = 1;
+ boolean res1, res2;
+
+ if (d1.active[0] > 1) {
+ check(d1, 1, false);
+ rx1 = d1.active[1];
+ rx2 = d1.active[3];
+ i1 += 4;
+ res1 = false;
+ res2 = true;
+ } else
+ if (d2.active[0] > 1) {
+ check(d2, 1, false);
+ rx1 = d2.active[1];
+ rx2 = d2.active[3];
+ i2 += 4;
+ res1 = true;
+ res2 = false;
+ } else {
+ continue;
+ }
+
+ outer:
+ while(true) {
+
+ while (res1) {
+ if (i1 >= d1.active[0]) {
+ dst.addRectCashed(rx1, top, rx2, bottom);
+ while(i2 < d2.active[0]) {
+ check(d2, i2, true);
+ i2 += 4;
+ }
+ break outer;
+ }
+ res1 = next(d1, i1);
+ i1 += 4;
+ }
+
+ while (res2) {
+ if (i2 >= d2.active[0]) {
+ dst.addRectCashed(rx1, top, rx2, bottom);
+ while(i1 < d1.active[0]) {
+ check(d1, i1, true);
+ i1 += 4;
+ }
+ break outer;
+ }
+ res2 = next(d2, i2);
+ i2 += 4;
+ }
+
+ res1 = true;
+ res2 = true;
+ } // while
+
+ d1.deleteActive(bottom);
+ d2.deleteActive(bottom);
+
+ }
+ }
+
+ static void simpleUnion(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) {
+ if (src1.getRectCount() < src2.getRectCount()) {
+ simpleUnion(src2, src1, dst);
+ } else {
+ Subtraction.simpleSubtract(src1, src2, dst);
+ int pos = dst.rect[0];
+ int size = src2.rect[0] - 1;
+ dst.rect = checkBufSize(dst.rect, size);
+ System.arraycopy(src2.rect,1, dst.rect, pos, size);
+ dst.resort();
+ }
+ }
+
+ MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) {
+
+ if (src1 == null || src1.isEmpty()) {
+ return new MultiRectArea(src2);
+ }
+
+ if (src2 == null || src2.isEmpty()) {
+ return new MultiRectArea(src1);
+ }
+
+ dst = new MultiRectArea.RectCash();
+
+ if (!src1.sorted || !src2.sorted ||
+ src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE)
+ {
+ simpleUnion(src1, src2, dst);
+ } else {
+ Rectangle bounds1 = src1.getBounds();
+ Rectangle bounds2 = src2.getBounds();
+ Rectangle bounds3 = bounds1.intersection(bounds2);
+
+ if (bounds3.width < 0 || bounds3.height < 0) {
+ if (bounds1.y + bounds1.height < bounds2.y) {
+ dst.setRect(addVerRegion(src1.rect, src2.rect), false);
+ } else
+ if (bounds2.y + bounds2.height < bounds1.y) {
+ dst.setRect(addVerRegion(src2.rect, src1.rect), false);
+ } else
+ if (bounds1.x < bounds2.x) {
+ dst.setRect(addHorRegion(src1.rect, src2.rect), false);
+ } else {
+ dst.setRect(addHorRegion(src2.rect, src1.rect), false);
+ }
+ } else {
+ unionRegions(src1.rect, src2.rect, bounds1.height + 2, bounds2.height + 2);
+ }
+ }
+
+ return dst;
+ }
+
+ int[] addVerRegion(int[] top, int[] bottom) {
+ int length = top[0] + bottom[0] - 1;
+ int[] dst = new int[length];
+ dst[0] = length;
+ System.arraycopy(top, 1, dst, 1, top[0] - 1);
+ System.arraycopy(bottom, 1, dst, top[0], bottom[0] - 1);
+ return dst;
+ }
+
+ int[] addHorRegion(int[] left, int[] right) {
+ int count1 = left[0];
+ int count2 = right[0];
+ int[] dst = new int[count1 + count2 + 1];
+ int count = 1;
+ int index1 = 1;
+ int index2 = 1;
+
+ int top1 = left[2];
+ int top2 = right[2];
+ int pos1, pos2;
+
+ while(true) {
+
+ if (index1 >= count1) {
+ System.arraycopy(right, index2, dst, count, count2 - index2);
+ count += count2 - index2;
+ break;
+ }
+ if (index2 >= count2) {
+ System.arraycopy(left, index1, dst, count, count1 - index1);
+ count += count1 - index1;
+ break;
+ }
+
+ if (top1 < top2) {
+ pos1 = index1;
+ do {
+ index1 += 4;
+ } while (index1 < count1 && (top1 = left[index1 + 1]) < top2);
+ System.arraycopy(left, pos1, dst, count, index1 - pos1);
+ count += index1 - pos1;
+ continue;
+ }
+
+ if (top1 > top2) {
+ pos2 = index2;
+ do {
+ index2 += 4;
+ } while (index2 < count2 && (top2 = right[index2 + 1]) < top1);
+ System.arraycopy(right, pos2, dst, count, index2 - pos2);
+ count += index2 - pos2;
+ continue;
+ }
+
+ int top = top1;
+ pos1 = index1;
+ pos2 = index2;
+ do {
+ index1 += 4;
+ } while(index1 < count1 && (top1 = left[index1 + 1]) == top);
+ do {
+ index2 += 4;
+ } while(index2 < count2 && (top2 = right[index2 + 1]) == top);
+
+ System.arraycopy(left, pos1, dst, count, index1 - pos1);
+ count += index1 - pos1;
+ System.arraycopy(right, pos2, dst, count, index2 - pos2);
+ count += index2 - pos2;
+ }
+
+ dst[0] = count;
+ return dst;
+ }
+
+ }
+
+ /**
+ * Subtraction class provides subtraction of two MultiRectAre aobjects
+ */
+ static class Subtraction {
+
+ static void subtractRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) {
+ Region d1 = new Region(reg1);
+ Region d2 = new Region(reg2);
+
+ int[] level = new int[height1 + height2];
+ int[] level1 = new int[height1];
+ int[] level2 = new int[height2];
+ d1.createLevel(level1);
+ d2.createLevel(level2);
+ Region.sortOrdered(level1, level2, level);
+
+ int top;
+ int bottom = level[1] - 1;
+ for(int i = 2; i < level[0]; i++) {
+
+ top = bottom + 1;
+ bottom = level[i] - 1;
+
+ d1.findActive(top, bottom);
+ if (d1.active[0] == 1) {
+ d2.deleteActive(bottom);
+ continue;
+ }
+
+ d2.findActive(top, bottom);
+
+ int i1 = 1;
+ int i2 = 1;
+
+ int rx1 = 0;
+ int rx2 = 0;
+
+ boolean next = true;
+
+ while(true) {
+
+ if (next) {
+ next = false;
+ if (i1 >= d1.active[0]) {
+ break;
+ }
+ // Bottom
+ d1.active[i1 + 1] = bottom + 1;
+ rx1 = d1.active[i1];
+ rx2 = d1.active[i1 + 2];
+ i1 += 4;
+ }
+
+ if (i2 >= d2.active[0]) {
+ dst.addRectCashed(rx1, top, rx2, bottom);
+ for(int j = i1; j < d1.active[0]; j += 4) {
+ dst.addRectCashed(d1.active[j], top, d1.active[j + 2], bottom);
+ d1.active[j + 1] = bottom + 1;
+ }
+ break;
+ }
+
+ int x1 = d2.active[i2];
+ int x2 = d2.active[i2 + 2];
+
+ if (rx1 < x1) {
+ if (rx2 >= x1) {
+ if (rx2 <= x2) {
+ // [-----------]
+ // [-------------]
+ dst.addRectCashed(rx1, top, x1 - 1, bottom);
+ next = true;
+ } else {
+ // [-----------------]
+ // [------]
+ dst.addRectCashed(rx1, top, x1 - 1, bottom);
+ rx1 = x2 + 1;
+ i2 += 4;
+ }
+ } else {
+ // [-----]
+ // [----]
+ dst.addRectCashed(rx1, top, rx2, bottom);
+ next = true;
+ }
+ } else {
+ if (rx1 <= x2) {
+ if (rx2 <= x2) {
+ // [------]
+ // [-----------]
+ next = true;
+ } else {
+ // [------------]
+ // [---------]
+ rx1 = x2 + 1;
+ i2 += 4;
+ }
+ } else {
+ // [----]
+ // [-----]
+ i2 += 4;
+ }
+ }
+
+ }
+ d1.deleteActive();
+ d2.deleteActive(bottom);
+ }
+ }
+
+ static void subtractRect(int x11, int y11, int x12, int y12, int[] rect, int index, MultiRectArea dst) {
+
+ for(int i = index; i < rect[0]; i += 4) {
+ int x21 = rect[i + 0];
+ int y21 = rect[i + 1];
+ int x22 = rect[i + 2];
+ int y22 = rect[i + 3];
+
+ if (x11 <= x22 && x12 >= x21 && y11 <= y22 && y12 >= y21) {
+ int top, bottom;
+ if (y11 < y21) {
+ subtractRect(x11, y11, x12, y21 - 1, rect, i + 4, dst);
+ top = y21;
+ } else {
+ top = y11;
+ }
+ if (y12 > y22) {
+ subtractRect(x11, y22 + 1, x12, y12, rect, i + 4, dst);
+ bottom = y22;
+ } else {
+ bottom = y12;
+ }
+ if (x11 < x21) {
+ subtractRect(x11, top, x21 - 1, bottom, rect, i + 4, dst);
+ }
+ if (x12 > x22) {
+ subtractRect(x22 + 1, top, x12, bottom, rect, i + 4, dst);
+ }
+ return;
+ }
+ }
+ dst.addRect(x11, y11, x12, y12);
+ }
+
+ static void simpleSubtract(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) {
+ for(int i = 1; i < src1.rect[0]; i += 4) {
+ subtractRect(
+ src1.rect[i + 0],
+ src1.rect[i + 1],
+ src1.rect[i + 2],
+ src1.rect[i + 3],
+ src2.rect,
+ 1,
+ dst);
+ }
+ dst.resort();
+ }
+
+ public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) {
+
+ if (src1 == null || src1.isEmpty()) {
+ return new MultiRectArea();
+ }
+
+ if (src2 == null || src2.isEmpty()) {
+ return new MultiRectArea(src1);
+ }
+
+ MultiRectArea.RectCash dst = new MultiRectArea.RectCash();
+
+ if (!src1.sorted || !src2.sorted ||
+ src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE)
+ {
+ simpleSubtract(src1, src2, dst);
+ } else {
+ Rectangle bounds1 = src1.getBounds();
+ Rectangle bounds2 = src2.getBounds();
+ Rectangle bounds3 = bounds1.intersection(bounds2);
+
+ if (bounds3.width > 0 && bounds3.height > 0) {
+ subtractRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2);
+ } else {
+ dst.setRect(src1.rect, true);
+ }
+ }
+
+ return dst;
+ }
+
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/Surface.java b/awt/org/apache/harmony/awt/gl/Surface.java
new file mode 100644
index 0000000..8b0ae38
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/Surface.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 10.11.2005
+ *
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Image;
+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.DirectColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.util.ArrayList;
+
+import org.apache.harmony.awt.gl.color.LUTColorConverter;
+
+
+/**
+ * This class is super class for others types of Surfaces.
+ * Surface is storing data and data format description, that are using
+ * in blitting operations
+ */
+public abstract class Surface implements Transparency{
+
+ // Color Space Types
+ public static final int sRGB_CS = 1;
+ public static final int Linear_RGB_CS = 2;
+ public static final int Linear_Gray_CS = 3;
+ public static final int Custom_CS = 0;
+
+ // Color Model Types
+ public static final int DCM = 1; // Direct Color Model
+ public static final int ICM = 2; // Index Color Model
+ public static final int CCM = 3; // Component Color Model
+
+ // Sample Model Types
+ public static final int SPPSM = 1; // Single Pixel Packed Sample Model
+ public static final int MPPSM = 2; // Multi Pixel Packed Sample Model
+ public static final int CSM = 3; // Component Sample Model
+ public static final int PISM = 4; // Pixel Interleaved Sample Model
+ public static final int BSM = 5; // Banded Sample Model
+
+ // Surface Types
+ private static final int ALPHA_MASK = 0xff000000;
+ private static final int RED_MASK = 0x00ff0000;
+ private static final int GREEN_MASK = 0x0000ff00;
+ private static final int BLUE_MASK = 0x000000ff;
+ private static final int RED_BGR_MASK = 0x000000ff;
+ private static final int GREEN_BGR_MASK = 0x0000ff00;
+ private static final int BLUE_BGR_MASK = 0x00ff0000;
+ private static final int RED_565_MASK = 0xf800;
+ private static final int GREEN_565_MASK = 0x07e0;
+ private static final int BLUE_565_MASK = 0x001f;
+ private static final int RED_555_MASK = 0x7c00;
+ private static final int GREEN_555_MASK = 0x03e0;
+ private static final int BLUE_555_MASK = 0x001f;
+
+ static{
+ //???AWT
+ /*
+ System.loadLibrary("gl"); //$NON-NLS-1$
+ initIDs();
+ */
+ }
+
+
+ protected long surfaceDataPtr; // Pointer for Native Surface data
+ protected int transparency = OPAQUE;
+ protected int width;
+ protected int height;
+
+ /**
+ * This list contains caches with the data of this surface that are valid at the moment.
+ * Surface should clear this list when its data is updated.
+ * Caches may check if they are still valid using isCacheValid method.
+ * When cache gets data from the surface, it should call addValidCache method of the surface.
+ */
+ private final ArrayList<Object> validCaches = new ArrayList<Object>();
+
+ public abstract ColorModel getColorModel();
+ public abstract WritableRaster getRaster();
+ public abstract int getSurfaceType(); // Syrface type. It is equal
+ // BufferedImge type
+ /**
+ * Lock Native Surface data
+ */
+ public abstract long lock();
+
+ /**
+ * Unlock Native Surface data
+ */
+ public abstract void unlock();
+
+ /**
+ * Dispose Native Surface data
+ */
+ public abstract void dispose();
+ public abstract Surface getImageSurface();
+
+ public long getSurfaceDataPtr(){
+ return surfaceDataPtr;
+ }
+
+ public final boolean isCaheValid(Object cache) {
+ return validCaches.contains(cache);
+ }
+
+ public final void addValidCache(Object cache) {
+ validCaches.add(cache);
+ }
+
+ protected final void clearValidCaches() {
+ validCaches.clear();
+ }
+
+ /**
+ * Returns could or coldn't the Surface be blit by Native blitter
+ * @return - true if the Surface could be blit by Native blitter,
+ * false in other case
+ */
+ public boolean isNativeDrawable(){
+ return true;
+ }
+
+ public int getTransparency() {
+ return transparency;
+ }
+
+ public int getWidth(){
+ return width;
+ }
+
+ public int getHeight(){
+ return height;
+ }
+
+ /**
+ * If Surface has Raster, this method returns data array of Raster's DataBuffer
+ * @return - data array
+ */
+ public Object getData(){
+ return null;
+ }
+
+ public boolean invalidated(){
+ return true;
+ }
+
+ public void validate(){}
+
+ public void invalidate(){}
+
+ /**
+ * Computation type of BufferedImage or Surface
+ * @param cm - ColorModel
+ * @param raster - WritableRaste
+ * @return - type of BufferedImage
+ */
+ public static int getType(ColorModel cm, WritableRaster raster){
+ int transferType = cm.getTransferType();
+ boolean hasAlpha = cm.hasAlpha();
+ ColorSpace cs = cm.getColorSpace();
+ int csType = cs.getType();
+ SampleModel sm = raster.getSampleModel();
+
+ if(csType == ColorSpace.TYPE_RGB){
+ if(cm instanceof DirectColorModel){
+ DirectColorModel dcm = (DirectColorModel) cm;
+ switch (transferType) {
+ case DataBuffer.TYPE_INT:
+ if (dcm.getRedMask() == RED_MASK &&
+ dcm.getGreenMask() == GREEN_MASK &&
+ dcm.getBlueMask() == BLUE_MASK) {
+ if (!hasAlpha) {
+ return BufferedImage.TYPE_INT_RGB;
+ }
+ if (dcm.getAlphaMask() == ALPHA_MASK) {
+ if (dcm.isAlphaPremultiplied()) {
+ return BufferedImage.TYPE_INT_ARGB_PRE;
+ }
+ return BufferedImage.TYPE_INT_ARGB;
+ }
+ return BufferedImage.TYPE_CUSTOM;
+ } else if (dcm.getRedMask() == RED_BGR_MASK &&
+ dcm.getGreenMask() == GREEN_BGR_MASK &&
+ dcm.getBlueMask() == BLUE_BGR_MASK) {
+ if (!hasAlpha) {
+ return BufferedImage.TYPE_INT_BGR;
+ }
+ } else {
+ return BufferedImage.TYPE_CUSTOM;
+ }
+ case DataBuffer.TYPE_USHORT:
+ if (dcm.getRedMask() == RED_555_MASK &&
+ dcm.getGreenMask() == GREEN_555_MASK &&
+ dcm.getBlueMask() == BLUE_555_MASK && !hasAlpha) {
+ return BufferedImage.TYPE_USHORT_555_RGB;
+ } else if (dcm.getRedMask() == RED_565_MASK &&
+ dcm.getGreenMask() == GREEN_565_MASK &&
+ dcm.getBlueMask() == BLUE_565_MASK) {
+ return BufferedImage.TYPE_USHORT_565_RGB;
+ }
+ default:
+ return BufferedImage.TYPE_CUSTOM;
+ }
+ }else if(cm instanceof IndexColorModel){
+ IndexColorModel icm = (IndexColorModel) cm;
+ int pixelBits = icm.getPixelSize();
+ if(transferType == DataBuffer.TYPE_BYTE){
+ if(sm instanceof MultiPixelPackedSampleModel && !hasAlpha &&
+ pixelBits < 5){
+ return BufferedImage.TYPE_BYTE_BINARY;
+ }else if(pixelBits == 8){
+ return BufferedImage.TYPE_BYTE_INDEXED;
+ }
+ }
+ return BufferedImage.TYPE_CUSTOM;
+ }else if(cm instanceof ComponentColorModel){
+ ComponentColorModel ccm = (ComponentColorModel) cm;
+ if(transferType == DataBuffer.TYPE_BYTE &&
+ sm instanceof ComponentSampleModel){
+ ComponentSampleModel csm =
+ (ComponentSampleModel) sm;
+ int[] offsets = csm.getBandOffsets();
+ int[] bits = ccm.getComponentSize();
+ boolean isCustom = false;
+ for (int i = 0; i < bits.length; i++) {
+ if (bits[i] != 8 ||
+ offsets[i] != offsets.length - 1 - i) {
+ isCustom = true;
+ break;
+ }
+ }
+ if (!isCustom) {
+ if (!ccm.hasAlpha()) {
+ return BufferedImage.TYPE_3BYTE_BGR;
+ } else if (ccm.isAlphaPremultiplied()) {
+ return BufferedImage.TYPE_4BYTE_ABGR_PRE;
+ } else {
+ return BufferedImage.TYPE_4BYTE_ABGR;
+ }
+ }
+ }
+ return BufferedImage.TYPE_CUSTOM;
+ }
+ return BufferedImage.TYPE_CUSTOM;
+ }else if(cs == LUTColorConverter.LINEAR_GRAY_CS){
+ if(cm instanceof ComponentColorModel &&
+ cm.getNumComponents() == 1){
+ int bits[] = cm.getComponentSize();
+ if(transferType == DataBuffer.TYPE_BYTE &&
+ bits[0] == 8){
+ return BufferedImage.TYPE_BYTE_GRAY;
+ }else if(transferType == DataBuffer.TYPE_USHORT &&
+ bits[0] == 16){
+ return BufferedImage.TYPE_USHORT_GRAY;
+ }else{
+ return BufferedImage.TYPE_CUSTOM;
+ }
+ }
+ return BufferedImage.TYPE_CUSTOM;
+ }
+ return BufferedImage.TYPE_CUSTOM;
+ }
+
+ public static Surface getImageSurface(Image image){
+ return AwtImageBackdoorAccessor.getInstance().getImageSurface(image);
+ }
+
+ @Override
+ protected void finalize() throws Throwable{
+ dispose();
+ }
+
+ public static boolean isGrayPallete(IndexColorModel icm){
+ return AwtImageBackdoorAccessor.getInstance().isGrayPallete(icm);
+ }
+
+ /**
+ * Initialization of Native data
+ *
+ */
+ //???AWT: private static native void initIDs();
+}
diff --git a/awt/org/apache/harmony/awt/gl/TextRenderer.java b/awt/org/apache/harmony/awt/gl/TextRenderer.java
new file mode 100644
index 0000000..f57952d
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/TextRenderer.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Graphics2D;
+import java.awt.font.GlyphVector;
+
+public abstract class TextRenderer {
+
+ /**
+ * Draws string on specified Graphics at desired position.
+ *
+ * @param g specified Graphics2D object
+ * @param str String object to draw
+ * @param x start X position to draw
+ * @param y start Y position to draw
+ */
+ public abstract void drawString(Graphics2D g, String str, float x, float y);
+
+ /**
+ * Draws string on specified Graphics at desired position.
+ *
+ * @param g specified Graphics2D object
+ * @param str String object to draw
+ * @param x start X position to draw
+ * @param y start Y position to draw
+ */
+ public void drawString(Graphics2D g, String str, int x, int y){
+ drawString(g, str, (float)x, (float)y);
+ }
+
+ /**
+ * Draws GlyphVector on specified Graphics at desired position.
+ *
+ * @param g specified Graphics2D object
+ * @param glyphVector GlyphVector object to draw
+ * @param x start X position to draw
+ * @param y start Y position to draw
+ */
+ public abstract void drawGlyphVector(Graphics2D g, GlyphVector glyphVector, float x, float y);
+}
diff --git a/awt/org/apache/harmony/awt/gl/XORComposite.java b/awt/org/apache/harmony/awt/gl/XORComposite.java
new file mode 100644
index 0000000..e27e1d3
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/XORComposite.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 21.11.2005
+ *
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.CompositeContext;
+import java.awt.RenderingHints;
+import java.awt.image.ColorModel;
+
+public class XORComposite implements Composite {
+
+ Color xorcolor;
+
+ public XORComposite(Color xorcolor){
+ this.xorcolor = xorcolor;
+ }
+
+ public CompositeContext createContext(ColorModel srcCM, ColorModel dstCM,
+ RenderingHints hints) {
+
+ return new ICompositeContext(this, srcCM, dstCM);
+ }
+
+ public Color getXORColor(){
+ return xorcolor;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/color/ColorConverter.java b/awt/org/apache/harmony/awt/gl/color/ColorConverter.java
new file mode 100644
index 0000000..c98e114
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/color/ColorConverter.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.color;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+
+/**
+ * This class combines ColorScaler, ICC_Transform and NativeImageFormat functionality
+ * in the workflows for different types of input/output pixel data.
+ */
+public class ColorConverter {
+ private ColorScaler scaler = new ColorScaler();
+
+ public void loadScalingData(ColorSpace cs) {
+ scaler.loadScalingData(cs);
+ }
+
+ /**
+ * Translates pixels, stored in source buffered image and writes the data
+ * to the destination image.
+ * @param t - ICC transform
+ * @param src - source image
+ * @param dst - destination image
+ */
+ public void translateColor(ICC_Transform t,
+ BufferedImage src, BufferedImage dst) {
+ NativeImageFormat srcIF = NativeImageFormat.createNativeImageFormat(src);
+ NativeImageFormat dstIF = NativeImageFormat.createNativeImageFormat(dst);
+
+ if (srcIF != null && dstIF != null) {
+ t.translateColors(srcIF, dstIF);
+ return;
+ }
+
+ srcIF = createImageFormat(src);
+ dstIF = createImageFormat(dst);
+
+ short srcChanData[] = (short[]) srcIF.getChannelData();
+ short dstChanData[] = (short[]) dstIF.getChannelData();
+
+ ColorModel srcCM = src.getColorModel();
+ int nColorChannels = srcCM.getNumColorComponents();
+ scaler.loadScalingData(srcCM.getColorSpace()); // input scaling data
+ ColorModel dstCM = dst.getColorModel();
+
+ // Prepare array for alpha channel
+ float alpha[] = null;
+ boolean saveAlpha = srcCM.hasAlpha() && dstCM.hasAlpha();
+ if (saveAlpha) {
+ alpha = new float[src.getWidth()*src.getHeight()];
+ }
+
+ WritableRaster wr = src.getRaster();
+ int srcDataPos = 0, alphaPos = 0;
+ float normalizedVal[];
+ for (int row=0, nRows = srcIF.getNumRows(); row<nRows; row++) {
+ for (int col=0, nCols = srcIF.getNumCols(); col<nCols; col++) {
+ normalizedVal = srcCM.getNormalizedComponents(
+ wr.getDataElements(col, row, null),
+ null, 0);
+ // Save alpha channel
+ if (saveAlpha) {
+ // We need nColorChannels'th element cause it's nChannels - 1
+ alpha[alphaPos++] = normalizedVal[nColorChannels];
+ }
+ scaler.scale(normalizedVal, srcChanData, srcDataPos);
+ srcDataPos += nColorChannels;
+ }
+ }
+
+ t.translateColors(srcIF, dstIF);
+
+ nColorChannels = dstCM.getNumColorComponents();
+ boolean fillAlpha = dstCM.hasAlpha();
+ scaler.loadScalingData(dstCM.getColorSpace()); // output scaling data
+ float dstPixel[] = new float[dstCM.getNumComponents()];
+ int dstDataPos = 0;
+ alphaPos = 0;
+ wr = dst.getRaster();
+
+ for (int row=0, nRows = dstIF.getNumRows(); row<nRows; row++) {
+ for (int col=0, nCols = dstIF.getNumCols(); col<nCols; col++) {
+ scaler.unscale(dstPixel, dstChanData, dstDataPos);
+ dstDataPos += nColorChannels;
+ if (fillAlpha) {
+ if (saveAlpha) {
+ dstPixel[nColorChannels] = alpha[alphaPos++];
+ } else {
+ dstPixel[nColorChannels] = 1f;
+ }
+ }
+ wr.setDataElements(col, row,
+ dstCM.getDataElements(dstPixel, 0 , null));
+ }
+ }
+ }
+
+ /**
+ * Translates pixels, stored in the float data buffer.
+ * Each pixel occupies separate array. Input pixels passed in the buffer
+ * are replaced by output pixels and then the buffer is returned
+ * @param t - ICC transform
+ * @param buffer - data buffer
+ * @param srcCS - source color space
+ * @param dstCS - destination color space
+ * @param nPixels - number of pixels
+ * @return translated pixels
+ */
+ public float[][] translateColor(ICC_Transform t,
+ float buffer[][],
+ ColorSpace srcCS,
+ ColorSpace dstCS,
+ int nPixels) {
+ // Scale source data
+ if (srcCS != null) { // if it is null use old scaling data
+ scaler.loadScalingData(srcCS);
+ }
+ int nSrcChannels = t.getNumInputChannels();
+ short srcShortData[] = new short[nPixels*nSrcChannels];
+ for (int i=0, srcDataPos = 0; i<nPixels; i++) {
+ scaler.scale(buffer[i], srcShortData, srcDataPos);
+ srcDataPos += nSrcChannels;
+ }
+
+ // Apply transform
+ short dstShortData[] = this.translateColor(t, srcShortData, null);
+
+ int nDstChannels = t.getNumOutputChannels();
+ int bufferSize = buffer[0].length;
+ if (bufferSize < nDstChannels + 1) { // Re-allocate buffer if needed
+ for (int i=0; i<nPixels; i++) {
+ // One extra element reserved for alpha
+ buffer[i] = new float[nDstChannels + 1];
+ }
+ }
+
+ // Unscale destination data
+ if (dstCS != null) { // if it is null use old scaling data
+ scaler.loadScalingData(dstCS);
+ }
+ for (int i=0, dstDataPos = 0; i<nPixels; i++) {
+ scaler.unscale(buffer[i], dstShortData, dstDataPos);
+ dstDataPos += nDstChannels;
+ }
+
+ return buffer;
+ }
+
+ /**
+ * Translates pixels stored in a raster.
+ * All data types are supported
+ * @param t - ICC transform
+ * @param src - source pixels
+ * @param dst - destination pixels
+ */
+ public void translateColor(ICC_Transform t, Raster src, WritableRaster dst) {
+ try{
+ NativeImageFormat srcFmt = NativeImageFormat.createNativeImageFormat(src);
+ NativeImageFormat dstFmt = NativeImageFormat.createNativeImageFormat(dst);
+
+ if (srcFmt != null && dstFmt != null) {
+ t.translateColors(srcFmt, dstFmt);
+ return;
+ }
+ } catch (IllegalArgumentException e) {
+ }
+
+ // Go ahead and rescale the source image
+ scaler.loadScalingData(src, t.getSrc());
+ short srcData[] = scaler.scale(src);
+
+ short dstData[] = translateColor(t, srcData, null);
+
+ scaler.loadScalingData(dst, t.getDst());
+ scaler.unscale(dstData, dst);
+ }
+
+ /**
+ * Translates pixels stored in an array of shorts.
+ * Samples are stored one-by-one, i.e. array structure is like following: RGBRGBRGB...
+ * The number of pixels is (size of the array) / (number of components).
+ * @param t - ICC transform
+ * @param src - source pixels
+ * @param dst - destination pixels
+ * @return destination pixels, stored in the array, passed in dst
+ */
+ public short[] translateColor(ICC_Transform t, short src[], short dst[]) {
+ NativeImageFormat srcFmt = createImageFormat(t, src, 0, true);
+ NativeImageFormat dstFmt = createImageFormat(t, dst, srcFmt.getNumCols(), false);
+
+ t.translateColors(srcFmt, dstFmt);
+
+ return (short[]) dstFmt.getChannelData();
+ }
+
+
+ /**
+ * Creates NativeImageFormat from buffered image.
+ * @param bi - buffered image
+ * @return created NativeImageFormat
+ */
+ private NativeImageFormat createImageFormat(BufferedImage bi) {
+ int nRows = bi.getHeight();
+ int nCols = bi.getWidth();
+ int nComps = bi.getColorModel().getNumColorComponents();
+ short imgData[] = new short[nRows*nCols*nComps];
+ return new NativeImageFormat(
+ imgData, nComps, nRows, nCols);
+ }
+
+ /**
+ * Creates one-row NativeImageFormat, using either nCols if it is positive,
+ * or arr.length to determine the number of pixels
+ *
+ * @param t - transform
+ * @param arr - short array or null if nCols is positive
+ * @param nCols - number of pixels in the array or 0 if array is not null
+ * @param in - is it an input or output array
+ * @return one-row NativeImageFormat
+ */
+ private NativeImageFormat createImageFormat(
+ ICC_Transform t, short arr[], int nCols, boolean in
+ ) {
+ int nComponents = in ? t.getNumInputChannels() : t.getNumOutputChannels();
+
+ if (arr == null || arr.length < nCols*nComponents) {
+ arr = new short[nCols*nComponents];
+ }
+
+ if (nCols == 0)
+ nCols = arr.length / nComponents;
+
+ return new NativeImageFormat(arr, nComponents, 1, nCols);
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/color/ColorScaler.java b/awt/org/apache/harmony/awt/gl/color/ColorScaler.java
new file mode 100644
index 0000000..a1cc169
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/color/ColorScaler.java
@@ -0,0 +1,355 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.color;
+
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+
+/**
+ * This class provides functionality for scaling color data when
+ * ranges of the source and destination color values differs.
+ */
+public class ColorScaler {
+ private static final float MAX_SHORT = 0xFFFF;
+ private static final float MAX_SIGNED_SHORT = 0x7FFF;
+
+ private static final float MAX_XYZ = 1f + (32767f/32768f);
+
+ // Cached values for scaling color data
+ private float[] channelMinValues = null;
+ private float[] channelMulipliers = null; // for scale
+ private float[] invChannelMulipliers = null; // for unscale
+
+ int nColorChannels = 0;
+
+ // For scaling rasters, false if transfer type is double or float
+ boolean isTTypeIntegral = false;
+
+ /**
+ * Loads scaling data for raster. Note, if profile pf is null,
+ * for non-integral data types multipliers are not initialized.
+ * @param r - raster
+ * @param pf - profile which helps to determine the ranges of the color data
+ */
+ public void loadScalingData(Raster r, ICC_Profile pf) {
+ boolean isSrcTTypeIntegral =
+ r.getTransferType() != DataBuffer.TYPE_FLOAT &&
+ r.getTransferType() != DataBuffer.TYPE_DOUBLE;
+ if (isSrcTTypeIntegral)
+ loadScalingData(r.getSampleModel());
+ else if (pf != null)
+ loadScalingData(pf);
+ }
+
+ /**
+ * Use this method only for integral transfer types.
+ * Extracts min/max values from the sample model
+ * @param sm - sample model
+ */
+ public void loadScalingData(SampleModel sm) {
+ // Supposing integral transfer type
+ isTTypeIntegral = true;
+
+ nColorChannels = sm.getNumBands();
+
+ channelMinValues = new float[nColorChannels];
+ channelMulipliers = new float[nColorChannels];
+ invChannelMulipliers = new float[nColorChannels];
+
+ boolean isSignedShort =
+ (sm.getTransferType() == DataBuffer.TYPE_SHORT);
+
+ float maxVal;
+ for (int i=0; i<nColorChannels; i++) {
+ channelMinValues[i] = 0;
+ if (isSignedShort) {
+ channelMulipliers[i] = MAX_SHORT / MAX_SIGNED_SHORT;
+ invChannelMulipliers[i] = MAX_SIGNED_SHORT / MAX_SHORT;
+ } else {
+ maxVal = ((1 << sm.getSampleSize(i)) - 1);
+ channelMulipliers[i] = MAX_SHORT / maxVal;
+ invChannelMulipliers[i] = maxVal / MAX_SHORT;
+ }
+ }
+ }
+
+ /**
+ * Use this method only for double of float transfer types.
+ * Extracts scaling data from the color space signature
+ * and other tags, stored in the profile
+ * @param pf - ICC profile
+ */
+ public void loadScalingData(ICC_Profile pf) {
+ // Supposing double or float transfer type
+ isTTypeIntegral = false;
+
+ nColorChannels = pf.getNumComponents();
+
+ // Get min/max values directly from the profile
+ // Very much like fillMinMaxValues in ICC_ColorSpace
+ float maxValues[] = new float[nColorChannels];
+ float minValues[] = new float[nColorChannels];
+
+ switch (pf.getColorSpaceType()) {
+ case ColorSpace.TYPE_XYZ:
+ minValues[0] = 0;
+ minValues[1] = 0;
+ minValues[2] = 0;
+ maxValues[0] = MAX_XYZ;
+ maxValues[1] = MAX_XYZ;
+ maxValues[2] = MAX_XYZ;
+ break;
+ case ColorSpace.TYPE_Lab:
+ minValues[0] = 0;
+ minValues[1] = -128;
+ minValues[2] = -128;
+ maxValues[0] = 100;
+ maxValues[1] = 127;
+ maxValues[2] = 127;
+ break;
+ default:
+ for (int i=0; i<nColorChannels; i++) {
+ minValues[i] = 0;
+ maxValues[i] = 1;
+ }
+ }
+
+ channelMinValues = minValues;
+ channelMulipliers = new float[nColorChannels];
+ invChannelMulipliers = new float[nColorChannels];
+
+ for (int i = 0; i < nColorChannels; i++) {
+ channelMulipliers[i] =
+ MAX_SHORT / (maxValues[i] - channelMinValues[i]);
+
+ invChannelMulipliers[i] =
+ (maxValues[i] - channelMinValues[i]) / MAX_SHORT;
+ }
+ }
+
+ /**
+ * Extracts scaling data from the color space
+ * @param cs - color space
+ */
+ public void loadScalingData(ColorSpace cs) {
+ nColorChannels = cs.getNumComponents();
+
+ channelMinValues = new float[nColorChannels];
+ channelMulipliers = new float[nColorChannels];
+ invChannelMulipliers = new float[nColorChannels];
+
+ for (int i = 0; i < nColorChannels; i++) {
+ channelMinValues[i] = cs.getMinValue(i);
+
+ channelMulipliers[i] =
+ MAX_SHORT / (cs.getMaxValue(i) - channelMinValues[i]);
+
+ invChannelMulipliers[i] =
+ (cs.getMaxValue(i) - channelMinValues[i]) / MAX_SHORT;
+ }
+ }
+
+ /**
+ * Scales and normalizes the whole raster and returns the result
+ * in the float array
+ * @param r - source raster
+ * @return scaled and normalized raster data
+ */
+ public float[][] scaleNormalize(Raster r) {
+ int width = r.getWidth();
+ int height = r.getHeight();
+ float result[][] = new float[width*height][nColorChannels];
+ float normMultipliers[] = new float[nColorChannels];
+
+ int pos = 0;
+ if (isTTypeIntegral) {
+ // Change max value from MAX_SHORT to 1f
+ for (int i=0; i<nColorChannels; i++) {
+ normMultipliers[i] = channelMulipliers[i] / MAX_SHORT;
+ }
+
+ int sample;
+ for (int row=r.getMinX(); row<width; row++) {
+ for (int col=r.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ sample = r.getSample(row, col, chan);
+ result[pos][chan] = (sample * normMultipliers[chan]);
+ }
+ pos++;
+ }
+ }
+ } else { // Just get the samples...
+ for (int row=r.getMinX(); row<width; row++) {
+ for (int col=r.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ result[pos][chan] = r.getSampleFloat(row, col, chan);
+ }
+ pos++;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Unscale the whole float array and put the result
+ * in the raster
+ * @param r - destination raster
+ * @param data - input pixels
+ */
+ public void unscaleNormalized(WritableRaster r, float data[][]) {
+ int width = r.getWidth();
+ int height = r.getHeight();
+ float normMultipliers[] = new float[nColorChannels];
+
+ int pos = 0;
+ if (isTTypeIntegral) {
+ // Change max value from MAX_SHORT to 1f
+ for (int i=0; i<nColorChannels; i++) {
+ normMultipliers[i] = invChannelMulipliers[i] * MAX_SHORT;
+ }
+
+ int sample;
+ for (int row=r.getMinX(); row<width; row++) {
+ for (int col=r.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ sample = (int) (data[pos][chan] * normMultipliers[chan] + 0.5f);
+ r.setSample(row, col, chan, sample);
+ }
+ pos++;
+ }
+ }
+ } else { // Just set the samples...
+ for (int row=r.getMinX(); row<width; row++) {
+ for (int col=r.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ r.setSample(row, col, chan, data[pos][chan]);
+ }
+ pos++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Scales the whole raster to short and returns the result
+ * in the array
+ * @param r - source raster
+ * @return scaled and normalized raster data
+ */
+ public short[] scale(Raster r) {
+ int width = r.getWidth();
+ int height = r.getHeight();
+ short result[] = new short[width*height*nColorChannels];
+
+ int pos = 0;
+ if (isTTypeIntegral) {
+ int sample;
+ for (int row=r.getMinX(); row<width; row++) {
+ for (int col=r.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ sample = r.getSample(row, col, chan);
+ result[pos++] =
+ (short) (sample * channelMulipliers[chan] + 0.5f);
+ }
+ }
+ }
+ } else {
+ float sample;
+ for (int row=r.getMinX(); row<width; row++) {
+ for (int col=r.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ sample = r.getSampleFloat(row, col, chan);
+ result[pos++] = (short) ((sample - channelMinValues[chan])
+ * channelMulipliers[chan] + 0.5f);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Unscales the whole data array and puts obtained values to the raster
+ * @param data - input data
+ * @param wr - destination raster
+ */
+ public void unscale(short[] data, WritableRaster wr) {
+ int width = wr.getWidth();
+ int height = wr.getHeight();
+
+ int pos = 0;
+ if (isTTypeIntegral) {
+ int sample;
+ for (int row=wr.getMinX(); row<width; row++) {
+ for (int col=wr.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ sample = (int) ((data[pos++] & 0xFFFF) *
+ invChannelMulipliers[chan] + 0.5f);
+ wr.setSample(row, col, chan, sample);
+ }
+ }
+ }
+ } else {
+ float sample;
+ for (int row=wr.getMinX(); row<width; row++) {
+ for (int col=wr.getMinY(); col<height; col++) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ sample = (data[pos++] & 0xFFFF) *
+ invChannelMulipliers[chan] + channelMinValues[chan];
+ wr.setSample(row, col, chan, sample);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Scales one pixel and puts obtained values to the chanData
+ * @param pixelData - input pixel
+ * @param chanData - output buffer
+ * @param chanDataOffset - output buffer offset
+ */
+ public void scale(float[] pixelData, short[] chanData, int chanDataOffset) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ chanData[chanDataOffset + chan] =
+ (short) ((pixelData[chan] - channelMinValues[chan]) *
+ channelMulipliers[chan] + 0.5f);
+ }
+ }
+
+ /**
+ * Unscales one pixel and puts obtained values to the pixelData
+ * @param pixelData - output pixel
+ * @param chanData - input buffer
+ * @param chanDataOffset - input buffer offset
+ */
+ public void unscale(float[] pixelData, short[] chanData, int chanDataOffset) {
+ for (int chan = 0; chan < nColorChannels; chan++) {
+ pixelData[chan] = (chanData[chanDataOffset + chan] & 0xFFFF)
+ * invChannelMulipliers[chan] + channelMinValues[chan];
+ }
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/color/ICC_ProfileHelper.java b/awt/org/apache/harmony/awt/gl/color/ICC_ProfileHelper.java
new file mode 100644
index 0000000..2f7e519
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/color/ICC_ProfileHelper.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.color;
+
+import java.awt.color.ICC_Profile;
+
+/**
+ * Includes utility methods for reading ICC profile data.
+ * Created to provide public access to ICC_Profile methods
+ * for classes outside of java.awt.color
+ */
+public class ICC_ProfileHelper {
+ /**
+ * Utility method.
+ * Gets integer value from the byte array
+ * @param byteArray - byte array
+ * @param idx - byte offset
+ * @return integer value
+ */
+ public static int getIntFromByteArray(byte[] byteArray, int idx) {
+ return (byteArray[idx] & 0xFF)|
+ ((byteArray[idx+1] & 0xFF) << 8) |
+ ((byteArray[idx+2] & 0xFF) << 16)|
+ ((byteArray[idx+3] & 0xFF) << 24);
+ }
+
+ /**
+ * Utility method.
+ * Gets big endian integer value from the byte array
+ * @param byteArray - byte array
+ * @param idx - byte offset
+ * @return integer value
+ */
+ public static int getBigEndianFromByteArray(byte[] byteArray, int idx) {
+ return ((byteArray[idx] & 0xFF) << 24) |
+ ((byteArray[idx+1] & 0xFF) << 16) |
+ ((byteArray[idx+2] & 0xFF) << 8) |
+ ( byteArray[idx+3] & 0xFF);
+ }
+
+ /**
+ * Utility method.
+ * Gets short value from the byte array
+ * @param byteArray - byte array
+ * @param idx - byte offset
+ * @return short value
+ */
+ public static short getShortFromByteArray(byte[] byteArray, int idx) {
+ return (short) ((byteArray[idx] & 0xFF) |
+ ((byteArray[idx+1] & 0xFF) << 8));
+ }
+
+ /**
+ * Used in ICC_Transform class to check the rendering intent of the profile
+ * @param profile - ICC profile
+ * @return rendering intent
+ */
+ public static int getRenderingIntent(ICC_Profile profile) {
+ return getIntFromByteArray(
+ profile.getData(ICC_Profile.icSigHead), // pf header
+ ICC_Profile.icHdrRenderingIntent
+ );
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/color/ICC_Transform.java b/awt/org/apache/harmony/awt/gl/color/ICC_Transform.java
new file mode 100644
index 0000000..27646c4
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/color/ICC_Transform.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.color;
+
+import java.awt.color.ICC_Profile;
+
+import org.apache.harmony.awt.gl.color.NativeCMM;
+
+/**
+ * This class encapsulates native ICC transform object, is responsible for its
+ * creation, destruction and passing its handle to the native CMM.
+ */
+public class ICC_Transform {
+ private long transformHandle;
+ private int numInputChannels;
+ private int numOutputChannels;
+ private ICC_Profile src;
+ private ICC_Profile dst;
+
+
+ /**
+ * @return Returns the number of input channels.
+ */
+ public int getNumInputChannels() {
+ return numInputChannels;
+ }
+
+ /**
+ * @return Returns the number of output channels.
+ */
+ public int getNumOutputChannels() {
+ return numOutputChannels;
+ }
+
+ /**
+ * @return Returns the dst.
+ */
+ public ICC_Profile getDst() {
+ return dst;
+ }
+
+ /**
+ * @return Returns the src.
+ */
+ public ICC_Profile getSrc() {
+ return src;
+ }
+
+ /**
+ * Constructs a multiprofile ICC transform
+ * @param profiles - list of ICC profiles
+ * @param renderIntents - only hints for CMM
+ */
+ public ICC_Transform(ICC_Profile[] profiles, int[] renderIntents) {
+ int numProfiles = profiles.length;
+
+ long[] profileHandles = new long[numProfiles];
+ for (int i=0; i<numProfiles; i++) {
+ profileHandles[i] = NativeCMM.getHandle(profiles[i]);
+ }
+
+ transformHandle = NativeCMM.cmmCreateMultiprofileTransform(
+ profileHandles,
+ renderIntents);
+
+ src = profiles[0];
+ dst = profiles[numProfiles-1];
+ numInputChannels = src.getNumComponents();
+ numOutputChannels = dst.getNumComponents();
+ }
+
+ /**
+ * This constructor is able to set intents by default
+ * @param profiles - list of ICC profiles
+ */
+ public ICC_Transform(ICC_Profile[] profiles) {
+ int numProfiles = profiles.length;
+ int[] renderingIntents = new int[numProfiles];
+
+ // Default is perceptual
+ int currRenderingIntent = ICC_Profile.icPerceptual;
+
+ // render as colorimetric for output device
+ if (profiles[0].getProfileClass() == ICC_Profile.CLASS_OUTPUT) {
+ currRenderingIntent = ICC_Profile.icRelativeColorimetric;
+ }
+
+ // get the transforms from each profile
+ for (int i = 0; i < numProfiles; i++) {
+ // first or last profile cannot be abstract
+ // if profile is abstract, the only possible way is
+ // use AToB0Tag (perceptual), see ICC spec
+ if (i != 0 &&
+ i != numProfiles - 1 &&
+ profiles[i].getProfileClass() == ICC_Profile.CLASS_ABSTRACT
+ ) {
+ currRenderingIntent = ICC_Profile.icPerceptual;
+ }
+
+ renderingIntents[i] = currRenderingIntent;
+ // use current rendering intent
+ // to select LUT from the next profile (chaining)
+ currRenderingIntent =
+ ICC_ProfileHelper.getRenderingIntent(profiles[i]);
+ }
+
+ // Get the profile handles and go ahead
+ long[] profileHandles = new long[numProfiles];
+ for (int i=0; i<numProfiles; i++) {
+ profileHandles[i] = NativeCMM.getHandle(profiles[i]);
+ }
+
+ transformHandle = NativeCMM.cmmCreateMultiprofileTransform(
+ profileHandles,
+ renderingIntents);
+
+ src = profiles[0];
+ dst = profiles[numProfiles-1];
+ numInputChannels = src.getNumComponents();
+ numOutputChannels = dst.getNumComponents();
+ }
+
+ @Override
+ protected void finalize() {
+ if (transformHandle != 0) {
+ NativeCMM.cmmDeleteTransform(transformHandle);
+ }
+ }
+
+ /**
+ * Invokes native color conversion
+ * @param src - source image format
+ * @param dst - destination image format
+ */
+ public void translateColors(NativeImageFormat src, NativeImageFormat dst) {
+ NativeCMM.cmmTranslateColors(transformHandle, src, dst);
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/color/LUTColorConverter.java b/awt/org/apache/harmony/awt/gl/color/LUTColorConverter.java
new file mode 100644
index 0000000..5ea6d25
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/color/LUTColorConverter.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+/*
+ * Created on 02.11.2004
+ *
+ */
+package org.apache.harmony.awt.gl.color;
+
+import java.awt.color.ColorSpace;
+
+public class LUTColorConverter {
+
+ private static byte from8lRGBtosRGB_LUT[];
+
+ private static byte from16lRGBtosRGB_LUT[];
+
+ private static byte fromsRGBto8lRGB_LUT[];
+
+ private static short fromsRGBto16lRGB_LUT[];
+
+ private static byte fromsRGBto8sRGB_LUTs[][];
+
+ public static ColorSpace LINEAR_RGB_CS;
+
+ public static ColorSpace LINEAR_GRAY_CS;
+
+ public static ColorSpace sRGB_CS;
+
+ public LUTColorConverter() {
+ }
+
+ /*
+ * This class prepared and returned lookup tables for conversion color
+ * values from Linear RGB Color Space to sRGB and vice versa.
+ * Conversion is producing according to sRGB Color Space definition.
+ * "A Standard Default Color Space for the Internet - sRGB",
+ * Michael Stokes (Hewlett-Packard), Matthew Anderson (Microsoft),
+ * Srinivasan Chandrasekar (Microsoft), Ricardo Motta (Hewlett-Packard)
+ * Version 1.10, November 5, 1996
+ * This document is available: http://www.w3.org/Graphics/Color/sRGB
+ */
+ public static byte[] getFrom8lRGBtosRGB_LUT() {
+ if (from8lRGBtosRGB_LUT == null) {
+ from8lRGBtosRGB_LUT = new byte[256];
+ float v;
+ for (int i = 0; i < 256; i++) {
+ v = (float)i / 255;
+ v = (v <= 0.04045f) ? v / 12.92f :
+ (float) Math.pow((v + 0.055) / 1.055, 2.4);
+ from8lRGBtosRGB_LUT[i] = (byte) Math.round(v * 255.0f);
+ }
+ }
+ return from8lRGBtosRGB_LUT;
+ }
+
+ public static byte[] getFrom16lRGBtosRGB_LUT() {
+ if (from16lRGBtosRGB_LUT == null) {
+ from16lRGBtosRGB_LUT = new byte[65536];
+ float v;
+ for (int i = 0; i < 65536; i++) {
+ v = (float) i / 65535;
+ v = (v <= 0.04045f) ? v / 12.92f :
+ (float) Math.pow((v + 0.055) / 1.055, 2.4);
+ from16lRGBtosRGB_LUT[i] = (byte) Math.round(v * 255.0f);
+ }
+ }
+ return from16lRGBtosRGB_LUT;
+ }
+
+ public static byte[] getFromsRGBto8lRGB_LUT() {
+ if (fromsRGBto8lRGB_LUT == null) {
+ fromsRGBto8lRGB_LUT = new byte[256];
+ float v;
+ for (int i = 0; i < 256; i++) {
+ v = (float) i / 255;
+ v = (v <= 0.0031308f) ? v * 12.92f :
+ ((float) Math.pow(v, 1.0 / 2.4)) * 1.055f - 0.055f;
+ fromsRGBto8lRGB_LUT[i] = (byte) Math.round(v * 255.0f);
+ }
+ }
+ return fromsRGBto8lRGB_LUT;
+ }
+
+ public static short[] getFromsRGBto16lRGB_LUT() {
+ if (fromsRGBto16lRGB_LUT == null) {
+ fromsRGBto16lRGB_LUT = new short[256];
+ float v;
+ for (int i = 0; i < 256; i++) {
+ v = (float) i / 255;
+ v = (v <= 0.0031308f) ? v * 12.92f :
+ ((float) Math.pow(v, 1.0 / 2.4)) * 1.055f - 0.055f;
+ fromsRGBto16lRGB_LUT[i] = (short) Math.round(v * 65535.0f);
+ }
+ }
+ return fromsRGBto16lRGB_LUT;
+ }
+
+ public static byte[] getsRGBLUT(int bits) {
+ if (bits < 1) return null;
+ int idx = bits -1;
+ if(fromsRGBto8sRGB_LUTs == null) fromsRGBto8sRGB_LUTs = new byte[16][];
+
+ if(fromsRGBto8sRGB_LUTs[idx] == null){
+ fromsRGBto8sRGB_LUTs[idx] = createLUT(bits);
+ }
+ return fromsRGBto8sRGB_LUTs[idx];
+ }
+
+ private static byte[] createLUT(int bits) {
+ int lutSize = (1 << bits);
+ byte lut[] = new byte[lutSize];
+ for (int i = 0; i < lutSize; i++) {
+ lut[i] = (byte) (255.0f / (lutSize - 1) + 0.5f);
+ }
+ return lut;
+ }
+
+ public static boolean is_LINEAR_RGB_CS(ColorSpace cs) {
+ return (cs == LINEAR_RGB_CS);
+ }
+
+ public static boolean is_LINEAR_GRAY_CS(ColorSpace cs) {
+ return (cs == LINEAR_GRAY_CS);
+ }
+
+ public static boolean is_sRGB_CS(ColorSpace cs) {
+ return (cs == sRGB_CS);
+ }
+
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/color/NativeCMM.java b/awt/org/apache/harmony/awt/gl/color/NativeCMM.java
new file mode 100644
index 0000000..7f8c7e6
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/color/NativeCMM.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.color;
+
+import java.awt.color.ICC_Profile;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+
+/**
+ * This class is a wrapper for the native CMM library
+ */
+public class NativeCMM {
+
+ /**
+ * Storage for profile handles, since they are private
+ * in ICC_Profile, but we need access to them.
+ */
+ private static HashMap<ICC_Profile, Long> profileHandles = new HashMap<ICC_Profile, Long>();
+
+ private static boolean isCMMLoaded;
+
+ public static void addHandle(ICC_Profile key, long handle) {
+ profileHandles.put(key, new Long(handle));
+ }
+
+ public static void removeHandle(ICC_Profile key) {
+ profileHandles.remove(key);
+ }
+
+ public static long getHandle(ICC_Profile key) {
+ return profileHandles.get(key).longValue();
+ }
+
+ /* ICC profile management */
+ public static native long cmmOpenProfile(byte[] data);
+ public static native void cmmCloseProfile(long profileID);
+ public static native int cmmGetProfileSize(long profileID);
+ public static native void cmmGetProfile(long profileID, byte[] data);
+ public static native int cmmGetProfileElementSize(long profileID, int signature);
+ public static native void cmmGetProfileElement(long profileID, int signature,
+ byte[] data);
+ public static native void cmmSetProfileElement(long profileID, int tagSignature,
+ byte[] data);
+
+
+ /* ICC transforms */
+ public static native long cmmCreateMultiprofileTransform(
+ long[] profileHandles,
+ int[] renderingIntents
+ );
+ public static native void cmmDeleteTransform(long transformHandle);
+ public static native void cmmTranslateColors(long transformHandle,
+ NativeImageFormat src,
+ NativeImageFormat dest);
+
+ static void loadCMM() {
+ if (!isCMMLoaded) {
+ AccessController.doPrivileged(
+ new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("lcmm"); //$NON-NLS-1$
+ return null;
+ }
+ } );
+ isCMMLoaded = true;
+ }
+ }
+
+ /* load native CMM library */
+ static {
+ loadCMM();
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java b/awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java
new file mode 100644
index 0000000..9594047
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java
@@ -0,0 +1,642 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.color;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.util.ArrayList;
+
+import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+
+/**
+ * This class converts java color/sample models to the LCMS pixel formats.
+ * It also encapsulates all the information about the image format, which native CMM
+ * needs to have in order to read/write data.
+ *
+ * At present planar formats (multiple bands) are not supported
+ * and they are handled as a common (custom) case.
+ * Samples other than 1 - 7 bytes and multiple of 8 bits are
+ * also handled as custom (and won't be supported in the nearest future).
+ */
+class NativeImageFormat {
+ //////////////////////////////////////////////
+ // LCMS Pixel types
+ private static final int PT_ANY = 0; // Don't check colorspace
+ // 1 & 2 are reserved
+ private static final int PT_GRAY = 3;
+ private static final int PT_RGB = 4;
+ // Skipping other since we don't use them here
+ ///////////////////////////////////////////////
+
+ // Conversion of predefined BufferedImage formats to LCMS formats
+ private static final int INT_RGB_LCMS_FMT =
+ colorspaceSh(PT_RGB)|
+ extraSh(1)|
+ channelsSh(3)|
+ bytesSh(1)|
+ doswapSh(1)|
+ swapfirstSh(1);
+
+ private static final int INT_ARGB_LCMS_FMT = INT_RGB_LCMS_FMT;
+
+ private static final int INT_BGR_LCMS_FMT =
+ colorspaceSh(PT_RGB)|
+ extraSh(1)|
+ channelsSh(3)|
+ bytesSh(1);
+
+ private static final int THREE_BYTE_BGR_LCMS_FMT =
+ colorspaceSh(PT_RGB)|
+ channelsSh(3)|
+ bytesSh(1)|
+ doswapSh(1);
+
+ private static final int FOUR_BYTE_ABGR_LCMS_FMT =
+ colorspaceSh(PT_RGB)|
+ extraSh(1)|
+ channelsSh(3)|
+ bytesSh(1)|
+ doswapSh(1);
+
+ private static final int BYTE_GRAY_LCMS_FMT =
+ colorspaceSh(PT_GRAY)|
+ channelsSh(1)|
+ bytesSh(1);
+
+ private static final int USHORT_GRAY_LCMS_FMT =
+ colorspaceSh(PT_GRAY)|
+ channelsSh(1)|
+ bytesSh(2);
+
+ // LCMS format packed into 32 bit value. For description
+ // of this format refer to LCMS documentation.
+ private int cmmFormat = 0;
+
+ // Dimensions
+ private int rows = 0;
+ private int cols = 0;
+
+ // Scanline may contain some padding in the end
+ private int scanlineStride = -1;
+
+ private Object imageData;
+ // It's possible to have offset from the beginning of the array
+ private int dataOffset;
+
+ // Has the image alpha channel? If has - here its band band offset goes
+ private int alphaOffset = -1;
+
+ // initializes proper field IDs
+ private static native void initIDs();
+
+ static {
+ NativeCMM.loadCMM();
+ initIDs();
+ }
+
+ ////////////////////////////////////
+ // LCMS image format encoders
+ ////////////////////////////////////
+ private static int colorspaceSh(int s) {
+ return (s << 16);
+ }
+
+ private static int swapfirstSh(int s) {
+ return (s << 14);
+ }
+
+ private static int flavorSh(int s) {
+ return (s << 13);
+ }
+
+ private static int planarSh(int s) {
+ return (s << 12);
+ }
+
+ private static int endianSh(int s) {
+ return (s << 11);
+ }
+
+ private static int doswapSh(int s) {
+ return (s << 10);
+ }
+
+ private static int extraSh(int s) {
+ return (s << 7);
+ }
+
+ private static int channelsSh(int s) {
+ return (s << 3);
+ }
+
+ private static int bytesSh(int s) {
+ return s;
+ }
+ ////////////////////////////////////
+ // End of LCMS image format encoders
+ ////////////////////////////////////
+
+ // Accessors
+ Object getChannelData() {
+ return imageData;
+ }
+
+ int getNumCols() {
+ return cols;
+ }
+
+ int getNumRows() {
+ return rows;
+ }
+
+ // Constructors
+ public NativeImageFormat() {
+ }
+
+ /**
+ * Simple image layout for common case with
+ * not optimized workflow.
+ *
+ * For hifi colorspaces with 5+ color channels imgData
+ * should be <code>byte</code> array.
+ *
+ * For common colorspaces with up to 4 color channels it
+ * should be <code>short</code> array.
+ *
+ * Alpha channel is handled by caller, not by CMS.
+ *
+ * Color channels are in their natural order (not BGR but RGB).
+ *
+ * @param imgData - array of <code>byte</code> or <code>short</code>
+ * @param nChannels - number of channels
+ * @param nRows - number of scanlines in the image
+ * @param nCols - number of pixels in one row of the image
+ */
+ public NativeImageFormat(Object imgData, int nChannels, int nRows, int nCols) {
+ if (imgData instanceof short[]) {
+ cmmFormat |= bytesSh(2);
+ }
+ else if (imgData instanceof byte[]) {
+ cmmFormat |= bytesSh(1);
+ }
+ else
+ // awt.47=First argument should be byte or short array
+ throw new IllegalArgumentException(Messages.getString("awt.47")); //$NON-NLS-1$
+
+ cmmFormat |= channelsSh(nChannels);
+
+ rows = nRows;
+ cols = nCols;
+
+ imageData = imgData;
+
+ dataOffset = 0;
+ }
+
+ /**
+ * Deduces image format from the buffered image type
+ * or color and sample models.
+ * @param bi - image
+ * @return image format object
+ */
+ public static NativeImageFormat createNativeImageFormat(BufferedImage bi) {
+ NativeImageFormat fmt = new NativeImageFormat();
+
+ switch (bi.getType()) {
+ case BufferedImage.TYPE_INT_RGB: {
+ fmt.cmmFormat = INT_RGB_LCMS_FMT;
+ break;
+ }
+
+ case BufferedImage.TYPE_INT_ARGB:
+ case BufferedImage.TYPE_INT_ARGB_PRE: {
+ fmt.cmmFormat = INT_ARGB_LCMS_FMT;
+ fmt.alphaOffset = 3;
+ break;
+ }
+
+ case BufferedImage.TYPE_INT_BGR: {
+ fmt.cmmFormat = INT_BGR_LCMS_FMT;
+ break;
+ }
+
+ case BufferedImage.TYPE_3BYTE_BGR: {
+ fmt.cmmFormat = THREE_BYTE_BGR_LCMS_FMT;
+ break;
+ }
+
+ case BufferedImage.TYPE_4BYTE_ABGR_PRE:
+ case BufferedImage.TYPE_4BYTE_ABGR: {
+ fmt.cmmFormat = FOUR_BYTE_ABGR_LCMS_FMT;
+ fmt.alphaOffset = 0;
+ break;
+ }
+
+ case BufferedImage.TYPE_BYTE_GRAY: {
+ fmt.cmmFormat = BYTE_GRAY_LCMS_FMT;
+ break;
+ }
+
+ case BufferedImage.TYPE_USHORT_GRAY: {
+ fmt.cmmFormat = USHORT_GRAY_LCMS_FMT;
+ break;
+ }
+
+ case BufferedImage.TYPE_BYTE_BINARY:
+ case BufferedImage.TYPE_USHORT_565_RGB:
+ case BufferedImage.TYPE_USHORT_555_RGB:
+ case BufferedImage.TYPE_BYTE_INDEXED: {
+ // A bunch of unsupported formats
+ return null;
+ }
+
+ default:
+ break; // Try to look at sample model and color model
+ }
+
+
+ if (fmt.cmmFormat == 0) {
+ ColorModel cm = bi.getColorModel();
+ SampleModel sm = bi.getSampleModel();
+
+ if (sm instanceof ComponentSampleModel) {
+ ComponentSampleModel csm = (ComponentSampleModel) sm;
+ fmt.cmmFormat = getFormatFromComponentModel(csm, cm.hasAlpha());
+ fmt.scanlineStride = calculateScanlineStrideCSM(csm, bi.getRaster());
+ } else if (sm instanceof SinglePixelPackedSampleModel) {
+ SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
+ fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, cm.hasAlpha());
+ fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, bi.getRaster());
+ }
+
+ if (cm.hasAlpha())
+ fmt.alphaOffset = calculateAlphaOffset(sm, bi.getRaster());
+ }
+
+ if (fmt.cmmFormat == 0)
+ return null;
+
+ if (!fmt.setImageData(bi.getRaster().getDataBuffer())) {
+ return null;
+ }
+
+ fmt.rows = bi.getHeight();
+ fmt.cols = bi.getWidth();
+
+ fmt.dataOffset = bi.getRaster().getDataBuffer().getOffset();
+
+ return fmt;
+ }
+
+ /**
+ * Deduces image format from the raster sample model.
+ * @param r - raster
+ * @return image format object
+ */
+ public static NativeImageFormat createNativeImageFormat(Raster r) {
+ NativeImageFormat fmt = new NativeImageFormat();
+ SampleModel sm = r.getSampleModel();
+
+ // Assume that there's no alpha
+ if (sm instanceof ComponentSampleModel) {
+ ComponentSampleModel csm = (ComponentSampleModel) sm;
+ fmt.cmmFormat = getFormatFromComponentModel(csm, false);
+ fmt.scanlineStride = calculateScanlineStrideCSM(csm, r);
+ } else if (sm instanceof SinglePixelPackedSampleModel) {
+ SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
+ fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, false);
+ fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, r);
+ }
+
+ if (fmt.cmmFormat == 0)
+ return null;
+
+ fmt.cols = r.getWidth();
+ fmt.rows = r.getHeight();
+ fmt.dataOffset = r.getDataBuffer().getOffset();
+
+ if (!fmt.setImageData(r.getDataBuffer()))
+ return null;
+
+ return fmt;
+ }
+
+ /**
+ * Obtains LCMS format from the component sample model
+ * @param sm - sample model
+ * @param hasAlpha - true if there's an alpha channel
+ * @return LCMS format
+ */
+ private static int getFormatFromComponentModel(ComponentSampleModel sm, boolean hasAlpha) {
+ // Multiple data arrays (banks) not supported
+ int bankIndex = sm.getBankIndices()[0];
+ for (int i=1; i < sm.getNumBands(); i++) {
+ if (sm.getBankIndices()[i] != bankIndex) {
+ return 0;
+ }
+ }
+
+ int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands();
+ int extra = hasAlpha ? 1 : 0;
+ int bytes = 1;
+ switch (sm.getDataType()) {
+ case DataBuffer.TYPE_BYTE:
+ bytes = 1; break;
+ case DataBuffer.TYPE_SHORT:
+ case DataBuffer.TYPE_USHORT:
+ bytes = 2; break;
+ case DataBuffer.TYPE_INT:
+ bytes = 4; break;
+ case DataBuffer.TYPE_DOUBLE:
+ bytes = 0; break;
+ default:
+ return 0; // Unsupported data type
+ }
+
+ int doSwap = 0;
+ int swapFirst = 0;
+ boolean knownFormat = false;
+
+ int i;
+
+ // "RGBA"
+ for (i=0; i < sm.getNumBands(); i++) {
+ if (sm.getBandOffsets()[i] != i) break;
+ }
+ if (i == sm.getNumBands()) { // Ok, it is it
+ doSwap = 0;
+ swapFirst = 0;
+ knownFormat = true;
+ }
+
+ // "ARGB"
+ if (!knownFormat) {
+ for (i=0; i < sm.getNumBands()-1; i++) {
+ if (sm.getBandOffsets()[i] != i+1) break;
+ }
+ if (sm.getBandOffsets()[i] == 0) i++;
+ if (i == sm.getNumBands()) { // Ok, it is it
+ doSwap = 0;
+ swapFirst = 1;
+ knownFormat = true;
+ }
+ }
+
+ // "BGRA"
+ if (!knownFormat) {
+ for (i=0; i < sm.getNumBands()-1; i++) {
+ if (sm.getBandOffsets()[i] != sm.getNumBands() - 2 - i) break;
+ }
+ if (sm.getBandOffsets()[i] == sm.getNumBands()-1) i++;
+ if (i == sm.getNumBands()) { // Ok, it is it
+ doSwap = 1;
+ swapFirst = 1;
+ knownFormat = true;
+ }
+ }
+
+ // "ABGR"
+ if (!knownFormat) {
+ for (i=0; i < sm.getNumBands(); i++) {
+ if (sm.getBandOffsets()[i] != sm.getNumBands() - 1 - i) break;
+ }
+ if (i == sm.getNumBands()) { // Ok, it is it
+ doSwap = 1;
+ swapFirst = 0;
+ knownFormat = true;
+ }
+ }
+
+ // XXX - Planar formats are not supported yet
+ if (!knownFormat)
+ return 0;
+
+ return
+ channelsSh(channels) |
+ bytesSh(bytes) |
+ extraSh(extra) |
+ doswapSh(doSwap) |
+ swapfirstSh(swapFirst);
+ }
+
+ /**
+ * Obtains LCMS format from the single pixel packed sample model
+ * @param sm - sample model
+ * @param hasAlpha - true if there's an alpha channel
+ * @return LCMS format
+ */
+ private static int getFormatFromSPPSampleModel(SinglePixelPackedSampleModel sm,
+ boolean hasAlpha) {
+ // Can we extract bytes?
+ int mask = sm.getBitMasks()[0] >>> sm.getBitOffsets()[0];
+ if (!(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF))
+ return 0;
+
+ // All masks are same?
+ for (int i = 1; i < sm.getNumBands(); i++) {
+ if ((sm.getBitMasks()[i] >>> sm.getBitOffsets()[i]) != mask)
+ return 0;
+ }
+
+ int pixelSize = 0;
+ // Check if data type is supported
+ if (sm.getDataType() == DataBuffer.TYPE_USHORT)
+ pixelSize = 2;
+ else if (sm.getDataType() == DataBuffer.TYPE_INT)
+ pixelSize = 4;
+ else
+ return 0;
+
+
+ int bytes = 0;
+ switch (mask) {
+ case 0xFF:
+ bytes = 1;
+ break;
+ case 0xFFFF:
+ bytes = 2;
+ break;
+ case 0xFFFFFFFF:
+ bytes = 4;
+ break;
+ default: return 0;
+ }
+
+
+ int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands();
+ int extra = hasAlpha ? 1 : 0;
+ extra += pixelSize/bytes - sm.getNumBands(); // Unused bytes?
+
+ // Form an ArrayList containing offset for each band
+ ArrayList<Integer> offsetsLst = new ArrayList<Integer>();
+ for (int k=0; k < sm.getNumBands(); k++) {
+ offsetsLst.add(new Integer(sm.getBitOffsets()[k]/(bytes*8)));
+ }
+
+ // Add offsets for unused space
+ for (int i=0; i<pixelSize/bytes; i++) {
+ if (offsetsLst.indexOf(new Integer(i)) < 0)
+ offsetsLst.add(new Integer(i));
+ }
+
+ int offsets[] = new int[pixelSize/bytes];
+ for (int i=0; i<offsetsLst.size(); i++) {
+ offsets[i] = offsetsLst.get(i).intValue();
+ }
+
+ int doSwap = 0;
+ int swapFirst = 0;
+ boolean knownFormat = false;
+
+ int i;
+
+ // "RGBA"
+ for (i=0; i < pixelSize; i++) {
+ if (offsets[i] != i) break;
+ }
+ if (i == pixelSize) { // Ok, it is it
+ doSwap = 0;
+ swapFirst = 0;
+ knownFormat = true;
+ }
+
+ // "ARGB"
+ if (!knownFormat) {
+ for (i=0; i < pixelSize-1; i++) {
+ if (offsets[i] != i+1) break;
+ }
+ if (offsets[i] == 0) i++;
+ if (i == pixelSize) { // Ok, it is it
+ doSwap = 0;
+ swapFirst = 1;
+ knownFormat = true;
+ }
+ }
+
+ // "BGRA"
+ if (!knownFormat) {
+ for (i=0; i < pixelSize-1; i++) {
+ if (offsets[i] != pixelSize - 2 - i) break;
+ }
+ if (offsets[i] == pixelSize-1) i++;
+ if (i == pixelSize) { // Ok, it is it
+ doSwap = 1;
+ swapFirst = 1;
+ knownFormat = true;
+ }
+ }
+
+ // "ABGR"
+ if (!knownFormat) {
+ for (i=0; i < pixelSize; i++) {
+ if (offsets[i] != pixelSize - 1 - i) break;
+ }
+ if (i == pixelSize) { // Ok, it is it
+ doSwap = 1;
+ swapFirst = 0;
+ knownFormat = true;
+ }
+ }
+
+ // XXX - Planar formats are not supported yet
+ if (!knownFormat)
+ return 0;
+
+ return
+ channelsSh(channels) |
+ bytesSh(bytes) |
+ extraSh(extra) |
+ doswapSh(doSwap) |
+ swapfirstSh(swapFirst);
+ }
+
+ /**
+ * Obtains data array from the DataBuffer object
+ * @param db - data buffer
+ * @return - true if successful
+ */
+ private boolean setImageData(DataBuffer db) {
+ AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance();
+ try {
+ imageData = dbAccess.getData(db);
+ } catch (IllegalArgumentException e) {
+ return false; // Unknown data buffer type
+ }
+
+ return true;
+ }
+
+ /**
+ * Calculates scanline stride in bytes
+ * @param csm - component sample model
+ * @param r - raster
+ * @return scanline stride in bytes
+ */
+ private static int calculateScanlineStrideCSM(ComponentSampleModel csm, Raster r) {
+ if (csm.getScanlineStride() != csm.getPixelStride()*csm.getWidth()) {
+ int dataTypeSize = DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
+ return csm.getScanlineStride()*dataTypeSize;
+ }
+ return -1;
+ }
+
+ /**
+ * Calculates scanline stride in bytes
+ * @param sppsm - sample model
+ * @param r - raster
+ * @return scanline stride in bytes
+ */
+ private static int calculateScanlineStrideSPPSM(SinglePixelPackedSampleModel sppsm, Raster r) {
+ if (sppsm.getScanlineStride() != sppsm.getWidth()) {
+ int dataTypeSize = DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
+ return sppsm.getScanlineStride()*dataTypeSize;
+ }
+ return -1;
+ }
+
+ /**
+ * Calculates byte offset of the alpha channel from the beginning of the pixel data
+ * @param sm - sample model
+ * @param r - raster
+ * @return byte offset of the alpha channel
+ */
+ private static int calculateAlphaOffset(SampleModel sm, Raster r) {
+ if (sm instanceof ComponentSampleModel) {
+ ComponentSampleModel csm = (ComponentSampleModel) sm;
+ int dataTypeSize =
+ DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
+ return
+ csm.getBandOffsets()[csm.getBandOffsets().length - 1] * dataTypeSize;
+ } else if (sm instanceof SinglePixelPackedSampleModel) {
+ SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
+ return sppsm.getBitOffsets()[sppsm.getBitOffsets().length - 1] / 8;
+ } else {
+ return -1; // No offset, don't copy alpha
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidFont.java b/awt/org/apache/harmony/awt/gl/font/AndroidFont.java
new file mode 100644
index 0000000..e8ad1bb
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/AndroidFont.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineMetrics;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.io.File;
+import java.util.Hashtable;
+import java.util.Locale;
+
+import org.apache.harmony.awt.gl.font.FontManager;
+import org.apache.harmony.awt.gl.font.FontPeerImpl;
+import org.apache.harmony.awt.gl.font.Glyph;
+import org.apache.harmony.awt.gl.font.LineMetricsImpl;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * Linux platform font peer implementation based on Xft and FreeType libraries.
+ */
+public class AndroidFont extends FontPeerImpl {
+
+ // Pairs of [begin, end],[..].. unicode ranges values
+ private int[] fontUnicodeRanges;
+
+ // table with loaded cached Glyphs
+ private Hashtable glyphs = new Hashtable();
+
+ // X11 display value
+ private long display = 0;
+
+ // X11 screen value
+ private int screen = 0;
+
+ public AndroidFont(String fontName, int fontStyle, int fontSize) {
+ /*
+ * Workaround : to initialize awt platform-dependent fields and libraries.
+ */
+ Toolkit.getDefaultToolkit();
+ this.name = fontName;
+ this.size = fontSize;
+ this.style = fontStyle;
+
+ initAndroidFont();
+ }
+
+ /**
+ * Initializes some native dependent font information, e.g. number of glyphs,
+ * font metrics, italic angle etc.
+ */
+ public void initAndroidFont(){
+ this.nlm = new AndroidLineMetrics(this, null, " "); //$NON-NLS-1$
+ this.ascent = nlm.getLogicalAscent();
+ this.descent = nlm.getLogicalDescent();
+ this.height = nlm.getHeight();
+ this.leading = nlm.getLogicalLeading();
+ this.maxAdvance = nlm.getLogicalMaxCharWidth();
+
+ if (this.fontType == FontManager.FONT_TYPE_T1){
+ this.defaultChar = 1;
+ } else {
+ this.defaultChar = 0;
+ }
+
+ this.maxCharBounds = new Rectangle2D.Float(0, -nlm.getAscent(), nlm.getMaxCharWidth(), this.height);
+ }
+
+
+ public boolean canDisplay(char chr) {
+ // TODO: to improve performance there is a sence to implement get
+ // unicode ranges to check if char can be displayed without
+ // native calls in isGlyphExists() method
+
+ return isGlyphExists(chr);
+ }
+
+ public LineMetrics getLineMetrics(String str, FontRenderContext frc, AffineTransform at) {
+
+ // Initialize baseline offsets
+ nlm.getBaselineOffsets();
+
+ LineMetricsImpl lm = (LineMetricsImpl)(this.nlm.clone());
+ lm.setNumChars(str.length());
+
+ if ((at != null) && (!at.isIdentity())){
+ lm.scale((float)at.getScaleX(), (float)at.getScaleY());
+ }
+
+ return lm;
+ }
+
+ public String getPSName() {
+ return psName;
+ }
+
+ public String getFamily(Locale l) {
+ // TODO: implement localized family
+ if (fontType == FontManager.FONT_TYPE_TT){
+ return this.getFamily();
+ }
+
+ return this.fontFamilyName;
+ }
+
+ public String getFontName(Locale l) {
+ if ((pFont == 0) || (this.fontType == FontManager.FONT_TYPE_T1)){
+ return this.name;
+ }
+
+ return this.getFontName();
+ }
+
+
+ public int getMissingGlyphCode() {
+ return getDefaultGlyph().getGlyphCode();
+ }
+
+ public Glyph getGlyph(char index) {
+ Glyph result = null;
+
+ Object key = new Integer(index);
+ if (glyphs.containsKey(key)) {
+ result = (Glyph) glyphs.get(key);
+ } else {
+ if (this.addGlyph(index)) {
+ result = (Glyph) glyphs.get(key);
+ } else {
+ result = this.getDefaultGlyph();
+ }
+ }
+
+ return result;
+ }
+
+ public Glyph getDefaultGlyph() {
+ throw new RuntimeException("DefaultGlyphs not implemented!");
+ }
+
+ /**
+ * Disposes native font handle. If this font peer was created from InputStream
+ * temporary created font resource file is deleted.
+ */
+ public void dispose(){
+ String tempDirName;
+ if (pFont != 0){
+ pFont = 0;
+
+ if (isCreatedFromStream()) {
+ File fontFile = new File(getTempFontFileName());
+ tempDirName = fontFile.getParent();
+ fontFile.delete();
+ }
+ }
+ }
+
+ /**
+ * Add glyph to cached Glyph objects in this LinuxFont object.
+ *
+ * @param uChar the specified character
+ * @return true if glyph of the specified character exists in this
+ * LinuxFont or this character is escape sequence character.
+ */
+ public boolean addGlyph(char uChar) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ /**
+ * Adds range of existing glyphs to this LinuxFont object
+ *
+ * @param uFirst the lowest range's bound, inclusive
+ * @param uLast the highest range's bound, exclusive
+ */
+ public void addGlyphs(char uFirst, char uLast) {
+
+ char index = uFirst;
+ if (uLast < uFirst) {
+ // awt.09=min range bound value is grater than max range bound
+ throw new IllegalArgumentException(Messages.getString("awt.09")); //$NON-NLS-1$
+ }
+ while (index < uLast) {
+ addGlyph(index);
+ index++;
+ }
+
+ }
+
+ /**
+ * Returns true if specified character has corresopnding glyph, false otherwise.
+ *
+ * @param uIndex specified char
+ */
+ public boolean isGlyphExists(char uIndex) {
+ throw new RuntimeException("DefaultGlyphs not implemented!");
+ }
+
+ /**
+ * Returns an array of unicode ranges that are supported by this LinuxFont.
+ */
+ public int[] getUnicodeRanges() {
+ int[] ranges = new int[fontUnicodeRanges.length];
+ System.arraycopy(fontUnicodeRanges, 0, ranges, 0,
+ fontUnicodeRanges.length);
+
+ return ranges;
+ }
+
+ /**
+ * Return Font object if it was successfully embedded in System
+ */
+ public static Font embedFont(String absolutePath){
+ throw new RuntimeException("embedFont not implemented!");
+ }
+
+ public String getFontName(){
+ if ((pFont != 0) && (faceName == null)){
+ if (this.fontType == FontManager.FONT_TYPE_T1){
+ faceName = getFamily();
+ }
+ }
+ return faceName;
+ }
+
+ public String getFamily() {
+ return fontFamilyName;
+ }
+
+ /**
+ * Returns initiated FontExtraMetrics instance of this WindowsFont.
+ */
+ public FontExtraMetrics getExtraMetrics(){
+ throw new RuntimeException("Not implemented!");
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidFontManager.java b/awt/org/apache/harmony/awt/gl/font/AndroidFontManager.java
new file mode 100644
index 0000000..063a256
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/AndroidFontManager.java
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.Font;
+import java.awt.peer.FontPeer;
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.harmony.awt.gl.font.FontManager;
+import org.apache.harmony.awt.gl.font.FontProperty;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+import android.util.Log;
+
+public class AndroidFontManager extends FontManager {
+
+ // set of all available faces supported by a system
+ String faces[];
+
+ // weight names according to xlfd structure
+ public static final String[] LINUX_WEIGHT_NAMES = {
+ "black", "bold", "demibold", "medium", "light" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ };
+
+ // slant names according to xlfd structure
+ public static final String[] LINUX_SLANT_NAMES = {
+ "i", "o", "r" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ };
+
+ /** Singleton AndroidFontManager instance */
+ public static final AndroidFontManager inst = new AndroidFontManager();
+
+ private AndroidFontManager() {
+ super();
+ faces = new String[] {/*"PLAIN",*/ "NORMAL", "BOLD", "ITALIC", "BOLDITALIC"};
+ initFontProperties();
+ }
+
+ public void initLCIDTable(){
+ throw new RuntimeException("Not implemented!");
+ }
+
+ /**
+ * Returns temporary File object to store data from InputStream.
+ * This File object saved to `~/.fonts/' folder that is included in the
+ * list of folders searched for font files, and this is where user-specific
+ * font files should be installed.
+ */
+ public File getTempFontFile()throws IOException{
+ File fontFile = File.createTempFile("jFont", ".ttf", new File(System.getProperty("user.home") +"/.fonts")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ fontFile.deleteOnExit();
+
+ return fontFile;
+ }
+
+ /**
+ * Initializes fProperties array field for the current system configuration font
+ * property file.
+ *
+ * RuntimeException is thrown if font property contains incorrect format of
+ * xlfd string.
+ *
+ * @return true is success, false if font property doesn't exist or doesn't
+ * contain roperties.
+ */
+ public boolean initFontProperties(){
+ File fpFile = getFontPropertyFile();
+ if (fpFile == null){
+ return false;
+ }
+
+ Properties props = getProperties(fpFile);
+ if (props == null){
+ return false;
+ }
+
+ for (int i=0; i < LOGICAL_FONT_NAMES.length; i++){
+ String lName = LOGICAL_FONT_NAMES[i];
+ for (int j=0; j < STYLE_NAMES.length; j++){
+ String styleName = STYLE_NAMES[j];
+ Vector propsVector = new Vector();
+
+ // Number of entries for a logical font
+ int numComp = 0;
+ // Is more entries for this style and logical font name left
+ boolean moreEntries = true;
+ String value = null;
+
+ while(moreEntries){
+ // Component Font Mappings property name
+ String property = FONT_MAPPING_KEYS[0].replaceAll("LogicalFontName", lName).replaceAll("StyleName", styleName).replaceAll("ComponentIndex", String.valueOf(numComp)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ value = props.getProperty(property);
+
+ // If the StyleName is omitted, it's assumed to be plain
+ if ((j == 0) && (value == null)){
+ property = FONT_MAPPING_KEYS[1].replaceAll("LogicalFontName", lName).replaceAll("ComponentIndex", String.valueOf(numComp)); //$NON-NLS-1$ //$NON-NLS-2$
+ value = props.getProperty(property);
+ }
+
+ if (value != null){
+ String[] fields = parseXLFD(value);
+
+ if (fields == null){
+ // awt.08=xfld parse string error: {0}
+ throw new RuntimeException(Messages.getString("awt.08", value)); //$NON-NLS-1$
+ }
+
+ String fontName = fields[1];
+ String weight = fields[2];
+ String italic = fields[3];
+
+ int style = getBoldStyle(weight) | getItalicStyle(italic);
+ // Component Font Character Encodings property value
+ String encoding = props.getProperty(FONT_CHARACTER_ENCODING.replaceAll("LogicalFontName", lName).replaceAll("ComponentIndex", String.valueOf(numComp))); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Exclusion Ranges property value
+ String exclString = props.getProperty(EXCLUSION_RANGES.replaceAll("LogicalFontName", lName).replaceAll("ComponentIndex", String.valueOf(numComp))); //$NON-NLS-1$ //$NON-NLS-2$
+ int[] exclRange = parseIntervals(exclString);
+
+ FontProperty fp = new AndroidFontProperty(lName, styleName, null, fontName, value, style, exclRange, encoding);
+
+ propsVector.add(fp);
+ numComp++;
+ } else {
+ moreEntries = false;
+ }
+ }
+ fProperties.put(LOGICAL_FONT_NAMES[i] + "." + j, propsVector); //$NON-NLS-1$
+ }
+ }
+
+ return true;
+
+ }
+
+ /**
+ * Returns style according to the xlfd weight string.
+ * If weight string is incorrect returned value is Font.PLAIN
+ *
+ * @param str weight name String
+ */
+ private int getBoldStyle(String str){
+ for (int i = 0; i < LINUX_WEIGHT_NAMES.length;i++){
+ if (str.equalsIgnoreCase(LINUX_WEIGHT_NAMES[i])){
+ return (i < 3) ? Font.BOLD : Font.PLAIN;
+ }
+ }
+ return Font.PLAIN;
+ }
+
+ /**
+ * Returns style according to the xlfd slant string.
+ * If slant string is incorrect returned value is Font.PLAIN
+ *
+ * @param str slant name String
+ */
+ private int getItalicStyle(String str){
+ for (int i = 0; i < LINUX_SLANT_NAMES.length;i++){
+ if (str.equalsIgnoreCase(LINUX_SLANT_NAMES[i])){
+ return (i < 2) ? Font.ITALIC : Font.PLAIN;
+ }
+ }
+ return Font.PLAIN;
+ }
+
+ /**
+ * Parse xlfd string and returns array of Strings with separate xlfd
+ * elements.<p>
+ *
+ * xlfd format:
+ * -Foundry-Family-Weight-Slant-Width-Style-PixelSize-PointSize-ResX-ResY-Spacing-AvgWidth-Registry-Encoding
+ * @param xlfd String parameter in xlfd format
+ */
+ public static String[] parseXLFD(String xlfd){
+ int fieldsCount = 14;
+ String fieldsDelim = "-"; //$NON-NLS-1$
+ String[] res = new String[fieldsCount];
+ if (!xlfd.startsWith(fieldsDelim)){
+ return null;
+ }
+
+ xlfd = xlfd.substring(1);
+ int i=0;
+ int pos;
+ for (i=0; i < fieldsCount-1; i++){
+ pos = xlfd.indexOf(fieldsDelim);
+ if (pos != -1){
+ res[i] = xlfd.substring(0, pos);
+ xlfd = xlfd.substring(pos + 1);
+ } else {
+ return null;
+ }
+ }
+ pos = xlfd.indexOf(fieldsDelim);
+
+ // check if no fields left
+ if(pos != -1){
+ return null;
+ }
+ res[fieldsCount-1] = xlfd;
+
+ return res;
+ }
+
+ public int getFaceIndex(String faceName){
+
+ for (int i = 0; i < faces.length; i++) {
+ if(faces[i].equals(faceName)){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public String[] getAllFamilies(){
+ if (allFamilies == null){
+ allFamilies = new String[]{"sans-serif", "serif", "monospace"};
+ }
+ return allFamilies;
+ }
+
+ public Font[] getAllFonts(){
+ Font[] fonts = new Font[faces.length];
+ for (int i =0; i < fonts.length;i++){
+ fonts[i] = new Font(faces[i], Font.PLAIN, 1);
+ }
+ return fonts;
+ }
+
+ public FontPeer createPhysicalFontPeer(String name, int style, int size) {
+ AndroidFont peer;
+ int familyIndex = getFamilyIndex(name);
+ if (familyIndex != -1){
+ // !! we use family names from the list with cached families because
+ // they are differ from the family names in xlfd structure, in xlfd
+ // family names mostly in lower case.
+ peer = new AndroidFont(getFamily(familyIndex), style, size);
+ peer.setFamily(getFamily(familyIndex));
+ return peer;
+ }
+ int faceIndex = getFaceIndex(name);
+ if (faceIndex != -1){
+
+ peer = new AndroidFont(name, style, size);
+ return peer;
+ }
+
+ return null;
+ }
+
+ public FontPeer createDefaultFont(int style, int size) {
+ Log.i("DEFAULT FONT", Integer.toString(style));
+ return new AndroidFont(DEFAULT_NAME, style, size);
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java b/awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java
new file mode 100644
index 0000000..0cfdc43
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ *
+ */
+package org.apache.harmony.awt.gl.font;
+
+/**
+ * Android FontProperty implementation, applicable for Linux formats of
+ * font property files.
+ */
+public class AndroidFontProperty extends FontProperty {
+
+ /** xlfd string that is applicable for Linux font.properties */
+ String xlfd;
+
+ /** logical name of the font corresponding to this FontProperty */
+ String logicalName;
+
+ /** style name of the font corresponding to this FontProperty */
+ String styleName;
+
+ public AndroidFontProperty(String _logicalName, String _styleName, String _fileName, String _name, String _xlfd, int _style, int[] exclusionRange, String _encoding){
+ this.logicalName = _logicalName;
+ this.styleName = _styleName;
+ this.name = _name;
+ this.encoding = _encoding;
+ this.exclRange = exclusionRange;
+ this.fileName = _fileName;
+ this.xlfd = _xlfd;
+ this.style = _style;
+ }
+
+ /**
+ * Returns logical name of the font corresponding to this FontProperty.
+ */
+ public String getLogicalName(){
+ return logicalName;
+ }
+
+ /**
+ * Returns style name of the font corresponding to this FontProperty.
+ */
+ public String getStyleName(){
+ return styleName;
+ }
+
+ /**
+ * Returns xlfd string of this FontProperty.
+ */
+ public String getXLFD(){
+ return xlfd;
+ }
+
+ public String toString(){
+ return new String(this.getClass().getName() +
+ "[name=" + name + //$NON-NLS-1$
+ ",fileName="+ fileName + //$NON-NLS-1$
+ ",Charset=" + encoding + //$NON-NLS-1$
+ ",exclRange=" + exclRange + //$NON-NLS-1$
+ ",xlfd=" + xlfd + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java b/awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java
new file mode 100644
index 0000000..4ce5aed
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java
@@ -0,0 +1,219 @@
+package org.apache.harmony.awt.gl.font;
+
+import com.android.internal.awt.AndroidGraphics2D;
+
+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.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import android.util.Log;
+import android.graphics.Path;
+
+public class AndroidGlyphVector extends GlyphVector {
+
+ // array of chars defined in constructor
+ public char[] charVector;
+
+ // array of Glyph objects, that describe information about glyphs
+ public Glyph[] vector;
+
+ // array of default positions of glyphs in GlyphVector
+ // without applying GlyphVector's transform
+ float[] defaultPositions;
+
+ // array of logical positions of glyphs in GlyphVector
+
+ float[] logicalPositions;
+
+ // array of visual (real) positions of glyphs in GlyphVector
+ public float[] visualPositions;
+
+ // FontRenderContext for this vector.
+ protected FontRenderContext vectorFRC;
+
+ // layout flags mask
+ protected int layoutFlags = 0;
+
+ // array of cached glyph outlines
+ protected Shape[] gvShapes;
+
+ FontPeerImpl peer;
+
+ // font corresponding to the GlyphVector
+ Font font;
+
+ // ascent of the font
+ float ascent;
+
+ // height of the font
+ float height;
+
+ // leading of the font
+ float leading;
+
+ // descent of the font
+ float descent;
+
+ // transform of the GlyphVector
+ AffineTransform transform;
+
+ @SuppressWarnings("deprecation")
+ public AndroidGlyphVector(char[] chars, FontRenderContext frc, Font fnt,
+ int flags) {
+ int len = chars.length;
+ this.font = fnt;
+ LineMetricsImpl lmImpl = (LineMetricsImpl)fnt.getLineMetrics(String.valueOf(chars), frc);
+ this.ascent = lmImpl.getAscent();
+ this.height = lmImpl.getHeight();
+ this.leading = lmImpl.getLeading();
+ this.descent = lmImpl.getDescent();
+ this.charVector = chars;
+ this.vectorFRC = frc;
+ }
+
+ public AndroidGlyphVector(char[] chars, FontRenderContext frc, Font fnt) {
+ this(chars, frc, fnt, 0);
+ }
+
+ public AndroidGlyphVector(String str, FontRenderContext frc, Font fnt) {
+ this(str.toCharArray(), frc, fnt, 0);
+ }
+
+ public AndroidGlyphVector(String str, FontRenderContext frc, Font fnt, int flags) {
+ this(str.toCharArray(), frc, fnt, flags);
+ }
+
+ @Override
+ public boolean equals(GlyphVector glyphVector) {
+ return false;
+ }
+
+ public char[] getGlyphs() {
+ return this.charVector;
+ }
+
+ @Override
+ public Font getFont() {
+ return this.font;
+ }
+
+ @Override
+ public FontRenderContext getFontRenderContext() {
+ return this.vectorFRC;
+ }
+
+ @Override
+ public int getGlyphCode(int glyphIndex) {
+ return charVector[glyphIndex];
+ }
+
+ @Override
+ public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
+ int[] codeReturn) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public Shape getGlyphLogicalBounds(int glyphIndex) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public GlyphMetrics getGlyphMetrics(int glyphIndex) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ public Path getAndroidGlyphOutline(int glyphIndex) {
+ AndroidGraphics2D g = AndroidGraphics2D.getInstance();
+ Path path = new Path();
+ char tmp[] = new char[1];
+ tmp[0] = charVector[glyphIndex];
+ ((AndroidGraphics2D)g).getAndroidPaint().getTextPath(new String(tmp), 0, 1, 0, 0, path);
+ return path;
+ }
+
+ @Override
+ public Shape getGlyphOutline(int glyphIndex) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public Point2D getGlyphPosition(int glyphIndex) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
+ float[] positionReturn) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public AffineTransform getGlyphTransform(int glyphIndex) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public Shape getGlyphVisualBounds(int glyphIndex) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public Rectangle2D getLogicalBounds() {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public int getNumGlyphs() {
+ return charVector.length;
+ }
+
+ @Override
+ public Shape getOutline(float x, float y) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public Shape getOutline() {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ public Path getAndroidOutline() {
+ AndroidGraphics2D g = AndroidGraphics2D.getInstance();
+ Path path = new Path();
+ ((AndroidGraphics2D)g).getAndroidPaint().getTextPath(new String(charVector), 0, charVector.length, 0, 0, path);
+ return path;
+ }
+
+ @Override
+ public Rectangle2D getVisualBounds() {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public void performDefaultLayout() {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public void setGlyphPosition(int glyphIndex, Point2D newPos) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+ @Override
+ public void setGlyphTransform(int glyphIndex, AffineTransform trans) {
+ throw new RuntimeException("Not implemented!");
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java b/awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java
new file mode 100644
index 0000000..f37be6d
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.font.FontRenderContext;
+import org.apache.harmony.awt.gl.font.LineMetricsImpl;
+
+
+/**
+ *
+ * Linux implementation of LineMetrics class
+ */
+public class AndroidLineMetrics extends LineMetricsImpl {
+
+ /**
+ * Constructor
+ */
+ public AndroidLineMetrics( AndroidFont fnt,
+ FontRenderContext frc,
+ String str){
+ numChars = str.length();
+ baseLineIndex = 0;
+
+ ascent = fnt.ascent; // Ascent of the font
+ descent = -fnt.descent; // Descent of the font
+ leading = fnt.leading; // External leading
+
+ height = ascent + descent + leading; // Height of the font ( == (ascent + descent + leading))
+ underlineThickness = 0.0f;
+ underlineOffset = 0.0f;
+ strikethroughThickness = 0.0f;
+ strikethroughOffset = 0.0f;
+ maxCharWidth = 0.0f;
+
+ // TODO: Find out pixel metrics
+ /*
+ * positive metrics rounded to the smallest int that is bigger than value
+ * negative metrics rounded to the smallest int that is lesser than value
+ * thicknesses rounded to int ((int)round(value + 0.5))
+ *
+ */
+
+ lAscent = (int)Math.ceil(fnt.ascent);// // Ascent of the font
+ lDescent = -(int)Math.ceil(fnt.descent);// Descent of the font
+ lLeading = (int)Math.ceil(leading); // External leading
+
+ lHeight = lAscent + lDescent + lLeading; // Height of the font ( == (ascent + descent + leading))
+
+ lUnderlineThickness = Math.round(underlineThickness);//(int)metrics[11];
+
+ if (underlineOffset >= 0){
+ lUnderlineOffset = (int)Math.ceil(underlineOffset);
+ } else {
+ lUnderlineOffset = (int)Math.floor(underlineOffset);
+ }
+
+ lStrikethroughThickness = Math.round(strikethroughThickness); //(int)metrics[13];
+
+ if (strikethroughOffset >= 0){
+ lStrikethroughOffset = (int)Math.ceil(strikethroughOffset);
+ } else {
+ lStrikethroughOffset = (int)Math.floor(strikethroughOffset);
+ }
+
+ lMaxCharWidth = (int)Math.ceil(maxCharWidth); //(int)metrics[15];
+ units_per_EM = 0;
+
+ }
+
+ public float[] getBaselineOffsets() {
+ // TODO: implement baseline offsets for TrueType fonts
+ if (baselineOffsets == null){
+ float[] baselineData = null;
+
+ // Temporary workaround:
+ // Commented out native data initialization, since it can
+ // cause failures with opening files in multithreaded applications.
+ //
+ // TODO: support work with truetype data in multithreaded
+ // applications.
+
+ // If font TrueType data is taken from BASE table
+// if ((this.font.getFontHandle() != 0) && (font.getFontType() == FontManager.FONT_TYPE_TT)){
+// baselineData = LinuxNativeFont.getBaselineOffsetsNative(font.getFontHandle(), font.getSize(), ascent, descent, units_per_EM);
+// }
+//
+ baseLineIndex = 0;
+ baselineOffsets = new float[]{0, (-ascent+descent)/2, -ascent};
+ }
+
+ return baselineOffsets;
+ }
+
+ public int getBaselineIndex() {
+ if (baselineOffsets == null){
+ // get offsets and set correct index
+ getBaselineOffsets();
+ }
+ return baseLineIndex;
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/BasicMetrics.java b/awt/org/apache/harmony/awt/gl/font/BasicMetrics.java
new file mode 100644
index 0000000..c0fb390
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/BasicMetrics.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.font.LineMetrics;
+import java.awt.font.GraphicAttribute;
+import java.awt.*;
+
+/**
+ * Date: May 14, 2005
+ * Time: 7:44:13 PM
+ *
+ * This class incapsulates text metrics specific for the text layout or
+ * for the separate text segment. Text segment is a text run with the constant direction
+ * and attributes like font, decorations, etc. BasicMetrics is also used to store
+ * calculated text metrics like advance, ascent or descent. this class is very similar to
+ * LineMetrics, but provides some additional info, constructors and is more transparent.
+ */
+public class BasicMetrics {
+ int baseLineIndex;
+
+ float ascent; // Ascent of the font
+ float descent; // Descent of the font
+ float leading; // External leading
+ float advance;
+
+ float italicAngle;
+ float superScriptOffset;
+
+ float underlineOffset;
+ float underlineThickness;
+
+ float strikethroughOffset;
+ float strikethroughThickness;
+
+ /**
+ * Constructs BasicMetrics from LineMetrics and font
+ * @param lm
+ * @param font
+ */
+ BasicMetrics(LineMetrics lm, Font font) {
+ ascent = lm.getAscent();
+ descent = lm.getDescent();
+ leading = lm.getLeading();
+
+ underlineOffset = lm.getUnderlineOffset();
+ underlineThickness = lm.getUnderlineThickness();
+
+ strikethroughOffset = lm.getStrikethroughOffset();
+ strikethroughThickness = lm.getStrikethroughThickness();
+
+ baseLineIndex = lm.getBaselineIndex();
+
+ italicAngle = font.getItalicAngle();
+ superScriptOffset = (float) font.getTransform().getTranslateY();
+ }
+
+ /**
+ * Constructs BasicMetrics from GraphicAttribute.
+ * It gets ascent and descent from the graphic attribute and
+ * computes reasonable defaults for other metrics.
+ * @param ga - graphic attribute
+ */
+ BasicMetrics(GraphicAttribute ga) {
+ ascent = ga.getAscent();
+ descent = ga.getDescent();
+ leading = 2;
+
+ baseLineIndex = ga.getAlignment();
+
+ italicAngle = 0;
+ superScriptOffset = 0;
+
+ underlineOffset = Math.max(descent/2, 1);
+
+ // Just suggested, should be cap_stem_width or something like that
+ underlineThickness = Math.max(ascent/13, 1);
+
+ strikethroughOffset = -ascent/2; // Something like middle of the line
+ strikethroughThickness = underlineThickness;
+ }
+
+ /**
+ * Copies metrics from the TextMetricsCalculator object.
+ * @param tmc - TextMetricsCalculator object
+ */
+ BasicMetrics(TextMetricsCalculator tmc) {
+ ascent = tmc.ascent;
+ descent = tmc.descent;
+ leading = tmc.leading;
+ advance = tmc.advance;
+ baseLineIndex = tmc.baselineIndex;
+ }
+
+ public float getAscent() {
+ return ascent;
+ }
+
+ public float getDescent() {
+ return descent;
+ }
+
+ public float getLeading() {
+ return leading;
+ }
+
+ public float getAdvance() {
+ return advance;
+ }
+
+ public int getBaseLineIndex() {
+ return baseLineIndex;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/CaretManager.java b/awt/org/apache/harmony/awt/gl/font/CaretManager.java
new file mode 100644
index 0000000..b18bdd5
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/CaretManager.java
@@ -0,0 +1,530 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ * @date: Jun 14, 2005
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.font.TextHitInfo;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.*;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * This class provides functionality for creating caret and highlight shapes
+ * (bidirectional text is also supported, but, unfortunately, not tested yet).
+ */
+public class CaretManager {
+ private TextRunBreaker breaker;
+
+ public CaretManager(TextRunBreaker breaker) {
+ this.breaker = breaker;
+ }
+
+ /**
+ * Checks if TextHitInfo is not out of the text range and throws the
+ * IllegalArgumentException if it is.
+ * @param info - text hit info
+ */
+ private void checkHit(TextHitInfo info) {
+ int idx = info.getInsertionIndex();
+
+ if (idx < 0 || idx > breaker.getCharCount()) {
+ // awt.42=TextHitInfo out of range
+ throw new IllegalArgumentException(Messages.getString("awt.42")); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Calculates and returns visual position from the text hit info.
+ * @param hitInfo - text hit info
+ * @return visual index
+ */
+ private int getVisualFromHitInfo(TextHitInfo hitInfo) {
+ final int idx = hitInfo.getCharIndex();
+
+ if (idx >= 0 && idx < breaker.getCharCount()) {
+ int visual = breaker.getVisualFromLogical(idx);
+ // We take next character for (LTR char + TRAILING info) and (RTL + LEADING)
+ if (hitInfo.isLeadingEdge() ^ ((breaker.getLevel(idx) & 0x1) == 0x0)) {
+ visual++;
+ }
+ return visual;
+ } else if (idx < 0) {
+ return breaker.isLTR() ? 0: breaker.getCharCount();
+ } else {
+ return breaker.isLTR() ? breaker.getCharCount() : 0;
+ }
+ }
+
+ /**
+ * Calculates text hit info from the visual position
+ * @param visual - visual position
+ * @return text hit info
+ */
+ private TextHitInfo getHitInfoFromVisual(int visual) {
+ final boolean first = visual == 0;
+
+ if (!(first || visual == breaker.getCharCount())) {
+ int logical = breaker.getLogicalFromVisual(visual);
+ return (breaker.getLevel(logical) & 0x1) == 0x0 ?
+ TextHitInfo.leading(logical) : // LTR
+ TextHitInfo.trailing(logical); // RTL
+ } else if (first) {
+ return breaker.isLTR() ?
+ TextHitInfo.trailing(-1) :
+ TextHitInfo.leading(breaker.getCharCount());
+ } else { // Last
+ return breaker.isLTR() ?
+ TextHitInfo.leading(breaker.getCharCount()) :
+ TextHitInfo.trailing(-1);
+ }
+ }
+
+ /**
+ * Creates caret info. Required for the getCaretInfo
+ * methods of the TextLayout
+ * @param hitInfo - specifies caret position
+ * @return caret info, see TextLayout.getCaretInfo documentation
+ */
+ public float[] getCaretInfo(TextHitInfo hitInfo) {
+ checkHit(hitInfo);
+ float res[] = new float[2];
+
+ int visual = getVisualFromHitInfo(hitInfo);
+ float advance, angle;
+ TextRunSegment seg;
+
+ if (visual < breaker.getCharCount()) {
+ int logIdx = breaker.getLogicalFromVisual(visual);
+ int segmentIdx = breaker.logical2segment[logIdx];
+ seg = breaker.runSegments.get(segmentIdx);
+ advance = seg.x + seg.getAdvanceDelta(seg.getStart(), logIdx);
+ angle = seg.metrics.italicAngle;
+
+ } else { // Last character
+ int logIdx = breaker.getLogicalFromVisual(visual-1);
+ int segmentIdx = breaker.logical2segment[logIdx];
+ seg = breaker.runSegments.get(segmentIdx);
+ advance = seg.x + seg.getAdvanceDelta(seg.getStart(), logIdx+1);
+ }
+
+ angle = seg.metrics.italicAngle;
+
+ res[0] = advance;
+ res[1] = angle;
+
+ return res;
+ }
+
+ /**
+ * Returns the next position to the right from the current caret position
+ * @param hitInfo - current position
+ * @return next position to the right
+ */
+ public TextHitInfo getNextRightHit(TextHitInfo hitInfo) {
+ checkHit(hitInfo);
+ int visual = getVisualFromHitInfo(hitInfo);
+
+ if (visual == breaker.getCharCount()) {
+ return null;
+ }
+
+ TextHitInfo newInfo;
+
+ while(visual <= breaker.getCharCount()) {
+ visual++;
+ newInfo = getHitInfoFromVisual(visual);
+
+ if (newInfo.getCharIndex() >= breaker.logical2segment.length) {
+ return newInfo;
+ }
+
+ if (hitInfo.getCharIndex() >= 0) { // Don't check for leftmost info
+ if (
+ breaker.logical2segment[newInfo.getCharIndex()] !=
+ breaker.logical2segment[hitInfo.getCharIndex()]
+ ) {
+ return newInfo; // We crossed segment boundary
+ }
+ }
+
+ TextRunSegment seg = breaker.runSegments.get(breaker.logical2segment[newInfo
+ .getCharIndex()]);
+ if (!seg.charHasZeroAdvance(newInfo.getCharIndex())) {
+ return newInfo;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the next position to the left from the current caret position
+ * @param hitInfo - current position
+ * @return next position to the left
+ */
+ public TextHitInfo getNextLeftHit(TextHitInfo hitInfo) {
+ checkHit(hitInfo);
+ int visual = getVisualFromHitInfo(hitInfo);
+
+ if (visual == 0) {
+ return null;
+ }
+
+ TextHitInfo newInfo;
+
+ while(visual >= 0) {
+ visual--;
+ newInfo = getHitInfoFromVisual(visual);
+
+ if (newInfo.getCharIndex() < 0) {
+ return newInfo;
+ }
+
+ // Don't check for rightmost info
+ if (hitInfo.getCharIndex() < breaker.logical2segment.length) {
+ if (
+ breaker.logical2segment[newInfo.getCharIndex()] !=
+ breaker.logical2segment[hitInfo.getCharIndex()]
+ ) {
+ return newInfo; // We crossed segment boundary
+ }
+ }
+
+ TextRunSegment seg = breaker.runSegments.get(breaker.logical2segment[newInfo
+ .getCharIndex()]);
+ if (!seg.charHasZeroAdvance(newInfo.getCharIndex())) {
+ return newInfo;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * For each visual caret position there are two hits. For the simple LTR text one is
+ * a trailing of the previous char and another is the leading of the next char. This
+ * method returns the opposite hit for the given hit.
+ * @param hitInfo - given hit
+ * @return opposite hit
+ */
+ public TextHitInfo getVisualOtherHit(TextHitInfo hitInfo) {
+ checkHit(hitInfo);
+
+ int idx = hitInfo.getCharIndex();
+
+ int resIdx;
+ boolean resIsLeading;
+
+ if (idx >= 0 && idx < breaker.getCharCount()) { // Hit info in the middle
+ int visual = breaker.getVisualFromLogical(idx);
+
+ // Char is LTR + LEADING info
+ if (((breaker.getLevel(idx) & 0x1) == 0x0) ^ hitInfo.isLeadingEdge()) {
+ visual++;
+ if (visual == breaker.getCharCount()) {
+ if (breaker.isLTR()) {
+ resIdx = breaker.getCharCount();
+ resIsLeading = true;
+ } else {
+ resIdx = -1;
+ resIsLeading = false;
+ }
+ } else {
+ resIdx = breaker.getLogicalFromVisual(visual);
+ if ((breaker.getLevel(resIdx) & 0x1) == 0x0) {
+ resIsLeading = true;
+ } else {
+ resIsLeading = false;
+ }
+ }
+ } else {
+ visual--;
+ if (visual == -1) {
+ if (breaker.isLTR()) {
+ resIdx = -1;
+ resIsLeading = false;
+ } else {
+ resIdx = breaker.getCharCount();
+ resIsLeading = true;
+ }
+ } else {
+ resIdx = breaker.getLogicalFromVisual(visual);
+ if ((breaker.getLevel(resIdx) & 0x1) == 0x0) {
+ resIsLeading = false;
+ } else {
+ resIsLeading = true;
+ }
+ }
+ }
+ } else if (idx < 0) { // before "start"
+ if (breaker.isLTR()) {
+ resIdx = breaker.getLogicalFromVisual(0);
+ resIsLeading = (breaker.getLevel(resIdx) & 0x1) == 0x0; // LTR char?
+ } else {
+ resIdx = breaker.getLogicalFromVisual(breaker.getCharCount() - 1);
+ resIsLeading = (breaker.getLevel(resIdx) & 0x1) != 0x0; // RTL char?
+ }
+ } else { // idx == breaker.getCharCount()
+ if (breaker.isLTR()) {
+ resIdx = breaker.getLogicalFromVisual(breaker.getCharCount() - 1);
+ resIsLeading = (breaker.getLevel(resIdx) & 0x1) != 0x0; // LTR char?
+ } else {
+ resIdx = breaker.getLogicalFromVisual(0);
+ resIsLeading = (breaker.getLevel(resIdx) & 0x1) == 0x0; // RTL char?
+ }
+ }
+
+ return resIsLeading ? TextHitInfo.leading(resIdx) : TextHitInfo.trailing(resIdx);
+ }
+
+ public Line2D getCaretShape(TextHitInfo hitInfo, TextLayout layout) {
+ return getCaretShape(hitInfo, layout, true, false, null);
+ }
+
+ /**
+ * Creates a caret shape.
+ * @param hitInfo - hit where to place a caret
+ * @param layout - text layout
+ * @param useItalic - unused for now, was used to create
+ * slanted carets for italic text
+ * @param useBounds - true if the cared should fit into the provided bounds
+ * @param bounds - bounds for the caret
+ * @return caret shape
+ */
+ public Line2D getCaretShape(
+ TextHitInfo hitInfo, TextLayout layout,
+ boolean useItalic, boolean useBounds, Rectangle2D bounds
+ ) {
+ checkHit(hitInfo);
+
+ float x1, x2, y1, y2;
+
+ int charIdx = hitInfo.getCharIndex();
+
+ if (charIdx >= 0 && charIdx < breaker.getCharCount()) {
+ TextRunSegment segment = breaker.runSegments.get(breaker.logical2segment[charIdx]);
+ y1 = segment.metrics.descent;
+ y2 = - segment.metrics.ascent - segment.metrics.leading;
+
+ x1 = x2 = segment.getCharPosition(charIdx) + (hitInfo.isLeadingEdge() ?
+ 0 : segment.getCharAdvance(charIdx));
+ // Decided that straight cursor looks better even for italic fonts,
+ // especially combined with highlighting
+ /*
+ // Not graphics, need to check italic angle and baseline
+ if (layout.getBaseline() >= 0) {
+ if (segment.metrics.italicAngle != 0 && useItalic) {
+ x1 -= segment.metrics.italicAngle * segment.metrics.descent;
+ x2 += segment.metrics.italicAngle *
+ (segment.metrics.ascent + segment.metrics.leading);
+
+ float baselineOffset =
+ layout.getBaselineOffsets()[layout.getBaseline()];
+ y1 += baselineOffset;
+ y2 += baselineOffset;
+ }
+ }
+ */
+ } else {
+ y1 = layout.getDescent();
+ y2 = - layout.getAscent() - layout.getLeading();
+ x1 = x2 = ((breaker.getBaseLevel() & 0x1) == 0 ^ charIdx < 0) ?
+ layout.getAdvance() : 0;
+ }
+
+ if (useBounds) {
+ y1 = (float) bounds.getMaxY();
+ y2 = (float) bounds.getMinY();
+
+ if (x2 > bounds.getMaxX()) {
+ x1 = x2 = (float) bounds.getMaxX();
+ }
+ if (x1 < bounds.getMinX()) {
+ x1 = x2 = (float) bounds.getMinX();
+ }
+ }
+
+ return new Line2D.Float(x1, y1, x2, y2);
+ }
+
+ /**
+ * Creates caret shapes for the specified offset. On the boundaries where
+ * the text is changing its direction this method may return two shapes
+ * for the strong and the weak carets, in other cases it would return one.
+ * @param offset - offset in the text.
+ * @param bounds - bounds to fit the carets into
+ * @param policy - caret policy
+ * @param layout - text layout
+ * @return one or two caret shapes
+ */
+ public Shape[] getCaretShapes(
+ int offset, Rectangle2D bounds,
+ TextLayout.CaretPolicy policy, TextLayout layout
+ ) {
+ TextHitInfo hit1 = TextHitInfo.afterOffset(offset);
+ TextHitInfo hit2 = getVisualOtherHit(hit1);
+
+ Shape caret1 = getCaretShape(hit1, layout);
+
+ if (getVisualFromHitInfo(hit1) == getVisualFromHitInfo(hit2)) {
+ return new Shape[] {caret1, null};
+ }
+ Shape caret2 = getCaretShape(hit2, layout);
+
+ TextHitInfo strongHit = policy.getStrongCaret(hit1, hit2, layout);
+ return strongHit.equals(hit1) ?
+ new Shape[] {caret1, caret2} :
+ new Shape[] {caret2, caret1};
+ }
+
+ /**
+ * Connects two carets to produce a highlight shape.
+ * @param caret1 - 1st caret
+ * @param caret2 - 2nd caret
+ * @return highlight shape
+ */
+ GeneralPath connectCarets(Line2D caret1, Line2D caret2) {
+ GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO);
+ path.moveTo((float) caret1.getX1(), (float) caret1.getY1());
+ path.lineTo((float) caret2.getX1(), (float) caret2.getY1());
+ path.lineTo((float) caret2.getX2(), (float) caret2.getY2());
+ path.lineTo((float) caret1.getX2(), (float) caret1.getY2());
+
+ path.closePath();
+
+ return path;
+ }
+
+ /**
+ * Creates a highlight shape from given two hits. This shape
+ * will always be visually contiguous
+ * @param hit1 - 1st hit
+ * @param hit2 - 2nd hit
+ * @param bounds - bounds to fit the shape into
+ * @param layout - text layout
+ * @return highlight shape
+ */
+ public Shape getVisualHighlightShape(
+ TextHitInfo hit1, TextHitInfo hit2,
+ Rectangle2D bounds, TextLayout layout
+ ) {
+ checkHit(hit1);
+ checkHit(hit2);
+
+ Line2D caret1 = getCaretShape(hit1, layout, false, true, bounds);
+ Line2D caret2 = getCaretShape(hit2, layout, false, true, bounds);
+
+ return connectCarets(caret1, caret2);
+ }
+
+ /**
+ * Suppose that the user visually selected a block of text which has
+ * several different levels (mixed RTL and LTR), so, in the logical
+ * representation of the text this selection may be not contigous.
+ * This methods returns a set of logical ranges for the arbitrary
+ * visual selection represented by two hits.
+ * @param hit1 - 1st hit
+ * @param hit2 - 2nd hit
+ * @return logical ranges for the selection
+ */
+ public int[] getLogicalRangesForVisualSelection(TextHitInfo hit1, TextHitInfo hit2) {
+ checkHit(hit1);
+ checkHit(hit2);
+
+ int visual1 = getVisualFromHitInfo(hit1);
+ int visual2 = getVisualFromHitInfo(hit2);
+
+ if (visual1 > visual2) {
+ int tmp = visual2;
+ visual2 = visual1;
+ visual1 = tmp;
+ }
+
+ // Max level is 255, so we don't need more than 512 entries
+ int results[] = new int[512];
+
+ int prevLogical, logical, runStart, numRuns = 0;
+
+ logical = runStart = prevLogical = breaker.getLogicalFromVisual(visual1);
+
+ // Get all the runs. We use the fact that direction is constant in all runs.
+ for (int i=visual1+1; i<=visual2; i++) {
+ logical = breaker.getLogicalFromVisual(i);
+ int diff = logical-prevLogical;
+
+ // Start of the next run encountered
+ if (diff > 1 || diff < -1) {
+ results[(numRuns)*2] = Math.min(runStart, prevLogical);
+ results[(numRuns)*2 + 1] = Math.max(runStart, prevLogical);
+ numRuns++;
+ runStart = logical;
+ }
+
+ prevLogical = logical;
+ }
+
+ // The last unsaved run
+ results[(numRuns)*2] = Math.min(runStart, logical);
+ results[(numRuns)*2 + 1] = Math.max(runStart, logical);
+ numRuns++;
+
+ int retval[] = new int[numRuns*2];
+ System.arraycopy(results, 0, retval, 0, numRuns*2);
+ return retval;
+ }
+
+ /**
+ * Creates a highlight shape from given two endpoints in the logical
+ * representation. This shape is not always visually contiguous
+ * @param firstEndpoint - 1st logical endpoint
+ * @param secondEndpoint - 2nd logical endpoint
+ * @param bounds - bounds to fit the shape into
+ * @param layout - text layout
+ * @return highlight shape
+ */
+ public Shape getLogicalHighlightShape(
+ int firstEndpoint, int secondEndpoint,
+ Rectangle2D bounds, TextLayout layout
+ ) {
+ GeneralPath res = new GeneralPath();
+
+ for (int i=firstEndpoint; i<=secondEndpoint; i++) {
+ int endRun = breaker.getLevelRunLimit(i, secondEndpoint);
+ TextHitInfo hit1 = TextHitInfo.leading(i);
+ TextHitInfo hit2 = TextHitInfo.trailing(endRun-1);
+
+ Line2D caret1 = getCaretShape(hit1, layout, false, true, bounds);
+ Line2D caret2 = getCaretShape(hit2, layout, false, true, bounds);
+
+ res.append(connectCarets(caret1, caret2), false);
+
+ i = endRun;
+ }
+
+ return res;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java b/awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java
new file mode 100644
index 0000000..4040a60
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java
@@ -0,0 +1,954 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphJustificationInfo;
+import java.awt.font.GlyphMetrics;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * GlyphVector implementation
+ */
+public class CommonGlyphVector extends GlyphVector {
+
+ // array of transforms of glyphs in GlyphVector
+ protected AffineTransform[] glsTransforms;
+
+ // array of chars defined in constructor
+ public char[] charVector;
+
+ // array of Glyph objects, that describe information about glyphs
+ public Glyph[] vector;
+
+ // array of default positions of glyphs in GlyphVector
+ // without applying GlyphVector's transform
+ float[] defaultPositions;
+
+ // array of logical positions of glyphs in GlyphVector
+
+ float[] logicalPositions;
+
+ // array of visual (real) positions of glyphs in GlyphVector
+ public float[] visualPositions;
+
+ // FontRenderContext for this vector.
+ protected FontRenderContext vectorFRC;
+
+ // layout flags mask
+ protected int layoutFlags = 0;
+
+ // array of cached glyph outlines
+ protected Shape[] gvShapes;
+
+ FontPeerImpl peer;
+
+ // font corresponding to the GlyphVector
+ Font font;
+
+ // ascent of the font
+ float ascent;
+
+ // height of the font
+ float height;
+
+ // leading of the font
+ float leading;
+
+ // descent of the font
+ float descent;
+
+ // transform of the GlyphVector
+ AffineTransform transform;
+
+ /**
+ * Creates new CommonGlyphVector object from the specified parameters.
+ *
+ * @param chars an array of chars
+ * @param frc FontRenderContext object
+ * @param fnt Font object
+ * @param flags layout flags
+ */
+ @SuppressWarnings("deprecation")
+ public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt,
+ int flags) {
+ int len = chars.length;
+
+ this.font = fnt;
+ this.transform = fnt.getTransform();
+ this.peer = (FontPeerImpl) fnt.getPeer();
+
+ gvShapes = new Shape[len];
+
+ // !! As pointed in API documentation for the
+ // getGlyphPosisitions(int index,int numEntries, float[] positionReturn)
+ // and getGlyphPosition(int index) methods, if the index is equals to
+ // the number of glyphs the position after the last glyph must be
+ // returned, thus there are n+1 positions and last (n+1) position
+ // points to the end of GlyphVector.
+
+ logicalPositions = new float[(len+1)<<1];
+ visualPositions = new float[(len+1)<<1];
+ defaultPositions = new float[(len+1)<<1];
+
+ glsTransforms = new AffineTransform[len];
+
+ this.charVector = chars;
+ this.vectorFRC = frc;
+ //LineMetricsImpl lmImpl = (LineMetricsImpl)peer.getLineMetrics();
+
+ LineMetricsImpl lmImpl = (LineMetricsImpl)fnt.getLineMetrics(String.valueOf(chars), frc);
+
+ this.ascent = lmImpl.getAscent();
+ this.height = lmImpl.getHeight();
+ this.leading = lmImpl.getLeading();
+ this.descent = lmImpl.getDescent();
+ this.layoutFlags = flags;
+
+ if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0){
+ char vector[] = new char[len];
+ for(int i=0; i < len; i++){
+ vector[i] = chars[len-i-1];
+ }
+ this.vector = peer.getGlyphs(vector);
+
+ } else {
+ this.vector = peer.getGlyphs(chars);
+ }
+
+ this.glsTransforms = new AffineTransform[len];
+
+ setDefaultPositions();
+ performDefaultLayout();
+ }
+
+ /**
+ * Creates new CommonGlyphVector object from the specified parameters.
+ * Layout flags set to default.
+ *
+ * @param chars an array of chars
+ * @param frc FontRenderContext object
+ * @param fnt Font object
+ */
+ public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt) {
+ this(chars, frc, fnt, 0);
+ }
+
+ /**
+ * Creates new CommonGlyphVector object from the specified parameters.
+ * Layout flags set to default.
+ *
+ * @param str specified string
+ * @param frc FontRenderContext object
+ * @param fnt Font object
+ */
+ public CommonGlyphVector(String str, FontRenderContext frc, Font fnt) {
+ this(str.toCharArray(), frc, fnt, 0);
+ }
+
+ /**
+ * Creates new CommonGlyphVector object from the specified parameters.
+ *
+ * @param str specified string
+ * @param frc FontRenderContext object
+ * @param fnt Font object
+ * @param flags layout flags
+ */
+ public CommonGlyphVector(String str, FontRenderContext frc, Font fnt, int flags) {
+ this(str.toCharArray(), frc, fnt, flags);
+ }
+
+ /**
+ * Set array of logical positions of the glyphs to
+ * default with their default advances and height.
+ */
+ void setDefaultPositions(){
+ int len = getNumGlyphs();
+
+ // First [x,y] is set into [0,0] position
+ // for this reason start index is 1
+ for (int i=1; i <= len; i++ ){
+ int idx = i << 1;
+ float advanceX = vector[i-1].getGlyphPointMetrics().getAdvanceX();
+ float advanceY = vector[i-1].getGlyphPointMetrics().getAdvanceY();
+
+ defaultPositions[idx] = defaultPositions[idx-2] + advanceX;
+ defaultPositions[idx+1] = defaultPositions[idx-1] + advanceY;
+
+ }
+ transform.transform(defaultPositions, 0, logicalPositions, 0, getNumGlyphs()+1);
+
+ }
+
+ /**
+ * Returnes the pixel bounds of this GlyphVector rendered at the
+ * specified x,y location with the given FontRenderContext.
+ *
+ * @param frc a FontRenderContext that is used
+ * @param x specified x coordinate value
+ * @param y specified y coordinate value
+ * @return a Rectangle that bounds pixels of this GlyphVector
+ */
+ @Override
+ public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
+
+ double xM, yM, xm, ym;
+
+ double minX = 0;
+ double minY = 0;
+ double maxX = 0;
+ double maxY = 0;
+
+ for (int i = 0; i < this.getNumGlyphs(); i++) {
+ Rectangle glyphBounds = this.getGlyphPixelBounds(i, frc, 0, 0);
+ xm = glyphBounds.getMinX();
+ ym = glyphBounds.getMinY();
+ xM = glyphBounds.getMaxX();
+ yM = glyphBounds.getMaxY();
+
+ if (i == 0) {
+ minX = xm;
+ minY = ym;
+ maxX = xM;
+ maxY = yM;
+ }
+
+ if (minX > xm) {
+ minX = xm;
+ }
+ if (minY > ym) {
+ minY = ym;
+ }
+ if (maxX < xM) {
+ maxX = xM;
+ }
+ if (maxY < yM) {
+ maxY = yM;
+ }
+ }
+ return new Rectangle((int)(minX + x), (int)(minY + y), (int)(maxX - minX), (int)(maxY - minY));
+
+ }
+
+ /**
+ * Returns the visual bounds of this GlyphVector.
+ * The visual bounds is the bounds of the total outline of
+ * this GlyphVector.
+ * @return a Rectangle2D that id the visual bounds of this GlyphVector
+ */
+ @Override
+ public Rectangle2D getVisualBounds() {
+ float xM, yM, xm, ym;
+ float minX = 0;
+ float minY = 0;
+ float maxX = 0;
+ float maxY = 0;
+ boolean firstIteration = true;
+
+ for (int i = 0; i < this.getNumGlyphs(); i++) {
+ Rectangle2D bounds = this.getGlyphVisualBounds(i).getBounds2D();
+ if (bounds.getWidth() == 0){
+ continue;
+ }
+ xm = (float)bounds.getX();
+ ym = (float)bounds.getY();
+
+ xM = (float)(xm + bounds.getWidth());
+
+ yM = ym + (float) bounds.getHeight();
+
+ if (firstIteration) {
+ minX = xm;
+ minY = ym;
+ maxX = xM;
+ maxY = yM;
+ firstIteration = false;
+ } else {
+ if (minX > xm) {
+ minX = xm;
+ }
+ if (minY > ym) {
+ minY = ym;
+ }
+ if (maxX < xM) {
+ maxX = xM;
+ }
+ if (maxY < yM) {
+ maxY = yM;
+ }
+
+ }
+ }
+
+ return (this.getNumGlyphs() != 0) ? new Rectangle2D.Float(minX, minY,
+ (maxX - minX), (maxY - minY)) : null;
+ }
+
+ /**
+ * Sets new position to the specified glyph.
+ */
+ @Override
+ public void setGlyphPosition(int glyphIndex, Point2D newPos) {
+ if ((glyphIndex > vector.length) || (glyphIndex < 0)) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+ float x = (float)newPos.getX();
+ float y = (float)newPos.getY();
+ int index = glyphIndex << 1;
+
+ if ((x != visualPositions[index]) || (y != visualPositions[index + 1])){
+ visualPositions[index] = x;
+ visualPositions[index+1] = y;
+ layoutFlags = layoutFlags | FLAG_HAS_POSITION_ADJUSTMENTS;
+ }
+
+ }
+
+ /**
+ * Returns the position of the specified glyph relative to the origin of
+ * this GlyphVector
+ * @return a Point2D that the origin of the glyph with specified index
+ */
+ @Override
+ public Point2D getGlyphPosition(int glyphIndex) {
+ if ((glyphIndex > vector.length) || (glyphIndex < 0)) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+ int index = glyphIndex << 1;
+ Point2D pos = new Point2D.Float(visualPositions[index], visualPositions[index+1]);
+
+ // For last position we don't have to transform !!
+ if(glyphIndex==vector.length){
+ return pos;
+ }
+
+ AffineTransform at = getGlyphTransform(glyphIndex);
+ if ((at == null) || (at.isIdentity())){
+ return pos;
+ }
+
+ pos.setLocation(pos.getX() + at.getTranslateX(), pos.getY() + at.getTranslateY());
+
+ return pos;
+ }
+
+ /**
+ * Sets new transform to the specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ * @param trans AffineTransform of the glyph with specified index
+ */
+ @Override
+ public void setGlyphTransform(int glyphIndex, AffineTransform trans) {
+ if ((glyphIndex >= vector.length) || (glyphIndex < 0)) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+
+ if ((trans == null) || (trans.isIdentity())) {
+ glsTransforms[glyphIndex] = null;
+ } else {
+ glsTransforms[glyphIndex] = new AffineTransform(trans);
+ layoutFlags = layoutFlags | FLAG_HAS_TRANSFORMS;
+ }
+ }
+
+ /**
+ * Returns the affine transform of the specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ * @return an AffineTransform of the glyph with specified index
+ */
+ @Override
+ public AffineTransform getGlyphTransform(int glyphIndex) {
+ if ((glyphIndex >= this.vector.length) || (glyphIndex < 0)) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+ return this.glsTransforms[glyphIndex];
+ }
+
+ /**
+ * Returns the metrics of the specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ */
+ @Override
+ public GlyphMetrics getGlyphMetrics(int glyphIndex) {
+
+ if ((glyphIndex < 0) || ((glyphIndex) >= this.getNumGlyphs())) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+ // TODO: is there a sence in GlyphMetrics
+ // if certain glyph or Font has a transform??
+ return this.vector[glyphIndex].getGlyphMetrics();
+ }
+
+ /**
+ * Returns a justification information for the glyph with specified glyph
+ * index.
+ * @param glyphIndex index of a glyph which GlyphJustificationInfo is to be
+ * received
+ * @return a GlyphJustificationInfo object that contains glyph justification
+ * properties of the specified glyph
+ */
+ @Override
+ public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) {
+ // TODO : Find out the source of Justification info
+ if (true) {
+ throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$
+ }
+ return null;
+ }
+
+ /**
+ * Returns the FontRenderContext parameter of this GlyphVector.
+ */
+ @Override
+ public FontRenderContext getFontRenderContext() {
+ return this.vectorFRC;
+ }
+
+ /**
+ * Returns the visual bounds of the specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ */
+ @Override
+ public Shape getGlyphVisualBounds(int glyphIndex) {
+ if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+
+ int idx = glyphIndex << 1;
+
+ AffineTransform fontTransform = this.transform;
+ double xOffs = fontTransform.getTranslateX();
+ double yOffs = fontTransform.getTranslateY();
+
+ if (vector[glyphIndex].getWidth() == 0){
+ return new Rectangle2D.Float((float)xOffs, (float)yOffs, 0, 0);
+ }
+
+ AffineTransform at = AffineTransform.getTranslateInstance(xOffs, yOffs);
+ AffineTransform glyphTransform = getGlyphTransform(glyphIndex);
+
+ if (transform.isIdentity() && ((glyphTransform == null) || glyphTransform.isIdentity())){
+ Rectangle2D blackBox = vector[glyphIndex].getGlyphMetrics().getBounds2D();
+ at.translate(visualPositions[idx], visualPositions[idx+1]);
+ return(at.createTransformedShape(blackBox));
+ }
+
+ GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex);
+ shape.transform(at);
+ return shape.getBounds2D();
+ }
+
+ /**
+ * Returnes the pixel bounds of the specified glyph within GlyphVector
+ * rendered at the specified x,y location.
+ *
+ * @param glyphIndex index of the glyph
+ * @param frc a FontRenderContext that is used
+ * @param x specified x coordinate value
+ * @param y specified y coordinate value
+ * @return a Rectangle that bounds pixels of the specified glyph
+ */
+ @Override
+ public Rectangle getGlyphPixelBounds(int glyphIndex, FontRenderContext frc,
+ float x, float y) {
+ // TODO : need to be implemented with FontRenderContext
+ if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+
+ int idx = glyphIndex << 1;
+
+ if (vector[glyphIndex].getWidth() == 0){
+ AffineTransform fontTransform = this.transform;
+ double xOffs = x + visualPositions[idx] + fontTransform.getTranslateX();
+ double yOffs = y + visualPositions[idx+1] + fontTransform.getTranslateY();
+ return new Rectangle((int)xOffs, (int)yOffs, 0, 0);
+ }
+
+ GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex);
+
+ AffineTransform at = AffineTransform.getTranslateInstance(x, y);
+
+ if (frc != null){
+ at.concatenate(frc.getTransform());
+ }
+
+ shape.transform(at);
+
+ Rectangle bounds = shape.getBounds();
+ return new Rectangle((int)bounds.getX(), (int)bounds.getY(),
+ (int)bounds.getWidth()-1, (int)bounds.getHeight()-1);
+ }
+
+ /**
+ * Returns a Shape that encloses specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ */
+ @Override
+ public Shape getGlyphOutline(int glyphIndex) {
+ if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+
+ if (gvShapes[glyphIndex] == null) {
+ gvShapes[glyphIndex] = vector[glyphIndex].getShape();
+ }
+
+ GeneralPath gp = (GeneralPath)((GeneralPath)gvShapes[glyphIndex]).clone();
+
+ /* Applying GlyphVector font transform */
+ AffineTransform at = (AffineTransform)this.transform.clone();
+
+ /* Applying Glyph transform */
+ AffineTransform glyphAT = getGlyphTransform(glyphIndex);
+ if (glyphAT != null){
+ at.preConcatenate(glyphAT);
+ }
+
+ int idx = glyphIndex << 1;
+
+ gp.transform(at);
+ gp.transform(AffineTransform.getTranslateInstance(visualPositions[idx], visualPositions[idx+1]));
+ return gp;
+ }
+
+
+ /**
+ * Returns a Shape that is the outline representation of this GlyphVector
+ * rendered at the specified x,y coordinates.
+ *
+ * @param x specified x coordinate value
+ * @param y specified y coordinate value
+ * @return a Shape object that is the outline of this GlyphVector
+ * at the specified coordinates.
+ */
+ @Override
+ public Shape getOutline(float x, float y) {
+ GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
+ for (int i = 0; i < this.vector.length; i++) {
+ GeneralPath outline = (GeneralPath)getGlyphOutline(i);
+
+ /* Applying translation to actual visual bounds */
+ outline.transform(AffineTransform.getTranslateInstance(x, y));
+ gp.append(outline, false);
+ }
+
+ return gp;
+ }
+
+ /**
+ * Returns a Shape that is the outline representation of this GlyphVector.
+ *
+ * @return a Shape object that is the outline of this GlyphVector
+ */
+ @Override
+ public Shape getOutline() {
+ return this.getOutline(0, 0);
+ }
+
+ /**
+ * Returns an array of glyphcodes for the specified glyphs.
+ *
+ * @param beginGlyphIndex the start index
+ * @param numEntries the number of glyph codes to get
+ * @param codeReturn the array that receives glyph codes' values
+ * @return an array that receives glyph codes' values
+ */
+ @Override
+ public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
+ int[] codeReturn) {
+
+ if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) {
+ // awt.44=beginGlyphIndex is out of vector's range
+ throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$
+ }
+
+ if (numEntries < 0) {
+ // awt.45=numEntries is out of vector's range
+ throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
+ }
+
+ if (codeReturn == null) {
+ codeReturn = new int[numEntries];
+ }
+
+ for (int i = beginGlyphIndex; i < beginGlyphIndex + numEntries; i++) {
+ codeReturn[i-beginGlyphIndex] = this.vector[i].getGlyphCode();
+ }
+
+ return codeReturn;
+ }
+
+ /**
+ * Returns an array of numEntries character indices for the specified glyphs.
+ *
+ * @param beginGlyphIndex the start index
+ * @param numEntries the number of glyph codes to get
+ * @param codeReturn the array that receives glyph codes' values
+ * @return an array that receives glyph char indices
+ */
+ @Override
+ public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries,
+ int[] codeReturn) {
+ if ((beginGlyphIndex < 0) || (beginGlyphIndex >= this.getNumGlyphs())) {
+ // awt.44=beginGlyphIndex is out of vector's range
+ throw new IllegalArgumentException(Messages.getString("awt.44")); //$NON-NLS-1$
+ }
+
+ if ((numEntries < 0)
+ || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) {
+ // awt.45=numEntries is out of vector's range
+ throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
+ }
+
+ if (codeReturn == null) {
+ codeReturn = new int[numEntries];
+ }
+
+ for (int i = 0; i < numEntries; i++) {
+ codeReturn[i] = this.getGlyphCharIndex(i + beginGlyphIndex);
+ }
+ return codeReturn;
+ }
+
+ /**
+ * Returns an array of numEntries glyphs positions from beginGlyphIndex
+ * glyph in Glyph Vector.
+ *
+ * @param beginGlyphIndex the start index
+ * @param numEntries the number of glyph codes to get
+ * @param positionReturn the array that receives glyphs' positions
+ * @return an array of floats that receives glyph char indices
+ */
+ @Override
+ public float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
+ float[] positionReturn) {
+
+ int len = (this.getNumGlyphs()+1) << 1;
+ beginGlyphIndex *= 2;
+ numEntries *= 2;
+
+ if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) {
+ // awt.44=beginGlyphIndex is out of vector's range
+ throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$
+ }
+
+ if (numEntries < 0) {
+ // awt.45=numEntries is out of vector's range
+ throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
+ }
+
+ if (positionReturn == null) {
+ positionReturn = new float[numEntries];
+ }
+
+ System.arraycopy(visualPositions, beginGlyphIndex, positionReturn, 0, numEntries);
+
+ return positionReturn;
+ }
+
+ /**
+ * Set numEntries elements of the visualPositions array from beginGlyphIndex
+ * of numEntries glyphs positions from beginGlyphIndex glyph in Glyph Vector.
+ *
+ * @param beginGlyphIndex the start index
+ * @param numEntries the number of glyph codes to get
+ * @param setPositions the array of positions to set
+ */
+ public void setGlyphPositions(int beginGlyphIndex, int numEntries,
+ float[] setPositions) {
+
+ int len = (this.getNumGlyphs()+1) << 1;
+ beginGlyphIndex *= 2;
+ numEntries *= 2;
+
+ if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) {
+ // awt.44=beginGlyphIndex is out of vector's range
+ throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$
+ }
+
+ if (numEntries < 0) {
+ // awt.45=numEntries is out of vector's range
+ throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$
+ }
+
+ System.arraycopy(setPositions, 0, visualPositions, beginGlyphIndex, numEntries);
+ layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS;
+
+ }
+
+ /**
+ * Set elements of the visualPositions array.
+ *
+ * @param setPositions the array of positions to set
+ */
+ public void setGlyphPositions(float[] setPositions) {
+
+ int len = (this.getNumGlyphs()+1) << 1;
+ if (len != setPositions.length){
+ // awt.46=length of setPositions array differs from the length of positions array
+ throw new IllegalArgumentException(Messages.getString("awt.46")); //$NON-NLS-1$
+ }
+
+ System.arraycopy(setPositions, 0, visualPositions, 0, len);
+ layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS;
+
+ }
+
+
+ /**
+ * Returns glyph code of the specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ */
+ @Override
+ public int getGlyphCode(int glyphIndex) {
+ if (glyphIndex >= this.vector.length || glyphIndex < 0) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+ return this.vector[glyphIndex].getGlyphCode();
+ }
+
+ /**
+ * Returns character index of the specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ */
+ @Override
+ public int getGlyphCharIndex(int glyphIndex) {
+
+ if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+
+ if ((this.layoutFlags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) {
+ return this.charVector.length - glyphIndex - 1;
+ }
+
+ return glyphIndex;
+ }
+
+ /**
+ * Returns a character value of the specified glyph.
+ *
+ * @param glyphIndex specified index of the glyph
+ */
+ public char getGlyphChar(int glyphIndex) {
+
+ if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) {
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+ return this.charVector[glyphIndex];
+ }
+
+ /**
+ * Assigns default positions to each glyph in this GlyphVector.
+ */
+ @Override
+ public void performDefaultLayout() {
+
+ System.arraycopy(logicalPositions, 0, visualPositions, 0, logicalPositions.length);
+
+ // Set position changes flag to zero
+ clearLayoutFlags(GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS);
+ }
+
+ /**
+ * Returns the number of glyphs in this Glyph Vector
+ */
+ @Override
+ public int getNumGlyphs() {
+ return vector.length;
+ }
+
+ /**
+ * Returns the logical bounds of this GlyphVector
+ */
+ @Override
+ public Rectangle2D getLogicalBounds(){
+ // XXX: for transforms where an angle between basis vectors is not 90 degrees
+ // Rectanlge2D class doesn't fit as Logical bounds. For this reason we use
+ // only non-transformed bounds!!
+
+ float x = visualPositions[0];
+ float width = visualPositions[visualPositions.length-2];
+
+ double scaleY = transform.getScaleY();
+
+ Rectangle2D bounds = new Rectangle2D.Float(x, (float)((-this.ascent-this.leading)*scaleY), width, (float)(this.height*scaleY));
+ return bounds;
+ }
+
+
+ /**
+ * Checks whether given GlyphVector equals to this GlyphVector.
+ * @param glyphVector GlyphVector object to compare
+ */
+ @Override
+ public boolean equals(GlyphVector glyphVector){
+ if (glyphVector == this){
+ return true;
+ }
+
+ if (glyphVector != null) {
+
+ if (!(glyphVector.getFontRenderContext().equals(this.vectorFRC) &&
+ glyphVector.getFont().equals(this.font))){
+ return false;
+ }
+
+ try {
+ boolean eq = true;
+ for (int i = 0; i < getNumGlyphs(); i++) {
+
+ int idx = i*2;
+ eq = (((CommonGlyphVector)glyphVector).visualPositions[idx] == this.visualPositions[idx]) &&
+ (((CommonGlyphVector)glyphVector).visualPositions[idx+1] == this.visualPositions[idx+1]) &&
+ (glyphVector.getGlyphCharIndex(i) == this.getGlyphCharIndex(i));
+
+ if (eq){
+ AffineTransform trans = glyphVector.getGlyphTransform(i);
+ if (trans == null){
+ eq = (this.glsTransforms[i] == null);
+ }else{
+ eq = this.glsTransforms[i].equals(trans);
+ }
+ }
+
+ if (!eq){
+ return false;
+ }
+ }
+
+ return eq;
+ } catch (ClassCastException e) {
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Returns flags describing the state of the GlyphVector.
+ */
+ @Override
+ public int getLayoutFlags() {
+ return layoutFlags;
+ }
+
+ /**
+ * Returns char with the specified index.
+ *
+ * @param index specified index of the char
+ *
+ */
+ public char getChar(int index) {
+ return this.charVector[index];
+
+ }
+
+ /**
+ * Clear desired flags in layout flags describing the state.
+ *
+ * @param clearFlags flags mask to clear
+ */
+
+ private void clearLayoutFlags(int clearFlags){
+ layoutFlags &= ~clearFlags;
+ }
+
+ /**
+ * Returns the logical bounds of the specified glyph within this CommonGlyphVector.
+ *
+ * @param glyphIndex index of the glyph to get it's logical bounds
+ * @return logical bounds of the specified glyph
+ */
+ @Override
+ public Shape getGlyphLogicalBounds(int glyphIndex){
+ if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())){
+ // awt.43=glyphIndex is out of vector's limits
+ throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$
+ }
+ Glyph glyph = this.vector[glyphIndex];
+
+ float x0 = visualPositions[glyphIndex*2];
+ float y0 = visualPositions[glyphIndex*2+1];
+ float advanceX = glyph.getGlyphPointMetrics().getAdvanceX();
+
+ GeneralPath gp = new GeneralPath();
+ gp.moveTo(0, -ascent - leading);
+ gp.lineTo(advanceX ,-ascent - leading);
+ gp.lineTo(advanceX, descent);
+ gp.lineTo(0, descent);
+ gp.lineTo(0, -ascent - leading);
+ gp.closePath();
+
+ /* Applying GlyphVector font transform */
+ AffineTransform at = (AffineTransform)this.transform.clone();
+
+ /* Applying Glyph transform */
+ AffineTransform glyphTransform = getGlyphTransform(glyphIndex);
+ if (glyphTransform != null){
+ at.concatenate(glyphTransform);
+ }
+
+ /* Applying translation to actual visual bounds */
+ at.preConcatenate(AffineTransform.getTranslateInstance(x0, y0));
+ gp.transform(at);
+ return gp;
+ }
+
+ /**
+ * Returns the Font parameter of this GlyphVector
+ */
+ @Override
+ public Font getFont(){
+ return this.font;
+ }
+
+
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/font/CompositeFont.java b/awt/org/apache/harmony/awt/gl/font/CompositeFont.java
new file mode 100644
index 0000000..70cb334
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/CompositeFont.java
@@ -0,0 +1,486 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineMetrics;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.harmony.awt.gl.font.FontPeerImpl;
+import org.apache.harmony.awt.gl.font.FontProperty;
+
+/**
+ * CompositeFont class is the implementation of logical font classes.
+ * Every logical font consists of several physical fonts that described
+ * in font.properties file according to the face name of this logical font.
+ */
+public class CompositeFont extends FontPeerImpl{
+
+ // a number of physical fonts that CompositeFont consist of
+ int numFonts;
+
+ // font family name
+ String family;
+
+ // font face name
+ String face;
+
+ String[] fontNames;
+
+ // an array of font properties applicable to this CompositeFont
+ FontProperty[] fontProperties;
+
+ // an array of font peers applicable to this CompositeFont
+ public FontPeerImpl[] fPhysicalFonts;
+
+ // missing glyph code field
+ int missingGlyphCode = -1;
+
+ // line metrics of this font
+ LineMetricsImpl nlm = null;
+
+ // cached num glyphs parameter of this font that is the sum of num glyphs of
+ // font peers composing this font
+ int cachedNumGlyphs = -1;
+ /**
+ * Creates CompositeFont object that is corresponding to the specified logical
+ * family name.
+ *
+ * @param familyName logical family name CompositeFont is to be created from
+ * @param faceName logical face name CompositeFont is to be created from
+ * @param _style style of the CompositeFont to be created
+ * @param _size size of the CompositeFont to be created
+ * @param fProperties an array of FontProperties describing physical fonts -
+ * parts of logical font
+ * @param physFonts an array of physical font peers related to the CompositeFont
+ * to be created
+ */
+ public CompositeFont(String familyName, String faceName, int _style, int _size, FontProperty[] fProperties, FontPeerImpl[] physFonts){
+ this.size = _size;
+ this.name = faceName;
+ this.family = familyName;
+ this.style = _style;
+ this.face = faceName;
+ this.psName = faceName;
+ this.fontProperties = fProperties;// !! Supposed that fProperties parameter != null
+ fPhysicalFonts = physFonts;
+ numFonts = fPhysicalFonts.length;
+ setDefaultLineMetrics("", null); //$NON-NLS-1$
+ this.uniformLM = false;
+ }
+
+ /**
+ * Returns the index of the FontPeer in array of physical fonts that is applicable
+ * for the given character. This font has to have the highest priority among fonts
+ * that can display this character and don't have exclusion range covering
+ * specified character. If there is no desired fonts -1 is returned.
+ *
+ * @param chr specified character
+ * @return index of the font from the array of physical fonts that will be used
+ * during processing of the specified character.
+ */
+ public int getCharFontIndex(char chr){
+ for (int i = 0; i < numFonts; i++){
+ if (fontProperties[i].isCharExcluded(chr)){
+ continue;
+ }
+ if (fPhysicalFonts[i].canDisplay(chr)){
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Returns the index of the FontPeer in array of physical fonts that is applicable
+ * for the given character. This font has to have the highest priority among fonts
+ * that can display this character and don't have exclusion range covering
+ * specified character. If there is no desired fonts default value is returned.
+ *
+ * @param chr specified character
+ * @param defaultValue default index that is returned if the necessary font couldn't be found.
+ * @return index of the font from the array of physical fonts that will be used
+ * during processing of the specified character.
+ */
+ public int getCharFontIndex(char chr, int defaultValue){
+ for (int i = 0; i < numFonts; i++){
+ if (fontProperties[i].isCharExcluded(chr)){
+ continue;
+ }
+ if (fPhysicalFonts[i].canDisplay(chr)){
+ return i;
+ }
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Returns true if one of the physical fonts composing this font CompositeFont
+ * can display specified character.
+ *
+ * @param chr specified character
+ */
+ @Override
+ public boolean canDisplay(char chr){
+ return (getCharFontIndex(chr) != -1);
+ }
+
+ /**
+ * Returns logical ascent (in pixels)
+ */
+ @Override
+ public int getAscent(){
+ return nlm.getLogicalAscent();
+ }
+
+ /**
+ * Returns LineMetrics instance scaled according to the specified transform.
+ *
+ * @param str specified String
+ * @param frc specified FontRenderContext
+ * @param at specified AffineTransform
+ */
+ @Override
+ public LineMetrics getLineMetrics(String str, FontRenderContext frc , AffineTransform at){
+ LineMetricsImpl lm = (LineMetricsImpl)(this.nlm.clone());
+ lm.setNumChars(str.length());
+
+ if ((at != null) && (!at.isIdentity())){
+ lm.scale((float)at.getScaleX(), (float)at.getScaleY());
+ }
+
+ return lm;
+ }
+
+ /**
+ * Returns cached LineMetrics instance for the null string or creates it if
+ * it wasn't cached yet.
+ */
+ @Override
+ public LineMetrics getLineMetrics(){
+ if (nlm == null){
+ setDefaultLineMetrics("", null); //$NON-NLS-1$
+ }
+
+ return this.nlm;
+ }
+
+ /**
+ * Creates LineMetrics instance and set cached LineMetrics field to it.
+ * Created LineMetrics has maximum values of the idividual metrics of all
+ * composing physical fonts. If there is only one physical font - it's
+ * LineMetrics object is returned.
+ *
+ * @param str specified String
+ * @param frc specified FontRenderContext
+ */
+ private void setDefaultLineMetrics(String str, FontRenderContext frc){
+ LineMetrics lm = fPhysicalFonts[0].getLineMetrics(str, frc, null);
+ float maxCharWidth = (float)fPhysicalFonts[0].getMaxCharBounds(frc).getWidth();
+
+ if (numFonts == 1) {
+ this.nlm = (LineMetricsImpl)lm;
+ return;
+ }
+
+ float[] baselineOffsets = lm.getBaselineOffsets();
+ int numChars = str.length();
+
+ // XXX: default value - common for all Fonts
+ int baseLineIndex = lm.getBaselineIndex();
+
+ float maxUnderlineThickness = lm.getUnderlineThickness();
+ float maxUnderlineOffset = lm.getUnderlineOffset();
+ float maxStrikethroughThickness = lm.getStrikethroughThickness();
+ float minStrikethroughOffset = lm.getStrikethroughOffset();
+ float maxLeading = lm.getLeading(); // External leading
+ float maxHeight = lm.getHeight(); // Height of the font ( == (ascent + descent + leading))
+ float maxAscent = lm.getAscent(); // Ascent of the font
+ float maxDescent = lm.getDescent(); // Descent of the font
+
+ for (int i = 1; i < numFonts; i++){
+ lm = fPhysicalFonts[i].getLineMetrics(str, frc, null);
+ if (maxUnderlineThickness < lm.getUnderlineThickness()){
+ maxUnderlineThickness = lm.getUnderlineThickness();
+ }
+
+ if (maxUnderlineOffset < lm.getUnderlineOffset()){
+ maxUnderlineOffset = lm.getUnderlineOffset();
+ }
+
+ if (maxStrikethroughThickness < lm.getStrikethroughThickness()){
+ maxStrikethroughThickness = lm.getStrikethroughThickness();
+ }
+
+ if (minStrikethroughOffset > lm.getStrikethroughOffset()){
+ minStrikethroughOffset = lm.getStrikethroughOffset();
+ }
+
+ if (maxLeading < lm.getLeading()){
+ maxLeading = lm.getLeading();
+ }
+
+ if (maxAscent < lm.getAscent()){
+ maxAscent = lm.getAscent();
+ }
+
+ if (maxDescent < lm.getDescent()){
+ maxDescent = lm.getDescent();
+ }
+
+ float width = (float)fPhysicalFonts[i].getMaxCharBounds(frc).getWidth();
+ if(maxCharWidth < width){
+ maxCharWidth = width;
+ }
+ for (int j =0; j < baselineOffsets.length; j++){
+ float[] offsets = lm.getBaselineOffsets();
+ if (baselineOffsets[j] > offsets[j]){
+ baselineOffsets[j] = offsets[j];
+ }
+ }
+
+ }
+ maxHeight = maxAscent + maxDescent + maxLeading;
+
+ this.nlm = new LineMetricsImpl(
+ numChars,
+ baseLineIndex,
+ baselineOffsets,
+ maxUnderlineThickness,
+ maxUnderlineOffset,
+ maxStrikethroughThickness,
+ minStrikethroughOffset,
+ maxLeading,
+ maxHeight,
+ maxAscent,
+ maxDescent,
+ maxCharWidth);
+
+ }
+
+ /**
+ * Returns the number of glyphs in this CompositeFont object.
+ */
+ @Override
+ public int getNumGlyphs(){
+ if (this.cachedNumGlyphs == -1){
+
+ this.cachedNumGlyphs = 0;
+
+ for (int i = 0; i < numFonts; i++){
+ this.cachedNumGlyphs += fPhysicalFonts[i].getNumGlyphs();
+ }
+ }
+
+ return this.cachedNumGlyphs;
+ }
+
+ /**
+ * Returns the italic angle of this object.
+ */
+ @Override
+ public float getItalicAngle(){
+ // !! only first physical font used to get this value
+ return fPhysicalFonts[0].getItalicAngle();
+ }
+
+ /**
+ * Returns rectangle that bounds the specified string in terms of composite line metrics.
+ *
+ * @param chars an array of chars
+ * @param start the initial offset in array of chars
+ * @param end the end offset in array of chars
+ * @param frc specified FontRenderContext
+ */
+ public Rectangle2D getStringBounds(char[] chars, int start, int end, FontRenderContext frc){
+
+ LineMetrics lm = getLineMetrics();
+ float minY = -lm.getAscent();
+ float minX = 0;
+ float height = lm.getHeight();
+ float width = 0;
+
+ for (int i = start; i < end; i++){
+ width += charWidth(chars[i]);
+ }
+
+ Rectangle2D rect2D = new Rectangle2D.Float(minX, minY, width, height);
+ return rect2D;
+
+ }
+
+ /**
+ * Returns maximum rectangle that encloses all maximum char bounds of
+ * physical fonts composing this CompositeFont.
+ *
+ * @param frc specified FontRenderContext
+ */
+ @Override
+ public Rectangle2D getMaxCharBounds(FontRenderContext frc){
+
+ Rectangle2D rect2D = fPhysicalFonts[0].getMaxCharBounds(frc);
+ float minY = (float)rect2D.getY();
+ float maxWidth = (float)rect2D.getWidth();
+ float maxHeight = (float)rect2D.getHeight();
+ if (numFonts == 1){
+ return rect2D;
+ }
+
+ for (int i = 1; i < numFonts; i++){
+ if (fPhysicalFonts[i] != null){
+ rect2D = fPhysicalFonts[i].getMaxCharBounds(frc);
+ float y = (float)rect2D.getY();
+ float mWidth = (float)rect2D.getWidth();
+ float mHeight = (float)rect2D.getHeight();
+ if (y < minY){
+ minY = y;
+ }
+ if (mWidth > maxWidth){
+ maxHeight = mWidth;
+ }
+
+ if (mHeight > maxHeight){
+ maxHeight = mHeight;
+ }
+ }
+ }
+
+ rect2D = new Rectangle2D.Float(0, minY, maxWidth, maxHeight);
+
+ return rect2D;
+ }
+
+ /**
+ * Returns font name.
+ */
+ @Override
+ public String getFontName(){
+ return face;
+ }
+
+ /**
+ * Returns font postscript name.
+ */
+ @Override
+ public String getPSName(){
+ return psName;
+ }
+
+ /**
+ * Returns font family name.
+ */
+ @Override
+ public String getFamily(){
+ return family;
+ }
+
+ /**
+ * Returns the code of the missing glyph.
+ */
+ @Override
+ public int getMissingGlyphCode(){
+ // !! only first physical font used to get this value
+ return fPhysicalFonts[0].getMissingGlyphCode();
+ }
+
+ /**
+ * Returns Glyph object corresponding to the specified character.
+ *
+ * @param ch specified char
+ */
+ @Override
+ public Glyph getGlyph(char ch){
+ for (int i = 0; i < numFonts; i++){
+ if (fontProperties[i].isCharExcluded(ch)){
+ continue;
+ }
+
+ /* Control symbols considered to be supported by the font peer */
+ if ((ch < 0x20) || fPhysicalFonts[i].canDisplay(ch)){
+ return fPhysicalFonts[i].getGlyph(ch);
+ }
+ }
+ return getDefaultGlyph();
+ }
+
+ /**
+ * Returns width of the char with specified index.
+ *
+ * @param ind specified index of the character
+ */
+ @Override
+ public int charWidth(int ind){
+ return charWidth((char)ind);
+ }
+
+ /**
+ * Returns width of the specified char.
+ *
+ * @param c specified character
+ */
+ @Override
+ public int charWidth(char c){
+ Glyph gl = this.getGlyph(c);
+ return (int)gl.getGlyphPointMetrics().getAdvanceX();
+ }
+
+ /**
+ * Returns debug information about this class.
+ */
+ @Override
+ public String toString(){
+ return new String(this.getClass().getName() +
+ "[name=" + this.name + //$NON-NLS-1$
+ ",style="+ this.style + //$NON-NLS-1$
+ ",fps=" + this.fontProperties + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Returns Glyph object corresponding to the default glyph.
+ */
+ @Override
+ public Glyph getDefaultGlyph(){
+ // !! only first physical font used to get this value
+ return fPhysicalFonts[0].getDefaultGlyph();
+ }
+
+ /**
+ * Returns FontExtraMetrics object with extra metrics
+ * related to this CompositeFont.
+ */
+ @Override
+ public FontExtraMetrics getExtraMetrics(){
+ // Returns FontExtraMetrics instanse of the first physical
+ // Font from the array of fonts.
+ return fPhysicalFonts[0].getExtraMetrics();
+ }
+
+ /**
+ * Disposes CompositeFont object's resources.
+ */
+ @Override
+ public void dispose() {
+ // Nothing to dispose
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java b/awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java
new file mode 100644
index 0000000..047ba6d
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ *
+ */
+package org.apache.harmony.awt.gl.font;
+
+/**
+ * Extra font metrics: sub/superscripts sizes, offsets, average char width.
+ */
+public class FontExtraMetrics {
+
+ /* !! Subscript/superscript metrics are undefined for Type1. As a possible
+ * solution we can use values for Type1, that are proportionate to TrueType
+ * ones:
+ * SubscriptSizeX == 0.7 * fontSize
+ * SubscriptSizeY == 0.65 * fontSize
+ * SubscriptOffsetX == 0;
+ * SubscriptOffsetY == 0.15 * fontSize;
+ * SuperscriptSizeX == 0.7 * fontSize
+ * SuperscriptSizeY == 0.65 * fontSize
+ * SuperscriptOffsetX == 0;
+ * SuperscriptOffsetY == 0.45 * fontSize
+ *
+ */
+
+ /*
+ * The average width of characters in the font.
+ */
+ private float lAverageCharWidth;
+
+ /*
+ * Horizontal size for subscripts.
+ */
+ private float lSubscriptSizeX;
+
+ /*
+ * Vertical size for subscripts.
+ */
+ private float lSubscriptSizeY;
+
+ /*
+ * Horizontal offset for subscripts, the offset from the character origin
+ * to the origin of the subscript character.
+ */
+ private float lSubscriptOffsetX;
+
+ /*
+ * Vertical offset for subscripts, the offset from the character origin
+ * to the origin of the subscript character.
+ */
+ private float lSubscriptOffsetY;
+
+ /*
+ * Horizontal size for superscripts.
+ */
+ private float lSuperscriptSizeX;
+
+ /*
+ * Vertical size for superscripts.
+ */
+ private float lSuperscriptSizeY;
+
+ /*
+ * Horizontal offset for superscripts, the offset from the character
+ * base line to the base line of the superscript character.
+ */
+ private float lSuperscriptOffsetX;
+
+ /*
+ * Vertical offset for superscripts, the offset from the character
+ * base line to the base line of the superscript character.
+ */
+ private float lSuperscriptOffsetY;
+
+ public FontExtraMetrics(){
+ // default constructor
+ }
+
+ public FontExtraMetrics(float[] metrics){
+ lAverageCharWidth = metrics[0];
+ lSubscriptSizeX = metrics[1];
+ lSubscriptSizeY = metrics[2];
+ lSubscriptOffsetX = metrics[3];
+ lSubscriptOffsetY = metrics[4];
+ lSuperscriptSizeX = metrics[5];
+ lSuperscriptSizeY = metrics[6];
+ lSuperscriptOffsetX = metrics[7];
+ lSuperscriptOffsetY = metrics[8];
+ }
+
+ public float getAverageCharWidth(){
+ return lAverageCharWidth;
+ }
+
+ public float getSubscriptSizeX(){
+ return lSubscriptSizeX;
+ }
+
+ public float getSubscriptSizeY(){
+ return lSubscriptSizeY;
+ }
+
+ public float getSubscriptOffsetX(){
+ return lSubscriptOffsetX;
+ }
+
+ public float getSubscriptOffsetY(){
+ return lSubscriptOffsetY;
+ }
+
+ public float getSuperscriptSizeX(){
+ return lSuperscriptSizeX;
+ }
+
+ public float getSuperscriptSizeY(){
+ return lSuperscriptSizeY;
+ }
+
+ public float getSuperscriptOffsetX(){
+ return lSuperscriptOffsetX;
+ }
+
+ public float getSuperscriptOffsetY(){
+ return lSuperscriptOffsetY;
+ }
+
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/FontFinder.java b/awt/org/apache/harmony/awt/gl/font/FontFinder.java
new file mode 100644
index 0000000..09bcf5c
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/FontFinder.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ * @date: Jul 12, 2005
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.Font;
+import java.awt.GraphicsEnvironment;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class chooses the default font for the given text.
+ * If it finds the character which current font is unable to display
+ * it starts the next font run and looks for the font which is able to
+ * display the current character. It also caches the font mappings
+ * (index in the array containing all fonts) for the characters,
+ * using that fact that scripts are mainly contiguous in the UTF-16 encoding
+ * and there's a high probability that the upper byte will be the same for the
+ * next character as for the previous. This allows to save the space used for the cache.
+ */
+public class FontFinder {
+ private static final float DEFAULT_FONT_SIZE = 12;
+
+ private static final Font fonts[] =
+ GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
+
+ private static final int NUM_BLOCKS = 256;
+ private static final int BLOCK_SIZE = 256;
+ private static final int INDEX_MASK = 0xFF;
+ private static final int BLOCK_SHIFT = 8;
+
+ // Maps characters into the fonts array
+ private static final int blocks[][] = new int[NUM_BLOCKS][];
+
+ /**
+ * Finds the font which is able to display the given character
+ * and saves the font mapping for this character
+ * @param c - character
+ * @return font
+ */
+ static Font findFontForChar(char c) {
+ int blockNum = c >> BLOCK_SHIFT;
+ int index = c & INDEX_MASK;
+
+ if (blocks[blockNum] == null) {
+ blocks[blockNum] = new int[BLOCK_SIZE];
+ }
+
+ if (blocks[blockNum][index] == 0) {
+ blocks[blockNum][index] = 1;
+
+ for (int i=0; i<fonts.length; i++) {
+ if (fonts[i].canDisplay(c)) {
+ blocks[blockNum][index] = i+1;
+ break;
+ }
+ }
+ }
+
+ return getDefaultSizeFont(blocks[blockNum][index]-1);
+ }
+
+ /**
+ * Derives the default size font
+ * @param i - index in the array of all fonts
+ * @return derived font
+ */
+ static Font getDefaultSizeFont(int i) {
+ if (fonts[i].getSize() != DEFAULT_FONT_SIZE) {
+ fonts[i] = fonts[i].deriveFont(DEFAULT_FONT_SIZE);
+ }
+
+ return fonts[i];
+ }
+
+ /**
+ * Assigns default fonts for the given text run.
+ * First three parameters are input, last three are output.
+ * @param text - given text
+ * @param runStart - start of the text run
+ * @param runLimit - end of the text run
+ * @param runStarts - starts of the resulting font runs
+ * @param fonts - mapping of the font run starts to the fonts
+ */
+ static void findFonts(char text[], int runStart, int runLimit, List<Integer> runStarts,
+ Map<Integer, Font> fonts) {
+ Font prevFont = null;
+ Font currFont;
+ for (int i = runStart; i < runLimit; i++) {
+ currFont = findFontForChar(text[i]);
+ if (currFont != prevFont) {
+ prevFont = currFont;
+ Integer idx = new Integer(i);
+ fonts.put(idx, currFont);
+ if (i != runStart) {
+ runStarts.add(idx);
+ }
+ }
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/FontManager.java b/awt/org/apache/harmony/awt/gl/font/FontManager.java
new file mode 100644
index 0000000..8354e25
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/FontManager.java
@@ -0,0 +1,819 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.Font;
+import java.awt.peer.FontPeer;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.harmony.awt.gl.CommonGraphics2DFactory;
+import org.apache.harmony.luni.util.NotImplementedException;
+
+
+public abstract class FontManager {
+
+ //???AWT
+ boolean NOT_IMP = false;
+
+ /**
+ * array of font families names
+ */
+ public String[] allFamilies;
+
+ public static final String DEFAULT_NAME = "Default"; /* Default font name */ //$NON-NLS-1$
+ public static final String DIALOG_NAME = "Dialog"; /* Dialog font name */ //$NON-NLS-1$
+
+ /**
+ * Set of constants applicable to the TrueType 'name' table.
+ */
+ public static final byte FAMILY_NAME_ID = 1; /* Family name identifier */
+ public static final byte FONT_NAME_ID = 4; /* Full font name identifier */
+ public static final byte POSTSCRIPT_NAME_ID = 6; /* PostScript name identifier */
+ public static final short ENGLISH_LANGID = 0x0409; /* English (United States)language identifier */
+
+ /**
+ * Set of constants describing font type.
+ */
+ public static final byte FONT_TYPE_TT = 4; /* TrueType type (TRUETYPE_FONTTYPE) */
+ public static final byte FONT_TYPE_T1 = 2; /* Type1 type (DEVICE_FONTTYPE) */
+ public static final byte FONT_TYPE_UNDEF = 0; /* Undefined type */
+
+ // logical family types (indices in FontManager.LOGICAL_FONT_NAMES)
+ static final int DIALOG = 3; // FF_SWISS
+ static final int SANSSERIF = 1; // FF_SWISS
+ static final int DIALOGINPUT = 4; // FF_MODERN
+ static final int MONOSPACED = 2; // FF_MODERN
+ static final int SERIF = 0; // FF_ROMAN
+
+
+ /**
+ * FontProperty related constants.
+ */
+ public static final String PLATFORM_FONT_NAME = "PlatformFontName"; //$NON-NLS-1$
+ public static final String LOGICAL_FONT_NAME = "LogicalFontName"; //$NON-NLS-1$
+ public static final String COMPONENT_INDEX = "ComponentIndex"; //$NON-NLS-1$
+ public static final String STYLE_INDEX = "StyleIndex"; //$NON-NLS-1$
+
+ public static final String[] FONT_MAPPING_KEYS = {
+ "LogicalFontName.StyleName.ComponentIndex", "LogicalFontName.ComponentIndex" //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ public static final String FONT_CHARACTER_ENCODING = "fontcharset.LogicalFontName.ComponentIndex"; //$NON-NLS-1$
+
+ public static final String EXCLUSION_RANGES = "exclusion.LogicalFontName.ComponentIndex"; //$NON-NLS-1$
+
+ public static final String FONT_FILE_NAME = "filename.PlatformFontName"; //$NON-NLS-1$
+
+ /**
+ * Available logical font families names.
+ */
+ public static final String[] LOGICAL_FONT_FAMILIES = {
+ "Serif", "SansSerif", "Monospaced", "Dialog", "DialogInput" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ };
+
+ /**
+ * Available logical font names.
+ */
+ public static final String[] LOGICAL_FONT_NAMES = {
+ "serif", "serif.plain", "serif.bold", "serif.italic", "serif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "sansserif", "sansserif.plain", "sansserif.bold", "sansserif.italic", "sansserif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "monospaced", "monospaced.plain", "monospaced.bold", "monospaced.italic", "monospaced.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "dialog", "dialog.plain", "dialog.bold", "dialog.italic", "dialog.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "dialoginput", "dialoginput.plain", "dialoginput.bold", "dialoginput.italic", "dialoginput.bolditalic" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ };
+
+ /**
+ * Available logical font face names.
+ */
+ public static final String[] LOGICAL_FONT_FACES = {
+ "Serif", "Serif.plain", "Serif.bold", "Serif.italic", "Serif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "Sansserif", "Sansserif.plain", "Sansserif.bold", "Sansserif.italic", "Sansserif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "Monospaced", "Monospaced.plain", "Monospaced.bold", "Monospaced.italic", "Monospaced.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "Dialog", "Dialog.plain", "Dialog.bold", "Dialog.italic", "Dialog.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "Dialoginput", "Dialoginput.plain", "Dialoginput.bold", "Dialoginput.italic", "Dialoginput.bolditalic" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ };
+
+ /**
+ * Set of font style names.
+ * Font.getStyle() corresponds to indexes in STYLE_NAMES array.
+ */
+ public static final String[] STYLE_NAMES = {
+ "plain", "bold", "italic", "bolditalic" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ };
+
+ /**
+ * Logical font styles names table where font styles names used
+ * as the key and the value is the index of this style name.
+ */
+ private static final Hashtable<String, Integer> style_keys = new Hashtable<String, Integer>(4);
+
+ /**
+ * Initialize font styles keys table.
+ */
+ static {
+ for (int i = 0; i < STYLE_NAMES.length; i++){
+ style_keys.put(STYLE_NAMES[i], Integer.valueOf(i));
+ }
+ }
+
+ /**
+ * Return font style from the logical style name.
+ *
+ * @param lName style name of the logical face
+ */
+ public static int getLogicalStyle(String lName){
+ Integer value = style_keys.get(lName);
+ return value != null ? value.intValue(): -1;
+ }
+
+ /**
+ * Set of possible "os" property values.
+ */
+ public static final String[] OS_VALUES = {
+ "NT", "98", "2000", "Me", "XP", // For Windows //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ "Redhat", "Turbo", "SuSE" // For Linux //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ };
+
+ /**
+ * Set of possible font.property file names.
+ * Language, Country, Encoding, OS, Version should be replaced with
+ * the values from current configuration.
+ */
+ public static final String[] FP_FILE_NAMES = {
+ "/lib/font.properties.Language_Country_Encoding.OSVersion", //$NON-NLS-1$
+ "/lib/font.properties.Language_Country_Encoding.OS", //$NON-NLS-1$
+ "/lib/font.properties.Language_Country_Encoding.Version", //$NON-NLS-1$
+ "/lib/font.properties.Language_Country_Encoding", //$NON-NLS-1$
+ "/lib/font.properties.Language_Country.OSVersion", //$NON-NLS-1$
+ "/lib/font.properties.Language_Country.OS", //$NON-NLS-1$
+ "/lib/font.properties.Language_Country.Version", //$NON-NLS-1$
+ "/lib/font.properties.Language_Country", //$NON-NLS-1$
+ "/lib/font.properties.Language_Encoding.OSVersion", //$NON-NLS-1$
+ "/lib/font.properties.Language_Encoding.OS", //$NON-NLS-1$
+ "/lib/font.properties.Language_Encoding.Version", //$NON-NLS-1$
+ "/lib/font.properties.Language_Encoding", //$NON-NLS-1$
+ "/lib/font.properties.Language.OSVersion", //$NON-NLS-1$
+ "/lib/font.properties.Language.OS", //$NON-NLS-1$
+ "/lib/font.properties.Language.Version", //$NON-NLS-1$
+ "/lib/font.properties.Language", //$NON-NLS-1$
+ "/lib/font.properties.Encoding.OSVersion", //$NON-NLS-1$
+ "/lib/font.properties.Encoding.OS", //$NON-NLS-1$
+ "/lib/font.properties.Encoding.Version", //$NON-NLS-1$
+ "/lib/font.properties.Encoding", //$NON-NLS-1$
+ "/lib/font.properties.OSVersion", //$NON-NLS-1$
+ "/lib/font.properties.OS", //$NON-NLS-1$
+ "/lib/font.properties.Version", //$NON-NLS-1$
+ "/lib/font.properties" //$NON-NLS-1$
+ };
+
+ /**
+ * Table with all available font properties corresponding
+ * to the current system configuration.
+ */
+ public Hashtable<String, Vector<FontProperty>> fProperties = new Hashtable<String, Vector<FontProperty>>();
+
+ public FontManager(){
+ allFamilies = getAllFamilies();
+ /*
+ * Creating and registering shutdown hook to free resources
+ * before object is destroyed.
+ */
+ //???AWT
+ //DisposeNativeHook shutdownHook = new DisposeNativeHook();
+ //Runtime.getRuntime().addShutdownHook(shutdownHook);
+ }
+
+ /**
+ * Maximum number of unreferenced font peers to keep.
+ */
+ public static final int EMPTY_FONTS_CAPACITY = 10;
+
+ /**
+ * Locale - Language ID hash table.
+ */
+ Hashtable<String, Short> tableLCID = new Hashtable<String, Short>();
+
+ /**
+ * Hash table that contains FontPeers instances.
+ */
+ public Hashtable<String, HashMapReference> fontsTable = new Hashtable<String, HashMapReference>();
+
+ /**
+ * ReferenceQueue for HashMapReference objects to check
+ * if they were collected by garbage collector.
+ */
+ public ReferenceQueue<FontPeer> queue = new ReferenceQueue<FontPeer>();
+
+ /**
+ * Singleton instance
+ */
+ public final static FontManager inst = CommonGraphics2DFactory.inst.getFontManager();
+
+ /**
+ * Gets singleton instance of FontManager
+ *
+ * @return instance of FontManager implementation
+ */
+ public static FontManager getInstance() {
+ return inst;
+ }
+
+ /**
+ * Returns platform-dependent Font peer created from the specified
+ * Font object from the table with cached FontPeers instances.
+ *
+ * Note, this method checks whether FontPeer with specified parameters
+ * exists in the table with cached FontPeers' instances. If there is no needed
+ * instance - it is created and cached.
+ *
+ * @param fontName name of the font
+ * @param _fontStyle style of the font
+ * @param size font size
+ *
+ * @return platform dependent FontPeer implementation created from
+ * the specified parameters
+ */
+ public FontPeer getFontPeer(String fontName, int _fontStyle, int size) {
+ updateFontsTable();
+
+ FontPeer peer = null;
+ String key;
+ String name;
+ int fontStyle = _fontStyle;
+
+ int logicalIndex = getLogicalFaceIndex(fontName);
+
+ if (logicalIndex != -1){
+ name = getLogicalFaceFromFont(fontStyle, logicalIndex);
+ fontStyle = getStyleFromLogicalFace(name);
+ key = name.concat(String.valueOf(size));
+ } else {
+ name = fontName;
+ key = name.concat(String.valueOf(fontStyle)).
+ concat(String.valueOf(size));
+ }
+
+ HashMapReference hmr = fontsTable.get(key);
+ if (hmr != null) {
+ peer = hmr.get();
+ }
+
+ if (peer == null) {
+ peer = createFontPeer(name, fontStyle, size, logicalIndex);
+ if (peer == null){
+ peer = getFontPeer(DIALOG_NAME, fontStyle, size);
+ }
+ fontsTable.put(key, new HashMapReference(key, peer, queue));
+ }
+
+ return peer;
+ }
+
+ /**
+ * Returns instance of font peer (logical or physical) according to the
+ * specified parameters.
+ *
+ * @param name font face name
+ * @param style style of the font
+ * @param size size of the font
+ * @param logicalIndex index of the logical face name in LOGICAL_FONT_FACES
+ * array or -1 if desired font peer is not logical.
+ */
+ private FontPeer createFontPeer(String name, int style, int size, int logicalIndex){
+ FontPeer peer;
+ if (logicalIndex != -1){
+ peer = createLogicalFontPeer(name, style, size);
+ }else {
+ peer = createPhysicalFontPeer(name, style, size);
+ }
+
+ return peer;
+ }
+
+ /**
+ * Returns family name for logical face names as a parameter.
+ *
+ * @param faceName logical font face name
+ */
+ public String getFamilyFromLogicalFace(String faceName){
+ int pos = faceName.indexOf("."); //$NON-NLS-1$
+ if (pos == -1){
+ return faceName;
+ }
+
+ return faceName.substring(0, pos);
+ }
+
+ /**
+ * Returns new logical font peer for the parameters specified using font
+ * properties.
+ *
+ * @param faceName face name of the logical font
+ * @param style style of the font
+ * @param size font size
+ *
+ */
+ private FontPeer createLogicalFontPeer(String faceName, int style, int size){
+ String family = getFamilyFromLogicalFace(faceName);
+ FontProperty[] fps = getFontProperties(family.toLowerCase() + "." + style); //$NON-NLS-1$
+ if (fps != null){
+ int numFonts = fps.length;
+ FontPeerImpl[] physicalFonts = new FontPeerImpl[numFonts];
+ for (int i = 0; i < numFonts; i++){
+ FontProperty fp = fps[i];
+
+ String name = fp.getName();
+ int fpStyle = fp.getStyle();
+ String key = name.concat(String.valueOf(fpStyle)).
+ concat(String.valueOf(size));
+
+ HashMapReference hmr = fontsTable.get(key);
+ if (hmr != null) {
+ physicalFonts[i] = (FontPeerImpl)hmr.get();
+ }
+
+ if (physicalFonts[i] == null){
+ physicalFonts[i] = (FontPeerImpl)createPhysicalFontPeer(name, fpStyle, size);
+ fontsTable.put(key, new HashMapReference(key, physicalFonts[i], queue));
+ }
+
+ if (physicalFonts[i] == null){
+ physicalFonts[i] = (FontPeerImpl)getDefaultFont(style, size);
+ }
+ }
+ return new CompositeFont(family, faceName, style, size, fps, physicalFonts);
+ }
+
+ // if there is no property for this logical font - default font is to be
+ // created
+ FontPeerImpl peer = (FontPeerImpl)getDefaultFont(style, size);
+
+ return peer;
+ }
+
+ /**
+ * Returns new physical font peer for the parameters specified using font properties
+ * This method must be overridden by subclasses implementations.
+ *
+ * @param faceName face name or family name of the font
+ * @param style style of the font
+ * @param size font size
+ *
+ */
+ public abstract FontPeer createPhysicalFontPeer(String name, int style, int size);
+
+ /**
+ * Returns default font peer class with "Default" name that is usually
+ * used when font with specified font names and style doesn't exsist
+ * on a system.
+ *
+ * @param style style of the font
+ * @param size size of the font
+ */
+ public FontPeer getDefaultFont(int style, int size){
+ updateFontsTable();
+
+ FontPeer peer = null;
+ String key = DEFAULT_NAME.concat(String.valueOf(style)).
+ concat(String.valueOf(size));
+
+ HashMapReference hmr = fontsTable.get(key);
+ if (hmr != null) {
+ peer = hmr.get();
+ }
+
+ if (peer == null) {
+ peer = createDefaultFont(style, size);
+
+ ((FontPeerImpl)peer).setFamily(DEFAULT_NAME);
+ ((FontPeerImpl)peer).setPSName(DEFAULT_NAME);
+ ((FontPeerImpl)peer).setFontName(DEFAULT_NAME);
+
+ fontsTable.put(key, new HashMapReference(key, peer, queue));
+ }
+
+ return peer;
+ }
+
+ /**
+ *
+ * Returns new default font peer with "Default" name for the parameters
+ * specified. This method must be overridden by subclasses implementations.
+ *
+ * @param style style of the font
+ * @param size size of the font
+ */
+ public abstract FontPeer createDefaultFont(int style, int size);
+
+ /**
+ * Returns face name of the logical font, which is the result
+ * of specified font style and face style union.
+ *
+ * @param fontStyle specified style of the font
+ * @param logicalIndex index of the specified face from the
+ * LOGICAL_FONT_FACES array
+ * @return resulting face name
+ */
+ public String getLogicalFaceFromFont(int fontStyle, int logicalIndex){
+ int style = 0;
+ String name = LOGICAL_FONT_FACES[logicalIndex];
+ int pos = name.indexOf("."); //$NON-NLS-1$
+
+ if (pos == -1){
+ return createLogicalFace(name, fontStyle);
+ }
+
+ String styleName = name.substring(pos+1);
+ name = name.substring(0, pos);
+
+ // appending font style to the face style
+ style = fontStyle | getLogicalStyle(styleName);
+
+ return createLogicalFace(name, style);
+ }
+
+ /**
+ * Function returns style value from logical face name.
+ *
+ * @param name face name
+ * @return font style
+ */
+ public int getStyleFromLogicalFace(String name){
+ int style;
+ int pos = name.indexOf("."); //$NON-NLS-1$
+
+ if (pos == -1){
+ return Font.PLAIN;
+ }
+
+ String styleName = name.substring(pos+1);
+
+ style = getLogicalStyle(styleName);
+
+ return style;
+ }
+
+ /**
+ * Returns logical face name corresponding to the logical
+ * family name and style of the font.
+ *
+ * @param family font family
+ * @param styleIndex index of the style name from the STYLE_NAMES array
+ */
+ public String createLogicalFace(String family, int styleIndex){
+ return family + "." + STYLE_NAMES[styleIndex]; //$NON-NLS-1$
+ }
+
+ /**
+ * Return language Id from LCID hash corresponding to the specified locale
+ *
+ * @param l specified locale
+ */
+ public Short getLCID(Locale l){
+ if (this.tableLCID.size() == 0){
+ initLCIDTable();
+ }
+
+ return tableLCID.get(l.toString());
+ }
+
+ /**
+ * Platform-dependent LCID table init.
+ */
+ public abstract void initLCIDTable();
+
+ /**
+ * Freeing native resources. This hook is used to avoid
+ * sudden application exit and to free resources created in native code.
+ */
+ private class DisposeNativeHook extends Thread {
+
+ @Override
+ public void run() {
+ try{
+ /* Disposing native font peer's resources */
+ Enumeration<String> kEnum = fontsTable.keys();
+
+ while(kEnum.hasMoreElements()){
+ Object key = kEnum.nextElement();
+ HashMapReference hmr = fontsTable.remove(key);
+ FontPeerImpl delPeer = (FontPeerImpl)hmr.get();
+
+ if ((delPeer != null) && (delPeer.getClass() != CompositeFont.class)){
+ // there's nothing to dispose in CompositeFont objects
+ delPeer.dispose();
+ }
+ }
+ } catch (Throwable t){
+ throw new RuntimeException(t);
+ }
+ }
+ }
+
+ /**
+ * Returns File object, created in a directory
+ * according to the System, where JVM is being ran.
+ *
+ * In Linux case we use ".fonts" directory (for fontconfig purpose),
+ * where font file from the stream will be stored, hence in LinuxFontManager this
+ * method is overridden.
+ * In Windows case we use Windows temp directory (default implementation)
+ *
+ */
+ public File getTempFontFile()throws IOException{
+ //???AWT
+ /*
+ File fontFile = File.createTempFile("jFont", ".ttf"); //$NON-NLS-1$ //$NON-NLS-2$
+ fontFile.deleteOnExit();
+
+ return fontFile;
+ */
+ if(NOT_IMP)
+ throw new NotImplementedException("getTempFontFile not Implemented");
+ return null;
+ }
+
+ /**
+ * Returns File object with font properties. It's name obtained using current
+ * system configuration properties and locale settings. If no appropriate
+ * file is found method returns null.
+ */
+ public static File getFontPropertyFile(){
+ File file = null;
+
+ String javaHome = System.getProperty("java.home"); //$NON-NLS-1$
+ Locale l = Locale.getDefault();
+ String language = l.getLanguage();
+ String country = l.getCountry();
+ String fileEncoding = System.getProperty("file.encoding"); //$NON-NLS-1$
+
+ String os = System.getProperty("os.name"); //$NON-NLS-1$
+
+ int i = 0;
+
+ // OS names from system properties don't match
+ // OS identifiers used in font.property files
+ for (; i < OS_VALUES.length; i++){
+ if (os.endsWith(OS_VALUES[i])){
+ os = OS_VALUES[i];
+ break;
+ }
+ }
+
+ if (i == OS_VALUES.length){
+ os = null;
+ }
+
+ String version = System.getProperty("os.version"); //$NON-NLS-1$
+ String pathname;
+
+ for (i = 0; i < FP_FILE_NAMES.length; i++){
+ pathname = FP_FILE_NAMES[i];
+ if (os != null){
+ pathname = pathname.replaceFirst("OS", os); //$NON-NLS-1$
+ }
+
+ pathname = javaHome + pathname;
+
+ pathname = pathname.replaceAll("Language", language). //$NON-NLS-1$
+ replaceAll("Country", country). //$NON-NLS-1$
+ replaceAll("Encoding", fileEncoding). //$NON-NLS-1$
+ replaceAll("Version", version); //$NON-NLS-1$
+
+ file = new File(pathname);
+
+ if (file.exists()){
+ break;
+ }
+ }
+
+ return file.exists() ? file : null;
+ }
+
+ /**
+ * Returns an array of integer range values
+ * if the parameter exclusionString has format:
+ * Range
+ * Range [, exclusionString]
+ *
+ * Range:
+ * Char-Char
+ *
+ * Char:
+ * HexDigit HexDigit HexDigit HexDigit
+ *
+ * Method returns null if the specified string is null.
+ *
+ * @param exclusionString string parameter in specified format
+ */
+ public static int[] parseIntervals(String exclusionString){
+ int[] results = null;
+
+ if (exclusionString == null){
+ return null;
+ }
+
+ String[] intervals = exclusionString.split(","); //$NON-NLS-1$
+
+ if (intervals != null){
+ int num = intervals.length;
+ if (num > 0){
+ results = new int[intervals.length << 1];
+ for (int i = 0; i < intervals.length; i++){
+ String ranges[] = intervals[i].split("-"); //$NON-NLS-1$
+ results[i*2] = Integer.parseInt(ranges[0], 16);
+ results[i*2+1] = Integer.parseInt(ranges[1], 16);
+
+ }
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Returns Properties from the properties file or null if
+ * there is an error with FileInputStream processing.
+ *
+ * @param file File object containing properties
+ */
+ public static Properties getProperties(File file){
+ Properties props = null;
+ FileInputStream fis = null;
+ try{
+ fis = new FileInputStream(file);
+ props = new Properties();
+ props.load(fis);
+ } catch (Exception e){
+ System.out.println(e);
+ }
+ return props;
+ }
+
+ /**
+ * Returns an array of FontProperties from the properties file
+ * with the specified property name "logical face.style". E.g.
+ * "dialog.2" corresponds to the font family Dialog with bold style.
+ *
+ * @param fpName key of the font properties in the properties set
+ */
+ public FontProperty[] getFontProperties(String fpName){
+ Vector<FontProperty> props = fProperties.get(fpName);
+
+ if (props == null){
+ return null;
+ }
+
+ int size = props.size();
+
+ if (size == 0){
+ return null;
+ }
+
+ FontProperty[] fps = new FontProperty[size];
+ for (int i=0; i < fps.length; i++){
+ fps[i] = props.elementAt(i);
+ }
+ return fps;
+ }
+
+ /**
+ * Returns index of the font name in array of font names or -1 if
+ * this font is not logical.
+ *
+ * @param fontName specified font name
+ */
+ public static int getLogicalFaceIndex(String fontName){
+ for (int i=0; i<LOGICAL_FONT_NAMES.length; i++ ){
+ if (LOGICAL_FONT_NAMES[i].equalsIgnoreCase(fontName)){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns true if specified family name is available in this
+ * GraphicsEnvironment.
+ *
+ * @param familyName the specified font family name
+ */
+ public boolean isFamilyExist(String familyName){
+ return (getFamilyIndex(familyName) != -1);
+ }
+
+ /**
+ * Returns index of family name from the array of family names available in
+ * this GraphicsEnvironment or -1 if no family name was found.
+ *
+ * @param familyName specified font family name
+ */
+ public int getFamilyIndex(String familyName){
+ for (int i=0; i<allFamilies.length; i++ ){
+ if (familyName.equalsIgnoreCase(allFamilies[i])){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns family with index specified from the array of family names available in
+ * this GraphicsEnvironment.
+ *
+ * @param index index of the family in families names array
+ */
+ public String getFamily(int index){
+ return allFamilies[index];
+ }
+ /**
+ * Returns index of face name from the array of face names available in
+ * this GraphicsEnvironment or -1 if no face name was found. Default return
+ * value is -1, method must be overridden by FontManager implementation.
+ *
+ * @param faceName font face name which index is to be searched
+ */
+ public int getFaceIndex(String faceName){
+ return -1;
+ }
+
+ public abstract String[] getAllFamilies();
+
+ public abstract Font[] getAllFonts();
+
+ /**
+ * Class contains SoftReference instance that can be stored in the
+ * Hashtable by means of key field corresponding to it.
+ */
+ private class HashMapReference extends SoftReference<FontPeer> {
+
+ /**
+ * The key for Hashtable.
+ */
+ private final String key;
+
+ /**
+ * Creates a new soft reference with the key specified and
+ * adding this reference in the reference queue specified.
+ *
+ * @param key the key in Hashtable
+ * @param value object that corresponds to the key
+ * @param queue reference queue where reference is to be added
+ */
+ public HashMapReference(final String key, final FontPeer value,
+ final ReferenceQueue<FontPeer> queue) {
+ super(value, queue);
+ this.key = key;
+ }
+
+ /**
+ * Returns the key that corresponds to the SoftReference instance
+ *
+ * @return the key in Hashtable with cached references
+ */
+ public Object getKey() {
+ return key;
+ }
+ }
+
+ /**
+ * Removes keys from the Hashtable with font peers which corresponding
+ * HashMapReference objects were garbage collected.
+ */
+ private void updateFontsTable() {
+ HashMapReference r;
+ //???AWT
+ //while ((r = (HashMapReference)queue.poll()) != null) {
+ // fontsTable.remove(r.getKey());
+ //}
+ }
+
+}
+
+
diff --git a/awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java b/awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java
new file mode 100644
index 0000000..7783317
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import com.android.internal.awt.AndroidGraphics2D;
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+//import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+
+import android.graphics.Paint;
+
+/**
+ * FontMetrics implementation
+ */
+
+public class FontMetricsImpl extends FontMetrics {
+
+ private static final long serialVersionUID = 844695615201925138L;
+
+ // ascent of the font
+ private int ascent;
+
+ // descent of the font
+ private int descent;
+
+ // leading of the font
+ private int leading;
+
+ // maximum ascent of the font
+ private int maxAscent;
+
+ // maximum descent of the font
+ private int maxDescent;
+
+ // maximum advance of the font
+ private int maxAdvance;
+
+ // array of char advance widths
+ private int[] widths = new int[256];
+
+ // font peer corresponding to this FontPeerImpl
+ private transient FontPeerImpl peer;
+
+ // X scale parameter of the font transform
+ private float scaleX = 1;
+
+ public AndroidGraphics2D mSg;
+
+ private Font mFn;
+
+ // Y scale parameter of the font transform
+ private float scaleY = 1;
+
+ /**
+ * Creates new FontMericsImpl object described by the specified Font.
+ *
+ * @param fnt
+ * the specified Font object
+ */
+ public FontMetricsImpl(Font fnt) {
+ super(fnt);
+ this.mFn = fnt;
+
+ mSg = AndroidGraphics2D.getInstance();
+ Paint p = mSg.getAndroidPaint();
+
+ this.ascent = (int)-p.ascent();
+ this.descent = (int)p.descent();
+ this.leading = p.getFontMetricsInt().leading;
+
+ AffineTransform at = fnt.getTransform();
+ if (!at.isIdentity()) {
+ scaleX = (float) at.getScaleX();
+ scaleY = (float) at.getScaleY();
+ }
+
+ /*
+ * metrics[5] - strikethrough thickness<p>
+ * -metrics[6] - strikethrough offset<p>
+ * metrics[7] - maximum char width<p>
+ * metrics[8] - ascent in pixels<p>
+ * metrics[9] - descent in pixles<p>
+ * metrics[10] - external leading in pixels<p>
+ * metrics[11] - underline thickness in pixels<p>
+ * -metrics[12] - underline offset in pixels<p>
+ * metrics[13] - strikethrough thickness in pixels<p>
+ * -metrics[14] - strikethrough offset in pixels<p>
+ * metrics[15] - maximum char width in pixels<p>
+
+ * @param _baselineData an array of 3 elements with baseline offsets metrics<p>
+ * _baselineData[0] - roman baseline offset<p>
+ * _baselineData[1] - center baseline offset<p>
+ * _baselineData[2] - hanging baseline offset<p>
+ */
+ }
+
+
+ /**
+ * Initialize the array of the first 256 chars' advance widths of the Font
+ * describing this FontMetricsImpl object.
+ */
+ private void initWidths() {
+
+ this.widths = new int[256];
+ for (int chr = 0; chr < 256; chr++) {
+ widths[chr] = (int) (getFontPeer().charWidth((char) chr) * scaleX);
+ }
+
+ }
+
+ /**
+ * Returns the ascent of the Font describing this FontMetricsImpl object.
+ */
+ @Override
+ public int getAscent() {
+ return this.ascent;
+ }
+
+ /**
+ * Returns the descent of the Font describing this FontMetricsImpl object.
+ */
+ @Override
+ public int getDescent() {
+ return this.descent;
+ }
+
+ /**
+ * Returns the leading of the Font describing this FontMetricsImpl object.
+ */
+ @Override
+ public int getLeading() {
+ return this.leading;
+ }
+
+ /**
+ * Returns the advance width of the specified char of the Font describing
+ * this FontMetricsImpl object.
+ *
+ * @param ch
+ * the char which width is to be returned
+ * @return the advance width of the specified char of the Font describing
+ * this FontMetricsImpl object
+ */
+ @Override
+ public int charWidth(int ch) {
+ if (ch < 256) {
+ return widths[ch];
+ }
+
+ return getFontPeer().charWidth((char) ch);
+ }
+
+ /**
+ * Returns the advance width of the specified char of the Font describing
+ * this FontMetricsImpl object.
+ *
+ * @param ch
+ * the char which width is to be returned
+ * @return the advance width of the specified char of the Font describing
+ * this FontMetricsImpl object
+ */
+ @Override
+ public int charWidth(char ch) {
+ if (ch < 256) {
+ return widths[ch];
+ }
+ return (int) (getFontPeer().charWidth(ch) * scaleX);
+ }
+
+ /**
+ * Returns the maximum advance of the Font describing this FontMetricsImpl
+ * object.
+ */
+ @Override
+ public int getMaxAdvance() {
+ return this.maxAdvance;
+ }
+
+ /**
+ * Returns the maximum ascent of the Font describing this FontMetricsImpl
+ * object.
+ */
+ @Override
+ public int getMaxAscent() {
+ return this.maxAscent;
+ }
+
+ /**
+ * Returns the maximum descent of the Font describing this FontMetricsImpl
+ * object.
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ @Override
+ public int getMaxDecent() {
+ return this.maxDescent;
+ }
+
+ /**
+ * Returns the maximum descent of the Font describing this FontMetricsImpl
+ * object.
+ */
+ @Override
+ public int getMaxDescent() {
+ return this.maxDescent;
+ }
+
+ /**
+ * Returns the advance widths of the first 256 characters in the Font
+ * describing this FontMetricsImpl object.
+ */
+ @Override
+ public int[] getWidths() {
+ return this.widths;
+ }
+
+ /**
+ * Returns the total advance width of the specified string in the metrics of
+ * the Font describing this FontMetricsImpl object.
+ *
+ * @param str
+ * the String which width is to be measured
+ * @return the total advance width of the specified string in the metrics of
+ * the Font describing this FontMetricsImpl object
+ */
+ @Override
+ public int stringWidth(String str) {
+
+ int width = 0;
+ char chr;
+
+ for (int i = 0; i < str.length(); i++) {
+ chr = str.charAt(i);
+ width += charWidth(chr);
+ }
+ return width;
+
+ /*
+ * float res = 0; int ln = str.length(); char[] c = new char[ln]; float[] f =
+ * new float[ln]; str.getChars(0, ln, c, 0); mSg.getPaint().getTextWidths(c, 0,
+ * ln, f);
+ *
+ * for(int i = 0; i < f.length; i++) { res += f[i]; } return (int)res;
+ */
+ }
+
+ /**
+ * Returns FontPeer implementation of the Font describing this
+ * FontMetricsImpl object.
+ *
+ * @return a FontPeer object, that is the platform dependent FontPeer
+ * implementation for the Font describing this FontMetricsImpl
+ * object.
+ */
+ @SuppressWarnings("deprecation")
+ public FontPeerImpl getFontPeer() {
+ if (peer == null) {
+ peer = (FontPeerImpl) font.getPeer();
+ }
+ return peer;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java b/awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java
new file mode 100644
index 0000000..14ff997
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java
@@ -0,0 +1,499 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+
+import com.android.internal.awt.AndroidGraphics2D;
+import com.android.internal.awt.AndroidGraphicsFactory;
+
+import java.awt.Graphics2D;
+import java.awt.Toolkit;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.peer.FontPeer;
+
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineMetrics;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+import android.graphics.Paint;
+
+/**
+ * Abstract class for platform dependent peer implementation of the Font class.
+ */
+public abstract class FontPeerImpl implements FontPeer{
+
+ // ascent of this font peer (in pixels)
+ int ascent;
+
+ // descent of this font peer (in pixels)
+ int descent;
+
+ // leading of this font peer (in pixels)
+ int leading;
+
+ // logical maximum advance of this font peer (in pixels)
+ int maxAdvance;
+
+ // the height of this font peer
+ float height;
+
+ // the style of this font peer
+ int style;
+
+ // the point size of this font peer (in pixels)
+ int size;
+
+ // the logical hight of this font peer (in pixels)
+ int logicalHeight;
+
+ // the name of this font peer
+ String name;
+
+ // family name of this font peer
+ String fontFamilyName;
+
+ // the Face name of this font peer
+ String faceName;
+
+ // bounds rectanlge of the largest character in this font peer
+ Rectangle2D maxCharBounds;
+
+ // italic angle value of this font peer
+ float italicAngle = 0.0f;
+
+ // the number of glyphs supported by this font peer
+ int numGlyphs = 0;
+
+ // native font handle
+ long pFont;
+
+ // cached line metrics object
+ LineMetricsImpl nlm;
+
+ // the postscript name of this font peer
+ String psName = null;
+
+ /**
+ * Default glyph index, that is used, when the desired glyph
+ * is unsupported in this Font.
+ */
+ public char defaultChar = (char)0xFFFF;
+
+ /**
+ * Uniform LineMetrics flag, that is false for CompositeFont.
+ * Default value is true.
+ */
+ boolean uniformLM = true;
+
+ /**
+ * Flag of the type of this Font that is indicate is the Font
+ * has TrueType or Type1 type. Default value is FONT_TYPE_UNDEF.
+ */
+ int fontType = FontManager.FONT_TYPE_UNDEF;
+
+ /**
+ * Flag if this Font was created from stream,
+ * this parameter used in finilize method.
+ */
+ private boolean createdFromStream = false;
+
+ // temorary Font file name, if this FontPeerImpl was created from InputStream
+ private String tempFontFileName = null;
+
+ // cached FontExtraMetrics object related to this font peer
+ FontExtraMetrics extraMetrix = null;
+
+ public abstract FontExtraMetrics getExtraMetrics();
+
+ /**
+ * Returns LineMetrics object with specified parameters
+ * @param str specified String
+ * @param frc specified render context
+ * @param at specified affine transform
+ * @return
+ */
+ public abstract LineMetrics getLineMetrics(String str, FontRenderContext frc, AffineTransform at);
+
+ /**
+ * Returns postscript name of the font.
+ */
+ public abstract String getPSName();
+
+ //private Graphics2D g = ((AndroidGraphicsFactory)Toolkit.getDefaultToolkit().getGraphicsFactory()).getGraphics2D();
+ //private Graphics2D g = AndroidGraphics2D.getInstance();
+
+ /**
+ * Set postscript name of the font to the specified parameter.
+ */
+ public void setPSName(String name){
+ this.psName = name;
+ }
+
+ /**
+ * Returns code of the missing glyph.
+ */
+ public abstract int getMissingGlyphCode();
+
+ /**
+ * Returns Glyph representation of the given char.
+ * @param ch specified char
+ */
+ public abstract Glyph getGlyph(char ch);
+
+ /**
+ * Disposes nesessary resources.
+ */
+ public abstract void dispose();
+
+ /**
+ * Returns Glyph represeting missing char.
+ */
+ public abstract Glyph getDefaultGlyph();
+
+ /**
+ * Returns true if this FontPeerImpl can display the specified char
+ */
+ public abstract boolean canDisplay(char c);
+
+ /**
+ * Returns family name of the font in specified locale settings.
+ * @param l specified Locale
+ */
+ public String getFamily(Locale l){
+ return this.getFamily();
+ }
+
+ /**
+ * Sets family name of the font in specified locale settings.
+ */
+ public void setFamily(String familyName){
+ this.fontFamilyName = familyName;
+ }
+
+ /**
+ * Returns face name of the font in specified locale settings.
+ * @param l specified Locale
+ */
+ public String getFontName(Locale l){
+ return this.getFontName();
+ }
+
+ /**
+ * Sets font name of the font in specified locale settings.
+ */
+ public void setFontName(String fontName){
+ this.faceName = fontName;
+ }
+
+ /**
+ * Returns true, if this font peer was created from InputStream, false otherwise.
+ * In case of creating fonts from InputStream some font peer implementations
+ * may need to free temporary resources.
+ */
+ public boolean isCreatedFromStream(){
+ return this.createdFromStream;
+ }
+
+ /**
+ * Sets createdFromStream flag to the specified parameter.
+ * If parameter is true it means font peer was created from InputStream.
+ *
+ * @param value true, if font peer was created from InputStream
+ */
+ public void setCreatedFromStream(boolean value){
+ this.createdFromStream = value;
+ }
+
+ /**
+ * Returns font file name of this font.
+ */
+ public String getTempFontFileName(){
+ return this.tempFontFileName;
+ }
+
+ /**
+ * Sets font file name of this font to the specified one.
+ * @param value String representing font file name
+ */
+ public void setFontFileName(String value){
+ this.tempFontFileName = value;
+ }
+
+ /**
+ * Returns the advance width of the specified char of this FontPeerImpl.
+ * Note, if glyph is absent in the font's glyphset - returned value
+ * is the advance of the deafualt glyph. For escape-chars returned
+ * width value is 0.
+ *
+ * @param ch the char which width is to be returned
+ * @return the advance width of the specified char of this FontPeerImpl
+ */
+ public int charWidth(char ch) {
+ Paint p;
+ AndroidGraphics2D g = AndroidGraphics2D.getInstance();
+ if(g == null) {
+ throw new RuntimeException("AndroidGraphics2D not instantiated!");
+ }
+ p = ((AndroidGraphics2D)g).getAndroidPaint();
+ char[] ca = {ch};
+ float[] fa = new float[1];
+ p.getTextWidths(ca, 0, 1, fa);
+ return (int)fa[0];
+ }
+
+ /**
+ * Returns the advance width of the specified char of this FontPeerImpl.
+ *
+ * @param ind the char which width is to be returned
+ * @return the advance width of the specified char of this FontPeerImpl
+ */
+ public int charWidth(int ind) {
+ return charWidth((char)ind);
+ }
+
+ /**
+ * Returns an array of Glyphs that represent characters from the specified
+ * Unicode range.
+ *
+ * @param uFirst start position in Unicode range
+ * @param uLast end position in Unicode range
+ * @return
+ */
+ public Glyph[] getGlyphs(char uFirst, char uLast) {
+
+ char i = uFirst;
+ int len = uLast - uFirst;
+ ArrayList<Glyph> lst = new ArrayList<Glyph>(len);
+
+ if (size < 0) {
+ // awt.09=min range bound value is greater than max range bound
+ throw new IllegalArgumentException(Messages.getString("awt.09")); //$NON-NLS-1$
+ }
+
+ while (i < uLast) {
+ lst.add(this.getGlyph(i));
+ }
+
+ return (Glyph[]) lst.toArray();
+ }
+
+ /**
+ * Returns an array of Glyphs representing given array of chars.
+ *
+ * @param chars specified array of chars
+ */
+ public Glyph[] getGlyphs(char[] chars) {
+ if (chars == null){
+ return null;
+ }
+
+ Glyph[] result = new Glyph[chars.length];
+
+ for (int i = 0; i < chars.length; i++) {
+ result[i] = this.getGlyph(chars[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Returns an array of Glyphs representing given string.
+ *
+ * @param str specified string
+ */
+ public Glyph[] getGlyphs(String str) {
+ char[] chars = str.toCharArray();
+ return this.getGlyphs(chars);
+ }
+
+ /**
+ * Returns family name of this FontPeerImpl.
+ */
+ public String getFamily() {
+ return fontFamilyName;
+ }
+
+ /**
+ * Returns face name of this FontPeerImpl.
+ */
+ public String getFontName() {
+ if (this.fontType == FontManager.FONT_TYPE_T1){
+ return this.fontFamilyName;
+ }
+
+ return faceName;
+ }
+
+ /**
+ * Returns height of this font peer in pixels.
+ */
+ public int getLogicalHeight() {
+ return logicalHeight;
+ }
+
+ /**
+ * Sets height of this font peer in pixels to the given value.
+ *
+ * @param newHeight new height in pixels value
+ */
+ public void setLogicalHeight(int newHeight) {
+ logicalHeight = newHeight;
+ }
+
+ /**
+ * Returns font size.
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Returns font style.
+ */
+ public int getStyle() {
+ return style;
+ }
+
+ /**
+ * Returns font name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the bounds of the largest char in this FontPeerImpl in
+ * specified render context.
+ *
+ * @param frc specified FontRenderContext
+ */
+ public Rectangle2D getMaxCharBounds(FontRenderContext frc) {
+ return maxCharBounds;
+ }
+
+ /**
+ * Returns the number of glyphs in this FontPeerImpl.
+ */
+ public int getNumGlyphs() {
+ return numGlyphs;
+ }
+
+ /**
+ * Returns tangens of the italic angle of this FontPeerImpl.
+ * If the FontPeerImpl has TrueType font type, italic angle value can be
+ * calculated as (CharSlopeRun / CharSlopeRise) in terms of GDI.
+ */
+ public float getItalicAngle() {
+ return italicAngle;
+ }
+
+ /**
+ * Returns height of this font peer.
+ */
+ public float getHeight(){
+ return height;
+ }
+
+ /**
+ * Returns cached LineMetrics object of this font peer.
+ */
+ public LineMetrics getLineMetrics(){
+ return nlm;
+ }
+
+ /**
+ * Returns native font handle of this font peer.
+ */
+ public long getFontHandle(){
+ return pFont;
+ }
+
+ /**
+ * Returns ascent of this font peer.
+ */
+ public int getAscent(){
+ Paint p;
+ AndroidGraphics2D g = AndroidGraphics2D.getInstance();
+ if(g == null) {
+ throw new RuntimeException("AndroidGraphics2D not instantiated!");
+ }
+ p = ((AndroidGraphics2D)g).getAndroidPaint();
+ return (int)p.ascent();
+ //return ascent;
+ }
+
+ /**
+ * Returns descent of this font peer.
+ */
+ public int getDescent(){
+ return descent;
+ }
+
+ /**
+ * Returns leading of this font peer.
+ */
+ public int getLeading(){
+ return leading;
+ }
+
+ /**
+ * Returns true if this font peer has uniform line metrics.
+ */
+ public boolean hasUniformLineMetrics(){
+ return uniformLM;
+ }
+
+ /**
+ * Returns type of this font.
+ *
+ * @return one of constant font type values.
+ */
+ public int getFontType(){
+ return fontType;
+ }
+
+ /**
+ * Sets new font type to the font object.
+ *
+ * @param newType new type value
+ */
+ public void setFontType(int newType){
+ if (newType == FontManager.FONT_TYPE_T1 || newType == FontManager.FONT_TYPE_TT){
+ fontType = newType;
+ }
+ }
+
+ /**
+ * Sets new font type to the font object.
+ *
+ * @param newType new type value
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+
+ dispose();
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/FontProperty.java b/awt/org/apache/harmony/awt/gl/font/FontProperty.java
new file mode 100644
index 0000000..4eb7cbb
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/FontProperty.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+
+/**
+ * Class containing font property information. This information can be found
+ * in font.property files. See API documentation, logical fonts description part.
+ *
+ */
+public class FontProperty {
+
+ // font file name
+ String fileName = null;
+
+ // name of the encoding to be used
+ String encoding = null;
+
+ // array of exclusion ranges (pairs of low and high unicode exclusion bounds)
+ int[] exclRange = null;
+
+ // font face name
+ String name = null;
+
+ // font style
+ int style = -1;
+
+ /**
+ * Returns font style of this font property.
+ */
+ public int getStyle(){
+ return this.style;
+ }
+
+ /**
+ * Returns font name of this font property.
+ */
+ public String getName(){
+ return this.name;
+ }
+
+ /**
+ * Returns encoding used in this font property.
+ */
+ public String getEncoding(){
+ return this.encoding;
+ }
+
+ /**
+ * Returns an array of exclusion ranges. This array contain pairs of
+ * low and high bounds of the intervals of characters to ignore in
+ * total Unicode characters range.
+ */
+ public int[] getExclusionRange(){
+ return this.exclRange;
+ }
+
+ /**
+ * Returns file name of the font that is described by this font property.
+ */
+ public String getFileName(){
+ return this.fileName;
+ }
+
+ /**
+ * Returns true if specified character covered by exclusion ranges of this
+ * font property, false otherwise.
+ *
+ * @param ch specified char to check
+ */
+ public boolean isCharExcluded(char ch){
+ if (exclRange == null ){
+ return false;
+ }
+
+ for (int i = 0; i < exclRange.length;){
+ int lb = exclRange[i++];
+ int hb = exclRange[i++];
+
+ if (ch >= lb && ch <= hb){
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/Glyph.java b/awt/org/apache/harmony/awt/gl/font/Glyph.java
new file mode 100644
index 0000000..44b8809
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/Glyph.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.Shape;
+import java.awt.font.GlyphJustificationInfo;
+import java.awt.font.GlyphMetrics;
+import java.awt.image.BufferedImage;
+
+public abstract class Glyph{
+
+ // character of the glyph
+ char glChar;
+
+ // precise glyph metrics
+ GlyphMetrics glMetrics;
+
+ // glyph metrics in pixels
+ GlyphMetrics glPointMetrics;
+
+ // glyph code of this Glyph
+ int glCode;
+
+ // justification info of this glyph
+ GlyphJustificationInfo glJustInfo;
+
+ // native font handle of the font corresponding to this glyph
+ long pFont;
+
+ // size of the font corresponding to this glyph
+ int fontSize;
+
+ // bitmap representation of the glyph
+ byte[] bitmap = null;
+
+ // Buffered image representation of the glyph
+ BufferedImage image;
+
+ // shape that representing the outline of this glyph
+ Shape glOutline = null;
+
+ /**
+ * image bitmap parameters
+ */
+
+ // top side bearing
+ public int bmp_top = 0;
+
+ // left side bearing
+ public int bmp_left = 0;
+
+ // number of bytes in row
+ public int bmp_pitch;
+
+ // number of rows
+ public int bmp_rows;
+
+ // width of the row
+ public int bmp_width;
+
+ /**
+ * Retruns handle to Native Font object
+ */
+ public long getPFont(){
+ return this.pFont;
+ }
+
+ /**
+ * Retruns char value of this glyph object
+ */
+ public char getChar(){
+ return glChar;
+ }
+
+ /**
+ * Retruns precise width of this glyph object
+ */
+ public int getWidth(){
+ return Math.round((float)glMetrics.getBounds2D().getWidth());
+ }
+
+ /**
+ * Retruns precise height of this glyph object
+ */
+ public int getHeight(){
+ return Math.round((float)glMetrics.getBounds2D().getHeight());
+ }
+
+ /**
+ * Retruns glyph code of this glyph object
+ */
+ public int getGlyphCode(){
+ return glCode;
+ }
+
+ /**
+ * Retruns GlyphMetrics of this glyph object with precise metrics.
+ */
+ public GlyphMetrics getGlyphMetrics(){
+ return glMetrics;
+ }
+
+ /**
+ * Retruns GlyphMetrics of this glyph object in pixels.
+ */
+ public GlyphMetrics getGlyphPointMetrics(){
+ return glPointMetrics;
+ }
+
+ /**
+ * Retruns GlyphJustificationInfo of this glyph object
+ */
+ public GlyphJustificationInfo getGlyphJustificationInfo(){
+ return glJustInfo;
+ }
+
+ /**
+ * Sets JustificationInfo of this glyph object
+ *
+ * @param newJustInfo GlyphJustificationInfo object to set to the Glyph object
+ */
+ public void setGlyphJustificationInfo(GlyphJustificationInfo newJustInfo){
+ this.glJustInfo = newJustInfo;
+ }
+
+ /**
+ * Returns an int array of 3 elements, so-called ABC structure that contains
+ * the width of the character:
+ * 1st element = left side bearing of the glyph
+ * 2nd element = width of the glyph
+ * 3d element = right side bearing of the glyph
+ */
+ public int[] getABC(){
+ int[] abc = new int[3];
+ abc[0] = (int)glMetrics.getLSB();
+ abc[1] = (int)glMetrics.getBounds2D().getWidth();
+ abc[2] = (int)glMetrics.getRSB();
+
+ return abc;
+ }
+
+ /**
+ * Sets BufferedImage representation of this glyph to the specified parameter.
+ *
+ * @param newImage new BufferedImage object to be set as BufferedImage
+ * representation.
+ */
+ public void setImage(BufferedImage newImage){
+ this.image = newImage;
+ }
+
+ /**
+ * Returns true if this Glyph and specified object are equal.
+ */
+ @Override
+ public boolean equals(Object obj){
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj != null) {
+ try {
+ Glyph gl = (Glyph)obj;
+
+ return ((this.getChar() == gl.getChar())
+ && (this.getGlyphMetrics().equals(gl.getGlyphMetrics()))
+ && (this.getGlyphCode() == gl.getGlyphCode()));
+ } catch (ClassCastException e) {
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns height of the glyph in points.
+ */
+ public int getPointHeight(){
+ return (int)glPointMetrics.getBounds2D().getHeight();
+ }
+
+ /**
+ * Returns width of the glyph in points.
+ */
+ public int getPointWidth(){
+ return (int)glPointMetrics.getBounds2D().getWidth();
+ }
+
+ public Shape getShape(){
+ if (glOutline == null){
+ glOutline = initOutline(this.glChar);
+ }
+ return glOutline;
+ }
+
+ /**
+ * Sets BufferedImage representation of this glyph.
+ */
+ public BufferedImage getImage(){
+ //!! Implementation classes must override this method
+ return null;
+ }
+
+ /**
+ * Returns array of bytes, representing image of this glyph
+ */
+ public abstract byte[] getBitmap();
+
+ /**
+ * Returns shape that represents outline of the specified character.
+ *
+ * @param c specified character
+ */
+ public abstract Shape initOutline(char c);
+
+}
+
+
diff --git a/awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java b/awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java
new file mode 100644
index 0000000..370146d
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.font.LineMetrics;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ *
+ * LineMetrics implementation class.
+ */
+
+public class LineMetricsImpl extends LineMetrics implements Cloneable{
+
+ // array of baseline offsets
+ float[] baselineOffsets;
+
+ // the number of characters to measure
+ int numChars;
+
+ // baseline index of the font corresponding to this line metrics
+ int baseLineIndex;
+
+ // underline thickness
+ float underlineThickness;
+
+ // underline offset
+ float underlineOffset;
+
+ // strikethrough thickness
+ float strikethroughThickness;
+
+ // strikethrough offset
+ float strikethroughOffset;
+
+ // External leading
+ float leading;
+
+ // Height of the font ( == (ascent+descent+leading))
+ float height;
+
+ // Ascent of the font
+ float ascent;
+
+ // Descent of the font
+ float descent;
+
+ // Width of the widest char in the font
+ float maxCharWidth;
+
+ // underline thickness (in pixels)
+ int lUnderlineThickness;
+
+ // underline offset (in pixels)
+ int lUnderlineOffset;
+
+ // strikethrough thickness (in pixels)
+ int lStrikethroughThickness;
+
+ // strikethrough offset (in pixels)
+ int lStrikethroughOffset;
+
+ // External leading (in pixels)
+ int lLeading;
+
+ // Height of the font ( == (ascent+descent+leading)) (in pixels)
+ int lHeight;
+
+ // Ascent of the font (in pixels)
+ int lAscent;
+
+ // Descent of the font (in pixels)
+ int lDescent;
+
+ // Width of the widest char in the font (in pixels)
+ int lMaxCharWidth;
+
+ // units per EM square in font value
+ int units_per_EM = 0;
+
+ /**
+ * Creates LineMetricsImpl object from specified parameters. If baseline data parameter
+ * is null than {0, (-ascent+descent)/2, -ascent} values are used for baseline offsets.
+ *
+ * @param len a number of characters
+ * @param metrics an array of 16 elements with metrics values that can be
+ * initialized in native code.<p>
+ * metrics[0] - ascent<p>
+ * metrics[1] - descent<p>
+ * metrics[2] - external leading<p>
+ * metrics[3] - underline thickness<p>
+ * -metrics[4] - underline offset<p>
+ * metrics[5] - strikethrough thickness<p>
+ * -metrics[6] - strikethrough offset<p>
+ * metrics[7] - maximum char width<p>
+ * metrics[8] - ascent in pixels<p>
+ * metrics[9] - descent in pixles<p>
+ * metrics[10] - external leading in pixels<p>
+ * metrics[11] - underline thickness in pixels<p>
+ * -metrics[12] - underline offset in pixels<p>
+ * metrics[13] - strikethrough thickness in pixels<p>
+ * -metrics[14] - strikethrough offset in pixels<p>
+ * metrics[15] - maximum char width in pixels<p>
+
+ * @param _baselineData an array of 3 elements with baseline offsets metrics<p>
+ * _baselineData[0] - roman baseline offset<p>
+ * _baselineData[1] - center baseline offset<p>
+ * _baselineData[2] - hanging baseline offset<p>
+ */
+ public LineMetricsImpl(int len, float[] metrics, float[] _baselineData){
+ numChars = len;
+
+ ascent = metrics[0]; // Ascent of the font
+ descent = metrics[1]; // Descent of the font
+ leading = metrics[2]; // External leading
+ height = metrics[0] + metrics[1] + metrics[2]; // Height of the font ( == (ascent + descent + leading))
+ }
+
+ /**
+ * Creates LineMetricsImpl object from specified parameters. If baseline data parameter
+ * is null than {0, (-ascent+descent)/2, -ascent} values are used for baseline offsets.
+ *
+ * @param _numChars number of chars
+ * @param _baseLineIndex index of the baseline offset
+ * @param _baselineOffsets an array of baseline offsets
+ * @param _underlineThickness underline thickness
+ * @param _underlineOffset underline offset
+ * @param _strikethroughThickness strikethrough thickness
+ * @param _strikethroughOffset strinkethrough offset
+ * @param _leading leading of the font
+ * @param _height font height
+ * @param _ascent ascent of the font
+ * @param _descent descent of the font
+ * @param _maxCharWidth max char width
+ */
+ public LineMetricsImpl(int _numChars, int _baseLineIndex,
+ float[] _baselineOffsets, float _underlineThickness,
+ float _underlineOffset, float _strikethroughThickness,
+ float _strikethroughOffset, float _leading, float _height,
+ float _ascent, float _descent, float _maxCharWidth) {
+
+ numChars = _numChars;
+ baseLineIndex = _baseLineIndex;
+ underlineThickness = _underlineThickness;
+ underlineOffset = _underlineOffset;
+ strikethroughThickness = _strikethroughThickness;
+ strikethroughOffset = _strikethroughOffset;
+ leading = _leading;
+ height = _height;
+ ascent = _ascent;
+ descent = _descent;
+ baselineOffsets = _baselineOffsets;
+ lUnderlineThickness = (int) underlineThickness;
+ lUnderlineOffset = (int) underlineOffset;
+ lStrikethroughThickness = (int) strikethroughThickness;
+ lStrikethroughOffset = (int) strikethroughOffset;
+ lLeading = (int) leading;
+ lHeight = (int) height;
+ lAscent = (int) ascent;
+ lDescent = (int) descent;
+ maxCharWidth = _maxCharWidth;
+ }
+
+ public LineMetricsImpl(){
+
+ }
+
+ /**
+ * All metrics are scaled according to scaleX and scaleY values.
+ * This function helps to recompute metrics according to the scale factors
+ * of desired AffineTransform.
+ *
+ * @param scaleX scale X factor
+ * @param scaleY scale Y factor
+ */
+ public void scale(float scaleX, float scaleY){
+ float absScaleX = Math.abs(scaleX);
+ float absScaleY = Math.abs(scaleY);
+
+ underlineThickness *= absScaleY;
+ underlineOffset *= scaleY;
+ strikethroughThickness *= absScaleY;
+ strikethroughOffset *= scaleY;
+ leading *= absScaleY;
+ height *= absScaleY;
+ ascent *= absScaleY;
+ descent *= absScaleY;
+
+ if(baselineOffsets == null) {
+ getBaselineOffsets();
+ }
+
+ for (int i=0; i< baselineOffsets.length; i++){
+ baselineOffsets[i] *= scaleY;
+ }
+
+ lUnderlineThickness *= absScaleY;
+ lUnderlineOffset *= scaleY;
+ lStrikethroughThickness *= absScaleY;
+ lStrikethroughOffset *= scaleY;
+ lLeading *= absScaleY;
+ lHeight *= absScaleY;
+ lAscent *= absScaleY;
+ lDescent *= absScaleY;
+ maxCharWidth *= absScaleX;
+
+ }
+
+
+ /**
+ * Returns offset of the baseline.
+ */
+ @Override
+ public float[] getBaselineOffsets() {
+ // XXX: at the moment there only horizontal metrics are taken into
+ // account. If there is no baseline information in TrueType font
+ // file default values used: {0, -ascent, (-ascent+descent)/2}
+
+ return baselineOffsets;
+ }
+
+ /**
+ * Returns a number of chars in specified text
+ */
+ @Override
+ public int getNumChars() {
+ return numChars;
+ }
+
+ /**
+ * Returns index of the baseline, one of predefined constants.
+ */
+ @Override
+ public int getBaselineIndex() {
+ // Baseline index is the deafult baseline index value
+ // taken from the TrueType table "BASE".
+ return baseLineIndex;
+ }
+
+ /**
+ * Returns thickness of the Underline.
+ */
+ @Override
+ public float getUnderlineThickness() {
+ return underlineThickness;
+ }
+
+ /**
+ * Returns offset of the Underline.
+ */
+ @Override
+ public float getUnderlineOffset() {
+ return underlineOffset;
+ }
+
+ /**
+ * Returns thickness of the Strikethrough line.
+ */
+ @Override
+ public float getStrikethroughThickness() {
+ return strikethroughThickness;
+ }
+
+ /**
+ * Returns offset of the Strikethrough line.
+ */
+ @Override
+ public float getStrikethroughOffset() {
+ return strikethroughOffset;
+ }
+
+ /**
+ * Returns the leading.
+ */
+ @Override
+ public float getLeading() {
+ return leading;
+ }
+
+ /**
+ * Returns the height of the font.
+ */
+ @Override
+ public float getHeight() {
+ //return height; // equals to (ascent + descent + leading);
+ return ascent + descent + leading;
+ }
+
+ /**
+ * Returns the descent.
+ */
+ @Override
+ public float getDescent() {
+ return descent;
+ }
+
+ /**
+ * Returns the ascent.
+ */
+ @Override
+ public float getAscent() {
+ return ascent;
+ }
+
+ /**
+ * Returns logical thickness of the Underline.
+ */
+ public int getLogicalUnderlineThickness() {
+ return lUnderlineThickness;
+ }
+
+ /**
+ * Returns logical offset of the Underline.
+ */
+ public int getLogicalUnderlineOffset() {
+ return lUnderlineOffset;
+ }
+
+ /**
+ * Returns logical thickness of the Strikethrough line.
+ */
+ public int getLogicalStrikethroughThickness() {
+ return lStrikethroughThickness;
+ }
+
+ /**
+ * Returns logical offset of the Strikethrough line.
+ */
+ public int getLogicalStrikethroughOffset() {
+ return lStrikethroughOffset;
+ }
+
+ /**
+ * Returns the logical leading.
+ */
+ public int getLogicalLeading() {
+ return lLeading;
+ }
+
+ /**
+ * Returns the logical height of the font.
+ */
+ public int getLogicalHeight() {
+ return lHeight; // equals to (ascent + descent + leading);
+ }
+
+ /**
+ * Returns the logical descent.
+ */
+ public int getLogicalDescent() {
+ return lDescent;
+ }
+
+ /**
+ * Returns the logical ascent.
+ */
+ public int getLogicalAscent() {
+ return lAscent;
+ }
+
+ /**
+ * Returns the logical size of the widest char.
+ */
+ public int getLogicalMaxCharWidth() {
+ return lMaxCharWidth;
+ }
+
+ /**
+ * Returns the size of the widest char.
+ */
+ public float getMaxCharWidth() {
+ return maxCharWidth;
+ }
+
+ /**
+ * Set num chars to the desired value.
+ *
+ * @param num specified number of chars
+ */
+ public void setNumChars(int num){
+ numChars = num;
+ }
+
+ @Override
+ public Object clone(){
+ try{
+ return super.clone();
+ }catch (CloneNotSupportedException e){
+ return null;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/font/TextDecorator.java b/awt/org/apache/harmony/awt/gl/font/TextDecorator.java
new file mode 100644
index 0000000..81905fd
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/TextDecorator.java
@@ -0,0 +1,433 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.TextAttribute;
+import java.awt.geom.Area;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.text.AttributedCharacterIterator.Attribute;
+import java.util.Map;
+
+/**
+ * This class is responsible for rendering text decorations like
+ * underline, strikethrough, text with background, etc.
+ */
+public class TextDecorator {
+ private static final TextDecorator inst = new TextDecorator();
+ private TextDecorator() {}
+ static TextDecorator getInstance() {
+ return inst;
+ }
+
+ /**
+ * This class encapsulates a set of decoration attributes for a single text run.
+ */
+ static class Decoration {
+ private static final BasicStroke UNDERLINE_LOW_ONE_PIXEL_STROKE =
+ new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10);
+
+ private static final BasicStroke UNDERLINE_LOW_TWO_PIXEL_STROKE =
+ new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10);
+
+ private static final BasicStroke UNDERLINE_LOW_DOTTED_STROKE =
+ new BasicStroke(
+ 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10,
+ new float[] { 1, 1 }, 0
+ );
+
+ private static final BasicStroke UNDERLINE_LOW_DOTTED_STROKE2 =
+ new BasicStroke(
+ 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10,
+ new float[] { 1, 1 }, 1
+ );
+
+ private static final BasicStroke UNDERLINE_LOW_DASHED_STROKE =
+ new BasicStroke(
+ 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10,
+ new float[] { 4, 4 }, 0
+ );
+
+ boolean ulOn = false; // Have standard underline?
+ BasicStroke ulStroke;
+
+ BasicStroke imUlStroke; // Stroke for INPUT_METHOD_UNDERLINE
+ BasicStroke imUlStroke2; // Specially for UNDERLINE_LOW_GRAY
+
+ boolean strikeThrough;
+ BasicStroke strikeThroughStroke;
+
+ boolean haveStrokes = false; // Strokes already created?
+
+ boolean swapBfFg;
+ Paint bg; // background color
+ Paint fg; // foreground color
+
+ Paint graphicsPaint; // Slot for saving current paint
+
+ Decoration(
+ Integer imUl,
+ boolean swap,
+ boolean sth,
+ Paint bg, Paint fg,
+ boolean ulOn) {
+
+ if (imUl != null) {
+ // Determine which stroke to use
+ if (imUl == TextAttribute.UNDERLINE_LOW_ONE_PIXEL) {
+ this.imUlStroke = Decoration.UNDERLINE_LOW_ONE_PIXEL_STROKE;
+ } else if (imUl == TextAttribute.UNDERLINE_LOW_TWO_PIXEL) {
+ this.imUlStroke = Decoration.UNDERLINE_LOW_TWO_PIXEL_STROKE;
+ } else if (imUl == TextAttribute.UNDERLINE_LOW_DOTTED) {
+ this.imUlStroke = Decoration.UNDERLINE_LOW_DOTTED_STROKE;
+ } else if (imUl == TextAttribute.UNDERLINE_LOW_GRAY) {
+ this.imUlStroke = Decoration.UNDERLINE_LOW_DOTTED_STROKE;
+ this.imUlStroke2 = Decoration.UNDERLINE_LOW_DOTTED_STROKE2;
+ } else if (imUl == TextAttribute.UNDERLINE_LOW_DASHED) {
+ this.imUlStroke = Decoration.UNDERLINE_LOW_DASHED_STROKE;
+ }
+ }
+
+ this.ulOn = ulOn; // Has underline
+ this.swapBfFg = swap;
+ this.strikeThrough = sth;
+ this.bg = bg;
+ this.fg = fg;
+ }
+
+ /**
+ * Creates strokes of proper width according to the info
+ * stored in the BasicMetrics
+ * @param metrics - basic metrics
+ */
+ private void getStrokes(BasicMetrics metrics) {
+ if (!haveStrokes) {
+ if (strikeThrough) {
+ strikeThroughStroke =
+ new BasicStroke(
+ metrics.strikethroughThickness,
+ BasicStroke.CAP_BUTT,
+ BasicStroke.JOIN_MITER,
+ 10
+ );
+ }
+
+ if (ulOn) {
+ ulStroke =
+ new BasicStroke(
+ metrics.underlineThickness,
+ BasicStroke.CAP_BUTT,
+ BasicStroke.JOIN_MITER,
+ 10
+ );
+ }
+
+ haveStrokes = true;
+ }
+ }
+ }
+
+ /**
+ * Creates Decoration object from the set of text attributes
+ * @param attributes - text attributes
+ * @return Decoration object
+ */
+ static Decoration getDecoration(Map<? extends Attribute, ?> attributes) {
+ if (attributes == null) {
+ return null; // It is for plain text
+ }
+
+ Object underline = attributes.get(TextAttribute.UNDERLINE);
+ boolean hasStandardUnderline = underline == TextAttribute.UNDERLINE_ON;
+
+ Object imUnderline = attributes.get(TextAttribute.INPUT_METHOD_UNDERLINE);
+ Integer imUl = (Integer) imUnderline;
+
+ boolean swapBgFg =
+ TextAttribute.SWAP_COLORS_ON.equals(
+ attributes.get(TextAttribute.SWAP_COLORS)
+ );
+
+ boolean strikeThrough =
+ TextAttribute.STRIKETHROUGH_ON.equals(
+ attributes.get(TextAttribute.STRIKETHROUGH)
+ );
+
+ Paint fg = (Paint) attributes.get(TextAttribute.FOREGROUND);
+ Paint bg = (Paint) attributes.get(TextAttribute.BACKGROUND);
+
+ if (
+ !hasStandardUnderline &&
+ imUnderline == null &&
+ fg == null &&
+ bg == null &&
+ !swapBgFg &&
+ !strikeThrough
+ ) {
+ return null;
+ }
+ return new Decoration(imUl, swapBgFg, strikeThrough, bg, fg, hasStandardUnderline);
+ }
+
+ /**
+ * Fills the background before drawing if needed.
+ *
+ * @param trs - text segment
+ * @param g2d - graphics to draw to
+ * @param xOffset - offset in X direction to the upper left corner of the
+ * layout from the origin of the graphics
+ * @param yOffset - offset in Y direction to the upper left corner of the
+ * layout from the origin of the graphics
+ */
+ static void prepareGraphics(
+ TextRunSegment trs, Graphics2D g2d,
+ float xOffset, float yOffset
+ ) {
+ Decoration d = trs.decoration;
+
+ if (d.fg == null && d.bg == null && d.swapBfFg == false) {
+ return; // Nothing to do
+ }
+
+ d.graphicsPaint = g2d.getPaint();
+
+ if (d.fg == null) {
+ d.fg = d.graphicsPaint;
+ }
+
+ if (d.swapBfFg) {
+ // Fill background area
+ g2d.setPaint(d.fg);
+ Rectangle2D bgArea = trs.getLogicalBounds();
+ Rectangle2D toFill =
+ new Rectangle2D.Double(
+ bgArea.getX() + xOffset,
+ bgArea.getY() + yOffset,
+ bgArea.getWidth(),
+ bgArea.getHeight()
+ );
+ g2d.fill(toFill);
+
+ // Set foreground color
+ g2d.setPaint(d.bg == null ? Color.WHITE : d.bg);
+ } else {
+ if (d.bg != null) { // Fill background area
+ g2d.setPaint(d.bg);
+ Rectangle2D bgArea = trs.getLogicalBounds();
+ Rectangle2D toFill =
+ new Rectangle2D.Double(
+ bgArea.getX() + xOffset,
+ bgArea.getY() + yOffset,
+ bgArea.getWidth(),
+ bgArea.getHeight()
+ );
+ g2d.fill(toFill);
+ }
+
+ // Set foreground color
+ g2d.setPaint(d.fg);
+ }
+ }
+
+ /**
+ * Restores the original state of the graphics if needed
+ * @param d - decoration
+ * @param g2d - graphics
+ */
+ static void restoreGraphics(Decoration d, Graphics2D g2d) {
+ if (d.fg == null && d.bg == null && d.swapBfFg == false) {
+ return; // Nothing to do
+ }
+
+ g2d.setPaint(d.graphicsPaint);
+ }
+
+ /**
+ * Renders the text decorations
+ * @param trs - text run segment
+ * @param g2d - graphics to render to
+ * @param xOffset - offset in X direction to the upper left corner
+ * of the layout from the origin of the graphics
+ * @param yOffset - offset in Y direction to the upper left corner
+ * of the layout from the origin of the graphics
+ */
+ static void drawTextDecorations(
+ TextRunSegment trs, Graphics2D g2d,
+ float xOffset, float yOffset
+ ) {
+ Decoration d = trs.decoration;
+
+ if (!d.ulOn && d.imUlStroke == null && !d.strikeThrough) {
+ return; // Nothing to do
+ }
+
+ float left = xOffset + (float) trs.getLogicalBounds().getMinX();
+ float right = xOffset + (float) trs.getLogicalBounds().getMaxX();
+
+ Stroke savedStroke = g2d.getStroke();
+
+ d.getStrokes(trs.metrics);
+
+ if (d.strikeThrough) {
+ float y = trs.y + yOffset + trs.metrics.strikethroughOffset;
+ g2d.setStroke(d.strikeThroughStroke);
+ g2d.draw(new Line2D.Float(left, y, right, y));
+ }
+
+ if (d.ulOn) {
+ float y = trs.y + yOffset + trs.metrics.underlineOffset;
+ g2d.setStroke(d.ulStroke);
+ g2d.draw(new Line2D.Float(left, y, right, y));
+ }
+
+ if (d.imUlStroke != null) {
+ float y = trs.y + yOffset + trs.metrics.underlineOffset;
+ g2d.setStroke(d.imUlStroke);
+ g2d.draw(new Line2D.Float(left, y, right, y));
+ if (d.imUlStroke2 != null) {
+ y++;
+ g2d.setStroke(d.imUlStroke2);
+ g2d.draw(new Line2D.Float(left, y, right, y));
+ }
+ }
+
+ g2d.setStroke(savedStroke);
+ }
+
+ /**
+ * Extends the visual bounds of the text run segment to
+ * include text decorations.
+ * @param trs - text segment
+ * @param segmentBounds - bounds of the undecorated text
+ * @param d - decoration
+ * @return extended bounds
+ */
+ static Rectangle2D extendVisualBounds(
+ TextRunSegment trs,
+ Rectangle2D segmentBounds,
+ Decoration d
+ ) {
+ if (d == null) {
+ return segmentBounds;
+ }
+ double minx = segmentBounds.getMinX();
+ double miny = segmentBounds.getMinY();
+ double maxx = segmentBounds.getMaxX();
+ double maxy = segmentBounds.getMaxY();
+
+ Rectangle2D lb = trs.getLogicalBounds();
+
+ if (d.swapBfFg || d.bg != null) {
+ minx = Math.min(lb.getMinX() - trs.x, minx);
+ miny = Math.min(lb.getMinY() - trs.y, miny);
+ maxx = Math.max(lb.getMaxX() - trs.x, maxx);
+ maxy = Math.max(lb.getMaxY() - trs.y, maxy);
+ }
+
+ if (d.ulOn || d.imUlStroke != null || d.strikeThrough) {
+ minx = Math.min(lb.getMinX() - trs.x, minx);
+ maxx = Math.max(lb.getMaxX() - trs.x, maxx);
+
+ d.getStrokes(trs.metrics);
+
+ if (d.ulStroke != null) {
+ maxy = Math.max(
+ maxy,
+ trs.metrics.underlineOffset +
+ d.ulStroke.getLineWidth()
+ );
+ }
+
+ if (d.imUlStroke != null) {
+ maxy = Math.max(
+ maxy,
+ trs.metrics.underlineOffset +
+ d.imUlStroke.getLineWidth() +
+ (d.imUlStroke2 == null ? 0 : d.imUlStroke2.getLineWidth())
+ );
+ }
+ }
+
+ return new Rectangle2D.Double(minx, miny, maxx-minx, maxy-miny);
+ }
+
+ /**
+ * Extends the outline of the text run segment to
+ * include text decorations.
+ * @param trs - text segment
+ * @param segmentOutline - outline of the undecorated text
+ * @param d - decoration
+ * @return extended outline
+ */
+ static Shape extendOutline(
+ TextRunSegment trs,
+ Shape segmentOutline,
+ Decoration d
+ ) {
+ if (d == null || !d.ulOn && d.imUlStroke == null && !d.strikeThrough) {
+ return segmentOutline; // Nothing to do
+ }
+
+ Area res = new Area(segmentOutline);
+
+ float left = (float) trs.getLogicalBounds().getMinX() - trs.x;
+ float right = (float) trs.getLogicalBounds().getMaxX() - trs.x;
+
+ d.getStrokes(trs.metrics);
+
+ if (d.strikeThrough) {
+ float y = trs.metrics.strikethroughOffset;
+ res.add(new Area(d.strikeThroughStroke.createStrokedShape(
+ new Line2D.Float(left, y, right, y)
+ )));
+ }
+
+ if (d.ulOn) {
+ float y = trs.metrics.underlineOffset;
+ res.add(new Area(d.ulStroke.createStrokedShape(
+ new Line2D.Float(left, y, right, y)
+ )));
+ }
+
+ if (d.imUlStroke != null) {
+ float y = trs.metrics.underlineOffset;
+ res.add(new Area(d.imUlStroke.createStrokedShape(
+ new Line2D.Float(left, y, right, y)
+ )));
+
+ if (d.imUlStroke2 != null) {
+ y++;
+ res.add(new Area(d.imUlStroke2.createStrokedShape(
+ new Line2D.Float(left, y, right, y)
+ )));
+ }
+ }
+
+ return res;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java b/awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java
new file mode 100644
index 0000000..be5762a
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.font.LineMetrics;
+import java.awt.font.GraphicAttribute;
+import java.awt.Font;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * This class operates with an arbitrary text string which can include
+ * any number of style, font and direction runs. It is responsible for computation
+ * of the text metrics, such as ascent, descent, leading and advance. Actually,
+ * each text run segment contains logic which allows it to compute its own metrics and
+ * responsibility of this class is to combine metrics for all segments included in the text,
+ * managed by the associated TextRunBreaker object.
+ */
+public class TextMetricsCalculator {
+ TextRunBreaker breaker; // Associated run breaker
+
+ // Metrics
+ float ascent = 0;
+ float descent = 0;
+ float leading = 0;
+ float advance = 0;
+
+ private float baselineOffsets[];
+ int baselineIndex;
+
+ public TextMetricsCalculator(TextRunBreaker breaker) {
+ this.breaker = breaker;
+ checkBaselines();
+ }
+
+ /**
+ * Returns either values cached by checkBaselines method or reasonable
+ * values for the TOP and BOTTOM alignments.
+ * @param baselineIndex - baseline index
+ * @return baseline offset
+ */
+ float getBaselineOffset(int baselineIndex) {
+ if (baselineIndex >= 0) {
+ return baselineOffsets[baselineIndex];
+ } else if (baselineIndex == GraphicAttribute.BOTTOM_ALIGNMENT) {
+ return descent;
+ } else if (baselineIndex == GraphicAttribute.TOP_ALIGNMENT) {
+ return -ascent;
+ } else {
+ // awt.3F=Invalid baseline index
+ throw new IllegalArgumentException(Messages.getString("awt.3F")); //$NON-NLS-1$
+ }
+ }
+
+ public float[] getBaselineOffsets() {
+ float ret[] = new float[baselineOffsets.length];
+ System.arraycopy(baselineOffsets, 0, ret, 0, baselineOffsets.length);
+ return ret;
+ }
+
+ /**
+ * Take baseline offsets from the first font or graphic attribute
+ * and normalizes them, than caches the results.
+ */
+ public void checkBaselines() {
+ // Take baseline offsets of the first font and normalize them
+ HashMap<Integer, Font> fonts = breaker.fonts;
+
+ Object val = fonts.get(new Integer(0));
+
+ if (val instanceof Font) {
+ Font firstFont = (Font) val;
+ LineMetrics lm = firstFont.getLineMetrics(breaker.text, 0, 1, breaker.frc);
+ baselineOffsets = lm.getBaselineOffsets();
+ baselineIndex = lm.getBaselineIndex();
+ } else if (val instanceof GraphicAttribute) {
+ // Get first graphic attribute and use it
+ GraphicAttribute ga = (GraphicAttribute) val;
+
+ int align = ga.getAlignment();
+
+ if (
+ align == GraphicAttribute.TOP_ALIGNMENT ||
+ align == GraphicAttribute.BOTTOM_ALIGNMENT
+ ) {
+ baselineIndex = GraphicAttribute.ROMAN_BASELINE;
+ } else {
+ baselineIndex = align;
+ }
+
+ baselineOffsets = new float[3];
+ baselineOffsets[0] = 0;
+ baselineOffsets[1] = (ga.getDescent() - ga.getAscent()) / 2.f;
+ baselineOffsets[2] = -ga.getAscent();
+ } else { // Use defaults - Roman baseline and zero offsets
+ baselineIndex = GraphicAttribute.ROMAN_BASELINE;
+ baselineOffsets = new float[3];
+ }
+
+ // Normalize offsets if needed
+ if (baselineOffsets[baselineIndex] != 0) {
+ float baseOffset = baselineOffsets[baselineIndex];
+ for (int i = 0; i < baselineOffsets.length; i++) {
+ baselineOffsets[i] -= baseOffset;
+ }
+ }
+ }
+
+ /**
+ * Computes metrics for the text managed by the associated TextRunBreaker
+ */
+ void computeMetrics() {
+
+ ArrayList<TextRunSegment> segments = breaker.runSegments;
+
+ float maxHeight = 0;
+ float maxHeightLeading = 0;
+
+ for (int i = 0; i < segments.size(); i++) {
+ TextRunSegment segment = segments.get(i);
+ BasicMetrics metrics = segment.metrics;
+ int baseline = metrics.baseLineIndex;
+
+ if (baseline >= 0) {
+ float baselineOffset = baselineOffsets[metrics.baseLineIndex];
+ float fixedDescent = metrics.descent + baselineOffset;
+
+ ascent = Math.max(ascent, metrics.ascent - baselineOffset);
+ descent = Math.max(descent, fixedDescent);
+ leading = Math.max(leading, fixedDescent + metrics.leading);
+ } else { // Position is not fixed by the baseline, need sum of ascent and descent
+ float height = metrics.ascent + metrics.descent;
+
+ maxHeight = Math.max(maxHeight, height);
+ maxHeightLeading = Math.max(maxHeightLeading, height + metrics.leading);
+ }
+ }
+
+ // Need to increase sizes for graphics?
+ if (maxHeightLeading != 0) {
+ descent = Math.max(descent, maxHeight - ascent);
+ leading = Math.max(leading, maxHeightLeading - ascent);
+ }
+
+ // Normalize leading
+ leading -= descent;
+
+ BasicMetrics currMetrics;
+ float currAdvance = 0;
+
+ for (int i = 0; i < segments.size(); i++) {
+ TextRunSegment segment = segments.get(breaker.getSegmentFromVisualOrder(i));
+ currMetrics = segment.metrics;
+
+ segment.y = getBaselineOffset(currMetrics.baseLineIndex)
+ + currMetrics.superScriptOffset;
+ segment.x = currAdvance;
+
+ currAdvance += segment.getAdvance();
+ }
+
+ advance = currAdvance;
+ }
+
+ /**
+ * Computes metrics and creates BasicMetrics object from them
+ * @return basic metrics
+ */
+ public BasicMetrics createMetrics() {
+ computeMetrics();
+ return new BasicMetrics(this);
+ }
+
+ /**
+ * Corrects advance after justification. Gets BasicMetrics object
+ * and updates advance stored into it.
+ * @param metrics - metrics with outdated advance which should be corrected
+ */
+ public void correctAdvance(BasicMetrics metrics) {
+ ArrayList<TextRunSegment> segments = breaker.runSegments;
+ TextRunSegment segment = segments.get(breaker
+ .getSegmentFromVisualOrder(segments.size() - 1));
+
+ advance = segment.x + segment.getAdvance();
+ metrics.advance = advance;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java b/awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java
new file mode 100644
index 0000000..be606f7
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java
@@ -0,0 +1,861 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
+import java.awt.im.InputMethodHighlight;
+import java.awt.font.*;
+import java.awt.*;
+import java.text.AttributedCharacterIterator;
+import java.text.Annotation;
+import java.text.AttributedCharacterIterator.Attribute;
+import java.util.*;
+
+import org.apache.harmony.awt.gl.font.TextDecorator.Decoration;
+import org.apache.harmony.awt.internal.nls.Messages;
+import org.apache.harmony.misc.HashCode;
+// TODO - bidi not implemented yet
+
+/**
+ * This class is responsible for breaking the text into the run segments
+ * with constant font, style, other text attributes and direction.
+ * It also stores the created text run segments and covers functionality
+ * related to the operations on the set of segments, like calculating metrics,
+ * rendering, justification, hit testing, etc.
+ */
+public class TextRunBreaker implements Cloneable {
+ AttributedCharacterIterator aci;
+ FontRenderContext frc;
+
+ char[] text;
+
+ byte[] levels;
+
+ HashMap<Integer, Font> fonts;
+ HashMap<Integer, Decoration> decorations;
+
+ // Related to default font substitution
+ int forcedFontRunStarts[];
+
+ ArrayList<TextRunSegment> runSegments = new ArrayList<TextRunSegment>();
+
+ // For fast retrieving of the segment containing
+ // character with known logical index
+ int logical2segment[];
+ int segment2visual[]; // Visual order of segments TODO - implement
+ int visual2segment[];
+ int logical2visual[];
+ int visual2logical[];
+
+ SegmentsInfo storedSegments;
+ private boolean haveAllSegments = false;
+ int segmentsStart, segmentsEnd;
+
+ float justification = 1.0f;
+
+ public TextRunBreaker(AttributedCharacterIterator aci, FontRenderContext frc) {
+ this.aci = aci;
+ this.frc = frc;
+
+ segmentsStart = aci.getBeginIndex();
+ segmentsEnd = aci.getEndIndex();
+
+ int len = segmentsEnd - segmentsStart;
+ text = new char[len];
+ aci.setIndex(segmentsEnd);
+ while (len-- != 0) { // Going in backward direction is faster? Simplier checks here?
+ text[len] = aci.previous();
+ }
+
+ createStyleRuns();
+ }
+
+ /**
+ * Visual order of text segments may differ from the logical order.
+ * This method calculates visual position of the segment from its logical position.
+ * @param segmentNum - logical position of the segment
+ * @return visual position of the segment
+ */
+ int getVisualFromSegmentOrder(int segmentNum) {
+ return (segment2visual == null) ? segmentNum : segment2visual[segmentNum];
+ }
+
+ /**
+ * Visual order of text segments may differ from the logical order.
+ * This method calculates logical position of the segment from its visual position.
+ * @param visual - visual position of the segment
+ * @return logical position of the segment
+ */
+ int getSegmentFromVisualOrder(int visual) {
+ return (visual2segment == null) ? visual : visual2segment[visual];
+ }
+
+ /**
+ * Visual order of the characters may differ from the logical order.
+ * This method calculates visual position of the character from its logical position.
+ * @param logical - logical position of the character
+ * @return visual position
+ */
+ int getVisualFromLogical(int logical) {
+ return (logical2visual == null) ? logical : logical2visual[logical];
+ }
+
+ /**
+ * Visual order of the characters may differ from the logical order.
+ * This method calculates logical position of the character from its visual position.
+ * @param visual - visual position
+ * @return logical position
+ */
+ int getLogicalFromVisual(int visual) {
+ return (visual2logical == null) ? visual : visual2logical[visual];
+ }
+
+ /**
+ * Calculates the end index of the level run, limited by the given text run.
+ * @param runStart - run start
+ * @param runEnd - run end
+ * @return end index of the level run
+ */
+ int getLevelRunLimit(int runStart, int runEnd) {
+ if (levels == null) {
+ return runEnd;
+ }
+ int endLevelRun = runStart + 1;
+ byte level = levels[runStart];
+
+ while (endLevelRun <= runEnd && levels[endLevelRun] == level) {
+ endLevelRun++;
+ }
+
+ return endLevelRun;
+ }
+
+ /**
+ * Adds InputMethodHighlight to the attributes
+ * @param attrs - text attributes
+ * @return patched text attributes
+ */
+ Map<? extends Attribute, ?> unpackAttributes(Map<? extends Attribute, ?> attrs) {
+ if (attrs.containsKey(TextAttribute.INPUT_METHOD_HIGHLIGHT)) {
+ Map<TextAttribute, ?> styles = null;
+
+ Object val = attrs.get(TextAttribute.INPUT_METHOD_HIGHLIGHT);
+
+ if (val instanceof Annotation) {
+ val = ((Annotation) val).getValue();
+ }
+
+ if (val instanceof InputMethodHighlight) {
+ InputMethodHighlight ihl = ((InputMethodHighlight) val);
+ styles = ihl.getStyle();
+
+ if (styles == null) {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ styles = tk.mapInputMethodHighlight(ihl);
+ }
+ }
+
+ if (styles != null) {
+ HashMap<Attribute, Object> newAttrs = new HashMap<Attribute, Object>();
+ newAttrs.putAll(attrs);
+ newAttrs.putAll(styles);
+ return newAttrs;
+ }
+ }
+
+ return attrs;
+ }
+
+ /**
+ * Breaks the text into separate style runs.
+ */
+ void createStyleRuns() {
+ // TODO - implement fast and simple case
+ fonts = new HashMap<Integer, Font>();
+ decorations = new HashMap<Integer, Decoration>();
+ ////
+
+ ArrayList<Integer> forcedFontRunStartsList = null;
+
+ Map<? extends Attribute, ?> attributes = null;
+
+ // Check justification attribute
+ Object val = aci.getAttribute(TextAttribute.JUSTIFICATION);
+ if (val != null) {
+ justification = ((Float) val).floatValue();
+ }
+
+ for (
+ int index = segmentsStart, nextRunStart = segmentsStart;
+ index < segmentsEnd;
+ index = nextRunStart, aci.setIndex(index)
+ ) {
+ nextRunStart = aci.getRunLimit();
+ attributes = unpackAttributes(aci.getAttributes());
+
+ TextDecorator.Decoration d = TextDecorator.getDecoration(attributes);
+ decorations.put(new Integer(index), d);
+
+ // Find appropriate font or place GraphicAttribute there
+
+ // 1. Try to pick up CHAR_REPLACEMENT (compatibility)
+ Font value = (Font)attributes.get(TextAttribute.CHAR_REPLACEMENT);
+
+ if (value == null) {
+ // 2. Try to Get FONT
+ value = (Font)attributes.get(TextAttribute.FONT);
+
+ if (value == null) {
+ // 3. Try to create font from FAMILY
+ if (attributes.get(TextAttribute.FAMILY) != null) {
+ value = Font.getFont(attributes);
+ }
+
+ if (value == null) {
+ // 4. No attributes found, using default.
+ if (forcedFontRunStartsList == null) {
+ forcedFontRunStartsList = new ArrayList<Integer>();
+ }
+ FontFinder.findFonts(
+ text,
+ index,
+ nextRunStart,
+ forcedFontRunStartsList,
+ fonts
+ );
+ value = fonts.get(new Integer(index));
+ }
+ }
+ }
+
+ fonts.put(new Integer(index), value);
+ }
+
+ // We have added some default fonts, so we have some extra runs in text
+ if (forcedFontRunStartsList != null) {
+ forcedFontRunStarts = new int[forcedFontRunStartsList.size()];
+ for (int i=0; i<forcedFontRunStartsList.size(); i++) {
+ forcedFontRunStarts[i] =
+ forcedFontRunStartsList.get(i).intValue();
+ }
+ }
+ }
+
+ /**
+ * Starting from the current position looks for the end of the text run with
+ * constant text attributes.
+ * @param runStart - start position
+ * @param maxPos - position where to stop if no run limit found
+ * @return style run limit
+ */
+ int getStyleRunLimit(int runStart, int maxPos) {
+ try {
+ aci.setIndex(runStart);
+ } catch(IllegalArgumentException e) { // Index out of bounds
+ if (runStart < segmentsStart) {
+ aci.first();
+ } else {
+ aci.last();
+ }
+ }
+
+ // If we have some extra runs we need to check for their limits
+ if (forcedFontRunStarts != null) {
+ for (int element : forcedFontRunStarts) {
+ if (element > runStart) {
+ maxPos = Math.min(element, maxPos);
+ break;
+ }
+ }
+ }
+
+ return Math.min(aci.getRunLimit(), maxPos);
+ }
+
+ /**
+ * Creates segments for the text run with
+ * constant decoration, font and bidi level
+ * @param runStart - run start
+ * @param runEnd - run end
+ */
+ public void createSegments(int runStart, int runEnd) {
+ int endStyleRun, endLevelRun;
+
+ // TODO - update levels
+
+ int pos = runStart, levelPos;
+
+ aci.setIndex(pos);
+ final int firstRunStart = aci.getRunStart();
+ Object tdd = decorations.get(new Integer(firstRunStart));
+ Object fontOrGAttr = fonts.get(new Integer(firstRunStart));
+
+ logical2segment = new int[runEnd - runStart];
+
+ do {
+ endStyleRun = getStyleRunLimit(pos, runEnd);
+
+ // runStart can be non-zero, but all arrays will be indexed from 0
+ int ajustedPos = pos - runStart;
+ int ajustedEndStyleRun = endStyleRun - runStart;
+ levelPos = ajustedPos;
+ do {
+ endLevelRun = getLevelRunLimit(levelPos, ajustedEndStyleRun);
+
+ if (fontOrGAttr instanceof GraphicAttribute) {
+ runSegments.add(
+ new TextRunSegmentImpl.TextRunSegmentGraphic(
+ (GraphicAttribute)fontOrGAttr,
+ endLevelRun - levelPos,
+ levelPos + runStart)
+ );
+ Arrays.fill(logical2segment, levelPos, endLevelRun, runSegments.size()-1);
+ } else {
+ TextRunSegmentImpl.TextSegmentInfo i =
+ new TextRunSegmentImpl.TextSegmentInfo(
+ levels == null ? 0 : levels[ajustedPos],
+ (Font) fontOrGAttr,
+ frc,
+ text,
+ levelPos + runStart,
+ endLevelRun + runStart
+ );
+
+ runSegments.add(
+ new TextRunSegmentImpl.TextRunSegmentCommon(
+ i,
+ (TextDecorator.Decoration) tdd
+ )
+ );
+ Arrays.fill(logical2segment, levelPos, endLevelRun, runSegments.size()-1);
+ }
+
+ levelPos = endLevelRun;
+ } while (levelPos < ajustedEndStyleRun);
+
+ // Prepare next iteration
+ pos = endStyleRun;
+ tdd = decorations.get(new Integer(pos));
+ fontOrGAttr = fonts.get(new Integer(pos));
+ } while (pos < runEnd);
+ }
+
+ /**
+ * Checks if text run segments are up to date and creates the new segments if not.
+ */
+ public void createAllSegments() {
+ if ( !haveAllSegments &&
+ (logical2segment == null ||
+ logical2segment.length != segmentsEnd - segmentsStart)
+ ) { // Check if we don't have all segments yet
+ resetSegments();
+ createSegments(segmentsStart, segmentsEnd);
+ }
+
+ haveAllSegments = true;
+ }
+
+ /**
+ * Calculates position where line should be broken without
+ * taking into account word boundaries.
+ * @param start - start index
+ * @param maxAdvance - maximum advance, width of the line
+ * @return position where to break
+ */
+ public int getLineBreakIndex(int start, float maxAdvance) {
+ int breakIndex;
+ TextRunSegment s = null;
+
+ for (
+ int segmentIndex = logical2segment[start];
+ segmentIndex < runSegments.size();
+ segmentIndex++
+ ) {
+ s = runSegments.get(segmentIndex);
+ breakIndex = s.getCharIndexFromAdvance(maxAdvance, start);
+
+ if (breakIndex < s.getEnd()) {
+ return breakIndex;
+ }
+ maxAdvance -= s.getAdvanceDelta(start, s.getEnd());
+ start = s.getEnd();
+ }
+
+ return s.getEnd();
+ }
+
+ /**
+ * Inserts character into the managed text.
+ * @param newParagraph - new character iterator
+ * @param insertPos - insertion position
+ */
+ public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) {
+ aci = newParagraph;
+
+ char insChar = aci.setIndex(insertPos);
+
+ Integer key = new Integer(insertPos);
+
+ insertPos -= aci.getBeginIndex();
+
+ char newText[] = new char[text.length + 1];
+ System.arraycopy(text, 0, newText, 0, insertPos);
+ newText[insertPos] = insChar;
+ System.arraycopy(text, insertPos, newText, insertPos+1, text.length - insertPos);
+ text = newText;
+
+ if (aci.getRunStart() == key.intValue() && aci.getRunLimit() == key.intValue() + 1) {
+ createStyleRuns(); // We have to create one new run, could be optimized
+ } else {
+ shiftStyleRuns(key, 1);
+ }
+
+ resetSegments();
+
+ segmentsEnd++;
+ }
+
+ /**
+ * Deletes character from the managed text.
+ * @param newParagraph - new character iterator
+ * @param deletePos - deletion position
+ */
+ public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) {
+ aci = newParagraph;
+
+ Integer key = new Integer(deletePos);
+
+ deletePos -= aci.getBeginIndex();
+
+ char newText[] = new char[text.length - 1];
+ System.arraycopy(text, 0, newText, 0, deletePos);
+ System.arraycopy(text, deletePos+1, newText, deletePos, newText.length - deletePos);
+ text = newText;
+
+ if (fonts.get(key) != null) {
+ fonts.remove(key);
+ }
+
+ shiftStyleRuns(key, -1);
+
+ resetSegments();
+
+ segmentsEnd--;
+ }
+
+ /**
+ * Shift all runs after specified position, needed to perfom insertion
+ * or deletion in the managed text
+ * @param pos - position where to start
+ * @param shift - shift, could be negative
+ */
+ private void shiftStyleRuns(Integer pos, final int shift) {
+ ArrayList<Integer> keys = new ArrayList<Integer>();
+
+ Integer key, oldkey;
+ for (Iterator<Integer> it = fonts.keySet().iterator(); it.hasNext(); ) {
+ oldkey = it.next();
+ if (oldkey.intValue() > pos.intValue()) {
+ keys.add(oldkey);
+ }
+ }
+
+ for (int i=0; i<keys.size(); i++) {
+ oldkey = keys.get(i);
+ key = new Integer(shift + oldkey.intValue());
+ fonts.put(key, fonts.remove(oldkey));
+ decorations.put(key, decorations.remove(oldkey));
+ }
+ }
+
+ /**
+ * Resets state of the class
+ */
+ private void resetSegments() {
+ runSegments = new ArrayList<TextRunSegment>();
+ logical2segment = null;
+ segment2visual = null;
+ visual2segment = null;
+ levels = null;
+ haveAllSegments = false;
+ }
+
+ private class SegmentsInfo {
+ ArrayList<TextRunSegment> runSegments;
+ int logical2segment[];
+ int segment2visual[];
+ int visual2segment[];
+ byte levels[];
+ int segmentsStart;
+ int segmentsEnd;
+ }
+
+ /**
+ * Saves the internal state of the class
+ * @param newSegStart - new start index in the text
+ * @param newSegEnd - new end index in the text
+ */
+ public void pushSegments(int newSegStart, int newSegEnd) {
+ storedSegments = new SegmentsInfo();
+ storedSegments.runSegments = this.runSegments;
+ storedSegments.logical2segment = this.logical2segment;
+ storedSegments.segment2visual = this.segment2visual;
+ storedSegments.visual2segment = this.visual2segment;
+ storedSegments.levels = this.levels;
+ storedSegments.segmentsStart = segmentsStart;
+ storedSegments.segmentsEnd = segmentsEnd;
+
+ resetSegments();
+
+ segmentsStart = newSegStart;
+ segmentsEnd = newSegEnd;
+ }
+
+ /**
+ * Restores the internal state of the class
+ */
+ public void popSegments() {
+ if (storedSegments == null) {
+ return;
+ }
+
+ this.runSegments = storedSegments.runSegments;
+ this.logical2segment = storedSegments.logical2segment;
+ this.segment2visual = storedSegments.segment2visual;
+ this.visual2segment = storedSegments.visual2segment;
+ this.levels = storedSegments.levels;
+ this.segmentsStart = storedSegments.segmentsStart;
+ this.segmentsEnd = storedSegments.segmentsEnd;
+ storedSegments = null;
+
+ if (runSegments.size() == 0 && logical2segment == null) {
+ haveAllSegments = false;
+ } else {
+ haveAllSegments = true;
+ }
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ TextRunBreaker res = (TextRunBreaker) super.clone();
+ res.storedSegments = null;
+ ArrayList<TextRunSegment> newSegments = new ArrayList<TextRunSegment>(runSegments.size());
+ for (int i = 0; i < runSegments.size(); i++) {
+ TextRunSegment seg = runSegments.get(i);
+ newSegments.add((TextRunSegment)seg.clone());
+ }
+ res.runSegments = newSegments;
+ return res;
+ } catch (CloneNotSupportedException e) {
+ // awt.3E=Clone not supported
+ throw new UnsupportedOperationException(Messages.getString("awt.3E")); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TextRunBreaker)) {
+ return false;
+ }
+
+ TextRunBreaker br = (TextRunBreaker) obj;
+
+ if (br.getACI().equals(aci) && br.frc.equals(frc)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.combine(aci.hashCode(), frc.hashCode());
+ }
+
+ /**
+ * Renders the managed text
+ * @param g2d - graphics where to render
+ * @param xOffset - offset in X direction to the upper left corner
+ * of the layout from the origin of the graphics
+ * @param yOffset - offset in Y direction to the upper left corner
+ * of the layout from the origin of the graphics
+ */
+ public void drawSegments(Graphics2D g2d, float xOffset, float yOffset) {
+ for (int i=0; i<runSegments.size(); i++) {
+ runSegments.get(i).draw(g2d, xOffset, yOffset);
+ }
+ }
+
+ /**
+ * Creates the black box bounds shape
+ * @param firstEndpoint - start position
+ * @param secondEndpoint - end position
+ * @return black box bounds shape
+ */
+ public Shape getBlackBoxBounds(int firstEndpoint, int secondEndpoint) {
+ GeneralPath bounds = new GeneralPath();
+
+ TextRunSegment segment;
+
+ for (int idx = firstEndpoint; idx < secondEndpoint; idx=segment.getEnd()) {
+ segment = runSegments.get(logical2segment[idx]);
+ bounds.append(segment.getCharsBlackBoxBounds(idx, secondEndpoint), false);
+ }
+
+ return bounds;
+ }
+
+ /**
+ * Creates visual bounds shape
+ * @return visual bounds rectangle
+ */
+ public Rectangle2D getVisualBounds() {
+ Rectangle2D bounds = null;
+
+ for (int i=0; i<runSegments.size(); i++) {
+ TextRunSegment s = runSegments.get(i);
+ if (bounds != null) {
+ Rectangle2D.union(bounds, s.getVisualBounds(), bounds);
+ } else {
+ bounds = s.getVisualBounds();
+ }
+ }
+
+ return bounds;
+ }
+
+ /**
+ * Creates logical bounds shape
+ * @return logical bounds rectangle
+ */
+ public Rectangle2D getLogicalBounds() {
+ Rectangle2D bounds = null;
+
+ for (int i=0; i<runSegments.size(); i++) {
+ TextRunSegment s = runSegments.get(i);
+ if (bounds != null) {
+ Rectangle2D.union(bounds, s.getLogicalBounds(), bounds);
+ } else {
+ bounds = s.getLogicalBounds();
+ }
+ }
+
+ return bounds;
+ }
+
+ public int getCharCount() {
+ return segmentsEnd - segmentsStart;
+ }
+
+ public byte getLevel(int idx) {
+ if (levels == null) {
+ return 0;
+ }
+ return levels[idx];
+ }
+
+ public int getBaseLevel() {
+ return 0;
+ }
+
+ public boolean isLTR() {
+ return true;
+ }
+
+ public char getChar(int index) {
+ return text[index];
+ }
+
+ public AttributedCharacterIterator getACI() {
+ return aci;
+ }
+
+ /**
+ * Creates outline shape for the managed text
+ * @return outline
+ */
+ public GeneralPath getOutline() {
+ GeneralPath outline = new GeneralPath();
+
+ TextRunSegment segment;
+
+ for (int i = 0; i < runSegments.size(); i++) {
+ segment = runSegments.get(i);
+ outline.append(segment.getOutline(), false);
+ }
+
+ return outline;
+ }
+
+ /**
+ * Calculates text hit info from the screen coordinates.
+ * Current implementation totally ignores Y coordinate.
+ * If X coordinate is outside of the layout boundaries, this
+ * method returns leftmost or rightmost hit.
+ * @param x - x coordinate of the hit
+ * @param y - y coordinate of the hit
+ * @return hit info
+ */
+ public TextHitInfo hitTest(float x, float y) {
+ TextRunSegment segment;
+
+ double endOfPrevSeg = -1;
+ for (int i = 0; i < runSegments.size(); i++) {
+ segment = runSegments.get(i);
+ Rectangle2D bounds = segment.getVisualBounds();
+ if ((bounds.getMinX() <= x && bounds.getMaxX() >= x) || // We are in the segment
+ (endOfPrevSeg < x && bounds.getMinX() > x)) { // We are somewhere between the segments
+ return segment.hitTest(x,y);
+ }
+ endOfPrevSeg = bounds.getMaxX();
+ }
+
+ return isLTR() ? TextHitInfo.trailing(text.length) : TextHitInfo.leading(0);
+ }
+
+ public float getJustification() {
+ return justification;
+ }
+
+ /**
+ * Calculates position of the last non whitespace character
+ * in the managed text.
+ * @return position of the last non whitespace character
+ */
+ public int getLastNonWhitespace() {
+ int lastNonWhitespace = text.length;
+
+ while (lastNonWhitespace >= 0) {
+ lastNonWhitespace--;
+ if (!Character.isWhitespace(text[lastNonWhitespace])) {
+ break;
+ }
+ }
+
+ return lastNonWhitespace;
+ }
+
+ /**
+ * Performs justification of the managed text by changing segment positions
+ * and positions of the glyphs inside of the segments.
+ * @param gap - amount of space which should be compensated by justification
+ */
+ public void justify(float gap) {
+ // Ignore trailing logical whitespace
+ int firstIdx = segmentsStart;
+ int lastIdx = getLastNonWhitespace() + segmentsStart;
+ JustificationInfo jInfos[] = new JustificationInfo[5];
+ float gapLeft = gap;
+
+ int highestPriority = -1;
+ // GlyphJustificationInfo.PRIORITY_KASHIDA is 0
+ // GlyphJustificationInfo.PRIORITY_NONE is 3
+ for (int priority = 0; priority <= GlyphJustificationInfo.PRIORITY_NONE + 1; priority++) {
+ JustificationInfo jInfo = new JustificationInfo();
+ jInfo.lastIdx = lastIdx;
+ jInfo.firstIdx = firstIdx;
+ jInfo.grow = gap > 0;
+ jInfo.gapToFill = gapLeft;
+
+ if (priority <= GlyphJustificationInfo.PRIORITY_NONE) {
+ jInfo.priority = priority;
+ } else {
+ jInfo.priority = highestPriority; // Last pass
+ }
+
+ for (int i = 0; i < runSegments.size(); i++) {
+ TextRunSegment segment = runSegments.get(i);
+ if (segment.getStart() <= lastIdx) {
+ segment.updateJustificationInfo(jInfo);
+ }
+ }
+
+ if (jInfo.priority == highestPriority) {
+ jInfo.absorb = true;
+ jInfo.absorbedWeight = jInfo.weight;
+ }
+
+ if (jInfo.weight != 0) {
+ if (highestPriority < 0) {
+ highestPriority = priority;
+ }
+ jInfos[priority] = jInfo;
+ } else {
+ continue;
+ }
+
+ gapLeft -= jInfo.growLimit;
+
+ if (((gapLeft > 0) ^ jInfo.grow) || gapLeft == 0) {
+ gapLeft = 0;
+ jInfo.gapPerUnit = jInfo.gapToFill/jInfo.weight;
+ break;
+ }
+ jInfo.useLimits = true;
+
+ if (jInfo.absorbedWeight > 0) {
+ jInfo.absorb = true;
+ jInfo.absorbedGapPerUnit =
+ (jInfo.gapToFill-jInfo.growLimit)/jInfo.absorbedWeight;
+ break;
+ }
+ }
+
+ float currJustificationOffset = 0;
+ for (int i = 0; i < runSegments.size(); i++) {
+ TextRunSegment segment =
+ runSegments.get(getSegmentFromVisualOrder(i));
+ segment.x += currJustificationOffset;
+ currJustificationOffset += segment.doJustification(jInfos);
+ }
+
+ justification = -1; // Make further justification impossible
+ }
+
+ /**
+ * This class represents the information collected before the actual
+ * justification is started and needed to perform the justification.
+ * This information is closely related to the information stored in the
+ * GlyphJustificationInfo for the text represented by glyph vectors.
+ */
+ class JustificationInfo {
+ boolean grow;
+ boolean absorb = false;
+ boolean useLimits = false;
+ int priority = 0;
+ float weight = 0;
+ float absorbedWeight = 0;
+ float growLimit = 0;
+
+ int lastIdx;
+ int firstIdx;
+
+ float gapToFill;
+
+ float gapPerUnit = 0; // Precalculated value, gapToFill / weight
+ float absorbedGapPerUnit = 0; // Precalculated value, gapToFill / weight
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/TextRunSegment.java b/awt/org/apache/harmony/awt/gl/font/TextRunSegment.java
new file mode 100644
index 0000000..1cd2c05
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/TextRunSegment.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.font.TextHitInfo;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Abstract class which represents the segment of the text with constant attributes
+ * running in one direction (i.e. constant level).
+ */
+public abstract class TextRunSegment implements Cloneable {
+ float x; // Calculated x location of this segment on the screen
+ float y; // Calculated y location of this segment on the screen
+
+ BasicMetrics metrics; // Metrics of this text run segment
+ TextDecorator.Decoration decoration; // Underline, srikethrough, etc.
+ Rectangle2D logicalBounds = null; // Logical bounding box for the segment
+ Rectangle2D visualBounds = null; // Visual bounding box for the segment
+
+ /**
+ * Returns start index of the segment
+ * @return start index
+ */
+ abstract int getStart();
+
+ /**
+ * Returns end index of the segment
+ * @return end index
+ */
+ abstract int getEnd();
+
+ /**
+ * Returns the number of characters in the segment
+ * @return number of characters
+ */
+ abstract int getLength();
+
+ /**
+ * Renders this text run segment
+ * @param g2d - graphics to render to
+ * @param xOffset - X offset from the graphics origin to the
+ * origin of the text layout
+ * @param yOffset - Y offset from the graphics origin to the
+ * origin of the text layout
+ */
+ abstract void draw(Graphics2D g2d, float xOffset, float yOffset);
+
+ /**
+ * Creates black box bounds shape for the specified range
+ * @param start - range sart
+ * @param limit - range end
+ * @return black box bounds shape
+ */
+ abstract Shape getCharsBlackBoxBounds(int start, int limit);
+
+ /**
+ * Returns the outline shape
+ * @return outline
+ */
+ abstract Shape getOutline();
+
+ /**
+ * Returns visual bounds of this segment
+ * @return visual bounds
+ */
+ abstract Rectangle2D getVisualBounds();
+
+ /**
+ * Returns logical bounds of this segment
+ * @return logical bounds
+ */
+ abstract Rectangle2D getLogicalBounds();
+
+ /**
+ * Calculates advance of the segment
+ * @return advance
+ */
+ abstract float getAdvance();
+
+ /**
+ * Calculates advance delta between two characters
+ * @param start - 1st position
+ * @param end - 2nd position
+ * @return advance increment between specified positions
+ */
+ abstract float getAdvanceDelta(int start, int end);
+
+ /**
+ * Calculates index of the character which advance is equal to
+ * the given. If the given advance is greater then the segment
+ * advance it returns the position after the last character.
+ * @param advance - given advance
+ * @param start - character, from which to start measuring advance
+ * @return character index
+ */
+ abstract int getCharIndexFromAdvance(float advance, int start);
+
+ /**
+ * Checks if the character doesn't contribute to the text advance
+ * @param index - character index
+ * @return true if the character has zero advance
+ */
+ abstract boolean charHasZeroAdvance(int index);
+
+ /**
+ * Calculates position of the character on the screen
+ * @param index - character index
+ * @return X coordinate of the character position
+ */
+ abstract float getCharPosition(int index);
+
+ /**
+ * Returns the advance of the individual character
+ * @param index - character index
+ * @return character advance
+ */
+ abstract float getCharAdvance(int index);
+
+ /**
+ * Creates text hit info from the hit position
+ * @param x - X coordinate relative to the origin of the layout
+ * @param y - Y coordinate relative to the origin of the layout
+ * @return hit info
+ */
+ abstract TextHitInfo hitTest(float x, float y);
+
+ /**
+ * Collects justification information into JustificationInfo object
+ * @param jInfo - JustificationInfo object
+ */
+ abstract void updateJustificationInfo(TextRunBreaker.JustificationInfo jInfo);
+
+ /**
+ * Performs justification of the segment.
+ * Updates positions of individual characters.
+ * @param jInfos - justification information, gathered by the previous passes
+ * @return amount of growth or shrink of the segment
+ */
+ abstract float doJustification(TextRunBreaker.JustificationInfo jInfos[]);
+
+ @Override
+ public abstract Object clone();
+}
diff --git a/awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java b/awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java
new file mode 100644
index 0000000..0ec2d05
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java
@@ -0,0 +1,979 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ */
+
+package org.apache.harmony.awt.gl.font;
+
+import java.awt.*;
+import java.awt.font.*;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+// XXX - TODO - bidi not implemented yet
+//import java.text.Bidi;
+import java.util.Arrays;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * Date: Apr 25, 2005
+ * Time: 4:33:18 PM
+ *
+ * This class contains the implementation of the behavior of the
+ * text run segment with constant text attributes and direction.
+ */
+public class TextRunSegmentImpl {
+
+ /**
+ * This class contains basic information required for creation
+ * of the glyph-based text run segment.
+ */
+ public static class TextSegmentInfo {
+ // XXX - TODO - bidi not implemented yet
+ //Bidi bidi;
+
+ Font font;
+ FontRenderContext frc;
+
+ char text[];
+
+ int start;
+ int end;
+ int length;
+
+ int flags = 0;
+
+ byte level = 0;
+
+ TextSegmentInfo(
+ byte level,
+ Font font, FontRenderContext frc,
+ char text[], int start, int end
+ ) {
+ this.font = font;
+ this.frc = frc;
+ this.text = text;
+ this.start = start;
+ this.end = end;
+ this.level = level;
+ length = end - start;
+ }
+ }
+
+ /**
+ * This class represents a simple text segment backed by the glyph vector
+ */
+ public static class TextRunSegmentCommon extends TextRunSegment {
+ TextSegmentInfo info;
+ private GlyphVector gv;
+ private float advanceIncrements[];
+ private int char2glyph[];
+ private GlyphJustificationInfo gjis[]; // Glyph justification info
+
+ TextRunSegmentCommon(TextSegmentInfo i, TextDecorator.Decoration d) {
+ // XXX - todo - check support bidi
+ i.flags &= ~0x09; // Clear bidi flags
+
+ if ((i.level & 0x1) != 0) {
+ i.flags |= Font.LAYOUT_RIGHT_TO_LEFT;
+ }
+
+ info = i;
+ this.decoration = d;
+
+ LineMetrics lm = i.font.getLineMetrics(i.text, i.start, i.end, i.frc);
+ this.metrics = new BasicMetrics(lm, i.font);
+
+ if (lm.getNumChars() != i.length) { // XXX todo - This should be handled
+ // awt.41=Font returned unsupported type of line metrics. This case is known, but not supported yet.
+ throw new UnsupportedOperationException(
+ Messages.getString("awt.41")); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public Object clone() {
+ return new TextRunSegmentCommon(info, decoration);
+ }
+
+ /**
+ * Creates glyph vector from the managed text if needed
+ * @return glyph vector
+ */
+ private GlyphVector getGlyphVector() {
+ if (gv==null) {
+ gv = info.font.layoutGlyphVector(
+ info.frc,
+ info.text,
+ info.start,
+ info.end - info.start, // NOTE: This parameter violates
+ // spec, it is count,
+ // not limit as spec states
+ info.flags
+ );
+ }
+
+ return gv;
+ }
+
+ /**
+ * Renders this text run segment
+ * @param g2d - graphics to render to
+ * @param xOffset - X offset from the graphics origin to the
+ * origin of the text layout
+ * @param yOffset - Y offset from the graphics origin to the
+ * origin of the text layout
+ */
+ @Override
+ void draw(Graphics2D g2d, float xOffset, float yOffset) {
+ if (decoration == null) {
+ g2d.drawGlyphVector(getGlyphVector(), xOffset + x, yOffset + y);
+ } else {
+ TextDecorator.prepareGraphics(this, g2d, xOffset, yOffset);
+ g2d.drawGlyphVector(getGlyphVector(), xOffset + x, yOffset + y);
+ TextDecorator.drawTextDecorations(this, g2d, xOffset, yOffset);
+ TextDecorator.restoreGraphics(decoration, g2d);
+ }
+ }
+
+ /**
+ * Returns visual bounds of this segment
+ * @return visual bounds
+ */
+ @Override
+ Rectangle2D getVisualBounds() {
+ if (visualBounds == null) {
+ visualBounds =
+ TextDecorator.extendVisualBounds(
+ this,
+ getGlyphVector().getVisualBounds(),
+ decoration
+ );
+
+ visualBounds.setRect(
+ x + visualBounds.getX(),
+ y + visualBounds.getY(),
+ visualBounds.getWidth(),
+ visualBounds.getHeight()
+ );
+ }
+
+ return (Rectangle2D) visualBounds.clone();
+ }
+
+ /**
+ * Returns logical bounds of this segment
+ * @return logical bounds
+ */
+ @Override
+ Rectangle2D getLogicalBounds() {
+ if (logicalBounds == null) {
+ logicalBounds = getGlyphVector().getLogicalBounds();
+
+ logicalBounds.setRect(
+ x + logicalBounds.getX(),
+ y + logicalBounds.getY(),
+ logicalBounds.getWidth(),
+ logicalBounds.getHeight()
+ );
+ }
+
+ return (Rectangle2D) logicalBounds.clone();
+ }
+
+ @Override
+ float getAdvance() {
+ return (float) getLogicalBounds().getWidth();
+ }
+
+ /**
+ * Attemts to map each character to the corresponding advance increment
+ */
+ void initAdvanceMapping() {
+ GlyphVector gv = getGlyphVector();
+ int charIndicies[] = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null);
+ advanceIncrements = new float[info.length];
+
+ for (int i=0; i<charIndicies.length; i++) {
+ advanceIncrements[charIndicies[i]] = gv.getGlyphMetrics(i).getAdvance();
+ }
+ }
+
+ /**
+ * Calculates advance delta between two characters
+ * @param start - 1st position
+ * @param end - 2nd position
+ * @return advance increment between specified positions
+ */
+ @Override
+ float getAdvanceDelta(int start, int end) {
+ // Get coordinates in the segment context
+ start -= info.start;
+ end -= info.start;
+
+ if (advanceIncrements == null) {
+ initAdvanceMapping();
+ }
+
+ if (start < 0) {
+ start = 0;
+ }
+ if (end > info.length) {
+ end = info.length;
+ }
+
+ float sum = 0;
+ for (int i=start; i<end; i++) {
+ sum += advanceIncrements[i];
+ }
+
+ return sum;
+ }
+
+ /**
+ * Calculates index of the character which advance is equal to
+ * the given. If the given advance is greater then the segment
+ * advance it returns the position after the last character.
+ * @param advance - given advance
+ * @param start - character, from which to start measuring advance
+ * @return character index
+ */
+ @Override
+ int getCharIndexFromAdvance(float advance, int start) {
+ // XXX - todo - probably, possible to optimize
+ // Add check if the given advance is greater then
+ // the segment advance in the beginning. In this case
+ // we don't need to run through all increments
+ if (advanceIncrements == null) {
+ initAdvanceMapping();
+ }
+
+ start -= info.start;
+
+ if (start < 0) {
+ start = 0;
+ }
+
+ int i = start;
+ for (; i<info.length; i++) {
+ advance -= advanceIncrements[i];
+ if (advance < 0) {
+ break;
+ }
+ }
+
+ return i + info.start;
+ }
+
+ @Override
+ int getStart() {
+ return info.start;
+ }
+
+ @Override
+ int getEnd() {
+ return info.end;
+ }
+
+ @Override
+ int getLength() {
+ return info.length;
+ }
+
+ /**
+ * Attemts to create mapping of the characters to glyphs in the glyph vector.
+ * @return array where for each character index stored corresponding glyph index
+ */
+ private int[] getChar2Glyph() {
+ if (char2glyph == null) {
+ GlyphVector gv = getGlyphVector();
+ char2glyph = new int[info.length];
+ Arrays.fill(char2glyph, -1);
+
+ // Fill glyph indicies for first characters corresponding to each glyph
+ int charIndicies[] = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null);
+ for (int i=0; i<charIndicies.length; i++) {
+ char2glyph[charIndicies[i]] = i;
+ }
+
+ // If several characters corresponds to one glyph, create mapping for them
+ // Suppose that these characters are going all together
+ int currIndex = 0;
+ for (int i=0; i<char2glyph.length; i++) {
+ if (char2glyph[i] < 0) {
+ char2glyph[i] = currIndex;
+ } else {
+ currIndex = char2glyph[i];
+ }
+ }
+ }
+
+ return char2glyph;
+ }
+
+ /**
+ * Creates black box bounds shape for the specified range
+ * @param start - range sart
+ * @param limit - range end
+ * @return black box bounds shape
+ */
+ @Override
+ Shape getCharsBlackBoxBounds(int start, int limit) {
+ start -= info.start;
+ limit -= info.start;
+
+ if (limit > info.length) {
+ limit = info.length;
+ }
+
+ GeneralPath result = new GeneralPath();
+
+ int glyphIndex = 0;
+
+ for (int i=start; i<limit; i++) {
+ glyphIndex = getChar2Glyph()[i];
+ result.append(getGlyphVector().getGlyphVisualBounds(glyphIndex), false);
+ }
+
+ // Shift to the segment's coordinates
+ result.transform(AffineTransform.getTranslateInstance(x, y));
+
+ return result;
+ }
+
+ /**
+ * Calculates position of the character on the screen
+ * @param index - character index
+ * @return X coordinate of the character position
+ */
+ @Override
+ float getCharPosition(int index) {
+ index -= info.start;
+
+ if (index > info.length) {
+ index = info.length;
+ }
+
+ float result = 0;
+
+ int glyphIndex = getChar2Glyph()[index];
+ result = (float) getGlyphVector().getGlyphPosition(glyphIndex).getX();
+
+ // Shift to the segment's coordinates
+ result += x;
+
+ return result;
+ }
+
+ /**
+ * Returns the advance of the individual character
+ * @param index - character index
+ * @return character advance
+ */
+ @Override
+ float getCharAdvance(int index) {
+ if (advanceIncrements == null) {
+ initAdvanceMapping();
+ }
+
+ return advanceIncrements[index - this.getStart()];
+ }
+
+ /**
+ * Returns the outline shape
+ * @return outline
+ */
+ @Override
+ Shape getOutline() {
+ AffineTransform t = AffineTransform.getTranslateInstance(x, y);
+ return t.createTransformedShape(
+ TextDecorator.extendOutline(
+ this,
+ getGlyphVector().getOutline(),
+ decoration
+ )
+ );
+ }
+
+ /**
+ * Checks if the character doesn't contribute to the text advance
+ * @param index - character index
+ * @return true if the character has zero advance
+ */
+ @Override
+ boolean charHasZeroAdvance(int index) {
+ if (advanceIncrements == null) {
+ initAdvanceMapping();
+ }
+
+ return advanceIncrements[index - this.getStart()] == 0;
+ }
+
+ /**
+ * Creates text hit info from the hit position
+ * @param hitX - X coordinate relative to the origin of the layout
+ * @param hitY - Y coordinate relative to the origin of the layout
+ * @return hit info
+ */
+ @Override
+ TextHitInfo hitTest(float hitX, float hitY) {
+ hitX -= x;
+
+ float glyphPositions[] =
+ getGlyphVector().getGlyphPositions(0, info.length+1, null);
+
+ int glyphIdx;
+ boolean leading = false;
+ for (glyphIdx = 1; glyphIdx <= info.length; glyphIdx++) {
+ if (glyphPositions[(glyphIdx)*2] >= hitX) {
+ float advance =
+ glyphPositions[(glyphIdx)*2] - glyphPositions[(glyphIdx-1)*2];
+ leading = glyphPositions[(glyphIdx-1)*2] + advance/2 > hitX ? true : false;
+ glyphIdx--;
+ break;
+ }
+ }
+
+ if (glyphIdx == info.length) {
+ glyphIdx--;
+ }
+
+ int charIdx = getGlyphVector().getGlyphCharIndex(glyphIdx);
+
+ return (leading) ^ ((info.level & 0x1) == 0x1)?
+ TextHitInfo.leading(charIdx + info.start) :
+ TextHitInfo.trailing(charIdx + info.start);
+ }
+
+ /**
+ * Collects GlyphJustificationInfo objects from the glyph vector
+ * @return array of all GlyphJustificationInfo objects
+ */
+ private GlyphJustificationInfo[] getGlyphJustificationInfos() {
+ if (gjis == null) {
+ GlyphVector gv = getGlyphVector();
+ int nGlyphs = gv.getNumGlyphs();
+ int charIndicies[] = gv.getGlyphCharIndices(0, nGlyphs, null);
+ gjis = new GlyphJustificationInfo[nGlyphs];
+
+ // Patch: temporary patch, getGlyphJustificationInfo is not implemented
+ float fontSize = info.font.getSize2D();
+ GlyphJustificationInfo defaultInfo =
+ new GlyphJustificationInfo(
+ 0, // weight
+ false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0, // grow
+ false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0); // shrink
+ GlyphJustificationInfo spaceInfo = new GlyphJustificationInfo(
+ fontSize, // weight
+ true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, fontSize, // grow
+ true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, fontSize); // shrink
+
+ ////////
+ // Temporary patch, getGlyphJustificationInfo is not implemented
+ for (int i = 0; i < nGlyphs; i++) {
+ //gjis[i] = getGlyphVector().getGlyphJustificationInfo(i);
+
+ char c = info.text[charIndicies[i] + info.start];
+ if (Character.isWhitespace(c)) {
+ gjis[i] = spaceInfo;
+ } else {
+ gjis[i] = defaultInfo;
+ }
+ // End patch
+ }
+ }
+
+ return gjis;
+ }
+
+ /**
+ * Collects justification information into JustificationInfo object
+ * @param jInfo - JustificationInfo object
+ */
+ @Override
+ void updateJustificationInfo(TextRunBreaker.JustificationInfo jInfo) {
+ int lastChar = Math.min(jInfo.lastIdx, info.end) - info.start;
+ boolean haveFirst = info.start <= jInfo.firstIdx;
+ boolean haveLast = info.end >= (jInfo.lastIdx + 1);
+
+ int prevGlyphIdx = -1;
+ int currGlyphIdx;
+
+ if (jInfo.grow) { // Check how much we can grow/shrink on current priority level
+ for (int i=0; i<lastChar; i++) {
+ currGlyphIdx = getChar2Glyph()[i];
+
+ if (currGlyphIdx == prevGlyphIdx) {
+ // Several chars could be represented by one glyph,
+ // suppose they are contiguous
+ continue;
+ }
+ prevGlyphIdx = currGlyphIdx;
+
+ GlyphJustificationInfo gji = getGlyphJustificationInfos()[currGlyphIdx];
+ if (gji.growPriority == jInfo.priority) {
+ jInfo.weight += gji.weight * 2;
+ jInfo.growLimit += gji.growLeftLimit;
+ jInfo.growLimit += gji.growRightLimit;
+ if (gji.growAbsorb) {
+ jInfo.absorbedWeight += gji.weight * 2;
+ }
+ }
+ }
+ } else {
+ for (int i=0; i<lastChar; i++) {
+ currGlyphIdx = getChar2Glyph()[i];
+ if (currGlyphIdx == prevGlyphIdx) {
+ continue;
+ }
+ prevGlyphIdx = currGlyphIdx;
+
+ GlyphJustificationInfo gji = getGlyphJustificationInfos()[currGlyphIdx];
+ if (gji.shrinkPriority == jInfo.priority) {
+ jInfo.weight += gji.weight * 2;
+ jInfo.growLimit -= gji.shrinkLeftLimit;
+ jInfo.growLimit -= gji.shrinkRightLimit;
+ if (gji.shrinkAbsorb) {
+ jInfo.absorbedWeight += gji.weight * 2;
+ }
+ }
+ }
+ }
+
+ if (haveFirst) { // Don't add padding before first char
+ GlyphJustificationInfo gji = getGlyphJustificationInfos()[getChar2Glyph()[0]];
+ jInfo.weight -= gji.weight;
+ if (jInfo.grow) {
+ jInfo.growLimit -= gji.growLeftLimit;
+ if (gji.growAbsorb) {
+ jInfo.absorbedWeight -= gji.weight;
+ }
+ } else {
+ jInfo.growLimit += gji.shrinkLeftLimit;
+ if (gji.shrinkAbsorb) {
+ jInfo.absorbedWeight -= gji.weight;
+ }
+ }
+ }
+
+ if (haveLast) { // Don't add padding after last char
+ GlyphJustificationInfo gji =
+ getGlyphJustificationInfos()[getChar2Glyph()[lastChar]];
+ jInfo.weight -= gji.weight;
+ if (jInfo.grow) {
+ jInfo.growLimit -= gji.growRightLimit;
+ if (gji.growAbsorb) {
+ jInfo.absorbedWeight -= gji.weight;
+ }
+ } else {
+ jInfo.growLimit += gji.shrinkRightLimit;
+ if (gji.shrinkAbsorb) {
+ jInfo.absorbedWeight -= gji.weight;
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs justification of the segment.
+ * Updates positions of individual characters.
+ * @param jInfos - justification information, gathered by the previous passes
+ * @return amount of growth or shrink of the segment
+ */
+ @Override
+ float doJustification(TextRunBreaker.JustificationInfo jInfos[]) {
+ int lastPriority =
+ jInfos[jInfos.length-1] == null ?
+ -1 : jInfos[jInfos.length-1].priority;
+
+ // Get the highest priority
+ int highestPriority = 0;
+ for (; highestPriority<jInfos.length; highestPriority++) {
+ if (jInfos[highestPriority] != null) {
+ break;
+ }
+ }
+
+ if (highestPriority == jInfos.length) {
+ return 0;
+ }
+
+ TextRunBreaker.JustificationInfo firstInfo = jInfos[highestPriority];
+ TextRunBreaker.JustificationInfo lastInfo =
+ lastPriority > 0 ? jInfos[lastPriority] : null;
+
+ boolean haveFirst = info.start <= firstInfo.firstIdx;
+ boolean haveLast = info.end >= (firstInfo.lastIdx + 1);
+
+ // Here we suppose that GLYPHS are ordered LEFT TO RIGHT
+ int firstGlyph = haveFirst ?
+ getChar2Glyph()[firstInfo.firstIdx - info.start] :
+ getChar2Glyph()[0];
+
+ int lastGlyph = haveLast ?
+ getChar2Glyph()[firstInfo.lastIdx - info.start] :
+ getChar2Glyph()[info.length - 1];
+ if (haveLast) {
+ lastGlyph--;
+ }
+
+ TextRunBreaker.JustificationInfo currInfo;
+ float glyphOffset = 0;
+ float positionIncrement = 0;
+ float sideIncrement = 0;
+
+ if (haveFirst) { // Don't add padding before first char
+ GlyphJustificationInfo gji = getGlyphJustificationInfos()[firstGlyph];
+ currInfo = jInfos[gji.growPriority];
+ if (currInfo != null) {
+ if (currInfo.useLimits) {
+ if (currInfo.absorb) {
+ glyphOffset += gji.weight * currInfo.absorbedGapPerUnit;
+ } else if (
+ lastInfo != null &&
+ lastInfo.priority == currInfo.priority
+ ) {
+ glyphOffset += gji.weight * lastInfo.absorbedGapPerUnit;
+ }
+ glyphOffset +=
+ firstInfo.grow ?
+ gji.growRightLimit :
+ -gji.shrinkRightLimit;
+ } else {
+ glyphOffset += gji.weight * currInfo.gapPerUnit;
+ }
+ }
+
+ firstGlyph++;
+ }
+
+ if (firstInfo.grow) {
+ for (int i=firstGlyph; i<=lastGlyph; i++) {
+ GlyphJustificationInfo gji = getGlyphJustificationInfos()[i];
+ currInfo = jInfos[gji.growPriority];
+ if (currInfo == null) {
+ // We still have to increment glyph position
+ Point2D glyphPos = getGlyphVector().getGlyphPosition(i);
+ glyphPos.setLocation(glyphPos.getX() + glyphOffset, glyphPos.getY());
+ getGlyphVector().setGlyphPosition(i, glyphPos);
+
+ continue;
+ }
+
+ if (currInfo.useLimits) {
+ glyphOffset += gji.growLeftLimit;
+ if (currInfo.absorb) {
+ sideIncrement = gji.weight * currInfo.absorbedGapPerUnit;
+ glyphOffset += sideIncrement;
+ positionIncrement = glyphOffset;
+ glyphOffset += sideIncrement;
+ } else if (lastInfo != null && lastInfo.priority == currInfo.priority) {
+ sideIncrement = gji.weight * lastInfo.absorbedGapPerUnit;
+ glyphOffset += sideIncrement;
+ positionIncrement = glyphOffset;
+ glyphOffset += sideIncrement;
+ } else {
+ positionIncrement = glyphOffset;
+ }
+ glyphOffset += gji.growRightLimit;
+ } else {
+ sideIncrement = gji.weight * currInfo.gapPerUnit;
+ glyphOffset += sideIncrement;
+ positionIncrement = glyphOffset;
+ glyphOffset += sideIncrement;
+ }
+
+ Point2D glyphPos = getGlyphVector().getGlyphPosition(i);
+ glyphPos.setLocation(glyphPos.getX() + positionIncrement, glyphPos.getY());
+ getGlyphVector().setGlyphPosition(i, glyphPos);
+ }
+ } else {
+ for (int i=firstGlyph; i<=lastGlyph; i++) {
+ GlyphJustificationInfo gji = getGlyphJustificationInfos()[i];
+ currInfo = jInfos[gji.shrinkPriority];
+ if (currInfo == null) {
+ // We still have to increment glyph position
+ Point2D glyphPos = getGlyphVector().getGlyphPosition(i);
+ glyphPos.setLocation(glyphPos.getX() + glyphOffset, glyphPos.getY());
+ getGlyphVector().setGlyphPosition(i, glyphPos);
+
+ continue;
+ }
+
+ if (currInfo.useLimits) {
+ glyphOffset -= gji.shrinkLeftLimit;
+ if (currInfo.absorb) {
+ sideIncrement = gji.weight * currInfo.absorbedGapPerUnit;
+ glyphOffset += sideIncrement;
+ positionIncrement = glyphOffset;
+ glyphOffset += sideIncrement;
+ } else if (lastInfo != null && lastInfo.priority == currInfo.priority) {
+ sideIncrement = gji.weight * lastInfo.absorbedGapPerUnit;
+ glyphOffset += sideIncrement;
+ positionIncrement = glyphOffset;
+ glyphOffset += sideIncrement;
+ } else {
+ positionIncrement = glyphOffset;
+ }
+ glyphOffset -= gji.shrinkRightLimit;
+ } else {
+ sideIncrement = gji.weight * currInfo.gapPerUnit;
+ glyphOffset += sideIncrement;
+ positionIncrement = glyphOffset;
+ glyphOffset += sideIncrement;
+ }
+
+ Point2D glyphPos = getGlyphVector().getGlyphPosition(i);
+ glyphPos.setLocation(glyphPos.getX() + positionIncrement, glyphPos.getY());
+ getGlyphVector().setGlyphPosition(i, glyphPos);
+ }
+ }
+
+
+ if (haveLast) { // Don't add padding after last char
+ lastGlyph++;
+
+ GlyphJustificationInfo gji = getGlyphJustificationInfos()[lastGlyph];
+ currInfo = jInfos[gji.growPriority];
+
+ if (currInfo != null) {
+ if (currInfo.useLimits) {
+ glyphOffset += firstInfo.grow ? gji.growLeftLimit : -gji.shrinkLeftLimit;
+ if (currInfo.absorb) {
+ glyphOffset += gji.weight * currInfo.absorbedGapPerUnit;
+ } else if (lastInfo != null && lastInfo.priority == currInfo.priority) {
+ glyphOffset += gji.weight * lastInfo.absorbedGapPerUnit;
+ }
+ } else {
+ glyphOffset += gji.weight * currInfo.gapPerUnit;
+ }
+ }
+
+ // Ajust positions of all glyphs after last glyph
+ for (int i=lastGlyph; i<getGlyphVector().getNumGlyphs()+1; i++) {
+ Point2D glyphPos = getGlyphVector().getGlyphPosition(i);
+ glyphPos.setLocation(glyphPos.getX() + glyphOffset, glyphPos.getY());
+ getGlyphVector().setGlyphPosition(i, glyphPos);
+ }
+ } else { // Update position after last glyph in glyph vector -
+ // to get correct advance for it
+ Point2D glyphPos = getGlyphVector().getGlyphPosition(lastGlyph+1);
+ glyphPos.setLocation(glyphPos.getX() + glyphOffset, glyphPos.getY());
+ getGlyphVector().setGlyphPosition(lastGlyph+1, glyphPos);
+ }
+
+ gjis = null; // We don't need justification infos any more
+ // Also we have to reset cached bounds and metrics
+ this.visualBounds = null;
+ this.logicalBounds = null;
+
+ return glyphOffset; // How much our segment grown or shrunk
+ }
+ }
+
+ public static class TextRunSegmentGraphic extends TextRunSegment {
+ GraphicAttribute ga;
+ int start;
+ int length;
+ float fullAdvance;
+
+ TextRunSegmentGraphic(GraphicAttribute attr, int len, int start) {
+ this.start = start;
+ length = len;
+ ga = attr;
+ metrics = new BasicMetrics(ga);
+ fullAdvance = ga.getAdvance() * length;
+ }
+
+ @Override
+ public Object clone() {
+ return new TextRunSegmentGraphic(ga, length, start);
+ }
+
+ // Renders this text run segment
+ @Override
+ void draw(Graphics2D g2d, float xOffset, float yOffset) {
+ if (decoration != null) {
+ TextDecorator.prepareGraphics(this, g2d, xOffset, yOffset);
+ }
+
+ float xPos = x + xOffset;
+ float yPos = y + yOffset;
+
+ for (int i=0; i < length; i++) {
+ ga.draw(g2d, xPos, yPos);
+ xPos += ga.getAdvance();
+ }
+
+ if (decoration != null) {
+ TextDecorator.drawTextDecorations(this, g2d, xOffset, yOffset);
+ TextDecorator.restoreGraphics(decoration, g2d);
+ }
+ }
+
+ // Returns visual bounds of this segment
+ @Override
+ Rectangle2D getVisualBounds() {
+ if (visualBounds == null) {
+ Rectangle2D bounds = ga.getBounds();
+
+ // First and last chars can be out of logical bounds, so we calculate
+ // (bounds.getWidth() - ga.getAdvance()) which is exactly the difference
+ bounds.setRect(
+ bounds.getMinX() + x,
+ bounds.getMinY() + y,
+ bounds.getWidth() - ga.getAdvance() + getAdvance(),
+ bounds.getHeight()
+ );
+ visualBounds = TextDecorator.extendVisualBounds(this, bounds, decoration);
+ }
+
+ return (Rectangle2D) visualBounds.clone();
+ }
+
+ @Override
+ Rectangle2D getLogicalBounds() {
+ if (logicalBounds == null) {
+ logicalBounds =
+ new Rectangle2D.Float(
+ x, y - metrics.ascent,
+ getAdvance(), metrics.ascent + metrics.descent
+ );
+ }
+
+ return (Rectangle2D) logicalBounds.clone();
+ }
+
+ @Override
+ float getAdvance() {
+ return fullAdvance;
+ }
+
+ @Override
+ float getAdvanceDelta(int start, int end) {
+ return ga.getAdvance() * (end - start);
+ }
+
+ @Override
+ int getCharIndexFromAdvance(float advance, int start) {
+ start -= this.start;
+
+ if (start < 0) {
+ start = 0;
+ }
+
+ int charOffset = (int) (advance/ga.getAdvance());
+
+ if (charOffset + start > length) {
+ return length + this.start;
+ }
+ return charOffset + start + this.start;
+ }
+
+ @Override
+ int getStart() {
+ return start;
+ }
+
+ @Override
+ int getEnd() {
+ return start + length;
+ }
+
+ @Override
+ int getLength() {
+ return length;
+ }
+
+ @Override
+ Shape getCharsBlackBoxBounds(int start, int limit) {
+ start -= this.start;
+ limit -= this.start;
+
+ if (limit > length) {
+ limit = length;
+ }
+
+ Rectangle2D charBounds = ga.getBounds();
+ charBounds.setRect(
+ charBounds.getX() + ga.getAdvance() * start + x,
+ charBounds.getY() + y,
+ charBounds.getWidth() + ga.getAdvance() * (limit - start),
+ charBounds.getHeight()
+ );
+
+ return charBounds;
+ }
+
+ @Override
+ float getCharPosition(int index) {
+ index -= start;
+ if (index > length) {
+ index = length;
+ }
+
+ return ga.getAdvance() * index + x;
+ }
+
+ @Override
+ float getCharAdvance(int index) {
+ return ga.getAdvance();
+ }
+
+ @Override
+ Shape getOutline() {
+ AffineTransform t = AffineTransform.getTranslateInstance(x, y);
+ return t.createTransformedShape(
+ TextDecorator.extendOutline(this, getVisualBounds(), decoration)
+ );
+ }
+
+ @Override
+ boolean charHasZeroAdvance(int index) {
+ return false;
+ }
+
+ @Override
+ TextHitInfo hitTest(float hitX, float hitY) {
+ hitX -= x;
+
+ float tmp = hitX / ga.getAdvance();
+ int hitIndex = Math.round(tmp);
+
+ if (tmp > hitIndex) {
+ return TextHitInfo.leading(hitIndex + this.start);
+ }
+ return TextHitInfo.trailing(hitIndex + this.start);
+ }
+
+ @Override
+ void updateJustificationInfo(TextRunBreaker.JustificationInfo jInfo) {
+ // Do nothing
+ }
+
+ @Override
+ float doJustification(TextRunBreaker.JustificationInfo jInfos[]) {
+ // Do nothing
+ return 0;
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java b/awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java
new file mode 100644
index 0000000..f1d64fb
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Alexey A. Petrenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.harmony.awt.gl.CommonGraphics2D;
+import org.apache.harmony.awt.gl.Surface;
+import org.apache.harmony.awt.gl.render.JavaBlitter;
+import org.apache.harmony.awt.gl.render.NativeImageBlitter;
+
+/**
+ * BufferedImageGraphics2D is implementation of CommonGraphics2D for
+ * drawing on buffered images.
+ */
+public class BufferedImageGraphics2D extends CommonGraphics2D {
+ private BufferedImage bi = null;
+ private Rectangle bounds = null;
+
+ public BufferedImageGraphics2D(BufferedImage bi) {
+ super();
+ this.bi = bi;
+ this.bounds = new Rectangle(0, 0, bi.getWidth(), bi.getHeight());
+ clip(bounds);
+ dstSurf = Surface.getImageSurface(bi);
+ if(dstSurf.isNativeDrawable()){
+ blitter = NativeImageBlitter.getInstance();
+ }else{
+ blitter = JavaBlitter.getInstance();
+ }
+ }
+
+ @Override
+ public void copyArea(int x, int y, int width, int height, int dx, int dy) {
+ }
+
+ @Override
+ public Graphics create() {
+ BufferedImageGraphics2D res = new BufferedImageGraphics2D(bi);
+ copyInternalFields(res);
+ return res;
+ }
+
+ @Override
+ public GraphicsConfiguration getDeviceConfiguration() {
+ return null;
+ }
+
+ public ColorModel getColorModel() {
+ return bi.getColorModel();
+ }
+
+ public WritableRaster getWritableRaster() {
+ return bi.getRaster();
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java b/awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java
new file mode 100644
index 0000000..0fe25a2
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.awt.gl.image;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DirectColorModel;
+import java.awt.image.ImageConsumer;
+import java.awt.image.ImageProducer;
+import java.awt.image.IndexColorModel;
+import java.awt.image.WritableRaster;
+import java.util.Hashtable;
+
+public class BufferedImageSource implements ImageProducer {
+
+ private Hashtable<?, ?> properties;
+ private ColorModel cm;
+ private WritableRaster raster;
+ private int width;
+ private int height;
+
+ private ImageConsumer ic;
+
+ public BufferedImageSource(BufferedImage image, Hashtable<?, ?> properties){
+ if(properties == null) {
+ this.properties = new Hashtable<Object, Object>();
+ } else {
+ this.properties = properties;
+ }
+
+ width = image.getWidth();
+ height = image.getHeight();
+ cm = image.getColorModel();
+ raster = image.getRaster();
+ }
+
+ public BufferedImageSource(BufferedImage image){
+ this(image, null);
+ }
+
+ public boolean isConsumer(ImageConsumer ic) {
+ return (this.ic == ic);
+ }
+
+ public void startProduction(ImageConsumer ic) {
+ addConsumer(ic);
+ }
+
+ public void requestTopDownLeftRightResend(ImageConsumer ic) {
+ }
+
+ public void removeConsumer(ImageConsumer ic) {
+ if (this.ic == ic) {
+ this.ic = null;
+ }
+ }
+
+ public void addConsumer(ImageConsumer ic) {
+ this.ic = ic;
+ startProduction();
+ }
+
+ private void startProduction(){
+ try {
+ ic.setDimensions(width, height);
+ ic.setProperties(properties);
+ ic.setColorModel(cm);
+ ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT |
+ ImageConsumer.COMPLETESCANLINES |
+ ImageConsumer.SINGLEFRAME |
+ ImageConsumer.SINGLEPASS);
+ if(cm instanceof IndexColorModel &&
+ raster.getTransferType() == DataBuffer.TYPE_BYTE ||
+ cm instanceof ComponentColorModel &&
+ raster.getTransferType() == DataBuffer.TYPE_BYTE &&
+ raster.getNumDataElements() == 1){
+ DataBufferByte dbb = (DataBufferByte) raster.getDataBuffer();
+ byte data[] = dbb.getData();
+ int off = dbb.getOffset();
+ ic.setPixels(0, 0, width, height, cm, data, off, width);
+ }else if(cm instanceof DirectColorModel &&
+ raster.getTransferType() == DataBuffer.TYPE_INT){
+ DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
+ int data[] = dbi.getData();
+ int off = dbi.getOffset();
+ ic.setPixels(0, 0, width, height, cm, data, off, width);
+ }else if(cm instanceof DirectColorModel &&
+ raster.getTransferType() == DataBuffer.TYPE_BYTE){
+ DataBufferByte dbb = (DataBufferByte) raster.getDataBuffer();
+ byte data[] = dbb.getData();
+ int off = dbb.getOffset();
+ ic.setPixels(0, 0, width, height, cm, data, off, width);
+ }else{
+ ColorModel rgbCM = ColorModel.getRGBdefault();
+ int pixels[] = new int[width];
+ Object pix = null;
+ for(int y = 0; y < height; y++){
+ for(int x = 0 ; x < width; x++){
+ pix = raster.getDataElements(x, y, pix);
+ pixels[x] = cm.getRGB(pix);
+ }
+ ic.setPixels(0, y, width, 1, rgbCM, pixels, 0, width);
+ }
+ }
+ ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
+ }catch (NullPointerException e){
+ if (ic != null) {
+ ic.imageComplete(ImageConsumer.IMAGEERROR);
+ }
+ }
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java
new file mode 100644
index 0000000..cc6d7cf
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+/*
+ * Created on 10.02.2005
+ *
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+public class ByteArrayDecodingImageSource extends DecodingImageSource {
+
+ byte imagedata[];
+ int imageoffset;
+ int imagelength;
+
+ public ByteArrayDecodingImageSource(byte imagedata[], int imageoffset,
+ int imagelength){
+ this.imagedata = imagedata;
+ this.imageoffset = imageoffset;
+ this.imagelength = imagelength;
+ }
+
+ public ByteArrayDecodingImageSource(byte imagedata[]){
+ this(imagedata, 0, imagedata.length);
+ }
+
+ @Override
+ protected boolean checkConnection() {
+ return true;
+ }
+
+ @Override
+ protected InputStream getInputStream() {
+ // BEGIN android-modified
+ // TODO: Why does a ByteArrayInputStream need to be buffered at all?
+ return new BufferedInputStream(new ByteArrayInputStream(imagedata,
+ imageoffset, imagelength), 1024);
+ // END android-modified
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/DataBufferListener.java b/awt/org/apache/harmony/awt/gl/image/DataBufferListener.java
new file mode 100644
index 0000000..8793050
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/DataBufferListener.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 13.03.2006
+ *
+ */
+package org.apache.harmony.awt.gl.image;
+
+public interface DataBufferListener {
+
+ void dataChanged();
+ void dataTaken();
+ void dataReleased();
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java
new file mode 100644
index 0000000..958d691
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+/*
+ * Created on 18.01.2005
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.awt.image.ImageConsumer;
+import java.awt.image.ImageProducer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This is an abstract class that encapsulates a main part of ImageProducer functionality
+ * for the images being decoded by the native decoders, like PNG, JPEG and GIF.
+ * It helps to integrate image decoders into producer/consumer model. It provides
+ * functionality for working with several decoder instances and several image consumers
+ * simultaneously.
+ */
+public abstract class DecodingImageSource implements ImageProducer {
+ List<ImageConsumer> consumers = new ArrayList<ImageConsumer>(5);
+ List<ImageDecoder> decoders = new ArrayList<ImageDecoder>(5);
+ boolean loading;
+
+ ImageDecoder decoder;
+
+ protected abstract boolean checkConnection();
+
+ protected abstract InputStream getInputStream();
+
+ public synchronized void addConsumer(ImageConsumer ic) {
+ if (!checkConnection()) { // No permission for this consumer
+ ic.imageComplete(ImageConsumer.IMAGEERROR);
+ return;
+ }
+
+ ImageConsumer cons = findConsumer(consumers, ic);
+
+ if (cons == null) { // Try to look in the decoders
+ ImageDecoder d = null;
+
+ // Check for all existing decoders
+ for (Iterator<ImageDecoder> i = decoders.iterator(); i.hasNext();) {
+ d = i.next();
+ cons = findConsumer(d.consumers, ic);
+ if (cons != null) {
+ break;
+ }
+ }
+ }
+
+ if (cons == null) { // Not found, add this consumer
+ consumers.add(ic);
+ }
+ }
+
+ /**
+ * This method stops sending data to the given consumer
+ * @param ic - consumer
+ */
+ private void abortConsumer(ImageConsumer ic) {
+ ic.imageComplete(ImageConsumer.IMAGEERROR);
+ consumers.remove(ic);
+ }
+
+ /**
+ * This method stops sending data to the list of consumers.
+ * @param consumersList - list of consumers
+ */
+ private void abortAllConsumers(List<ImageConsumer> consumersList) {
+ for (ImageConsumer imageConsumer : consumersList) {
+ abortConsumer(imageConsumer);
+ }
+ }
+
+ public synchronized void removeConsumer(ImageConsumer ic) {
+ ImageDecoder d = null;
+
+ // Remove in all existing decoders
+ for (Iterator<ImageDecoder> i = decoders.iterator(); i.hasNext();) {
+ d = i.next();
+ removeConsumer(d.consumers, ic);
+ if (d.consumers.size() <= 0) {
+ d.terminate();
+ }
+ }
+
+ // Remove in the current queue of consumers
+ removeConsumer(consumers, ic);
+ }
+
+ /**
+ * Static implementation of removeConsumer method
+ * @param consumersList - list of consumers
+ * @param ic - consumer to be removed
+ */
+ private static void removeConsumer(List<ImageConsumer> consumersList, ImageConsumer ic) {
+ ImageConsumer cons = null;
+
+ for (Iterator<ImageConsumer> i = consumersList.iterator(); i.hasNext();) {
+ cons = i.next();
+ if (cons.equals(ic)) {
+ i.remove();
+ }
+ }
+ }
+
+ public void requestTopDownLeftRightResend(ImageConsumer consumer) {
+ // Do nothing
+ }
+
+ public synchronized void startProduction(ImageConsumer ic) {
+ if (ic != null) {
+ addConsumer(ic);
+ }
+
+ if (!loading && consumers.size() > 0) {
+ ImageLoader.addImageSource(this);
+ loading = true;
+ }
+ }
+
+ public synchronized boolean isConsumer(ImageConsumer ic) {
+ ImageDecoder d = null;
+
+ // Check for all existing decoders
+ for (Iterator<ImageDecoder> i = decoders.iterator(); i.hasNext();) {
+ d = i.next();
+ if (findConsumer(d.consumers, ic) != null) {
+ return true;
+ }
+ }
+
+ // Check current queue of consumers
+ return findConsumer(consumers, ic) != null;
+ }
+
+ /**
+ * Checks if the consumer is in the list and returns it it is there
+ * @param consumersList - list of consumers
+ * @param ic - consumer
+ * @return consumer if found, null otherwise
+ */
+ private static ImageConsumer findConsumer(List<ImageConsumer> consumersList, ImageConsumer ic) {
+ ImageConsumer res = null;
+
+ for (Iterator<ImageConsumer> i = consumersList.iterator(); i.hasNext();) {
+ res = i.next();
+ if (res.equals(ic)) {
+ return res;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Use this method to finish decoding or lock the list of consumers
+ * for a particular decoder
+ * @param d - decoder
+ */
+ synchronized void lockDecoder(ImageDecoder d) {
+ if (d == decoder) {
+ decoder = null;
+ startProduction(null);
+ }
+ }
+
+ /**
+ * Tries to find an appropriate decoder for the input stream and adds it
+ * to the list of decoders
+ * @return created decoder
+ */
+ private ImageDecoder createDecoder() {
+ InputStream is = getInputStream();
+
+ ImageDecoder decoder;
+
+ if (is == null) {
+ decoder = null;
+ } else {
+ decoder = ImageDecoder.createDecoder(this, is);
+ }
+
+ if (decoder != null) {
+ synchronized (this) {
+ decoders.add(decoder);
+ this.decoder = decoder;
+ loading = false;
+ consumers = new ArrayList<ImageConsumer>(5); // Reset queue
+ }
+
+ return decoder;
+ }
+ // We were not able to find appropriate decoder
+ List<ImageConsumer> cs;
+ synchronized (this) {
+ cs = consumers;
+ consumers = new ArrayList<ImageConsumer>(5);
+ loading = false;
+ }
+ abortAllConsumers(cs);
+
+ return null;
+ }
+
+ /**
+ * Stop the given decoder and remove it from the list
+ * @param dr - decoder
+ */
+ private synchronized void removeDecoder(ImageDecoder dr) {
+ lockDecoder(dr);
+ decoders.remove(dr);
+ }
+
+ /**
+ * This method serves as an entry point.
+ * It starts the decoder and loads the image data.
+ */
+ public void load() {
+ synchronized (this) {
+ if (consumers.size() == 0) {
+ loading = false;
+ return;
+ }
+ }
+
+ ImageDecoder d = createDecoder();
+ if (d != null) {
+ try {
+ decoder.decodeImage();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ removeDecoder(d);
+ abortAllConsumers(d.consumers);
+ }
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java
new file mode 100644
index 0000000..54d4664
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+/*
+ * Created on 20.01.2005
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public class FileDecodingImageSource extends DecodingImageSource {
+ String filename;
+
+ public FileDecodingImageSource(String file) {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkRead(file);
+ }
+
+ filename = file;
+ }
+
+ @Override
+protected boolean checkConnection() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ try {
+ security.checkRead(filename);
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+protected InputStream getInputStream() {
+ try {
+ // BEGIN android-modified
+ return new BufferedInputStream(new FileInputStream(filename), 8192);
+ // END android-modified
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/GifDecoder.java b/awt/org/apache/harmony/awt/gl/image/GifDecoder.java
new file mode 100644
index 0000000..7ecb15b
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/GifDecoder.java
@@ -0,0 +1,692 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+/*
+* Created on 27.01.2005
+*/
+package org.apache.harmony.awt.gl.image;
+
+import java.awt.image.ColorModel;
+import java.awt.image.ImageConsumer;
+import java.awt.image.IndexColorModel;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+
+public class GifDecoder extends ImageDecoder {
+ // initializes proper field IDs
+ private static native void initIDs();
+
+ static {
+ System.loadLibrary("gl"); //$NON-NLS-1$
+ initIDs();
+ }
+
+ // ImageConsumer hints: common
+ private static final int baseHints =
+ ImageConsumer.SINGLEPASS | ImageConsumer.COMPLETESCANLINES |
+ ImageConsumer.SINGLEFRAME;
+ // ImageConsumer hints: interlaced
+ private static final int interlacedHints =
+ baseHints | ImageConsumer.RANDOMPIXELORDER;
+
+ // Impossible color value - no translucent pixels allowed
+ static final int IMPOSSIBLE_VALUE = 0x0FFFFFFF;
+
+ // I/O buffer
+ private static final int BUFFER_SIZE = 1024;
+ private byte buffer[] = new byte[BUFFER_SIZE];
+
+ GifDataStream gifDataStream = new GifDataStream();
+ GifGraphicBlock currBlock;
+
+ // Pointer to native structure which store decoding state
+ // between subsequent decoding/IO-suspension cycles
+ private long hNativeDecoder; // NULL initially
+
+ // Number of bytes eaten by the native decoder
+ private int bytesConsumed;
+
+ private boolean consumersPrepared;
+ private Hashtable<String, String> properties = new Hashtable<String, String>();
+
+ // Could be set up by java code or native method when
+ // transparent pixel index changes or local color table encountered
+ private boolean forceRGB;
+
+ private byte screenBuffer[];
+ private int screenRGBBuffer[];
+
+ public GifDecoder(DecodingImageSource src, InputStream is) {
+ super(src, is);
+ }
+
+ private static native int[] toRGB(byte imageData[], byte colormap[], int transparentColor);
+
+ private static native void releaseNativeDecoder(long hDecoder);
+
+ private native int decode(
+ byte input[],
+ int bytesInBuffer,
+ long hDecoder,
+ GifDataStream dataStream,
+ GifGraphicBlock currBlock
+ );
+
+ private int[] getScreenRGBBuffer() {
+ if (screenRGBBuffer == null) {
+ if (screenBuffer != null) {
+ int transparentColor =
+ gifDataStream.logicalScreen.globalColorTable.cm.getTransparentPixel();
+ transparentColor = transparentColor > 0 ? transparentColor : IMPOSSIBLE_VALUE;
+ screenRGBBuffer =
+ toRGB(
+ screenBuffer,
+ gifDataStream.logicalScreen.globalColorTable.colors,
+ transparentColor
+ );
+ } else {
+ int size = gifDataStream.logicalScreen.logicalScreenHeight *
+ gifDataStream.logicalScreen.logicalScreenWidth;
+ screenRGBBuffer = new int[size];
+ }
+ }
+
+ return screenRGBBuffer;
+ }
+
+ private void prepareConsumers() {
+ GifLogicalScreen gls = gifDataStream.logicalScreen;
+ setDimensions(gls.logicalScreenWidth,
+ gls.logicalScreenHeight);
+ setProperties(properties);
+
+ currBlock = gifDataStream.graphicBlocks.get(0);
+ if (forceRGB) {
+ setColorModel(ColorModel.getRGBdefault());
+ } else {
+ setColorModel(gls.globalColorTable.getColorModel(currBlock.transparentColor));
+ }
+
+ // Fill screen buffer with the background or transparent color
+ if (forceRGB) {
+ int fillColor = 0xFF000000;
+ if (gls.backgroundColor != IMPOSSIBLE_VALUE) {
+ fillColor = gls.backgroundColor;
+ }
+
+ Arrays.fill(getScreenRGBBuffer(), fillColor);
+ } else {
+ int fillColor = 0;
+
+ if (gls.backgroundColor != IMPOSSIBLE_VALUE) {
+ fillColor = gls.backgroundColor;
+ } else {
+ fillColor = gls.globalColorTable.cm.getTransparentPixel();
+ }
+
+ screenBuffer = new byte[gls.logicalScreenHeight*gls.logicalScreenWidth];
+ Arrays.fill(screenBuffer, (byte) fillColor);
+ }
+
+ setHints(interlacedHints); // XXX - always random pixel order
+ }
+
+ @Override
+ public void decodeImage() throws IOException {
+ try {
+ int bytesRead = 0;
+ int needBytes, offset, bytesInBuffer = 0;
+ boolean eosReached = false;
+ GifGraphicBlock blockToDispose = null;
+
+ // Create new graphic block
+ if (currBlock == null) {
+ currBlock = new GifGraphicBlock();
+ gifDataStream.graphicBlocks.add(currBlock);
+ }
+
+ // Read from the input stream
+ for (;;) {
+ needBytes = BUFFER_SIZE - bytesInBuffer;
+ offset = bytesInBuffer;
+
+ bytesRead = inputStream.read(buffer, offset, needBytes);
+
+ if (bytesRead < 0) {
+ eosReached = true;
+ bytesRead = 0;
+ } // Don't break, maybe something left in buffer
+
+ // Keep track on how much bytes left in buffer
+ bytesInBuffer += bytesRead;
+
+ // Here we pass number of new bytes read from the input stream (bytesRead)
+ // since native decoder uses java buffer and doesn't have its own
+ // buffer. So it adds this number to the number of bytes left
+ // in buffer from the previous call.
+ int numLines = decode(
+ buffer,
+ bytesRead,
+ hNativeDecoder,
+ gifDataStream,
+ currBlock);
+
+ // Keep track on how much bytes left in buffer
+ bytesInBuffer -= bytesConsumed;
+
+ if (
+ !consumersPrepared &&
+ gifDataStream.logicalScreen.completed &&
+ gifDataStream.logicalScreen.globalColorTable.completed &&
+ (currBlock.imageData != null || // Have transparent pixel filled
+ currBlock.rgbImageData != null)
+ ) {
+ prepareConsumers();
+ consumersPrepared = true;
+ }
+
+ if (bytesConsumed < 0) {
+ break; // Error exit
+ }
+
+ if (currBlock != null) {
+ if (numLines != 0) {
+ // Dispose previous image only before showing next
+ if (blockToDispose != null) {
+ blockToDispose.dispose();
+ blockToDispose = null;
+ }
+
+ currBlock.sendNewData(this, numLines);
+ }
+
+ if (currBlock.completed && hNativeDecoder != 0) {
+ blockToDispose = currBlock; // Dispose only before showing new pixels
+ currBlock = new GifGraphicBlock();
+ gifDataStream.graphicBlocks.add(currBlock);
+ }
+ }
+
+ if (hNativeDecoder == 0) {
+ break;
+ }
+
+ if (eosReached && numLines == 0) { // Maybe image is truncated...
+ releaseNativeDecoder(hNativeDecoder);
+ break;
+ }
+ }
+ } finally {
+ closeStream();
+ }
+
+ // Here all animation goes
+ // Repeat image loopCount-1 times or infinitely if loopCount = 0
+ if (gifDataStream.loopCount != 1) {
+ if (currBlock.completed == false) {
+ gifDataStream.graphicBlocks.remove(currBlock);
+ }
+
+ int numFrames = gifDataStream.graphicBlocks.size();
+ // At first last block will be disposed
+ GifGraphicBlock gb =
+ gifDataStream.graphicBlocks.get(numFrames-1);
+
+ ImageLoader.beginAnimation();
+
+ while (gifDataStream.loopCount != 1) {
+ if (gifDataStream.loopCount != 0) {
+ gifDataStream.loopCount--;
+ }
+
+ // Show all frames
+ for (int i=0; i<numFrames; i++) {
+ gb.dispose();
+ gb = gifDataStream.graphicBlocks.get(i);
+
+ // Show one frame
+ if (forceRGB) {
+ setPixels(
+ gb.imageLeft,
+ gb.imageTop,
+ gb.imageWidth,
+ gb.imageHeight,
+ ColorModel.getRGBdefault(),
+ gb.getRgbImageData(),
+ 0,
+ gb.imageWidth
+ );
+ } else {
+ setPixels(
+ gb.imageLeft,
+ gb.imageTop,
+ gb.imageWidth,
+ gb.imageHeight,
+ null,
+ gb.imageData,
+ 0,
+ gb.imageWidth
+ );
+ }
+ }
+ }
+ ImageLoader.endAnimation();
+ }
+
+ imageComplete(ImageConsumer.STATICIMAGEDONE);
+ }
+
+ void setComment(String newComment) {
+ Object currComment = properties.get("comment"); //$NON-NLS-1$
+
+ if (currComment == null) {
+ properties.put("comment", newComment); //$NON-NLS-1$
+ } else {
+ properties.put("comment", (String) currComment + "\n" + newComment); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ setProperties(properties);
+ }
+
+ class GifDataStream {
+ // Indicates that reading of the whole data stream accomplished
+ boolean completed = false;
+
+ // Added to support Netscape 2.0 application
+ // extension block.
+ int loopCount = 1;
+
+ GifLogicalScreen logicalScreen = new GifLogicalScreen();
+ List<GifGraphicBlock> graphicBlocks = new ArrayList<GifGraphicBlock>(10); // Of GifGraphicBlocks
+
+ // Comments from the image
+ String comments[];
+ }
+
+ class GifLogicalScreen {
+ // Indicates that reading of this block accomplished
+ boolean completed = false;
+
+ int logicalScreenWidth;
+ int logicalScreenHeight;
+
+ int backgroundColor = IMPOSSIBLE_VALUE;
+
+ GifColorTable globalColorTable = new GifColorTable();
+ }
+
+ class GifGraphicBlock {
+ // Indicates that reading of this block accomplished
+ boolean completed = false;
+
+ final static int DISPOSAL_NONE = 0;
+ final static int DISPOSAL_NODISPOSAL = 1;
+ final static int DISPOSAL_BACKGROUND = 2;
+ final static int DISPOSAL_RESTORE = 3;
+
+ int disposalMethod;
+ int delayTime; // Multiplied by 10 already
+ int transparentColor = IMPOSSIBLE_VALUE;
+
+ int imageLeft;
+ int imageTop;
+ int imageWidth;
+ int imageHeight;
+
+ // Auxilliary variables to minimize computations
+ int imageRight;
+ int imageBottom;
+
+ boolean interlace;
+
+ // Don't need local color table - if it is specified
+ // image data are converted to RGB in the native code
+
+ byte imageData[] = null;
+ int rgbImageData[] = null;
+
+ private int currY = 0; // Current output scanline
+
+ int[] getRgbImageData() {
+ if (rgbImageData == null) {
+ rgbImageData =
+ toRGB(
+ imageData,
+ gifDataStream.logicalScreen.globalColorTable.colors,
+ transparentColor
+ );
+ if (transparentColor != IMPOSSIBLE_VALUE) {
+ transparentColor =
+ gifDataStream.logicalScreen.globalColorTable.cm.getRGB(transparentColor);
+ transparentColor &= 0x00FFFFFF;
+ }
+ }
+ return rgbImageData;
+ }
+
+ private void replaceTransparentPixels(int numLines) {
+ List<GifGraphicBlock> graphicBlocks = gifDataStream.graphicBlocks;
+ int prevBlockIndex = graphicBlocks.indexOf(this) - 1;
+
+ if (prevBlockIndex >= 0) {
+ int maxY = currY + numLines + imageTop;
+ int offset = currY * imageWidth;
+
+ // Update right and bottom coordinates
+ imageRight = imageLeft + imageWidth;
+ imageBottom = imageTop + imageHeight;
+
+ int globalWidth = gifDataStream.logicalScreen.logicalScreenWidth;
+ int pixelValue, imageOffset;
+ int rgbData[] = forceRGB ? getRgbImageData() : null;
+
+ for (int y = currY + imageTop; y < maxY; y++) {
+ imageOffset = globalWidth * y + imageLeft;
+ for (int x = imageLeft; x < imageRight; x++) {
+ pixelValue = forceRGB ?
+ rgbData[offset] :
+ imageData[offset] & 0xFF;
+ if (pixelValue == transparentColor) {
+ if (forceRGB) {
+ pixelValue = getScreenRGBBuffer() [imageOffset];
+ rgbData[offset] = pixelValue;
+ } else {
+ pixelValue = screenBuffer [imageOffset];
+ imageData[offset] = (byte) pixelValue;
+ }
+ }
+ offset++;
+ imageOffset++;
+ } // for
+ } // for
+
+ } // if (prevBlockIndex >= 0)
+ }
+
+ public void sendNewData(GifDecoder decoder, int numLines) {
+ // Get values for transparent pixels
+ // from the perevious frames
+ if (transparentColor != IMPOSSIBLE_VALUE) {
+ replaceTransparentPixels(numLines);
+ }
+
+ if (forceRGB) {
+ decoder.setPixels(
+ imageLeft,
+ imageTop + currY,
+ imageWidth,
+ numLines,
+ ColorModel.getRGBdefault(),
+ getRgbImageData(),
+ currY*imageWidth,
+ imageWidth
+ );
+ } else {
+ decoder.setPixels(
+ imageLeft,
+ imageTop + currY,
+ imageWidth,
+ numLines,
+ null,
+ imageData,
+ currY*imageWidth,
+ imageWidth
+ );
+ }
+
+ currY += numLines;
+ }
+
+ public void dispose() {
+ imageComplete(ImageConsumer.SINGLEFRAMEDONE);
+
+ // Show current frame until delayInterval will not elapse
+ if (delayTime > 0) {
+ try {
+ Thread.sleep(delayTime);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } else {
+ Thread.yield(); // Allow consumers to consume data
+ }
+
+ // Don't dispose if image is outside of the visible area
+ if (imageLeft > gifDataStream.logicalScreen.logicalScreenWidth ||
+ imageTop > gifDataStream.logicalScreen.logicalScreenHeight) {
+ disposalMethod = DISPOSAL_NONE;
+ }
+
+ switch(disposalMethod) {
+ case DISPOSAL_BACKGROUND: {
+ if (forceRGB) {
+ getRgbImageData(); // Ensure that transparentColor is RGB, not index
+
+ int data[] = new int[imageWidth*imageHeight];
+
+ // Compatibility: Fill with transparent color if we have one
+ if (transparentColor != IMPOSSIBLE_VALUE) {
+ Arrays.fill(
+ data,
+ transparentColor
+ );
+ } else {
+ Arrays.fill(
+ data,
+ gifDataStream.logicalScreen.backgroundColor
+ );
+ }
+
+ setPixels(
+ imageLeft,
+ imageTop,
+ imageWidth,
+ imageHeight,
+ ColorModel.getRGBdefault(),
+ data,
+ 0,
+ imageWidth
+ );
+
+ sendToScreenBuffer(data);
+ } else {
+ byte data[] = new byte[imageWidth*imageHeight];
+
+ // Compatibility: Fill with transparent color if we have one
+ if (transparentColor != IMPOSSIBLE_VALUE) {
+ Arrays.fill(
+ data,
+ (byte) transparentColor
+ );
+ } else {
+ Arrays.fill(
+ data,
+ (byte) gifDataStream.logicalScreen.backgroundColor
+ );
+ }
+
+ setPixels(
+ imageLeft,
+ imageTop,
+ imageWidth,
+ imageHeight,
+ null,
+ data,
+ 0,
+ imageWidth
+ );
+
+ sendToScreenBuffer(data);
+ }
+ break;
+ }
+ case DISPOSAL_RESTORE: {
+ screenBufferToScreen();
+ break;
+ }
+ case DISPOSAL_NONE:
+ case DISPOSAL_NODISPOSAL:
+ default: {
+ // Copy transmitted data to the screen buffer
+ Object data = forceRGB ? (Object) getRgbImageData() : imageData;
+ sendToScreenBuffer(data);
+ break;
+ }
+ }
+ }
+
+ private void sendToScreenBuffer(Object data) {
+ int dataInt[];
+ byte dataByte[];
+
+ int width = gifDataStream.logicalScreen.logicalScreenWidth;
+
+
+ if (forceRGB) {
+ dataInt = (int[]) data;
+
+ if (imageWidth == width) {
+ System.arraycopy(dataInt,
+ 0,
+ getScreenRGBBuffer(),
+ imageLeft + imageTop*width,
+ dataInt.length
+ );
+ } else { // Each scanline
+ copyScanlines(dataInt, getScreenRGBBuffer(), width);
+ }
+ } else {
+ dataByte = (byte[]) data;
+
+ if (imageWidth == width) {
+ System.arraycopy(dataByte,
+ 0,
+ screenBuffer,
+ imageLeft + imageTop*width,
+ dataByte.length
+ );
+ } else { // Each scanline
+ copyScanlines(dataByte, screenBuffer, width);
+ }
+ }
+ } // sendToScreenBuffer
+
+ private void copyScanlines(Object src, Object dst, int width) {
+ for (int i=0; i<imageHeight; i++) {
+ System.arraycopy(src,
+ i*imageWidth,
+ dst,
+ imageLeft + i*width + imageTop*width,
+ imageWidth
+ );
+ } // for
+ }
+
+ private void screenBufferToScreen() {
+ int width = gifDataStream.logicalScreen.logicalScreenWidth;
+
+ Object dst = forceRGB ?
+ (Object) new int[imageWidth*imageHeight] :
+ new byte[imageWidth*imageHeight];
+
+ Object src = forceRGB ?
+ getScreenRGBBuffer() :
+ (Object) screenBuffer;
+
+ int offset = 0;
+ Object toSend;
+
+ if (width == imageWidth) {
+ offset = imageWidth * imageTop;
+ toSend = src;
+ } else {
+ for (int i=0; i<imageHeight; i++) {
+ System.arraycopy(src,
+ imageLeft + i*width + imageTop*width,
+ dst,
+ i*imageWidth,
+ imageWidth
+ );
+ } // for
+ toSend = dst;
+ }
+
+ if (forceRGB) {
+ setPixels(
+ imageLeft,
+ imageTop,
+ imageWidth,
+ imageHeight,
+ ColorModel.getRGBdefault(),
+ (int [])toSend,
+ offset,
+ imageWidth
+ );
+ } else {
+ setPixels(
+ imageLeft,
+ imageTop,
+ imageWidth,
+ imageHeight,
+ null,
+ (byte [])toSend,
+ offset,
+ imageWidth
+ );
+ }
+ }
+ }
+
+ class GifColorTable {
+ // Indicates that reading of this block accomplished
+ boolean completed = false;
+
+ IndexColorModel cm = null;
+ int size = 0; // Actual number of colors in the color table
+ byte colors[] = new byte[256*3];
+
+ IndexColorModel getColorModel(int transparentColor) {
+ if (cm != null) {
+ if (transparentColor != cm.getTransparentPixel()) {
+ return cm = null; // Force default ARGB color model
+ }
+ return cm;
+ } else
+ if (completed && size > 0) {
+ if (transparentColor == IMPOSSIBLE_VALUE) {
+ return cm =
+ new IndexColorModel(8, size, colors, 0, false);
+ }
+
+ if (transparentColor > size) {
+ size = transparentColor + 1;
+ }
+ return cm =
+ new IndexColorModel(8, size, colors, 0, false, transparentColor);
+ }
+
+ return cm = null; // Force default ARGB color model
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/ImageDecoder.java b/awt/org/apache/harmony/awt/gl/image/ImageDecoder.java
new file mode 100644
index 0000000..d16128e
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/ImageDecoder.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+/*
+ * Created on 18.01.2005
+ */
+package org.apache.harmony.awt.gl.image;
+
+import com.android.internal.awt.AndroidImageDecoder;
+
+import java.awt.image.ColorModel;
+import java.awt.image.ImageConsumer;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ConcurrentModificationException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * This class contains common functionality for all image decoders.
+ */
+public abstract class ImageDecoder {
+
+ /** Image types */
+ public static final int GENERIC_DECODER = 0;
+ public static final int JPG_DECODER = 1;
+ public static final int GIF_DECODER = 2;
+ public static final int PNG_DECODER = 3;
+
+ private static final int MAX_BYTES_IN_SIGNATURE = 8;
+
+ protected List<ImageConsumer> consumers;
+ protected InputStream inputStream;
+ protected DecodingImageSource src;
+
+ protected boolean terminated;
+
+ /**
+ * Chooses appropriate image decoder by looking into input stream and checking
+ * the image signature.
+ * @param src - image producer, required for passing data to it from the
+ * created decoder via callbacks
+ * @param is - stream
+ * @return decoder
+ */
+ static ImageDecoder createDecoder(DecodingImageSource src, InputStream is) {
+ InputStream markable;
+
+ if (!is.markSupported()) {
+ // BEGIN android-modified
+ markable = new BufferedInputStream(is, 8192);
+ // END android-modified
+ } else {
+ markable = is;
+ }
+
+ // Read the signature from the stream and then reset it back
+ try {
+ markable.mark(MAX_BYTES_IN_SIGNATURE);
+
+ byte[] signature = new byte[MAX_BYTES_IN_SIGNATURE];
+ markable.read(signature, 0, MAX_BYTES_IN_SIGNATURE);
+ markable.reset();
+
+ if ((signature[0] & 0xFF) == 0xFF &&
+ (signature[1] & 0xFF) == 0xD8 &&
+ (signature[2] & 0xFF) == 0xFF) { // JPEG
+ return loadDecoder(PNG_DECODER, src, is);
+ } else if ((signature[0] & 0xFF) == 0x47 && // G
+ (signature[1] & 0xFF) == 0x49 && // I
+ (signature[2] & 0xFF) == 0x46) { // F
+ return loadDecoder(GIF_DECODER, src, is);
+ } else if ((signature[0] & 0xFF) == 137 && // PNG signature: 137 80 78 71 13 10 26 10
+ (signature[1] & 0xFF) == 80 &&
+ (signature[2] & 0xFF) == 78 &&
+ (signature[3] & 0xFF) == 71 &&
+ (signature[4] & 0xFF) == 13 &&
+ (signature[5] & 0xFF) == 10 &&
+ (signature[6] & 0xFF) == 26 &&
+ (signature[7] & 0xFF) == 10) {
+ return loadDecoder(JPG_DECODER, src, is);
+ }
+
+ return loadDecoder(GENERIC_DECODER, src, is);
+
+ } catch (IOException e) { // Silently
+ }
+
+ return null;
+ }
+
+ /*
+ * In the future, we might return different decoders for differen image types.
+ * But for now, we always return the generic one.
+ * Also: we could add a factory to load image decoder.
+ */
+ private static ImageDecoder loadDecoder(int type, DecodingImageSource src,
+ InputStream is) {
+ return new AndroidImageDecoder(src, is);
+ }
+
+ protected ImageDecoder(DecodingImageSource _src, InputStream is) {
+ src = _src;
+ consumers = src.consumers;
+ inputStream = is;
+ }
+
+ public abstract void decodeImage() throws IOException;
+
+ public synchronized void closeStream() {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Stops the decoding by interrupting the current decoding thread.
+ * Used when all consumers are removed and there's no more need to
+ * run the decoder.
+ */
+ public void terminate() {
+ src.lockDecoder(this);
+ closeStream();
+
+ AccessController.doPrivileged(
+ new PrivilegedAction<Void>() {
+ public Void run() {
+ Thread.currentThread().interrupt();
+ return null;
+ }
+ }
+ );
+
+ terminated = true;
+ }
+
+ protected void setDimensions(int w, int h) {
+ if (terminated) {
+ return;
+ }
+
+ for (ImageConsumer ic : consumers) {
+ ic.setDimensions(w, h);
+ }
+ }
+
+ protected void setProperties(Hashtable<?, ?> props) {
+ if (terminated) {
+ return;
+ }
+
+ for (ImageConsumer ic : consumers) {
+ ic.setProperties(props);
+ }
+ }
+
+ protected void setColorModel(ColorModel cm) {
+ if (terminated) {
+ return;
+ }
+
+ for (ImageConsumer ic : consumers) {
+ ic.setColorModel(cm);
+ }
+ }
+
+ protected void setHints(int hints) {
+ if (terminated) {
+ return;
+ }
+
+ for (ImageConsumer ic : consumers) {
+ ic.setHints(hints);
+ }
+ }
+
+ protected void setPixels(
+ int x, int y,
+ int w, int h,
+ ColorModel model,
+ byte pix[],
+ int off, int scansize
+ ) {
+ if (terminated) {
+ return;
+ }
+
+ src.lockDecoder(this);
+
+ for (ImageConsumer ic : consumers) {
+ ic.setPixels(x, y, w, h, model, pix, off, scansize);
+ }
+ }
+
+ protected void setPixels(
+ int x, int y,
+ int w, int h,
+ ColorModel model,
+ int pix[],
+ int off, int scansize
+ ) {
+ if (terminated) {
+ return;
+ }
+
+ src.lockDecoder(this);
+
+ for (ImageConsumer ic : consumers) {
+ ic.setPixels(x, y, w, h, model, pix, off, scansize);
+ }
+ }
+
+ protected void imageComplete(int status) {
+ if (terminated) {
+ return;
+ }
+
+ src.lockDecoder(this);
+
+ ImageConsumer ic = null;
+
+ for (Iterator<ImageConsumer> i = consumers.iterator(); i.hasNext();) {
+ try {
+ ic = i.next();
+ } catch (ConcurrentModificationException e) {
+ i = consumers.iterator();
+ continue;
+ }
+ ic.imageComplete(status);
+ }
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/ImageLoader.java b/awt/org/apache/harmony/awt/gl/image/ImageLoader.java
new file mode 100644
index 0000000..5c7d180
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/ImageLoader.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+/*
+ * Created on 18.01.2005
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This class provides functionality for simultaneous loading of
+ * several images and running animation.
+ */
+public class ImageLoader extends Thread {
+ // Contains ImageLoader objects
+ // and queue of image sources waiting to be loaded
+ static class ImageLoadersStorage {
+ private static final int MAX_THREADS = 5;
+ private static final int TIMEOUT = 4000;
+ static ImageLoadersStorage instance;
+
+ List<DecodingImageSource> queue = new LinkedList<DecodingImageSource>();
+ List<Thread> loaders = new ArrayList<Thread>(MAX_THREADS);
+
+ private int freeLoaders;
+
+ private ImageLoadersStorage() {}
+
+ static ImageLoadersStorage getStorage() {
+ if (instance == null) {
+ instance = new ImageLoadersStorage();
+ }
+
+ return instance;
+ }
+ }
+
+ ImageLoader() {
+ super();
+ setDaemon(true);
+ }
+
+ /**
+ * This method creates a new thread which is able to load an image
+ * or run animation (if the number of existing loader threads does not
+ * exceed the limit).
+ */
+ private static void createLoader() {
+ final ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
+
+ synchronized(storage.loaders) {
+ if (storage.loaders.size() < ImageLoadersStorage.MAX_THREADS) {
+ AccessController.doPrivileged(
+ new PrivilegedAction<Void>() {
+ public Void run() {
+ ImageLoader loader = new ImageLoader();
+ storage.loaders.add(loader);
+ loader.start();
+ return null;
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Adds a new image source to the queue and starts a new loader
+ * thread if required
+ * @param imgSrc - image source
+ */
+ public static void addImageSource(DecodingImageSource imgSrc) {
+ ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
+ synchronized(storage.queue) {
+ if (!storage.queue.contains(imgSrc)) {
+ storage.queue.add(imgSrc);
+ }
+ if (storage.freeLoaders == 0) {
+ createLoader();
+ }
+
+ storage.queue.notify();
+ }
+ }
+
+ /**
+ * Waits for a new ImageSource until timout expires.
+ * Loader thread will terminate after returning from this method
+ * if timeout expired and image source was not picked up from the queue.
+ * @return image source picked up from the queue or null if timeout expired
+ */
+ private static DecodingImageSource getWaitingImageSource() {
+ ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
+
+ synchronized(storage.queue) {
+ DecodingImageSource isrc = null;
+
+ if (storage.queue.size() == 0) {
+ try {
+ storage.freeLoaders++;
+ storage.queue.wait(ImageLoadersStorage.TIMEOUT);
+ } catch (InterruptedException e) {
+ return null;
+ } finally {
+ storage.freeLoaders--;
+ }
+ }
+
+ if (storage.queue.size() > 0) {
+ isrc = storage.queue.get(0);
+ storage.queue.remove(0);
+ }
+
+ return isrc;
+ }
+ }
+
+ /**
+ * Entry point of the loader thread. Picks up image sources and
+ * runs decoders for them while there are available image sources in the queue.
+ * If there are no and timeout expires it terminates.
+ */
+ @Override
+ public void run() {
+ ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
+
+ try {
+ while (storage.loaders.contains(this)) {
+ Thread.interrupted(); // Reset the interrupted flag
+ DecodingImageSource isrc = getWaitingImageSource();
+ if (isrc != null) {
+ try {
+ isrc.load();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ break; // Don't wait if timeout expired - terminate loader
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ synchronized(storage.loaders) {
+ storage.loaders.remove(Thread.currentThread());
+ }
+ }
+ }
+
+ /**
+ * Removes current thread from loaders (so we are able
+ * to create more loaders) and decreases its priority.
+ */
+ static void beginAnimation() {
+ ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
+ Thread currThread = Thread.currentThread();
+
+ synchronized(storage) {
+ storage.loaders.remove(currThread);
+
+ if (storage.freeLoaders < storage.queue.size()) {
+ createLoader();
+ }
+ }
+
+ currThread.setPriority(Thread.MIN_PRIORITY);
+ }
+
+ /**
+ * Sends the current thread to wait for the new images to load
+ * if there are free placeholders for loaders
+ */
+ static void endAnimation() {
+ ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
+ Thread currThread = Thread.currentThread();
+
+ synchronized(storage) {
+ if (storage.loaders.size() < ImageLoadersStorage.MAX_THREADS &&
+ !storage.loaders.contains(currThread)
+ ) {
+ storage.loaders.add(currThread);
+ }
+ }
+
+ currThread.setPriority(Thread.NORM_PRIORITY);
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/image/JpegDecoder.java b/awt/org/apache/harmony/awt/gl/image/JpegDecoder.java
new file mode 100644
index 0000000..2e64427
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/JpegDecoder.java
@@ -0,0 +1,231 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.awt.image.*;
+import java.awt.color.ColorSpace;
+import java.awt.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+public class JpegDecoder extends ImageDecoder {
+ // Only 2 output colorspaces expected. Others are converted into
+ // these ones.
+ // 1. Grayscale
+ public static final int JCS_GRAYSCALE = 1;
+ // 2. RGB
+ public static final int JCS_RGB = 2;
+
+ // Flags for the consumer, progressive JPEG
+ private static final int hintflagsProgressive =
+ ImageConsumer.SINGLEFRAME | // JPEG is a static image
+ ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible
+ ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines
+ // Flags for the consumer, singlepass JPEG
+ private static final int hintflagsSingle =
+ ImageConsumer.SINGLEPASS |
+ hintflagsProgressive;
+
+ // Buffer for the stream
+ private static final int BUFFER_SIZE = 1024;
+ private byte buffer[] = new byte[BUFFER_SIZE];
+
+ // 3 possible color models only
+ private static ColorModel cmRGB;
+ private static ColorModel cmGray;
+
+ // initializes proper field IDs
+ private static native void initIDs();
+
+ // Pointer to native structure which store decoding state
+ // between subsequent decoding/IO-suspension cycles
+ private long hNativeDecoder = 0; // NULL initially
+
+ private boolean headerDone = false;
+
+ // Next 4 members are filled by the native method (decompress).
+ // We can simply check if imageWidth is still negative to find
+ // out if they are already filled.
+ private int imageWidth = -1;
+ private int imageHeight = -1;
+ private boolean progressive = false;
+ private int jpegColorSpace = 0;
+
+ // Stores number of bytes consumed by the native decoder
+ private int bytesConsumed = 0;
+ // Stores current scanline returned by the decoder
+ private int currScanline = 0;
+
+ private ColorModel cm = null;
+
+ static {
+ System.loadLibrary("jpegdecoder"); //$NON-NLS-1$
+
+ cmGray = new ComponentColorModel(
+ ColorSpace.getInstance(ColorSpace.CS_GRAY),
+ false, false,
+ Transparency.OPAQUE, DataBuffer.TYPE_BYTE
+ );
+
+ // Create RGB color model
+ cmRGB = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
+
+ initIDs();
+ }
+
+ public JpegDecoder(DecodingImageSource src, InputStream is) {
+ super(src, is);
+ }
+
+ /*
+ public JpegDecoder(InputStream iStream, ImageConsumer iConsumer) {
+ inputStream = iStream;
+ consumer = iConsumer;
+ }
+ */
+
+ /**
+ * @return - not NULL if call is successful
+ */
+ private native Object decode(
+ byte[] input,
+ int bytesInBuffer,
+ long hDecoder);
+
+ private static native void releaseNativeDecoder(long hDecoder);
+
+ @Override
+ public void decodeImage() throws IOException {
+ try {
+ int bytesRead = 0, dataLength = 0;
+ boolean eosReached = false;
+ int needBytes, offset, bytesInBuffer = 0;
+ byte byteOut[] = null;
+ int intOut[] = null;
+ // Read from the input stream
+ for (;;) {
+ needBytes = BUFFER_SIZE - bytesInBuffer;
+ offset = bytesInBuffer;
+
+ bytesRead = inputStream.read(buffer, offset, needBytes);
+
+ if (bytesRead < 0) {
+ bytesRead = 0;//break;
+ eosReached = true;
+ } // Don't break, maybe something left in buffer
+
+ // Keep track on how much bytes left in buffer
+ bytesInBuffer += bytesRead;
+
+ // Here we pass overall number of bytes left in the java buffer
+ // (bytesInBuffer) since jpeg decoder has its own buffer and consumes
+ // as many bytes as it can. If there are any unconsumed bytes
+ // it didn't add them to its buffer...
+ Object arr = decode(
+ buffer,
+ bytesInBuffer,
+ hNativeDecoder);
+
+ // Keep track on how much bytes left in buffer
+ bytesInBuffer -= bytesConsumed;
+
+ if (!headerDone && imageWidth != -1) {
+ returnHeader();
+ headerDone = true;
+ }
+
+ if (bytesConsumed < 0) {
+ break; // Error exit
+ }
+
+ if (arr instanceof byte[]) {
+ byteOut = (byte[]) arr;
+ dataLength = byteOut.length;
+ returnData(byteOut, currScanline);
+ } else if (arr instanceof int[]) {
+ intOut = (int[]) arr;
+ dataLength = intOut.length;
+ returnData(intOut, currScanline);
+ } else {
+ dataLength = 0;
+ }
+
+ if (hNativeDecoder == 0) {
+ break;
+ }
+
+ if (dataLength == 0 && eosReached) {
+ releaseNativeDecoder(hNativeDecoder);
+ break; // Probably image is truncated
+ }
+ }
+ imageComplete(ImageConsumer.STATICIMAGEDONE);
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ closeStream();
+ }
+ }
+
+ public void returnHeader() {
+ setDimensions(imageWidth, imageHeight);
+
+ switch (jpegColorSpace) {
+ case JCS_GRAYSCALE: cm = cmGray; break;
+ case JCS_RGB: cm = cmRGB; break;
+ default:
+ // awt.3D=Unknown colorspace
+ throw new IllegalArgumentException(Messages.getString("awt.3D")); //$NON-NLS-1$
+ }
+ setColorModel(cm);
+
+ setHints(progressive ? hintflagsProgressive : hintflagsSingle);
+
+ setProperties(new Hashtable<Object, Object>()); // Empty
+ }
+
+ // Send the data to the consumer
+ public void returnData(int data[], int currScanLine) {
+ // Send 1 or more scanlines to the consumer.
+ int numScanlines = data.length / imageWidth;
+ if (numScanlines > 0) {
+ setPixels(
+ 0, currScanLine - numScanlines,
+ imageWidth, numScanlines,
+ cm, data, 0, imageWidth
+ );
+ }
+ }
+
+ public void returnData(byte data[], int currScanLine) {
+ int numScanlines = data.length / imageWidth;
+ if (numScanlines > 0) {
+ setPixels(
+ 0, currScanLine - numScanlines,
+ imageWidth, numScanlines,
+ cm, data, 0, imageWidth
+ );
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/OffscreenImage.java b/awt/org/apache/harmony/awt/gl/image/OffscreenImage.java
new file mode 100644
index 0000000..3445f8e
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/OffscreenImage.java
@@ -0,0 +1,532 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+/*
+ * Created on 22.12.2004
+ *
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DirectColorModel;
+import java.awt.image.ImageConsumer;
+import java.awt.image.ImageObserver;
+import java.awt.image.ImageProducer;
+import java.awt.image.IndexColorModel;
+import java.awt.image.WritableRaster;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.harmony.awt.gl.ImageSurface;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+
+/**
+ * This class represent implementation of abstract Image class
+ */
+public class OffscreenImage extends Image implements ImageConsumer {
+
+ static final ColorModel rgbCM = ColorModel.getRGBdefault();
+ ImageProducer src;
+ BufferedImage image;
+ ColorModel cm;
+ WritableRaster raster;
+ boolean isIntRGB;
+ Hashtable<?, ?> properties;
+ Vector<ImageObserver> observers;
+ int width;
+ int height;
+ int imageState;
+ int hints;
+ private boolean producing;
+ private ImageSurface imageSurf;
+
+ public OffscreenImage(ImageProducer ip){
+ imageState = 0;
+ src = ip;
+ width = -1;
+ height = -1;
+ observers = new Vector<ImageObserver>();
+ producing = false;
+ }
+
+ @Override
+ public Object getProperty(String name, ImageObserver observer) {
+ if(name == null) {
+ // awt.38=Property name is not defined
+ throw new NullPointerException(Messages.getString("awt.38")); //$NON-NLS-1$
+ }
+ if(properties == null){
+ addObserver(observer);
+ startProduction();
+ if(properties == null) {
+ return null;
+ }
+ }
+ Object prop = properties.get(name);
+ if(prop == null) {
+ prop = UndefinedProperty;
+ }
+ return prop;
+ }
+
+ @Override
+ public ImageProducer getSource() {
+ return src;
+ }
+
+ @Override
+ public int getWidth(ImageObserver observer) {
+ if((imageState & ImageObserver.WIDTH) == 0){
+ addObserver(observer);
+ startProduction();
+ if((imageState & ImageObserver.WIDTH) == 0) {
+ return -1;
+ }
+ }
+ return width;
+ }
+
+ @Override
+ public int getHeight(ImageObserver observer) {
+ if((imageState & ImageObserver.HEIGHT) == 0){
+ addObserver(observer);
+ startProduction();
+ if((imageState & ImageObserver.HEIGHT) == 0) {
+ return -1;
+ }
+ }
+ return height;
+ }
+
+ @Override
+ public Graphics getGraphics() {
+ // awt.39=This method is not implemented for image obtained from ImageProducer
+ throw new UnsupportedOperationException(Messages.getString("awt.39")); //$NON-NLS-1$
+ }
+
+ @Override
+ public void flush() {
+ stopProduction();
+ imageUpdate(this, ImageObserver.ABORT, -1, -1, -1, -1);
+ imageState &= ~ImageObserver.ERROR;
+ imageState = 0;
+ image = null;
+ cm = null;
+ raster = null;
+ hints = 0;
+ width = -1;
+ height = -1;
+ }
+
+ public void setProperties(Hashtable<?, ?> properties) {
+ this.properties = properties;
+ imageUpdate(this, ImageObserver.PROPERTIES, 0, 0, width, height);
+ }
+
+ public void setColorModel(ColorModel cm) {
+ this.cm = cm;
+ }
+
+ /*
+ * We suppose what in case loading JPEG image then image has DirectColorModel
+ * and for infill image Raster will use setPixels method with int array.
+ *
+ * In case loading GIF image, for raster infill, is used setPixels method with
+ * byte array and Color Model is IndexColorModel. But Color Model may
+ * be changed during this process. Then is called setPixels method with
+ * int array and image force to default color model - int ARGB. The rest
+ * pixels are sending in DirectColorModel.
+ */
+ public void setPixels(int x, int y, int w, int h, ColorModel model,
+ int[] pixels, int off, int scansize) {
+ if(raster == null){
+ if(cm == null){
+ if(model == null) {
+ // awt.3A=Color Model is null
+ throw new NullPointerException(Messages.getString("awt.3A")); //$NON-NLS-1$
+ }
+ cm = model;
+ }
+ createRaster();
+ }
+
+ if(model == null) {
+ model = cm;
+ }
+ if(cm != model){
+ forceToIntARGB();
+ }
+
+ if(cm == model && model.getTransferType() == DataBuffer.TYPE_INT &&
+ raster.getNumDataElements() == 1){
+
+ DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
+ int data[] = dbi.getData();
+ int scanline = raster.getWidth();
+ int rof = dbi.getOffset() + y * scanline + x;
+ for(int lineOff = off, line = y; line < y + h;
+ line++, lineOff += scansize, rof += scanline){
+
+ System.arraycopy(pixels, lineOff, data, rof, w);
+ }
+
+ }else if(isIntRGB){
+ int buff[] = new int[w];
+ DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
+ int data[] = dbi.getData();
+ int scanline = raster.getWidth();
+ int rof = dbi.getOffset() + y * scanline + x;
+ for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize,
+ rof += scanline) {
+
+ for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
+ buff[idx] = model.getRGB(pixels[sOff + idx]);
+ }
+ System.arraycopy(buff, 0, data, rof, w);
+ }
+ }else{
+ Object buf = null;
+ for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
+ for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
+ int rgb = model.getRGB(pixels[sOff + idx]);
+ buf = cm.getDataElements(rgb, buf);
+ raster.setDataElements(sx, sy, buf);
+ }
+ }
+ }
+
+ if (imageSurf != null) {
+ imageSurf.invalidate();
+ }
+
+ imageUpdate(this, ImageObserver.SOMEBITS, 0, 0, width, height);
+ }
+
+ public void setPixels(int x, int y, int w, int h, ColorModel model,
+ byte[] pixels, int off, int scansize) {
+
+ if(raster == null){
+ if(cm == null){
+ if(model == null) {
+ // awt.3A=Color Model is null
+ throw new NullPointerException(Messages.getString("awt.3A")); //$NON-NLS-1$
+ }
+ cm = model;
+ }
+ createRaster();
+ }
+ if(model == null) {
+ model = cm;
+ }
+ if(model != cm){
+ forceToIntARGB();
+ }
+
+ if(isIntRGB){
+ int buff[] = new int[w];
+ IndexColorModel icm = (IndexColorModel) model;
+ int colorMap[] = new int[icm.getMapSize()];
+ icm.getRGBs(colorMap);
+ DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
+ int data[] = dbi.getData();
+ int scanline = raster.getWidth();
+ int rof = dbi.getOffset() + y * scanline + x;
+ if(model instanceof IndexColorModel){
+
+ for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize,
+ rof += scanline) {
+ for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
+ buff[idx] = colorMap[pixels[sOff + idx] & 0xff];
+ }
+ System.arraycopy(buff, 0, data, rof, w);
+ }
+ }else{
+
+ for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize,
+ rof += scanline) {
+ for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
+ buff[idx] = model.getRGB(pixels[sOff + idx] & 0xff);
+ }
+ System.arraycopy(buff, 0, data, rof, w);
+ }
+ }
+ }else if(model == cm && model.getTransferType() == DataBuffer.TYPE_BYTE &&
+ raster.getNumDataElements() == 1){
+
+ DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer();
+ byte data[] = dbb.getData();
+ int scanline = raster.getWidth();
+ int rof = dbb.getOffset() + y * scanline + x;
+ for(int lineOff = off, line = y; line < y + h;
+ line++, lineOff += scansize, rof += scanline){
+ System.arraycopy(pixels, lineOff, data, rof, w);
+ }
+ // BEGIN android-added (taken from newer Harmony)
+ }else if(model == cm && model.getTransferType() == DataBuffer.TYPE_BYTE &&
+ cm instanceof ComponentColorModel){
+
+ int nc = cm.getNumComponents();
+ byte stride[] = new byte[scansize];
+ for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
+ System.arraycopy(pixels, sOff, stride, 0, scansize);
+
+ raster.setDataElements(x, sy, w, 1, stride);
+ }
+ // END android-added
+ }else {
+ for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) {
+ for (int sx = x, idx = 0; sx < x + w; sx++, idx++) {
+ int rgb = model.getRGB(pixels[sOff + idx] & 0xff);
+ raster.setDataElements(sx, sy, cm.getDataElements(rgb, null));
+ }
+ }
+ }
+
+ if (imageSurf != null) {
+ imageSurf.invalidate();
+ }
+
+ imageUpdate(this, ImageObserver.SOMEBITS, 0, 0, width, height);
+ }
+
+ public void setDimensions(int width, int height) {
+ if(width <= 0 || height <= 0){
+ imageComplete(ImageObserver.ERROR);
+ return;
+ }
+
+ this.width = width;
+ this.height = height;
+ imageUpdate(this, (ImageObserver.HEIGHT | ImageObserver.WIDTH),
+ 0, 0, width, height);
+ }
+
+ public void setHints(int hints) {
+ this.hints = hints;
+ }
+
+ public void imageComplete(int state) {
+ int flag;
+ switch(state){
+ case IMAGEABORTED:
+ flag = ImageObserver.ABORT;
+ break;
+ case IMAGEERROR:
+ flag = ImageObserver.ERROR | ImageObserver.ABORT;
+ break;
+ case SINGLEFRAMEDONE:
+ flag = ImageObserver.FRAMEBITS;
+ break;
+ case STATICIMAGEDONE:
+ flag = ImageObserver.ALLBITS;
+ break;
+ default:
+ // awt.3B=Incorrect ImageConsumer completion status
+ throw new IllegalArgumentException(Messages.getString("awt.3B")); //$NON-NLS-1$
+ }
+ imageUpdate(this, flag, 0, 0, width, height);
+
+ if((flag & (ImageObserver.ERROR | ImageObserver.ABORT |
+ ImageObserver.ALLBITS)) != 0 ) {
+ stopProduction();
+ observers.removeAllElements();
+ }
+ }
+
+ public /*synchronized*/ BufferedImage getBufferedImage(){
+ if(image == null){
+ ColorModel model = getColorModel();
+ WritableRaster wr = getRaster();
+ if(model != null && wr != null) {
+ image = new BufferedImage(model, wr, model.isAlphaPremultiplied(), null);
+ }
+ }
+ return image;
+ }
+
+ public /*synchronized*/ int checkImage(ImageObserver observer){
+ addObserver(observer);
+ return imageState;
+ }
+
+ public /*synchronized*/ boolean prepareImage(ImageObserver observer){
+ if((imageState & ImageObserver.ERROR) != 0){
+ if(observer != null){
+ observer.imageUpdate(this, ImageObserver.ERROR |
+ ImageObserver.ABORT, -1, -1, -1, -1);
+ }
+ return false;
+ }
+ if((imageState & ImageObserver.ALLBITS) != 0) {
+ return true;
+ }
+ addObserver(observer);
+ startProduction();
+ return ((imageState & ImageObserver.ALLBITS) != 0);
+ }
+
+ public /*synchronized*/ ColorModel getColorModel(){
+ if(cm == null) {
+ startProduction();
+ }
+ return cm;
+ }
+
+ public /*synchronized*/ WritableRaster getRaster(){
+ if(raster == null) {
+ startProduction();
+ }
+ return raster;
+ }
+
+ public int getState(){
+ return imageState;
+ }
+
+ private /*synchronized*/ void addObserver(ImageObserver observer){
+ if(observer != null){
+ if(observers.contains(observer)) {
+ return;
+ }
+ if((imageState & ImageObserver.ERROR) != 0){
+ observer.imageUpdate(this, ImageObserver.ERROR |
+ ImageObserver.ABORT, -1, -1, -1, -1);
+ return;
+ }
+ if((imageState & ImageObserver.ALLBITS) != 0){
+ observer.imageUpdate(this, imageState, 0, 0, width, height);
+ return;
+ }
+ observers.addElement(observer);
+ }
+ }
+
+ private synchronized void startProduction(){
+ if(!producing){
+ imageState &= ~ImageObserver.ABORT;
+ producing = true;
+ src.startProduction(this);
+ }
+ }
+
+ private synchronized void stopProduction(){
+ producing = false;
+ src.removeConsumer(this);
+ }
+
+ private void createRaster(){
+ try{
+ raster = cm.createCompatibleWritableRaster(width, height);
+ isIntRGB = false;
+ if(cm instanceof DirectColorModel){
+ DirectColorModel dcm = (DirectColorModel) cm;
+ if(dcm.getTransferType() == DataBuffer.TYPE_INT &&
+ dcm.getRedMask() == 0xff0000 &&
+ dcm.getGreenMask() == 0xff00 &&
+ dcm.getBlueMask() == 0xff){
+ isIntRGB = true;
+ }
+ }
+ }catch(Exception e){
+ cm = ColorModel.getRGBdefault();
+ raster = cm.createCompatibleWritableRaster(width, height);
+ isIntRGB = true;
+ }
+ }
+
+ private /*synchronized*/ void imageUpdate(Image img, int infoflags, int x, int y,
+ int width, int height){
+
+ imageState |= infoflags;
+ for (ImageObserver observer : observers) {
+ observer.imageUpdate(this, infoflags, x, y, width, height);
+ }
+
+// notifyAll();
+ }
+
+ private void forceToIntARGB(){
+
+ int w = raster.getWidth();
+ int h = raster.getHeight();
+
+ WritableRaster destRaster = rgbCM.createCompatibleWritableRaster(w, h);
+
+ Object obj = null;
+ int pixels[] = new int[w];
+
+ if(cm instanceof IndexColorModel){
+ IndexColorModel icm = (IndexColorModel) cm;
+ int colorMap[] = new int[icm.getMapSize()];
+ icm.getRGBs(colorMap);
+
+ for (int y = 0; y < h; y++) {
+ obj = raster.getDataElements(0, y, w, 1, obj);
+ byte ba[] = (byte[]) obj;
+ for (int x = 0; x < ba.length; x++) {
+ pixels[x] = colorMap[ba[x] & 0xff];
+ }
+ destRaster.setDataElements(0, y, w, 1, pixels);
+ }
+
+ }else{
+ for(int y = 0; y < h; y++){
+ for(int x = 0; x < w; x++){
+ obj = raster.getDataElements(x, y, obj);
+ pixels[x] = cm.getRGB(obj);
+ }
+ destRaster.setDataElements(0, y, w, 1, pixels);
+ }
+ }
+
+ synchronized(this){
+ if(imageSurf != null){
+ imageSurf.dispose();
+ imageSurf = null;
+ }
+ if(image != null){
+ image.flush();
+ image = null;
+ }
+ cm = rgbCM;
+ raster = destRaster;
+ isIntRGB = true;
+ }
+ }
+
+ public ImageSurface getImageSurface() {
+ if (imageSurf == null) {
+ ColorModel model = getColorModel();
+ WritableRaster wr = getRaster();
+ if(model != null && wr != null) {
+ imageSurf = new ImageSurface(model, wr);
+ }
+ }
+ return imageSurf;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java b/awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java
new file mode 100644
index 0000000..1748e1b
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+/*
+ * Created on 30.09.2004
+ *
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+
+public class OrdinaryWritableRaster extends WritableRaster {
+
+ public OrdinaryWritableRaster(SampleModel sampleModel,
+ DataBuffer dataBuffer, Rectangle aRegion,
+ Point sampleModelTranslate, WritableRaster parent) {
+ super(sampleModel, dataBuffer, aRegion, sampleModelTranslate, parent);
+ }
+
+ public OrdinaryWritableRaster(SampleModel sampleModel,
+ DataBuffer dataBuffer, Point origin) {
+ super(sampleModel, dataBuffer, origin);
+ }
+
+ public OrdinaryWritableRaster(SampleModel sampleModel, Point origin) {
+ super(sampleModel, origin);
+ }
+
+ @Override
+ public void setDataElements(int x, int y, Object inData) {
+ super.setDataElements(x, y, inData);
+ }
+
+ @Override
+ public void setDataElements(int x, int y, int w, int h, Object inData) {
+ super.setDataElements(x, y, w, h, inData);
+ }
+
+ @Override
+ public WritableRaster createWritableChild(int parentX, int parentY, int w,
+ int h, int childMinX, int childMinY, int[] bandList) {
+ return super.createWritableChild(parentX, parentY, w, h, childMinX,
+ childMinY, bandList);
+ }
+
+ @Override
+ public WritableRaster createWritableTranslatedChild(int childMinX,
+ int childMinY) {
+ return super.createWritableTranslatedChild(childMinX, childMinY);
+ }
+
+ @Override
+ public WritableRaster getWritableParent() {
+ return super.getWritableParent();
+ }
+
+ @Override
+ public void setRect(Raster srcRaster) {
+ super.setRect(srcRaster);
+ }
+
+ @Override
+ public void setRect(int dx, int dy, Raster srcRaster) {
+ super.setRect(dx, dy, srcRaster);
+ }
+
+ @Override
+ public void setDataElements(int x, int y, Raster inRaster) {
+ super.setDataElements(x, y, inRaster);
+ }
+
+ @Override
+ public void setPixel(int x, int y, int[] iArray) {
+ super.setPixel(x, y, iArray);
+ }
+
+ @Override
+ public void setPixel(int x, int y, float[] fArray) {
+ super.setPixel(x, y, fArray);
+ }
+
+ @Override
+ public void setPixel(int x, int y, double[] dArray) {
+ super.setPixel(x, y, dArray);
+ }
+
+ @Override
+ public void setPixels(int x, int y, int w, int h, int[] iArray) {
+ super.setPixels(x, y, w, h, iArray);
+ }
+
+ @Override
+ public void setPixels(int x, int y, int w, int h, float[] fArray) {
+ super.setPixels(x, y, w, h, fArray);
+ }
+
+ @Override
+ public void setPixels(int x, int y, int w, int h, double[] dArray) {
+ super.setPixels(x, y, w, h, dArray);
+ }
+
+ @Override
+ public void setSamples(int x, int y, int w, int h, int b, int[] iArray) {
+ super.setSamples(x, y, w, h, b, iArray);
+ }
+
+ @Override
+ public void setSamples(int x, int y, int w, int h, int b, float[] fArray) {
+ super.setSamples(x, y, w, h, b, fArray);
+ }
+
+ @Override
+ public void setSamples(int x, int y, int w, int h, int b, double[] dArray) {
+ super.setSamples(x, y, w, h, b, dArray);
+ }
+
+ @Override
+ public void setSample(int x, int y, int b, int s) {
+ super.setSample(x, y, b, s);
+ }
+
+ @Override
+ public void setSample(int x, int y, int b, float s) {
+ super.setSample(x, y, b, s);
+ }
+
+ @Override
+ public void setSample(int x, int y, int b, double s) {
+ super.setSample(x, y, b, s);
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/image/PngDecoder.java b/awt/org/apache/harmony/awt/gl/image/PngDecoder.java
new file mode 100644
index 0000000..7e85600
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/PngDecoder.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Oleg V. Khaschansky
+ * @version $Revision$
+ *
+ * @date: Jul 22, 2005
+ */
+
+package org.apache.harmony.awt.gl.image;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+import java.awt.color.ColorSpace;
+import java.awt.image.*;
+import java.awt.*;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+public class PngDecoder extends ImageDecoder {
+ // initializes proper field IDs
+ private static native void initIDs();
+
+ static {
+ System.loadLibrary("gl"); //$NON-NLS-1$
+ initIDs();
+ }
+
+ private static final int hintflags =
+ ImageConsumer.SINGLEFRAME | // PNG is a static image
+ ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible
+ ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines
+
+ // Each pixel is a grayscale sample.
+ private static final int PNG_COLOR_TYPE_GRAY = 0;
+ // Each pixel is an R,G,B triple.
+ private static final int PNG_COLOR_TYPE_RGB = 2;
+ // Each pixel is a palette index, a PLTE chunk must appear.
+ private static final int PNG_COLOR_TYPE_PLTE = 3;
+ // Each pixel is a grayscale sample, followed by an alpha sample.
+ private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4;
+ // Each pixel is an R,G,B triple, followed by an alpha sample.
+ private static final int PNG_COLOR_TYPE_RGBA = 6;
+
+ private static final int INPUT_BUFFER_SIZE = 4096;
+ private byte buffer[] = new byte[INPUT_BUFFER_SIZE];
+
+ // Buffers for decoded image data
+ byte byteOut[];
+ int intOut[];
+
+ // Native pointer to png decoder data
+ private long hNativeDecoder;
+
+ int imageWidth, imageHeight;
+ int colorType;
+ int bitDepth;
+ byte cmap[];
+
+ boolean transferInts; // Is transfer type int?.. or byte?
+ int dataElementsPerPixel = 1;
+
+ ColorModel cm;
+
+ int updateFromScanline; // First scanline to update
+ int numScanlines; // Number of scanlines to update
+
+ private native long decode(byte[] input, int bytesInBuffer, long hDecoder);
+
+ private static native void releaseNativeDecoder(long hDecoder);
+
+ public PngDecoder(DecodingImageSource src, InputStream is) {
+ super(src, is);
+ }
+
+ @Override
+ public void decodeImage() throws IOException {
+ try {
+ int bytesRead = 0;
+ int needBytes, offset, bytesInBuffer = 0;
+ // Read from the input stream
+ for (;;) {
+ needBytes = INPUT_BUFFER_SIZE - bytesInBuffer;
+ offset = bytesInBuffer;
+
+ bytesRead = inputStream.read(buffer, offset, needBytes);
+
+ if (bytesRead < 0) { // Break, nothing to read from buffer, image truncated?
+ releaseNativeDecoder(hNativeDecoder);
+ break;
+ }
+
+ // Keep track on how much bytes left in buffer
+ bytesInBuffer += bytesRead;
+ hNativeDecoder = decode(buffer, bytesInBuffer, hNativeDecoder);
+ // PNG decoder always consumes all bytes at once
+ bytesInBuffer = 0;
+
+ // if (bytesConsumed < 0)
+ //break; // Error exit
+
+ returnData();
+
+ // OK, we decoded all the picture in the right way...
+ if (hNativeDecoder == 0) {
+ break;
+ }
+ }
+
+ imageComplete(ImageConsumer.STATICIMAGEDONE);
+ } catch (IOException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ imageComplete(ImageConsumer.IMAGEERROR);
+ throw e;
+ } finally {
+ closeStream();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private void returnHeader() { // Called from native code
+ setDimensions(imageWidth, imageHeight);
+
+ switch (colorType) {
+ case PNG_COLOR_TYPE_GRAY: {
+ if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) {
+ // awt.3C=Unknown PNG color type
+ throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
+ }
+
+ // Create gray color model
+ int numEntries = 1 << bitDepth;
+ int scaleFactor = 255 / (numEntries-1);
+ byte comps[] = new byte[numEntries];
+ for (int i = 0; i < numEntries; i++) {
+ comps[i] = (byte) (i * scaleFactor);
+ }
+ cm = new IndexColorModel(/*bitDepth*/8, numEntries, comps, comps, comps);
+
+ transferInts = false;
+ break;
+ }
+
+ case PNG_COLOR_TYPE_RGB: {
+ if (bitDepth != 8) {
+ // awt.3C=Unknown PNG color type
+ throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
+ }
+
+ cm = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
+
+ transferInts = true;
+ break;
+ }
+
+ case PNG_COLOR_TYPE_PLTE: {
+ if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) {
+ // awt.3C=Unknown PNG color type
+ throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
+ }
+
+ cm = new IndexColorModel(/*bitDepth*/8, cmap.length / 3, cmap, 0, false);
+
+ transferInts = false;
+ break;
+ }
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA: {
+ if (bitDepth != 8) {
+ // awt.3C=Unknown PNG color type
+ throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
+ }
+
+ cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
+ true, false,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+
+ transferInts = false;
+ dataElementsPerPixel = 2;
+ break;
+ }
+
+ case PNG_COLOR_TYPE_RGBA: {
+ if (bitDepth != 8) {
+ // awt.3C=Unknown PNG color type
+ throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
+ }
+
+ cm = ColorModel.getRGBdefault();
+
+ transferInts = true;
+ break;
+ }
+ default:
+ // awt.3C=Unknown PNG color type
+ throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
+ }
+
+ // Create output buffer
+ if (transferInts) {
+ intOut = new int[imageWidth * imageHeight];
+ } else {
+ byteOut = new byte[imageWidth * imageHeight * dataElementsPerPixel];
+ }
+
+ setColorModel(cm);
+
+ setHints(hintflags);
+ setProperties(new Hashtable<Object, Object>()); // Empty
+ }
+
+ // Send the data to the consumer
+ private void returnData() {
+ // Send 1 or more scanlines to the consumer.
+ if (numScanlines > 0) {
+ // Native decoder could have returned
+ // some data from the next pass, handle it here
+ int pass1, pass2;
+ if (updateFromScanline + numScanlines > imageHeight) {
+ pass1 = imageHeight - updateFromScanline;
+ pass2 = updateFromScanline + numScanlines - imageHeight;
+ } else {
+ pass1 = numScanlines;
+ pass2 = 0;
+ }
+
+ transfer(updateFromScanline, pass1);
+ if (pass2 != 0) {
+ transfer(0, pass2);
+ }
+ }
+ }
+
+ private void transfer(int updateFromScanline, int numScanlines) {
+ if (transferInts) {
+ setPixels(
+ 0, updateFromScanline,
+ imageWidth, numScanlines,
+ cm, intOut,
+ updateFromScanline * imageWidth,
+ imageWidth
+ );
+ } else {
+ setPixels(
+ 0, updateFromScanline,
+ imageWidth, numScanlines,
+ cm, byteOut,
+ updateFromScanline * imageWidth * dataElementsPerPixel,
+ imageWidth * dataElementsPerPixel
+ );
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java b/awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java
new file mode 100644
index 0000000..46545f9
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java
@@ -0,0 +1,282 @@
+package org.apache.harmony.awt.gl.image;
+
+// A simple PNG decoder source code in Java.
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.IndexColorModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.zip.CRC32;
+import java.util.zip.InflaterInputStream;
+
+//import javax.swing.JFrame;
+
+public class PngDecoderJava {
+
+/*
+ public static void main(String[] args) throws Exception {
+ String name = "logo.png";
+ if (args.length > 0)
+ name = args[0];
+ InputStream in = PngDecoderJava.class.getResourceAsStream(name);
+ final BufferedImage image = PngDecoderJava.decode(in);
+ in.close();
+
+ JFrame f = new JFrame() {
+ public void paint(Graphics g) {
+ Insets insets = getInsets();
+ g.drawImage(image, insets.left, insets.top, null);
+ }
+ };
+ f.setVisible(true);
+ Insets insets = f.getInsets();
+ f.setSize(image.getWidth() + insets.left + insets.right, image
+ .getHeight()
+ + insets.top + insets.bottom);
+ }
+ */
+
+ public static BufferedImage decode(InputStream in) throws IOException {
+ DataInputStream dataIn = new DataInputStream(in);
+ readSignature(dataIn);
+ PNGData chunks = readChunks(dataIn);
+
+ long widthLong = chunks.getWidth();
+ long heightLong = chunks.getHeight();
+ if (widthLong > Integer.MAX_VALUE || heightLong > Integer.MAX_VALUE)
+ throw new IOException("That image is too wide or tall.");
+ int width = (int) widthLong;
+ int height = (int) heightLong;
+
+ ColorModel cm = chunks.getColorModel();
+ WritableRaster raster = chunks.getRaster();
+
+ BufferedImage image = new BufferedImage(cm, raster, false, null);
+
+ return image;
+ }
+
+ protected static void readSignature(DataInputStream in) throws IOException {
+ long signature = in.readLong();
+ if (signature != 0x89504e470d0a1a0aL)
+ throw new IOException("PNG signature not found!");
+ }
+
+ protected static PNGData readChunks(DataInputStream in) throws IOException {
+ PNGData chunks = new PNGData();
+
+ boolean trucking = true;
+ while (trucking) {
+ try {
+ // Read the length.
+ int length = in.readInt();
+ if (length < 0)
+ throw new IOException("Sorry, that file is too long.");
+ // Read the type.
+ byte[] typeBytes = new byte[4];
+ in.readFully(typeBytes);
+ // Read the data.
+ byte[] data = new byte[length];
+ in.readFully(data);
+ // Read the CRC.
+ long crc = in.readInt() & 0x00000000ffffffffL; // Make it
+ // unsigned.
+ if (verifyCRC(typeBytes, data, crc) == false)
+ throw new IOException("That file appears to be corrupted.");
+
+ PNGChunk chunk = new PNGChunk(typeBytes, data);
+ chunks.add(chunk);
+ } catch (EOFException eofe) {
+ trucking = false;
+ }
+ }
+ return chunks;
+ }
+
+ protected static boolean verifyCRC(byte[] typeBytes, byte[] data, long crc) {
+ CRC32 crc32 = new CRC32();
+ crc32.update(typeBytes);
+ crc32.update(data);
+ long calculated = crc32.getValue();
+ return (calculated == crc);
+ }
+}
+
+class PNGData {
+ private int mNumberOfChunks;
+
+ private PNGChunk[] mChunks;
+
+ public PNGData() {
+ mNumberOfChunks = 0;
+ mChunks = new PNGChunk[10];
+ }
+
+ public void add(PNGChunk chunk) {
+ mChunks[mNumberOfChunks++] = chunk;
+ if (mNumberOfChunks >= mChunks.length) {
+ PNGChunk[] largerArray = new PNGChunk[mChunks.length + 10];
+ System.arraycopy(mChunks, 0, largerArray, 0, mChunks.length);
+ mChunks = largerArray;
+ }
+ }
+
+ public long getWidth() {
+ return getChunk("IHDR").getUnsignedInt(0);
+ }
+
+ public long getHeight() { return getChunk("IHDR").getUnsignedInt(4);
+ }
+
+ public short getBitsPerPixel() {
+ return getChunk("IHDR").getUnsignedByte(8);
+ }
+
+ public short getColorType() {
+ return getChunk("IHDR").getUnsignedByte(9);
+ }
+
+ public short getCompression() {
+ return getChunk("IHDR").getUnsignedByte(10);
+ }
+
+ public short getFilter() {
+ return getChunk("IHDR").getUnsignedByte(11);
+ }
+
+ public short getInterlace() {
+ return getChunk("IHDR").getUnsignedByte(12);
+ }
+
+ public ColorModel getColorModel() {
+ short colorType = getColorType();
+ int bitsPerPixel = getBitsPerPixel();
+
+ if (colorType == 3) {
+ byte[] paletteData = getChunk("PLTE").getData();
+ int paletteLength = paletteData.length / 3;
+ return new IndexColorModel(bitsPerPixel, paletteLength,
+ paletteData, 0, false);
+ }
+ System.out.println("Unsupported color type: " + colorType);
+ return null;
+ }
+
+ public WritableRaster getRaster() {
+ int width = (int) getWidth();
+ int height = (int) getHeight();
+ int bitsPerPixel = getBitsPerPixel();
+ short colorType = getColorType();
+
+ if (colorType == 3) {
+ byte[] imageData = getImageData();
+ //Orig: DataBuffer db = new DataBufferByte(imageData, imageData.length);
+ int len = Math.max(imageData.length, (width - 1) * (height -1));
+ DataBuffer db = new DataBufferByte(imageData, len);
+ WritableRaster raster = Raster.createPackedRaster(db, width,
+ height, bitsPerPixel, null);
+ return raster;
+ } else
+ System.out.println("Unsupported color type!");
+ return null;
+ }
+
+ public byte[] getImageData() {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ // Write all the IDAT data into the array.
+ for (int i = 0; i < mNumberOfChunks; i++) {
+ PNGChunk chunk = mChunks[i];
+ if (chunk.getTypeString().equals("IDAT")) {
+ out.write(chunk.getData());
+ }
+ }
+ out.flush();
+ // Now deflate the data.
+ InflaterInputStream in = new InflaterInputStream(
+ new ByteArrayInputStream(out.toByteArray()));
+ ByteArrayOutputStream inflatedOut = new ByteArrayOutputStream();
+ int readLength;
+ byte[] block = new byte[8192];
+ while ((readLength = in.read(block)) != -1)
+ inflatedOut.write(block, 0, readLength);
+ inflatedOut.flush();
+ byte[] imageData = inflatedOut.toByteArray();
+ // Compute the real length.
+ int width = (int) getWidth();
+ int height = (int) getHeight();
+ int bitsPerPixel = getBitsPerPixel();
+ int length = width * height * bitsPerPixel / 8;
+
+ byte[] prunedData = new byte[length];
+
+ // We can only deal with non-interlaced images.
+ if (getInterlace() == 0) {
+ int index = 0;
+ for (int i = 0; i < length; i++) {
+ if ((i * 8 / bitsPerPixel) % width == 0) {
+ index++; // Skip the filter byte.
+ }
+ prunedData[i] = imageData[index++];
+ }
+ } else
+ System.out.println("Couldn't undo interlacing.");
+
+ return prunedData;
+ } catch (IOException ioe) {
+ }
+ return null;
+ }
+
+ public PNGChunk getChunk(String type) {
+ for (int i = 0; i < mNumberOfChunks; i++)
+ if (mChunks[i].getTypeString().equals(type))
+ return mChunks[i];
+ return null;
+ }
+}
+
+class PNGChunk {
+ private byte[] mType;
+
+ private byte[] mData;
+
+ public PNGChunk(byte[] type, byte[] data) {
+ mType = type;
+ mData = data;
+ }
+
+ public String getTypeString() {
+ try {
+ return new String(mType, "UTF8");
+ } catch (UnsupportedEncodingException uee) {
+ return "";
+ }
+ }
+
+ public byte[] getData() {
+ return mData;
+ }
+
+ public long getUnsignedInt(int offset) {
+ long value = 0;
+ for (int i = 0; i < 4; i++)
+ value += (mData[offset + i] & 0xff) << ((3 - i) * 8);
+ return value;
+ }
+
+ public short getUnsignedByte(int offset) {
+ return (short) (mData[offset] & 0x00ff);
+ }
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java
new file mode 100644
index 0000000..a1899d6
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ */
+/*
+ * Created on 10.02.2005
+ *
+ */
+package org.apache.harmony.awt.gl.image;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+
+public class URLDecodingImageSource extends DecodingImageSource {
+
+ URL url;
+
+ public URLDecodingImageSource(URL url){
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkConnect(url.getHost(), url.getPort());
+ try {
+ Permission p = url.openConnection().getPermission();
+ security.checkPermission(p);
+ } catch (IOException e) {
+ }
+ }
+ this.url = url;
+ }
+
+ @Override
+ protected boolean checkConnection() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ try {
+ security.checkConnect(url.getHost(), url.getPort());
+ return true;
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ protected InputStream getInputStream() {
+ try{
+ URLConnection uc = url.openConnection();
+ // BEGIN android-modified
+ return new BufferedInputStream(uc.getInputStream(), 8192);
+ // END android-modified
+ }catch(IOException e){
+ return null;
+ }
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/render/Blitter.java b/awt/org/apache/harmony/awt/gl/render/Blitter.java
new file mode 100644
index 0000000..3b8012e
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/Blitter.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 14.11.2005
+ *
+ */
+package org.apache.harmony.awt.gl.render;
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.geom.AffineTransform;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+import org.apache.harmony.awt.gl.Surface;
+
+/**
+ * The interface for objects which can drawing Images on other Images which have
+ * Graphics or on the display.
+ */
+public interface Blitter {
+
+ public abstract void blit(int srcX, int srcY, Surface srcSurf,
+ int dstX, int dstY, Surface dstSurf, int width, int height,
+ AffineTransform sysxform, AffineTransform xform,
+ Composite comp, Color bgcolor,
+ MultiRectArea clip);
+
+ public abstract void blit(int srcX, int srcY, Surface srcSurf,
+ int dstX, int dstY, Surface dstSurf, int width, int height,
+ AffineTransform sysxform, Composite comp, Color bgcolor,
+ MultiRectArea clip);
+
+ public abstract void blit(int srcX, int srcY, Surface srcSurf,
+ int dstX, int dstY, Surface dstSurf, int width, int height,
+ Composite comp, Color bgcolor, MultiRectArea clip);
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java b/awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java
new file mode 100644
index 0000000..b643b41
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java
@@ -0,0 +1,502 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.render;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+
+public class JavaArcRasterizer {
+
+ /**
+ * Adds particular arc segment to mra
+ */
+ static void addX0LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int x1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x2 = line[i];
+ int y = cy + (b - i);
+ if (x1 <= finish && x2 >= start) {
+ mra.addRect(cx + Math.max(x1, start), y, cx + Math.min(x2, finish), y);
+ }
+ x1 = x2 + 1;
+ }
+ }
+
+ static void addX1LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int x1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x2 = line[i];
+ int y = cy - (b - i);
+ if (x1 <= finish && x2 >= start) {
+ mra.addRect(cx + Math.max(x1, start), y, cx + Math.min(x2, finish), y);
+ }
+ x1 = x2 + 1;
+ }
+ }
+
+ static void addX2LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int x1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x2 = line[i];
+ int y = cy - (b - i);
+ if (x1 <= finish && x2 >= start) {
+ mra.addRect(cx - Math.min(x2, finish), y, cx - Math.max(x1, start), y);
+ }
+ x1 = x2 + 1;
+ }
+ }
+
+ static void addX3LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int x1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x2 = line[i];
+ int y = cy + (b - i);
+ if (x1 <= finish && x2 >= start) {
+ mra.addRect(cx - Math.min(x2, finish), y, cx - Math.max(x1, start), y);
+ }
+ x1 = x2 + 1;
+ }
+ }
+
+ static void addY0LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int y1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x = cx + (b - i);
+ int y2 = line[i];
+ if (y1 <= finish && y2 >= start) {
+ mra.addRect(x, cy + Math.max(y1, start), x, cy + Math.min(y2, finish));
+ }
+ y1 = y2 + 1;
+ }
+ }
+
+ static void addY1LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int y1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x = cx - (b - i);
+ int y2 = line[i];
+ if (y1 <= finish && y2 >= start) {
+ mra.addRect(x, cy + Math.max(y1, start), x, cy + Math.min(y2, finish));
+ }
+ y1 = y2 + 1;
+ }
+ }
+
+ static void addY2LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int y1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x = cx - (b - i);
+ int y2 = line[i];
+ if (y1 <= finish && y2 >= start) {
+ mra.addRect(x, cy - Math.min(y2, finish), x, cy - Math.max(y1, start));
+ }
+ y1 = y2 + 1;
+ }
+ }
+
+ static void addY3LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
+ int y1 = 0;
+ for(int i = 0; i < line.length; i++) {
+ int x = cx + (b - i);
+ int y2 = line[i];
+ if (y1 <= finish && y2 >= start) {
+ mra.addRect(x, cy - Math.min(y2, finish), x, cy - Math.max(y1, start));
+ }
+ y1 = y2 + 1;
+ }
+ }
+
+ static void addX0Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx + prev, cy + (b - i), cx + line[i], cy + (b - i));
+ prev = line[i] + 1;
+ }
+ }
+
+ static void addX1Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx + prev, cy - (b - i), cx + line[i], cy - (b - i));
+ prev = line[i] + 1;
+ }
+ }
+
+ static void addX2Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx - line[i], cy - (b - i), cx - prev, cy - (b - i));
+ prev = line[i] + 1;
+ }
+ }
+
+ static void addX3Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx - line[i], cy + (b - i), cx - prev, cy + (b - i));
+ prev = line[i] + 1;
+ }
+ }
+
+ static void addY0Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx + (a - i), cy + prev, cx + (a - i), cy + line[i]);
+ prev = line[i] + 1;
+ }
+ }
+
+ static void addY1Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx - (a - i), cy + prev, cx - (a - i), cy + line[i]);
+ prev = line[i] + 1;
+ }
+ }
+
+ static void addY2Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx - (a - i), cy - line[i], cx - (a - i), cy - prev);
+ prev = line[i] + 1;
+ }
+ }
+
+ static void addY3Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
+ int prev = 0;
+ for(int i = 0; i < line.length; i++) {
+ mra.addRect(cx + (a - i), cy - line[i], cx + (a - i), cy - prev);
+ prev = line[i] + 1;
+ }
+ }
+
+ /**
+ * Returns normalized angle (from 0 to 360 degrees)
+ */
+ static double getNormAngle(double angle) {
+ angle -= Math.floor(angle / 360) * 360;
+ if (angle < 0) {
+ angle += 360;
+ }
+ return angle;
+ }
+
+ /**
+ * Creates arc lookup table
+ */
+ static int[] createLine(int a, int b, int xcount, int ycount) {
+ int[] buf = new int[b - ycount + 1];
+ int d = a * a + 2 * b * b - 2 * a * a * b;
+ int x = 0;
+ int y = b;
+ while (y >= ycount) {
+ if (d < 0) {
+ d = d + b * b * (4 * x + 6);
+ } else {
+ buf[b - y] = x;
+ d = d + b * b * (4 * x + 6) + 4 * a * a * (1 - y);
+ y--;
+ }
+ x++;
+ }
+ return buf;
+ }
+
+ /**
+ * Adds head/tail arc segment to MultiRectArea
+ */
+ static void addSeg(MultiRectArea mra, int cx1, int cy1, int cx2, int cy2, int a, int b, int[] xline, int[] yline, int[] bounds) {
+ switch(bounds[0]) {
+ case 0:
+ addY3LineSeg(mra, yline, cx2, cy1, a, bounds[1], bounds[2]);
+ break;
+ case 1:
+ addX1LineSeg(mra, xline, cx2, cy1, b, bounds[1], bounds[2]);
+ break;
+ case 2:
+ addX2LineSeg(mra, xline, cx1, cy1, b, bounds[1], bounds[2]);
+ break;
+ case 3:
+ addY2LineSeg(mra, yline, cx1, cy1, a, bounds[1], bounds[2]);
+ break;
+ case 4:
+ addY1LineSeg(mra, yline, cx1, cy2, a, bounds[1], bounds[2]);
+ break;
+ case 5:
+ addX3LineSeg(mra, xline, cx1, cy2, b, bounds[1], bounds[2]);
+ break;
+ case 6:
+ addX0LineSeg(mra, xline, cx2, cy2, b, bounds[1], bounds[2]);
+ break;
+ case 7:
+ addY0LineSeg(mra, yline, cx2, cy2, a, bounds[1], bounds[2]);
+ break;
+ }
+ }
+
+ /**
+ * Returns bounds for non quadratic arc head
+ */
+ static int[] getSegment1(double angle, int ax, int ay, int xcount, int ycount) {
+ int[] bounds = new int[3];
+ switch((int)(angle / 90)) {
+ case 0:
+ if (xcount < ax) {
+ bounds[0] = 0; // Y3
+ bounds[1] = -ay;
+ bounds[2] = ycount;
+ } else {
+ bounds[0] = 1; // X1
+ bounds[1] = 0;
+ bounds[2] = ax;
+ }
+ break;
+ case 1:
+ if (xcount > -ax) {
+ bounds[0] = 2; // X2
+ bounds[1] = -ax;
+ bounds[2] = xcount;
+ } else {
+ bounds[0] = 3; // Y2
+ bounds[1] = 0;
+ bounds[2] = -ay;
+ }
+ break;
+ case 2:
+ if (xcount < -ax) {
+ bounds[0] = 4; // Y1
+ bounds[1] = ay;
+ bounds[2] = ycount;
+ } else {
+ bounds[0] = 5; // X3
+ bounds[1] = 0;
+ bounds[2] = -ax;
+ }
+ break;
+ case 3:
+ if (xcount > ax) {
+ bounds[0] = 6; // X0
+ bounds[1] = ax;
+ bounds[2] = xcount;
+ } else {
+ bounds[0] = 7; // Y0
+ bounds[1] = 0;
+ bounds[2] = ay;
+ }
+ break;
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns bounds for non quadratic arc tail
+ */
+ static int[] getSegment2(double angle, int ax, int ay, int xcount, int ycount) {
+ int[] bounds = new int[3];
+ switch((int)(angle / 90)) {
+ case 0:
+ if (xcount < ax) {
+ bounds[0] = 0; // Y3
+ bounds[1] = 0;
+ bounds[2] = -ay;
+ } else {
+ bounds[0] = 1; // X1
+ bounds[1] = ax;
+ bounds[2] = xcount;
+ }
+ break;
+ case 1:
+ if (xcount > -ax) {
+ bounds[0] = 2; // X2
+ bounds[1] = 0;
+ bounds[2] = -ax;
+ } else {
+ bounds[0] = 3; // Y2
+ bounds[1] = -ay;
+ bounds[2] = ycount;
+ }
+ break;
+ case 2:
+ if (xcount < -ax) {
+ bounds[0] = 4; // Y1
+ bounds[1] = 0;
+ bounds[2] = ay;
+ } else {
+ bounds[0] = 5; // X3
+ bounds[1] = -ax;
+ bounds[2] = xcount;
+ }
+ break;
+ case 3:
+ if (xcount > ax) {
+ bounds[0] = 6; // X0
+ bounds[1] = 0;
+ bounds[2] = ax;
+ } else {
+ bounds[0] = 7; // Y0
+ bounds[1] = ay;
+ bounds[2] = ycount;
+ }
+ break;
+ }
+ return bounds;
+ }
+
+ /**
+ * Rasterizes arc using clippind and dashing style
+ * @param x1 - the x coordinate of the left-upper corner of the arc bounds
+ * @param y1 - the y coordinate of the left-upper corner of the arc bounds
+ * @param width - the width of the arc bounds
+ * @param height - the height of the arc bounds
+ * @param angleStart - the start angle of the arc in degrees
+ * @param angleExtent - the angle extent in degrees
+ * @param clip - the MultiRectArea object of clipping area
+ * @return a MultiRectArea of rasterizer arc
+ */
+ public static MultiRectArea rasterize(int x, int y, int width, int height, double angleStart, double angleExtent, MultiRectArea clip) {
+
+ MultiRectArea mra = new MultiRectArea(false);
+
+ int cx1, cx2, cy1, cy2;
+ cx1 = cx2 = x + width / 2;
+ cy1 = cy2 = y + height / 2;
+
+ if (width % 2 == 0) {
+ cx2--;
+ }
+
+ if (height % 2 == 0) {
+ cy2--;
+ }
+
+ int a = width / 2;
+ int b = height / 2;
+ double c = Math.sqrt(a * a + b * b);
+
+ int xcount, ycount;
+ if (a < b) {
+ xcount = (int)Math.ceil(a * a / c);
+ ycount = (int)Math.floor(b * b / c);
+ } else {
+ xcount = (int)Math.floor(a * a / c);
+ ycount = (int)Math.ceil(b * b / c);
+ }
+
+ int[] xline = createLine(a, b, xcount, ycount);
+ int[] yline = createLine(b, a, ycount, xcount);
+
+ // Correct lines
+ int i = xline.length;
+ while(xline[--i] > xcount) {
+ xline[i] = xcount;
+ }
+
+ i = yline.length;
+ while(yline[--i] > ycount) {
+ yline[i] = ycount;
+ }
+
+ if (Math.abs(angleExtent) >= 360) {
+ // Rasterize CIRCLE
+ addX0Line(mra, xline, cx2, cy2, b);
+ addX1Line(mra, xline, cx2, cy1, b);
+ addX2Line(mra, xline, cx1, cy1, b);
+ addX3Line(mra, xline, cx1, cy2, b);
+ addY0Line(mra, yline, cx2, cy2, a);
+ addY1Line(mra, yline, cx1, cy2, a);
+ addY2Line(mra, yline, cx1, cy1, a);
+ addY3Line(mra, yline, cx2, cy1, a);
+ } else {
+ // Rasterize ARC
+ angleStart = getNormAngle(angleStart);
+ double angleFinish = getNormAngle(angleStart + angleExtent);
+
+ if (angleExtent < 0) {
+ double tmp = angleStart;
+ angleStart = angleFinish;
+ angleFinish = tmp;
+ }
+
+ double radStart = -Math.toRadians(angleStart);
+ double radFinish = -Math.toRadians(angleFinish);
+ int ax1 = (int)(a * Math.cos(radStart));
+ int ay1 = (int)(b * Math.sin(radStart));
+ int ax2 = (int)(a * Math.cos(radFinish));
+ int ay2 = (int)(b * Math.sin(radFinish));
+
+ int[] seg1 = getSegment1(angleStart, ax1, ay1, xcount, ycount);
+ int[] seg2 = getSegment2(angleFinish, ax2, ay2, xcount, ycount);
+
+ // Start and Finish located in the same quater
+ if (angleStart < angleFinish && seg1[0] == seg2[0]) {
+ if (seg1[0] % 2 == 0) {
+ seg1[2] = seg2[2];
+ } else {
+ seg1[1] = seg2[1];
+ }
+ addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg1);
+ return mra;
+ }
+
+ addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg1);
+ addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg2);
+
+ int startSeg = (seg1[0] + 1) % 8;
+ int finishSeg = seg2[0];
+
+ while (startSeg != finishSeg) {
+ switch(startSeg) {
+ case 0:
+ addY3Line(mra, yline, cx2, cy1, a);
+ break;
+ case 1:
+ addX1Line(mra, xline, cx2, cy1, b);
+ break;
+ case 2:
+ addX2Line(mra, xline, cx1, cy1, b);
+ break;
+ case 3:
+ addY2Line(mra, yline, cx1, cy1, a);
+ break;
+ case 4:
+ addY1Line(mra, yline, cx1, cy2, a);
+ break;
+ case 5:
+ addX3Line(mra, xline, cx1, cy2, b);
+ break;
+ case 6:
+ addX0Line(mra, xline, cx2, cy2, b);
+ break;
+ case 7:
+ addY0Line(mra, yline, cx2, cy2, a);
+ break;
+ }
+ startSeg = (startSeg + 1) % 8;
+ }
+ }
+
+ if (clip != null) {
+ mra.intersect(clip);
+ }
+
+ return mra;
+ }
+
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/render/JavaBlitter.java b/awt/org/apache/harmony/awt/gl/render/JavaBlitter.java
new file mode 100644
index 0000000..67e0a59
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/JavaBlitter.java
@@ -0,0 +1,611 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 18.11.2005
+ *
+ */
+package org.apache.harmony.awt.gl.render;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.CompositeContext;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+import org.apache.harmony.awt.gl.Surface;
+import org.apache.harmony.awt.gl.XORComposite;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * Java implenetation of the Blitter interface. Using when we can't
+ * draw images natively.
+ */
+public class JavaBlitter implements Blitter {
+
+ /**
+ * Instead of multiplication and division we are using values from
+ * Lookup tables.
+ */
+ static byte mulLUT[][]; // Lookup table for multiplication
+ static byte divLUT[][]; // Lookup table for division
+
+ static{
+ mulLUT = new byte[256][256];
+ for(int i = 0; i < 256; i++){
+ for(int j = 0; j < 256; j++){
+ mulLUT[i][j] = (byte)((float)(i * j)/255 + 0.5f);
+ }
+ }
+ divLUT = new byte[256][256];
+ for(int i = 1; i < 256; i++){
+ for(int j = 0; j < i; j++){
+ divLUT[i][j] = (byte)(((float)j / 255) / ((float)i/ 255) * 255 + 0.5f);
+ }
+ for(int j = i; j < 256; j++){
+ divLUT[i][j] = (byte)255;
+ }
+ }
+ }
+
+ final static int AlphaCompositeMode = 1;
+ final static int XORMode = 2;
+
+ final static JavaBlitter inst = new JavaBlitter();
+
+ public static JavaBlitter getInstance(){
+ return inst;
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, AffineTransform sysxform,
+ AffineTransform xform, Composite comp, Color bgcolor,
+ MultiRectArea clip) {
+
+ if(xform == null){
+ blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height,
+ sysxform, comp, bgcolor, clip);
+ }else{
+ double scaleX = xform.getScaleX();
+ double scaleY = xform.getScaleY();
+ double scaledX = dstX / scaleX;
+ double scaledY = dstY / scaleY;
+ AffineTransform at = new AffineTransform();
+ at.setToTranslation(scaledX, scaledY);
+ xform.concatenate(at);
+ sysxform.concatenate(xform);
+ blit(srcX, srcY, srcSurf, 0, 0, dstSurf, width, height,
+ sysxform, comp, bgcolor, clip);
+ }
+
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, AffineTransform sysxform,
+ Composite comp, Color bgcolor, MultiRectArea clip) {
+
+ if(sysxform == null) {
+ sysxform = new AffineTransform();
+ }
+ int type = sysxform.getType();
+ switch(type){
+ case AffineTransform.TYPE_TRANSLATION:
+ dstX += sysxform.getTranslateX();
+ dstY += sysxform.getTranslateY();
+ case AffineTransform.TYPE_IDENTITY:
+ blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf,
+ width, height, comp, bgcolor, clip);
+ break;
+ default:
+ int srcW = srcSurf.getWidth();
+ int srcH = srcSurf.getHeight();
+
+ int w = srcX + width < srcW ? width : srcW - srcX;
+ int h = srcY + height < srcH ? height : srcH - srcY;
+
+ ColorModel srcCM = srcSurf.getColorModel();
+ Raster srcR = srcSurf.getRaster().createChild(srcX, srcY,
+ w, h, 0, 0, null);
+
+ ColorModel dstCM = dstSurf.getColorModel();
+ WritableRaster dstR = dstSurf.getRaster();
+
+ transformedBlit(srcCM, srcR, 0, 0, dstCM, dstR, dstX, dstY, w, h,
+ sysxform, comp, bgcolor, clip);
+
+ }
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, Composite comp,
+ Color bgcolor, MultiRectArea clip) {
+
+ javaBlt(srcX, srcY, srcSurf.getWidth(), srcSurf.getHeight(),
+ srcSurf.getColorModel(), srcSurf.getRaster(), dstX, dstY,
+ dstSurf.getWidth(), dstSurf.getHeight(),
+ dstSurf.getColorModel(), dstSurf.getRaster(),
+ width, height, comp, bgcolor, clip);
+
+ }
+ public void javaBlt(int srcX, int srcY, int srcW, int srcH,
+ ColorModel srcCM, Raster srcRast, int dstX, int dstY,
+ int dstW, int dstH, ColorModel dstCM, WritableRaster dstRast,
+ int width, int height, Composite comp, Color bgcolor,
+ MultiRectArea clip){
+
+ int srcX2 = srcW - 1;
+ int srcY2 = srcH - 1;
+ int dstX2 = dstW - 1;
+ int dstY2 = dstH - 1;
+
+ if(srcX < 0){
+ width += srcX;
+ srcX = 0;
+ }
+ if(srcY < 0){
+ height += srcY;
+ srcY = 0;
+ }
+
+ if(dstX < 0){
+ width += dstX;
+ srcX -= dstX;
+ dstX = 0;
+ }
+ if(dstY < 0){
+ height += dstY;
+ srcY -= dstY;
+ dstY = 0;
+ }
+
+ if(srcX > srcX2 || srcY > srcY2) {
+ return;
+ }
+ if(dstX > dstX2 || dstY > dstY2) {
+ return;
+ }
+
+ if(srcX + width > srcX2) {
+ width = srcX2 - srcX + 1;
+ }
+ if(srcY + height > srcY2) {
+ height = srcY2 - srcY + 1;
+ }
+ if(dstX + width > dstX2) {
+ width = dstX2 - dstX + 1;
+ }
+ if(dstY + height > dstY2) {
+ height = dstY2 - dstY + 1;
+ }
+
+ if(width <= 0 || height <= 0) {
+ return;
+ }
+
+ int clipRects[];
+ if(clip != null) {
+ clipRects = clip.rect;
+ } else {
+ clipRects = new int[]{5, 0, 0, dstW - 1, dstH - 1};
+ }
+
+ boolean isAlphaComp = false;
+ int rule = 0;
+ float alpha = 0;
+ boolean isXORComp = false;
+ Color xorcolor = null;
+ CompositeContext cont = null;
+
+ if(comp instanceof AlphaComposite){
+ isAlphaComp = true;
+ AlphaComposite ac = (AlphaComposite) comp;
+ rule = ac.getRule();
+ alpha = ac.getAlpha();
+ }else if(comp instanceof XORComposite){
+ isXORComp = true;
+ XORComposite xcomp = (XORComposite) comp;
+ xorcolor = xcomp.getXORColor();
+ }else{
+ cont = comp.createContext(srcCM, dstCM, null);
+ }
+
+ for(int i = 1; i < clipRects[0]; i += 4){
+ int _sx = srcX;
+ int _sy = srcY;
+
+ int _dx = dstX;
+ int _dy = dstY;
+
+ int _w = width;
+ int _h = height;
+
+ int cx = clipRects[i]; // Clipping left top X
+ int cy = clipRects[i + 1]; // Clipping left top Y
+ int cx2 = clipRects[i + 2]; // Clipping right bottom X
+ int cy2 = clipRects[i + 3]; // Clipping right bottom Y
+
+ if(_dx > cx2 || _dy > cy2 || dstX2 < cx || dstY2 < cy) {
+ continue;
+ }
+
+ if(cx > _dx){
+ int shx = cx - _dx;
+ _w -= shx;
+ _dx = cx;
+ _sx += shx;
+ }
+
+ if(cy > _dy){
+ int shy = cy - _dy;
+ _h -= shy;
+ _dy = cy;
+ _sy += shy;
+ }
+
+ if(_dx + _w > cx2 + 1){
+ _w = cx2 - _dx + 1;
+ }
+
+ if(_dy + _h > cy2 + 1){
+ _h = cy2 - _dy + 1;
+ }
+
+ if(_sx > srcX2 || _sy > srcY2) {
+ continue;
+ }
+
+ if(isAlphaComp){
+ alphaCompose(_sx, _sy, srcCM, srcRast, _dx, _dy,
+ dstCM, dstRast, _w, _h, rule, alpha, bgcolor);
+ }else if(isXORComp){
+ xorCompose(_sx, _sy, srcCM, srcRast, _dx, _dy,
+ dstCM, dstRast, _w, _h, xorcolor);
+ }else{
+ Raster sr = srcRast.createChild(_sx, _sy, _w, _h, 0, 0, null);
+ WritableRaster dr = dstRast.createWritableChild(_dx, _dy,
+ _w, _h, 0, 0, null);
+ cont.compose(sr, dr, dr);
+ }
+ }
+ }
+
+ void alphaCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast,
+ int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast,
+ int width, int height, int rule, float alpha, Color bgcolor){
+
+ Object srcPixel, dstPixel;
+ int srcConstAllpha = (int)(alpha * 255 + 0.5f);
+ int srcRGB, dstRGB = 0;
+
+ if(bgcolor != null){
+ dstRGB = bgcolor.getRGB();
+ }
+
+ for(int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++){
+ for(int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++){
+ srcPixel = srcRast.getDataElements(sx, sy, null);
+ srcRGB = srcCM.getRGB(srcPixel);
+ if(bgcolor == null){
+ dstPixel = dstRast.getDataElements(dx, dy, null);
+ dstRGB = dstCM.getRGB(dstPixel);
+ }
+
+ dstRGB = compose(srcRGB, srcCM.isAlphaPremultiplied(),
+ dstRGB, dstCM.hasAlpha(), dstCM.isAlphaPremultiplied(),
+ rule, srcConstAllpha);
+
+ dstPixel = dstCM.getDataElements(dstRGB, null);
+ dstRast.setDataElements(dx,dy,dstPixel);
+ }
+ }
+ }
+
+ void xorCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast,
+ int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast,
+ int width, int height, Color xorcolor){
+
+ Object srcPixel, dstPixel;
+ int xorRGB = xorcolor.getRGB();
+ int srcRGB, dstRGB;
+
+ for(int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++){
+ for(int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++){
+ srcPixel = srcRast.getDataElements(sx, sy, null);
+ dstPixel = dstRast.getDataElements(dx, dy, null);
+
+ srcRGB = srcCM.getRGB(srcPixel);
+ dstRGB = dstCM.getRGB(dstPixel);
+ dstRGB = srcRGB ^ xorRGB ^ dstRGB;
+
+ dstRGB = 0xff000000 | dstRGB;
+ dstPixel = dstCM.getDataElements(dstRGB, dstPixel);
+ dstRast.setDataElements(dx,dy,dstPixel);
+
+ }
+ }
+
+ }
+
+ private void transformedBlit(ColorModel srcCM, Raster srcR, int srcX, int srcY,
+ ColorModel dstCM, WritableRaster dstR, int dstX, int dstY,
+ int width, int height, AffineTransform at, Composite comp,
+ Color bgcolor,MultiRectArea clip) {
+
+ Rectangle srcBounds = new Rectangle(width, height);
+ Rectangle dstBlitBounds = new Rectangle(dstX, dstY, srcR.getWidth(), srcR.getHeight());
+
+ Rectangle transSrcBounds = getBounds2D(at, srcBounds).getBounds();
+ Rectangle transDstBlitBounds = getBounds2D(at, dstBlitBounds).getBounds();
+
+ int translateX = transDstBlitBounds.x - transSrcBounds.x;
+ int translateY = transDstBlitBounds.y - transSrcBounds.y;
+
+ AffineTransform inv = null;
+ try {
+ inv = at.createInverse();
+ } catch (NoninvertibleTransformException e) {
+ return;
+ }
+
+ double[] m = new double[6];
+ inv.getMatrix(m);
+
+ int clipRects[];
+ if(clip != null) {
+ clipRects = clip.rect;
+ } else {
+ clipRects = new int[]{5, 0, 0, dstR.getWidth(), dstR.getHeight()};
+ }
+
+ int compType = 0;
+ int srcConstAlpha = 0;
+ int rule = 0;
+ int bgRGB = bgcolor == null ? 0 : bgcolor.getRGB();
+ int srcRGB = 0, dstRGB = 0;
+ Object srcVal = null, dstVal = null;
+ if(comp instanceof AlphaComposite){
+ compType = AlphaCompositeMode;
+ AlphaComposite ac = (AlphaComposite) comp;
+ rule = ac.getRule();
+ srcConstAlpha = (int)(ac.getAlpha() * 255 + 0.5f);
+ }else if(comp instanceof XORComposite){
+ compType = XORMode;
+ XORComposite xor = (XORComposite) comp;
+ bgRGB = xor.getXORColor().getRGB();
+ }
+
+ for(int i = 1; i < clipRects[0]; i += 4){
+ Rectangle dstBounds = new Rectangle(clipRects[i], clipRects[i + 1], 0, 0);
+ dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 1]);
+ dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 3] + 1);
+ dstBounds.add(clipRects[i], clipRects[i + 3] + 1);
+
+ Rectangle bounds = dstBounds.intersection(transDstBlitBounds);
+
+ int minSrcX = srcBounds.x;
+ int minSrcY = srcBounds.y;
+ int maxSrcX = minSrcX + srcBounds.width;
+ int maxSrcY = minSrcY + srcBounds.height;
+
+ int minX = bounds.x;
+ int minY = bounds.y;
+ int maxX = minX + bounds.width;
+ int maxY = minY + bounds.height;
+
+ int hx = (int)((m[0] * 256) + 0.5);
+ int hy = (int)((m[1] * 256) + 0.5);
+ int vx = (int)((m[2] * 256) + 0.5);
+ int vy = (int)((m[3] * 256) + 0.5);
+ int sx = (int)((m[4] + m[0] * (bounds.x - translateX) + m[2] * (bounds.y - translateY)) * 256 + 0.5);
+ int sy = (int)((m[5] + m[1] * (bounds.x - translateX) + m[3] * (bounds.y - translateY)) * 256 + 0.5);
+
+ vx -= hx * bounds.width;
+ vy -= hy * bounds.width;
+
+ for(int y = minY; y < maxY; y++) {
+ for(int x = minX; x < maxX; x++) {
+ int px = sx >> 8;
+ int py = sy >> 8;
+ if (px >= minSrcX && py >= minSrcY && px < maxSrcX && py < maxSrcY) {
+ switch(compType){
+ case AlphaCompositeMode:
+ srcVal = srcR.getDataElements(px , py , null);
+ srcRGB = srcCM.getRGB(srcVal);
+ if(bgcolor != null){
+ dstRGB = bgRGB;
+ }else{
+ dstVal = dstR.getDataElements(x, y, null);
+ dstRGB = dstCM.getRGB(dstVal);
+ }
+ dstRGB = compose(srcRGB, srcCM.isAlphaPremultiplied(),
+ dstRGB, dstCM.hasAlpha(), dstCM.isAlphaPremultiplied(),
+ rule, srcConstAlpha);
+ dstVal = dstCM.getDataElements(dstRGB, null);
+ dstR.setDataElements(x, y, dstVal);
+ break;
+
+ case XORMode:
+ srcVal = srcR.getDataElements(px , py , null);
+ srcRGB = srcCM.getRGB(srcVal);
+ dstVal = dstR.getDataElements(x, y, null);
+ dstRGB = dstCM.getRGB(dstVal);
+ dstRGB = srcRGB ^ bgRGB;
+
+ dstRGB = 0xff000000 | dstRGB;
+ dstVal = dstCM.getDataElements(dstRGB, null);
+ dstR.setDataElements(x, y, dstVal);
+ break;
+
+ default:
+ // awt.37=Unknown composite type {0}
+ throw new IllegalArgumentException(Messages.getString("awt.37", //$NON-NLS-1$
+ comp.getClass()));
+ }
+ }
+ sx += hx;
+ sy += hy;
+ }
+ sx += vx;
+ sy += vy;
+ }
+ }
+
+ }
+
+ private Rectangle2D getBounds2D(AffineTransform at, Rectangle r) {
+ int x = r.x;
+ int y = r.y;
+ int width = r.width;
+ int height = r.height;
+
+ float[] corners = {
+ x, y,
+ x + width, y,
+ x + width, y + height,
+ x, y + height
+ };
+
+ at.transform(corners, 0, corners, 0, 4);
+
+ Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0], corners[1], 0 , 0);
+ bounds.add(corners[2], corners[3]);
+ bounds.add(corners[4], corners[5]);
+ bounds.add(corners[6], corners[7]);
+
+ return bounds;
+ }
+
+ private int compose(int srcRGB, boolean isSrcAlphaPre,
+ int dstRGB, boolean dstHasAlpha, boolean isDstAlphaPre,
+ int rule, int srcConstAlpha){
+
+ int sa, sr, sg, sb, da, dr, dg, db;
+
+ sa = (srcRGB >> 24) & 0xff;
+ sr = (srcRGB >> 16) & 0xff;
+ sg = (srcRGB >> 8) & 0xff;
+ sb = srcRGB & 0xff;
+
+ if(isSrcAlphaPre){
+ sa = mulLUT[srcConstAlpha][sa] & 0xff;
+ sr = mulLUT[srcConstAlpha][sr] & 0xff;
+ sg = mulLUT[srcConstAlpha][sg] & 0xff;
+ sb = mulLUT[srcConstAlpha][sb] & 0xff;
+ }else{
+ sa = mulLUT[srcConstAlpha][sa] & 0xff;
+ sr = mulLUT[sa][sr] & 0xff;
+ sg = mulLUT[sa][sg] & 0xff;
+ sb = mulLUT[sa][sb] & 0xff;
+ }
+
+ da = (dstRGB >> 24) & 0xff;
+ dr = (dstRGB >> 16) & 0xff;
+ dg = (dstRGB >> 8) & 0xff;
+ db = dstRGB & 0xff;
+
+ if(!isDstAlphaPre){
+ dr = mulLUT[da][dr] & 0xff;
+ dg = mulLUT[da][dg] & 0xff;
+ db = mulLUT[da][db] & 0xff;
+ }
+
+ int Fs = 0;
+ int Fd = 0;
+ switch(rule){
+ case AlphaComposite.CLEAR:
+ break;
+
+ case AlphaComposite.DST:
+ Fd = 255;
+ break;
+
+ case AlphaComposite.DST_ATOP:
+ Fs = 255 - da;
+ Fd = sa;
+ break;
+
+ case AlphaComposite.DST_IN:
+ Fd = sa;
+ break;
+
+ case AlphaComposite.DST_OUT:
+ Fd = 255 - sa;
+ break;
+
+ case AlphaComposite.DST_OVER:
+ Fs = 255 - da;
+ Fd = 255;
+ break;
+
+ case AlphaComposite.SRC:
+ Fs = 255;
+ break;
+
+ case AlphaComposite.SRC_ATOP:
+ Fs = da;
+ Fd = 255 - sa;
+ break;
+
+ case AlphaComposite.SRC_IN:
+ Fs = da;
+ break;
+
+ case AlphaComposite.SRC_OUT:
+ Fs = 255 - da;
+ break;
+
+ case AlphaComposite.SRC_OVER:
+ Fs = 255;
+ Fd = 255 - sa;
+ break;
+
+ case AlphaComposite.XOR:
+ Fs = 255 - da;
+ Fd = 255 - sa;
+ break;
+ }
+ dr = (mulLUT[sr][Fs] & 0xff) + (mulLUT[dr][Fd] & 0xff);
+ dg = (mulLUT[sg][Fs] & 0xff) + (mulLUT[dg][Fd] & 0xff);
+ db = (mulLUT[sb][Fs] & 0xff) + (mulLUT[db][Fd] & 0xff);
+
+ da = (mulLUT[sa][Fs] & 0xff) + (mulLUT[da][Fd] & 0xff);
+
+ if(!isDstAlphaPre){
+ if(da != 255){
+ dr = divLUT[da][dr] & 0xff;
+ dg = divLUT[da][dg] & 0xff;
+ db = divLUT[da][db] & 0xff;
+ }
+ }
+ if(!dstHasAlpha) {
+ da = 0xff;
+ }
+ dstRGB = (da << 24) | (dr << 16) | (dg << 8) | db;
+
+ return dstRGB;
+
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java b/awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java
new file mode 100644
index 0000000..eb6f7b5
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java
@@ -0,0 +1,760 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.render;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+
+
+public class JavaLineRasterizer {
+
+ /**
+ * LineDasher class provides dashing for particular dash style
+ */
+ public static class LineDasher {
+
+ int index;
+ float pos;
+ float phase;
+ float dash[];
+ float inv[];
+ boolean visible;
+
+ public LineDasher() {
+ }
+
+ public LineDasher(float dash[], float phase) {
+ this.dash = dash;
+ this.phase = phase;
+
+ inv = new float[dash.length];
+ int j = dash.length;
+ for (float element : dash) {
+ inv[--j] = element;
+ }
+ index = 0;
+ while (phase > dash[index]) {
+ phase -= dash[index];
+ index = (index + 1) % dash.length;
+ }
+ visible = index % 2 == 0;
+ }
+
+ void move(float step) { // main dasher
+ pos += step;
+ step += phase;
+ while(step >= dash[index]) {
+ step -= dash[index];
+ index = (index + 1) % dash.length;
+ visible = !visible;
+ }
+ phase = step;
+ }
+
+ float nextDash() {
+ phase = 0.0f;
+ index = (index + 1) % dash.length;
+ visible = !visible;
+ return dash[index];
+ }
+
+ LineDasher createDiagonal(double k, float length, boolean invert) {
+ LineDasher local = new LineDasher();
+ local.dash = new float[dash.length];
+ if (invert) { // inverted dasher
+ move(length);
+ local.phase = (float)((dash[index] - phase) * k);
+ local.visible = visible;
+ local.index = inv.length - index - 1;
+ for(int i = 0; i < inv.length; i++) {
+ local.dash[i] = (float)(inv[i] * k);
+ }
+ } else {
+ local.phase = (float)(phase * k);
+ local.visible = visible;
+ local.index = index;
+ for(int i = 0; i < dash.length; i++) {
+ local.dash[i] = (float)(dash[i] * k);
+ }
+ move(length);
+ }
+ return local;
+ }
+
+ LineDasher createOrtogonal(float length, boolean invert) {
+ LineDasher local = new LineDasher();
+ local.dash = new float[dash.length];
+ if (invert) { // inverted dasher
+ move(length);
+ local.phase = dash[index] - phase;
+ local.visible = visible;
+ local.index = inv.length - index - 1;
+ local.dash = inv;
+ } else {
+ local.phase = phase;
+ local.visible = visible;
+ local.index = index;
+ local.dash = dash;
+ move(length);
+ }
+ return local;
+ }
+
+ LineDasher createChild(float start) {
+ LineDasher child = new LineDasher();
+ child.phase = phase;
+ child.visible = visible;
+ child.index = index;
+ child.dash = dash;
+ child.move(start);
+ return child;
+ }
+
+ }
+
+ /**
+ * Line class provides rasterization for different line types
+ */
+ abstract static class Line {
+
+ int x1, y1, x2, y2;
+ int x, y;
+ MultiRectArea dst;
+
+ Line(int x1, int y1, int x2, int y2, MultiRectArea dst) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ this.dst = dst;
+ }
+
+ static abstract class Diag extends Line {
+ int dx, dy, adx, ady, sx, sy;
+ int eBase, ePos, eNeg;
+ int xcount;
+ int e;
+
+ Diag(int x1, int y1, int x2, int y2, MultiRectArea dst) {
+ super(x1, y1, x2, y2, dst);
+ dx = x2 - x1;
+ dy = y2 - y1;
+ sy = 1;
+ if (dx > 0) {
+ adx = dx;
+ sx = 1;
+ } else {
+ adx = -dx;
+ sx = -1;
+ }
+ ady = dy;
+ }
+
+ float getLength() {
+ return (float)Math.sqrt(dx * dx + dy * dy);
+ }
+
+ static class Hor extends Diag {
+
+ Hor(int x1, int y1, int x2, int y2, MultiRectArea dst) {
+ super(x1, y1, x2, y2, dst);
+ eBase = ady + ady - adx;
+ ePos = 2 * (ady - adx);
+ eNeg = ady + ady;
+ xcount = adx;
+ }
+
+ @Override
+ void rasterize() {
+ e = eBase;
+ x = x1;
+ y = y1;
+ rasterize(xcount);
+ }
+
+ @Override
+ void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) {
+ e = eBase + 2 * (ady * Math.abs(nx1 - x1) - adx * Math.abs(ny1 - y1));
+ x = nx1;
+ y = ny1;
+ rasterize(dx > 0 ? nx2 - nx1 : nx1 - nx2);
+ }
+
+ @Override
+ void rasterize(int count) {
+ int px = x;
+ while (count-- > 0) {
+ if (e >= 0) {
+ if (sx > 0) {
+ dst.addRect(px, y, x, y);
+ } else {
+ dst.addRect(x, y, px, y);
+ }
+ x += sx;
+ y += sy;
+ e += ePos;
+ px = x;
+ } else {
+ e += eNeg;
+ x += sx;
+ }
+ }
+ if (sx > 0) {
+ dst.addRect(px, y, x, y);
+ } else {
+ dst.addRect(x, y, px, y);
+ }
+ }
+
+ @Override
+ void skip(int count) {
+ while (count-- > 0) {
+ x += sx;
+ if (e >= 0) {
+ y += sy;
+ e += ePos;
+ } else {
+ e += eNeg;
+ }
+ }
+ }
+
+ }
+
+ static class Ver extends Diag {
+
+ Ver(int x1, int y1, int x2, int y2, MultiRectArea dst) {
+ super(x1, y1, x2, y2, dst);
+ eBase = adx + adx - ady;
+ ePos = 2 * (adx - ady);
+ eNeg = adx + adx;
+ xcount = ady;
+ }
+
+ @Override
+ void rasterize() {
+ e = eBase;
+ x = x1;
+ y = y1;
+ rasterize(xcount);
+ }
+
+ @Override
+ void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) {
+ e = eBase + 2 * (adx * Math.abs(ny1 - y1) - ady * Math.abs(nx1 - x1));
+ x = nx1;
+ y = ny1;
+ rasterize(ny2 - ny1);
+ }
+
+ @Override
+ void rasterize(int count) {
+ int py = y;
+ while (count-- > 0) {
+ if (e >= 0) {
+ dst.addRect(x, py, x, y);
+ x += sx;
+ y += sy;
+ e += ePos;
+ py = y;
+ } else {
+ y += sy;
+ e += eNeg;
+ }
+ }
+ dst.addRect(x, py, x, y);
+ }
+
+ @Override
+ void skip(int count) {
+ while (count-- > 0) {
+ y += sy;
+ if (e >= 0) {
+ x += sx;
+ e += ePos;
+ } else {
+ e += eNeg;
+ }
+ }
+ }
+
+ }
+
+ static class HorDashed extends Hor {
+
+ LineDasher local;
+
+ HorDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) {
+ super(x1, y1, x2, y2, dst);
+ float length = getLength();
+ local = dasher.createDiagonal(xcount / length, length, invert);
+ }
+
+ @Override
+ void rasterize() {
+ e = eBase;
+ x = x1;
+ y = y1;
+ rasterizeDash(xcount, local);
+ }
+
+ @Override
+ void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) {
+ e = eBase + 2 * (ady * Math.abs(nx1 - x1) - adx * Math.abs(ny1 - y1));
+ x = nx1;
+ y = ny1;
+ rasterizeDash(Math.abs(nx2 - nx1), local.createChild(Math.abs(nx1 - x1)));
+ }
+
+ }
+
+ static class VerDashed extends Ver {
+
+ LineDasher local;
+
+ VerDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) {
+ super(x1, y1, x2, y2, dst);
+ float length = getLength();
+ local = dasher.createDiagonal(xcount / length, length, invert);
+ }
+
+ @Override
+ void rasterize() {
+ e = eBase;
+ x = x1;
+ y = y1;
+ rasterizeDash(xcount, local);
+ }
+
+ @Override
+ void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) {
+ e = eBase + 2 * (adx * Math.abs(ny1 - y1) - ady * Math.abs(nx1 - x1));
+ x = nx1;
+ y = ny1;
+ rasterizeDash(ny2 - ny1, local.createChild(ny1 - y1));
+ }
+
+ }
+
+ @Override
+ void rasterize(int[] clip, int index) {
+ int cx1 = clip[index + 0];
+ int cy1 = clip[index + 1];
+ int cx2 = clip[index + 2] + 1;
+ int cy2 = clip[index + 3] + 1;
+
+ int code1 =
+ (x1 < cx1 ? 1 : 0) | (x1 >= cx2 ? 2 : 0) |
+ (y1 < cy1 ? 8 : 0) | (y1 >= cy2 ? 4 : 0);
+ int code2 =
+ (x2 < cx1 ? 1 : 0) | (x2 >= cx2 ? 2 : 0) |
+ (y2 < cy1 ? 8 : 0) | (y2 >= cy2 ? 4 : 0);
+
+ // Outside
+ if ((code1 & code2) != 0) {
+ return;
+ }
+
+ // Inside
+ if (code1 == 0 && code2 == 0) {
+ rasterize();
+ return;
+ }
+
+ // Clip
+ int nx1 = x1;
+ int ny1 = y1;
+ int nx2 = x2;
+ int ny2 = y2;
+ // need to clip
+ cx1 -= x1; cx2 -= x1;
+ cy1 -= y1; cy2 -= y1;
+// int d;
+ int newx1 = 0, newy1 = 0, newx2 = 0, newy2 = 0;
+ if (code1 != 0) {
+ newx1 = Integer.MAX_VALUE;
+ if ((code1 & 8) != 0) {
+ // clip point 1 with top clip bound
+ newy1 = cy1;
+ newx1 = clipY(dx, dy, newy1, true);
+
+ } else if ((code1 & 4) != 0) {
+ // clip point 1 with bottom clip bound
+ newy1 = cy2 - 1;
+ newx1 = clipY(dx, dy, newy1, false);
+ }
+ if ((code1 & 1) != 0 && (cx1 > newx1 || newx1 == Integer.MAX_VALUE)) {
+ // clip point 1 with left clip bound
+ newx1 = cx1;
+ newy1 = clipX(dx, dy, newx1, false);
+ } else if ((code1 & 2) != 0 && (newx1 >= cx2 || newx1 == Integer.MAX_VALUE)) {
+ // clip point 1 with right clip bound
+ newx1 = cx2 - 1;
+ newy1 = clipX(dx, dy, newx1, false);
+ }
+ if (newx1 < cx1 || newx1 >= cx2 || newy1 < cy1 || newy1 >= cy2) {
+ return;
+ }
+// d = 2 * (ady * Math.abs(newx1) - adx * Math.abs(newy1)) + 2 * ady - adx;
+ } else {
+// d = (ady << 1) - adx;
+ }
+
+ if (code2 != 0) {
+ newx2=Integer.MAX_VALUE;
+ if ((code2 & 8) != 0) {
+ // clip point 2 with top clip bound
+ newy2 = cy1;
+ newx2 = clipY(dx, dy, newy2, true);
+ } else if ((code2 & 4) != 0) {
+ // clip point 2 with bottom clip bound
+ newy2 = cy2 - 1;
+ newx2 = clipY(dx, dy, newy2, false);
+ }
+ if ((code2 & 1) != 0 && (cx1 > newx2 || newx2 == Integer.MAX_VALUE)) {
+ // clip point 2 with left clip bound
+ newx2 = cx1;
+ newy2 = clipX(dx, dy, newx2, false);
+ } else if ((code2 & 2) != 0 && (newx2 >= cx2 || newx2 == Integer.MAX_VALUE)) {
+ // clip point 2 with right clip bound
+ newx2 = cx2 - 1;
+ newy2 = clipX(dx, dy, newx2, false);
+ }
+ if (newx2 < cx1 || newx2 >= cx2 || newy2 < cy1 || newy2 >= cy2) {
+ return;
+ }
+ nx2 = x1 + newx2;
+ ny2 = y1 + newy2;
+ }
+ nx1 = x1 + newx1;
+ ny1 = y1 + newy1;
+
+ rasterizeClipped(nx1, ny1, nx2, ny2);
+ }
+
+ abstract void rasterizeClipped(int nx1, int ny1, int nx2, int ny2);
+
+ }
+
+ static abstract class Ortog extends Line {
+
+ Ortog(int x1, int y1, int x2, int y2, MultiRectArea dst) {
+ super(x1, y1, x2, y2, dst);
+ }
+
+ static class Hor extends Ortog {
+
+ int dx;
+
+ Hor(int x1, int y1, int x2, int y2, MultiRectArea dst) {
+ super(x1, y1, x2, y2, dst);
+ dx = x2 - x1;
+ }
+
+ @Override
+ void rasterize() {
+ if (dx > 0) {
+ dst.addRect(x1, y1, x2, y2);
+ } else {
+ dst.addRect(x2, y2, x1, y1);
+ }
+ }
+
+ @Override
+ void rasterize(int step) {
+ int px = x;
+ if (dx > 0) {
+ x += step;
+ dst.addRect(px, y1, x - 1, y2);
+ } else {
+ x -= step;
+ dst.addRect(x + 1, y2, px, y1);
+ }
+ }
+
+ @Override
+ void skip(int step) {
+ if (dx > 0) {
+ x += step;
+ } else {
+ x -= step;
+ }
+ }
+
+ void rasterizeClipped(int nx1, int nx2) {
+ if (nx1 < nx2) {
+ dst.addRect(nx1, y1, nx2, y1);
+ } else {
+ dst.addRect(nx2, y1, nx1, y1);
+ }
+ }
+
+ @Override
+ void rasterize(int[] clip, int index) {
+ if (y1 >= clip[index + 1] && y1 <= clip[index + 3]) {
+ int cx1 = clip[index + 0];
+ int cx2 = clip[index + 2];
+ if (x1 <= cx2 && x2 >= cx1) {
+ int nx1, nx2;
+ if (dx > 0) {
+ nx1 = Math.max(x1, cx1);
+ nx2 = Math.min(x2, cx2);
+ } else {
+ nx2 = Math.max(x2, cx1);
+ nx1 = Math.min(x1, cx2);
+ }
+ rasterizeClipped(nx1, nx2);
+ }
+ }
+ }
+
+ }
+
+ static class Ver extends Ortog {
+
+ int dy;
+
+ Ver(int x1, int y1, int x2, int y2, MultiRectArea dst) {
+ super(x1, y1, x2, y2, dst);
+ dy = y2 - y1;
+ }
+
+ @Override
+ void rasterize() {
+ dst.addRect(x1, y1, x2, y2);
+ }
+
+ @Override
+ void rasterize(int step) {
+ int py = y;
+ y += step;
+ dst.addRect(x1, py, x2, y - 1);
+ }
+
+ @Override
+ void skip(int step) {
+ y += step;
+ }
+
+ void rasterizeClipped(int ny1, int ny2) {
+ dst.addRect(x1, ny1, x1, ny2);
+ }
+
+ @Override
+ void rasterize(int[] clip, int index) {
+ if (x1 >= clip[index] && x1 <= clip[index + 2]) {
+ int cy1 = clip[index + 1];
+ int cy2 = clip[index + 3];
+ if (y1 <= cy2 && y2 >= cy1) {
+ rasterizeClipped(Math.max(y1, cy1), Math.min(y2, cy2));
+ }
+ }
+ }
+
+ }
+
+ static class HorDashed extends Hor {
+
+ LineDasher local;
+
+ HorDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher) {
+ super(x1, y1, x2, y2, dst);
+ dx = x2 - x1;
+ local = dasher.createOrtogonal(Math.abs(dx), false);
+ }
+
+ @Override
+ void rasterize() {
+ x = x1;
+ y = y1;
+ rasterizeDash(Math.abs(dx), local);
+ }
+
+ @Override
+ void rasterizeClipped(int nx1, int nx2) {
+ x = nx1;
+ y = y1;
+ rasterizeDash(Math.abs(nx2 - nx1), local.createChild(Math.abs(nx1 - x1)));
+ }
+
+ }
+
+ static class VerDashed extends Ver {
+
+ LineDasher local;
+
+ VerDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) {
+ super(x1, y1, x2, y2, dst);
+ dy = y2 - y1;
+ local = dasher.createOrtogonal(dy, invert);
+ }
+
+ @Override
+ void rasterize() {
+ x = x1;
+ y = y1;
+ rasterizeDash(dy, local);
+ }
+
+ @Override
+ void rasterizeClipped(int ny1, int ny2) {
+ x = x1;
+ y = ny1;
+ rasterizeDash(ny2 - ny1, local.createChild(ny1));
+ }
+
+ }
+
+ }
+
+ abstract void rasterize();
+ abstract void rasterize(int[] clip, int index);
+ abstract void rasterize(int count);
+ abstract void skip(int count);
+
+ void rasterizeDash(int count, LineDasher dasher) {
+ float delta = dasher.dash[dasher.index] - dasher.phase;
+ int step = (int)delta;
+ delta -= step;
+ while(count > step) {
+ if (dasher.visible) {
+ rasterize(step);
+ } else {
+ skip(step);
+ }
+ count -= step;
+ delta += dasher.nextDash();
+ step = (int)delta;
+ delta -= step;
+ }
+ if (count > 0 && dasher.visible) {
+ rasterize(count);
+ dasher.move(count);
+ }
+ }
+
+ }
+
+ /**
+ * Common clipping method
+ */
+ static int clip(int dX1, int dX2, int cX, boolean top) {
+ int adX1 = dX1 < 0 ? -dX1 : dX1;
+ int adX2 = dX2 < 0 ? -dX2 : dX2;
+ if (adX1 <= adX2) {
+ // obtuse intersection angle
+ return ((dX1 << 1) * cX + (dX1 > 0 ? dX2 : -dX2)) / (dX2 << 1);
+ }
+ int k;
+ if (top) {
+ k = -dX1 + (dX2 < 0 ? 0 : dX1 > 0 ? (dX2 << 1) : -(dX2 << 1));
+ } else {
+ k = dX1 + (dX2 > 0 ? 0 : dX1 > 0 ? (dX2 << 1) : -(dX2 << 1));
+ }
+
+ k += dX1 > 0 == dX2 > 0 ? -1 : 1;
+ return ((dX1 << 1) * cX + k) / (dX2 << 1);
+ }
+
+ /**
+ * Clipping along X axis
+ */
+ static int clipX(int dx, int dy, int cy, boolean top) {
+ return clip(dy, dx, cy, top);
+ }
+
+ /**
+ * Clipping along Y axis
+ */
+ static int clipY(int dx, int dy, int cx, boolean top) {
+ return clip(dx, dy, cx, top);
+ }
+
+ /**
+ * Rasterizes line using clippind and dashing style
+ * @param x1 - the x coordinate of the first control point
+ * @param y1 - the y coordinate of the first control point
+ * @param x2 - the x coordinate of the second control point
+ * @param y2 - the y coordinate of the second control point
+ * @param clip - the MultiRectArea object of clipping area
+ * @param dasher - the dasher style
+ * @param invert - the invert indicator, always false
+ * @return a MultiRectArea of rasterizer line
+ */
+ public static MultiRectArea rasterize(int x1, int y1, int x2, int y2, MultiRectArea clip, LineDasher dasher, boolean invert) {
+
+ MultiRectArea dst = new MultiRectArea(false);
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ // Point
+ if (dx == 0 && dy == 0) {
+ if ((clip == null || clip.contains(x1, y1)) && (dasher == null || dasher.visible)) {
+ dst = new MultiRectArea(x1, y1, x1, y1);
+ }
+ return dst;
+ }
+
+ if (dy < 0) {
+ return rasterize(x2, y2, x1, y1, clip, dasher, true);
+ }
+
+ Line line;
+ if (dasher == null) {
+ if (dx == 0) {
+ line = new Line.Ortog.Ver(x1, y1, x2, y2, dst);
+ } else
+ if (dy == 0) {
+ line = new Line.Ortog.Hor(x1, y1, x2, y2, dst);
+ } else {
+ if (dy < Math.abs(dx)) {
+ line = new Line.Diag.Hor(x1, y1, x2, y2, dst);
+ } else {
+ line = new Line.Diag.Ver(x1, y1, x2, y2, dst);
+ }
+ }
+ } else {
+ if (dx == 0) {
+ line = new Line.Ortog.VerDashed(x1, y1, x2, y2, dst, dasher, invert);
+ } else
+ if (dy == 0) {
+ line = new Line.Ortog.HorDashed(x1, y1, x2, y2, dst, dasher);
+ } else {
+ if (dy < Math.abs(dx)) {
+ line = new Line.Diag.HorDashed(x1, y1, x2, y2, dst, dasher, invert);
+ } else {
+ line = new Line.Diag.VerDashed(x1, y1, x2, y2, dst, dasher, invert);
+ }
+ }
+ }
+
+
+ if (clip == null || clip.isEmpty()) {
+ line.rasterize();
+ } else {
+ for(int i = 1; i < clip.rect[0]; i += 4) {
+ line.rasterize(clip.rect, i);
+ }
+ }
+
+ return dst;
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java b/awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java
new file mode 100644
index 0000000..dbaaf53
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.render;
+
+import java.awt.Shape;
+import java.awt.geom.PathIterator;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+public class JavaShapeRasterizer {
+
+ static final int POINT_CAPACITY = 16;
+
+ int edgesCount;
+ int edgeCur;
+ int[] edgesX;
+ int[] edgesY;
+ int[] edgesYS; // Y coordinate of edge START point
+ int[] edgesN;
+ int[] edgesDY;
+ int[] bounds;
+ int boundCount;
+ boolean[] edgesExt; // Extremal points
+
+ int activeCount;
+ float[] activeX;
+ int[] activeYEnd;
+ float[] activeXStep;
+ int[] activeDY;
+ boolean[] activeExt;
+
+ int[] crossX;
+ int[] crossDY;
+
+ Filler filler;
+
+ /**
+ * Rasterization filler for different path rules
+ */
+ static abstract class Filler {
+
+ static class NonZero extends Filler {
+ @Override
+ void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y) {
+
+ int[] dst = new int[length];
+ int dstLength = 1;
+ dst[0] = points[0];
+ int count = 0;
+ boolean inside = true;
+ for(int i = 0; i < length; i++) {
+ count += orient[i] > 0 ? 1 : -1;
+ if (count == 0) {
+ dst[dstLength++] = points[i];
+ inside = false;
+ } else {
+ if (!inside) {
+ dst[dstLength++] = points[i];
+ inside = true;
+ }
+ }
+
+ }
+
+ for(int i = 1; i < dstLength; i += 2) {
+ dst[i]--;
+ }
+
+ dstLength = excludeEmpty(dst, dstLength);
+// System.out.println("test");
+
+ dstLength = union(dst, dstLength);
+
+ rect.addLine(dst, dstLength);
+ }
+ }
+
+ static class EvenOdd extends Filler {
+ @Override
+ void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y) {
+ /*
+ int[] buf = new int[length];
+ int j = 0;
+ for(int i = 0; i < length - 1; i++) {
+ if (points[i] != points[i + 1]) {
+ buf[j++] = points[i];
+ }
+ }
+ */
+ for(int i = 1; i < length; i += 2) {
+ points[i]--;
+ }
+
+ length = excludeEmpty(points, length);
+// System.out.println("test");
+
+ length = union(points, length);
+ rect.addLine(points, length);
+ /*
+ for(int i = 0; i < length;) {
+ rect.add(points[i++], y, points[i++], y);
+ }
+ */
+ }
+ }
+
+ abstract void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y);
+
+ static int excludeEmpty(int[] points, int length) {
+ int i = 0;
+ while(i < length) {
+ if (points[i] <= points[i + 1]) {
+ i += 2;
+ } else {
+ length -= 2;
+ System.arraycopy(points, i + 2, points, i, length - i);
+ }
+ }
+ return length;
+ }
+
+ static int union(int[] points, int length) {
+ int i = 1;
+ while(i < length - 1) {
+ if (points[i] < points[i - 1]) {
+ System.arraycopy(points, i + 1, points, i - 1, length - i - 1);
+ length -= 2;
+ } else
+ if (points[i] >= points[i + 1] - 1) {
+ System.arraycopy(points, i + 2, points, i, length - i - 2);
+ length -= 2;
+ } else {
+ i += 2;
+ }
+ }
+ return length;
+ }
+
+ }
+
+ public JavaShapeRasterizer() {
+ }
+
+ /**
+ * Checks buffer size and realloc if necessary
+ */
+ int[] checkBufSize(int[] buf, int size) {
+ if (size == buf.length) {
+ int[] tmp;
+ tmp = new int[size + POINT_CAPACITY];
+ System.arraycopy(buf, 0, tmp, 0, buf.length);
+ buf = tmp;
+ }
+ return buf;
+ }
+
+ /**
+ * Adds to the buffers new edge
+ */
+ void addEdge(int x, int y, int num) {
+ edgesX = checkBufSize(edgesX, edgesCount);
+ edgesY = checkBufSize(edgesY, edgesCount);
+ edgesN = checkBufSize(edgesN, edgesCount);
+ edgesX[edgesCount] = x;
+ edgesY[edgesCount] = y;
+ edgesN[edgesCount] = (num << 16) | edgesCount;
+ edgesCount++;
+ }
+
+ /**
+ * Prepare all buffers and variable to rasterize shape
+ */
+ void makeBuffer(PathIterator path, double flatness) {
+ edgesX = new int[POINT_CAPACITY];
+ edgesY = new int[POINT_CAPACITY];
+ edgesN = new int[POINT_CAPACITY];
+ bounds = new int[POINT_CAPACITY];
+ boundCount = 0;
+ edgesCount = 0;
+
+ if (path.getWindingRule() == PathIterator.WIND_EVEN_ODD) {
+ filler = new Filler.EvenOdd();
+ } else {
+ filler = new Filler.NonZero();
+ }
+ float[] coords = new float[2];
+ boolean closed = true;
+ while (!path.isDone()) {
+ switch(path.currentSegment(coords)) {
+ case PathIterator.SEG_MOVETO:
+ if (!closed) {
+ boundCount++;
+ bounds = checkBufSize(bounds, boundCount);
+ bounds[boundCount] = edgesCount;
+ }
+ addEdge((int)coords[0], (int)coords[1], boundCount);
+ closed = false;
+ break;
+ case PathIterator.SEG_LINETO:
+ addEdge((int)coords[0], (int)coords[1], boundCount);
+ break;
+ case PathIterator.SEG_CLOSE:
+ boundCount++;
+ bounds = checkBufSize(bounds, boundCount);
+ bounds[boundCount] = edgesCount;
+ closed = true;
+ break;
+ default:
+ // awt.36=Wrong segment
+ throw new RuntimeException(Messages.getString("awt.36")); //$NON-NLS-1$
+ }
+ path.next();
+ }
+ if (!closed) {
+ boundCount++;
+ bounds = checkBufSize(bounds, boundCount);
+ bounds[boundCount] = edgesCount;
+ }
+ }
+
+ /**
+ * Sort buffers
+ */
+ void sort(int[] master, int[] slave, int length) {
+ for(int i = 0; i < length - 1; i++) {
+ int num = i;
+ int min = master[num];
+ for(int j = i + 1; j < length; j++) {
+ if (master[j] < min) {
+ num = j;
+ min = master[num];
+ }
+ }
+ if (num != i) {
+ master[num] = master[i];
+ master[i] = min;
+ min = slave[num];
+ slave[num] = slave[i];
+ slave[i] = min;
+ }
+ }
+ }
+
+ int getNext(int cur) {
+ int n = edgesN[cur];
+ int bound = n >> 16;
+ int num = (n & 0xFFFF) + 1;
+ if (num == bounds[bound + 1]) {
+ return bounds[bound];
+ }
+ return num;
+ }
+
+ int getPrev(int cur) {
+ int n = edgesN[cur];
+ int bound = n >> 16;
+ int num = (n & 0xFFFF) - 1;
+ if (num < bounds[bound]) {
+ return bounds[bound + 1] - 1;
+ }
+ return num;
+ }
+
+ int getNextShape(int cur) {
+ int bound = edgesN[cur] >> 16;
+ return bounds[bound + 1];
+ }
+
+ void init() {
+
+ edgesYS = new int[edgesCount];
+ System.arraycopy(edgesY, 0, edgesYS, 0, edgesCount);
+ // Create edgesDY
+ edgesDY = new int[edgesCount];
+ for(int i = 0; i < edgesCount; i++) {
+ int dy = edgesY[getNext(i)] - edgesY[i];
+ edgesDY[i] = dy;
+ }
+
+ // Create edgesExt
+ edgesExt = new boolean[edgesCount];
+ int prev = -1;
+ int i = 0;
+ int pos = 0;
+ while(i < edgesCount) {
+
+ TOP: {
+ do {
+ if (edgesDY[i] > 0) {
+ break TOP;
+ }
+ i = getNext(i);
+ } while (i != pos);
+ i = pos = getNextShape(i);
+ continue;
+ }
+
+ BOTTOM: {
+ do {
+ if (edgesDY[i] < 0) {
+ break BOTTOM;
+ }
+ if (edgesDY[i] > 0) {
+ prev = i;
+ }
+ i = getNext(i);
+ } while (i != pos);
+ i = pos = getNextShape(i);
+ continue;
+ }
+
+ if (prev != -1) {
+ edgesExt[prev] = true;
+ }
+ edgesExt[i] = true;
+ }
+
+ // Sort edgesY and edgesN
+ sort(edgesYS, edgesN, edgesCount);
+
+ edgeCur = 0;
+ activeCount = 0;
+ activeX = new float[edgesCount];
+ activeYEnd = new int[edgesCount];
+ activeXStep = new float[edgesCount];
+ activeDY = new int[edgesCount];
+ activeExt = new boolean[edgesCount];
+
+ crossX = new int[edgesCount];
+ crossDY = new int[edgesCount];
+ }
+
+ /**
+ * Marks edge as active
+ */
+ void addActiveEdge(int levelY, int start, int end, boolean back) {
+ int dy = back ? -edgesDY[end] : edgesDY[start];
+ if (dy <= 0) {
+ return;
+ }
+ int x1 = edgesX[start];
+ int dx = edgesX[end] - x1;
+ activeX[activeCount] = x1;
+ activeYEnd[activeCount] = edgesY[end];
+ activeXStep[activeCount] = dx / (float)dy;
+ activeDY[activeCount] = back ? -dy : dy;
+ activeExt[activeCount] = back ? edgesExt[end] : edgesExt[start];
+ activeCount++;
+ }
+
+ /**
+ * Find new active edges
+ */
+ int findActiveEdges(int levelY) {
+
+ int edgeActive = edgeCur;
+ while (edgeActive < edgesCount && edgesYS[edgeActive] == levelY) {
+ edgeActive++;
+ }
+
+ int activeNext = edgeActive;
+
+ while (edgeActive > edgeCur) {
+ edgeActive--;
+ int num = edgesN[edgeActive] & 0xFFFF;
+ addActiveEdge(levelY, num, getPrev(edgeActive), true);
+ addActiveEdge(levelY, num, getNext(edgeActive), false);
+ }
+
+ edgeCur = activeNext;
+
+ if (activeNext == edgesCount) {
+ return edgesY[edgesCount - 1];
+ }
+ return edgesYS[activeNext];
+ }
+
+ /**
+ * Rasterizes shape with particular flatness
+ * @param shape - the souze Shape to be rasterized
+ * @param flatness - the rasterization flatness
+ * @return a MultiRectArea of rasterized shape
+ */
+ public MultiRectArea rasterize(Shape shape, double flatness) {
+
+ PathIterator path = shape.getPathIterator(null, flatness);
+
+ // Shape is empty
+ if (path.isDone()) {
+ return new MultiRectArea();
+ }
+
+ makeBuffer(path, flatness);
+
+ init();
+
+ int y = edgesYS[0];
+ int nextY = y;
+ int crossCount;
+
+ MultiRectArea.LineCash rect = new MultiRectArea.LineCash(edgesCount);
+ rect.setLine(y);
+
+ while(y <= nextY) {
+
+ crossCount = 0;
+
+ if (y == nextY) {
+
+ int i = activeCount;
+ while(i > 0) {
+ i--;
+ if (activeYEnd[i] == y) {
+
+ activeCount--;
+ int length = activeCount - i;
+ if (length != 0) {
+ int pos = i + 1;
+ System.arraycopy(activeX, pos, activeX, i, length);
+ System.arraycopy(activeYEnd, pos, activeYEnd, i, length);
+ System.arraycopy(activeXStep, pos, activeXStep, i, length);
+ System.arraycopy(activeDY, pos, activeDY, i, length);
+ System.arraycopy(activeExt, pos, activeExt, i, length);
+ }
+ }
+ }
+
+ nextY = findActiveEdges(y);
+ }
+
+ // Get X crossings
+ for(int i = 0; i < activeCount; i++) {
+ crossX[crossCount] = (int)Math.ceil(activeX[i]);
+ crossDY[crossCount] = activeDY[i];
+ crossCount++;
+ }
+
+ if (crossCount == 0) {
+ rect.skipLine();
+ } else {
+ // Sort X crossings
+ sort(crossX, crossDY, crossCount);
+ filler.add(rect, crossX, crossDY, crossCount, y);
+ }
+
+ for(int i = 0; i < activeCount; i++) {
+ activeX[i] += activeXStep[i];
+ }
+
+ y++;
+ }
+
+ return rect;
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java b/awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java
new file mode 100644
index 0000000..322ba57
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Ilya S. Okomin
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.gl.render;
+
+import java.awt.*;
+import java.awt.image.*;
+
+
+import java.awt.font.GlyphMetrics;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+
+import org.apache.harmony.awt.gl.TextRenderer;
+import org.apache.harmony.awt.gl.font.CommonGlyphVector;
+import org.apache.harmony.awt.gl.font.FontPeerImpl;
+import org.apache.harmony.awt.gl.font.Glyph;
+import org.apache.harmony.awt.gl.image.BufferedImageGraphics2D;
+
+public class JavaTextRenderer extends TextRenderer {
+
+ public static final JavaTextRenderer inst = new JavaTextRenderer();
+
+ @Override
+ public void drawGlyphVector(Graphics2D g, GlyphVector glyphVector,
+ float x, float y) {
+
+ AffineTransform at = g.getTransform();
+ Rectangle c = g.getClipBounds();
+ if (at != null){
+ int atType = at.getType();
+ if (atType == AffineTransform.TYPE_TRANSLATION) {
+ c.translate((int)Math.round(at.getTranslateX()), (int)Math.round(at.getTranslateY()));
+ }
+ }
+
+ WritableRaster wr = ((BufferedImageGraphics2D)g).getWritableRaster();
+ ColorModel cm = ((BufferedImageGraphics2D)g).getColorModel();
+
+ Rectangle rBounds = wr.getBounds();
+
+ Object color = cm.getDataElements(g.getColor().getRGB(), null);
+
+ drawClipGlyphVector(wr, color, glyphVector, (int)Math.round(x + at.getTranslateX()), (int)Math.round(y + at.getTranslateY()),
+ Math.max(c.x,rBounds.x),
+ Math.max(c.y,rBounds.y),
+ Math.min((int)Math.round(c.getMaxX()), (int)Math.round(rBounds.getMaxX())),
+ Math.min((int)Math.round(c.getMaxY()), (int)Math.round(rBounds.getMaxY())));
+
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void drawString(Graphics2D g, String str, float x, float y) {
+ AffineTransform at = g.getTransform();
+ Rectangle c = g.getClipBounds();
+ if (at != null){
+ int atType = at.getType();
+ if (atType == AffineTransform.TYPE_TRANSLATION) {
+ c.translate((int)Math.round(at.getTranslateX()), (int)Math.round(at.getTranslateY()));
+ }
+ }
+ WritableRaster wr = ((BufferedImageGraphics2D)g).getWritableRaster();
+ ColorModel cm = ((BufferedImageGraphics2D)g).getColorModel();
+ Rectangle rBounds = wr.getBounds();
+
+ Object color = cm.getDataElements(g.getColor().getRGB(), null);
+
+ drawClipString(wr, color, str, (FontPeerImpl) (g.getFont().getPeer()),
+ (int)Math.round(x + at.getTranslateX()), (int)Math.round(y + at.getTranslateY()),
+ Math.max(c.x,rBounds.x),
+ Math.max(c.y,rBounds.y),
+ Math.min((int)Math.round(c.getMaxX()), (int)Math.round(rBounds.getMaxX())),
+ Math.min((int)Math.round(c.getMaxY()), (int)Math.round(rBounds.getMaxY())));
+
+ }
+
+ /**
+ *
+ * Draws string on specified raster at desired position.
+ *
+ * @param raster specified WritableRaster to draw at
+ * @param color color of the text
+ * @param glyphVector GlyphVector object to draw
+ * @param x start X position to draw
+ * @param y start Y position to draw
+ * @param cMinX minimum x of the raster area to draw
+ * @param cMinY minimum y of the raster area to draw
+ * @param cMaxX maximum x of the raster area to draw
+ * @param cMaxY maximum y of the raster area to draw
+ */
+ public void drawClipGlyphVector(WritableRaster raster, Object color,
+ GlyphVector glyphVector, int x, int y,
+ int cMinX, int cMinY, int cMaxX, int cMaxY) {
+ // TODO: implement complex clipping
+
+ int xSrcSurf, ySrcSurf; // Start point in String rectangle
+ int xDstSurf, yDstSurf; // Start point in Surface rectangle
+ int clWidth, clHeight;
+
+ for (int i = 0; i < glyphVector.getNumGlyphs(); i++) {
+ Glyph gl = ((CommonGlyphVector) glyphVector).vector[i];
+
+ if (gl.getPointWidth() == 0) {
+ continue;
+ }
+
+ byte[] data = gl.getBitmap();
+ if (data != null) {
+ Point2D pos = glyphVector.getGlyphPosition(i);
+
+ xSrcSurf = 0;//gl.bmp_left;
+ ySrcSurf = 0;//gl.bmp_rows - gl.bmp_top;
+
+ xDstSurf = x + (int)pos.getX() + (int) gl.getGlyphPointMetrics().getLSB();// + gl.bmp_left;
+ yDstSurf = y - gl.bmp_top/*getPointHeight()*/ + (int) pos.getY();// - (gl.bmp_rows-gl.bmp_top);
+
+ int textWidth = gl.bmp_width;
+ int textHeight = gl.getPointHeight();
+
+ // if Regions don't intersect
+ if ((xDstSurf > cMaxX) || (yDstSurf > cMaxY) || (xDstSurf + textWidth < cMinX)
+ || (yDstSurf + textHeight < cMinY)) {
+ // Nothing to do
+ } else {
+ if (xDstSurf >= cMinX) {
+ clWidth = Math.min(textWidth, cMaxX - xDstSurf);
+ } else {
+ xSrcSurf += cMinX - xDstSurf;
+ clWidth = Math.min(cMaxX - cMinX, textWidth - (cMinX - xDstSurf));
+ xDstSurf = cMinX;
+ }
+ if (yDstSurf >= cMinY) {
+ clHeight = Math.min(textHeight, cMaxY - yDstSurf);
+ } else {
+ ySrcSurf += cMinY - yDstSurf;
+ clHeight = Math.min(cMaxY - cMinY, textHeight - (cMinY - yDstSurf));
+ yDstSurf = cMinY;
+ }
+ // Drawing on the Raster
+ for (int h=0; h<clHeight; h++){
+ for (int w=0; w < clWidth ; w++) {
+ byte currByte = data[(ySrcSurf + h)*gl.bmp_pitch + (xSrcSurf+w)/8];
+ boolean emptyByte = ((currByte & (1 << (7 - ((xSrcSurf+w) % 8)))) != 0);
+ if (emptyByte) {
+ raster.setDataElements(xDstSurf+w, yDstSurf+h, color);
+ } else {
+ // Nothing to do
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Draws string on specified raster at desired position.
+ *
+ * @param raster specified WritableRaster to draw at
+ * @param color color of the text
+ * @param str text to draw
+ * @param font font peer to use for drawing text
+ * @param x start X position to draw
+ * @param y start Y position to draw
+ * @param cMinX minimum x of the raster area to draw
+ * @param cMinY minimum y of the raster area to draw
+ * @param cMaxX maximum x of the raster area to draw
+ * @param cMaxY maximum y of the raster area to draw
+ */
+ public void drawClipString(WritableRaster raster, Object color, String str,
+ FontPeerImpl font, int x, int y, int cMinX, int cMinY, int cMaxX,
+ int cMaxY) {
+ // TODO: implement complex clipping
+
+ int xSrcSurf, ySrcSurf; // Start point in String rectangle
+ int xDstSurf, yDstSurf; // Start point in Surface rectangle
+ int clWidth, clHeight;
+
+ char[] chars = str.toCharArray();
+
+ int xBaseLine = x;
+ int yBaseLine = y;
+
+ for (char element : chars) {
+ Glyph gl = font.getGlyph(element);
+ GlyphMetrics pointMetrics = gl.getGlyphPointMetrics();
+ if (gl.getWidth() == 0) {
+ xBaseLine += pointMetrics.getAdvanceX();
+ continue;
+ }
+
+ byte[] data = gl.getBitmap();
+ if (data == null) {
+ xBaseLine += pointMetrics.getAdvanceX();
+ } else {
+
+ xSrcSurf = 0;
+ ySrcSurf = 0;
+
+ xDstSurf = Math.round(xBaseLine + gl.getGlyphPointMetrics().getLSB());
+ yDstSurf = yBaseLine - gl.bmp_top;
+
+ int textWidth = gl.bmp_width;
+ int textHeight = gl.getPointHeight();
+
+ // if Regions don't intersect
+ if ((xDstSurf > cMaxX) || (yDstSurf > cMaxY) || (xDstSurf + textWidth < cMinX)
+ || (yDstSurf + textHeight < cMinY)) {
+ // Nothing to do
+ } else {
+ if (xDstSurf >= cMinX) {
+ clWidth = Math.min(textWidth, cMaxX - xDstSurf);
+ } else {
+ xSrcSurf += cMinX - xDstSurf;
+ clWidth = Math.min(cMaxX - cMinX, textWidth - (cMinX - xDstSurf));
+ xDstSurf = cMinX;
+ }
+ if (yDstSurf >= cMinY) {
+ clHeight = Math.min(textHeight, cMaxY - yDstSurf);
+ } else {
+ ySrcSurf += cMinY - yDstSurf;
+ clHeight = Math.min(cMaxY - cMinY, textHeight - (cMinY - yDstSurf));
+ yDstSurf = cMinY;
+ }
+
+ // Drawing on the Raster
+ for (int h=0; h<clHeight; h++){
+ for (int w=0; w < clWidth ; w++) {
+ byte currByte = data[(ySrcSurf + h)*gl.bmp_pitch + (xSrcSurf+w)/8];
+ boolean emptyByte = ((currByte & (1 << (7 - ((xSrcSurf+w) % 8)))) != 0);
+ if (emptyByte) {
+ raster.setDataElements(xDstSurf+w, yDstSurf+h, color);
+ } else {
+ // Nothing to do
+ }
+ }
+ }
+ }
+ xBaseLine += pointMetrics.getAdvanceX();
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/gl/render/NativeImageBlitter.java b/awt/org/apache/harmony/awt/gl/render/NativeImageBlitter.java
new file mode 100644
index 0000000..b0ebc97
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/NativeImageBlitter.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 26.11.2005
+ *
+ */
+package org.apache.harmony.awt.gl.render;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+
+import org.apache.harmony.awt.gl.ImageSurface;
+import org.apache.harmony.awt.gl.MultiRectArea;
+import org.apache.harmony.awt.gl.Surface;
+import org.apache.harmony.awt.gl.XORComposite;
+
+/**
+ * This kind of blitters is intended for drawing one image on the buffered
+ * or volatile image. For the moment we can blit natively Buffered Images which
+ * have sRGB, Linear_RGB, Linear_Gray Color Space and type different
+ * from BufferedImage.TYPE_CUSTOM, Volatile Images and Images which received
+ * using Toolkit and Component classes.
+ */
+public class NativeImageBlitter implements Blitter {
+
+
+ final static NativeImageBlitter inst = new NativeImageBlitter();
+
+ public static NativeImageBlitter getInstance(){
+ return inst;
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, AffineTransform sysxform,
+ AffineTransform xform, Composite comp, Color bgcolor,
+ MultiRectArea clip) {
+
+ if(!srcSurf.isNativeDrawable()){
+ JavaBlitter.inst.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height,
+ sysxform, xform, comp, bgcolor, clip);
+ }else{
+ if(xform == null){
+ blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height,
+ sysxform, comp, bgcolor, clip);
+ }else{
+ double scaleX = xform.getScaleX();
+ double scaleY = xform.getScaleY();
+ double scaledX = dstX / scaleX;
+ double scaledY = dstY / scaleY;
+ AffineTransform at = new AffineTransform();
+ at.setToTranslation(scaledX, scaledY);
+ xform.concatenate(at);
+ sysxform.concatenate(xform);
+ blit(srcX, srcY, srcSurf, 0, 0, dstSurf, width, height,
+ sysxform, comp, bgcolor, clip);
+ }
+ }
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, AffineTransform sysxform,
+ Composite comp, Color bgcolor, MultiRectArea clip) {
+
+ if(!srcSurf.isNativeDrawable()){
+ JavaBlitter.inst.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height,
+ sysxform, comp, bgcolor, clip);
+ }else{
+ int type = sysxform.getType();
+ switch(type){
+ case AffineTransform.TYPE_TRANSLATION:
+ dstX += sysxform.getTranslateX();
+ dstY += sysxform.getTranslateY();
+ case AffineTransform.TYPE_IDENTITY:
+ blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf,
+ width, height, comp, bgcolor, clip);
+ break;
+ default:
+ // TODO Need to realize Affine Transformation
+ if(srcSurf instanceof ImageSurface){
+ JavaBlitter.inst.blit(srcX, srcY, srcSurf, dstX, dstY,
+ dstSurf, width, height,
+ sysxform, comp, bgcolor, clip);
+ }else{
+ int w = srcSurf.getWidth();
+ int h = srcSurf.getHeight();
+ BufferedImage tmp = new BufferedImage(w, h,
+ BufferedImage.TYPE_INT_RGB);
+ Surface tmpSurf = Surface.getImageSurface(tmp);
+ blit(0, 0, srcSurf, 0, 0, tmpSurf,
+ w, h, AlphaComposite.SrcOver, null, null);
+ JavaBlitter.inst.blit(srcX, srcY, tmpSurf, dstX, dstY,
+ dstSurf, width, height,
+ sysxform, comp, bgcolor, clip);
+ }
+ }
+ }
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, Composite comp,
+ Color bgcolor, MultiRectArea clip) {
+
+ if(!srcSurf.isNativeDrawable()){
+ JavaBlitter.inst.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height,
+ comp, bgcolor, clip);
+ }else{
+ long dstSurfStruct = dstSurf.getSurfaceDataPtr();
+ Object dstData = dstSurf.getData();
+ int clipRects[];
+ if(clip != null){
+ clipRects = clip.rect;
+ }else{
+ clipRects = new int[]{5, 0, 0, dstSurf.getWidth(),
+ dstSurf.getHeight()};
+ }
+
+ if(!(srcSurf instanceof ImageSurface)){
+ srcSurf = srcSurf.getImageSurface();
+ if(bgcolor != null){
+ bgcolor = null;
+ }
+ }
+
+ long srcSurfStruct = srcSurf.getSurfaceDataPtr();
+ Object srcData = srcSurf.getData();
+ if(comp instanceof AlphaComposite){
+ AlphaComposite ac = (AlphaComposite) comp;
+ int compType = ac.getRule();
+ float alpha = ac.getAlpha();
+ if(bgcolor != null){
+ bltBG(srcX, srcY, srcSurfStruct, srcData,
+ dstX, dstY, dstSurfStruct, dstData,
+ width, height, bgcolor.getRGB(),
+ compType, alpha, clipRects, srcSurf.invalidated());
+ dstSurf.invalidate();
+ srcSurf.validate();
+ }else{
+ blt(srcX, srcY, srcSurfStruct, srcData,
+ dstX, dstY, dstSurfStruct, dstData,
+ width, height, compType, alpha,
+ clipRects, srcSurf.invalidated());
+ dstSurf.invalidate();
+ srcSurf.validate();
+ }
+ }else if(comp instanceof XORComposite){
+ XORComposite xcomp = (XORComposite) comp;
+ xor(srcX, srcY, srcSurfStruct, srcData,
+ dstX, dstY, dstSurfStruct, dstData,
+ width, height, xcomp.getXORColor().getRGB(),
+ clipRects, srcSurf.invalidated());
+ dstSurf.invalidate();
+ srcSurf.validate();
+ }else{
+ if(srcSurf instanceof ImageSurface){
+ JavaBlitter.inst.blit(srcX, srcY, srcSurf, dstX, dstY,
+ dstSurf, width, height,
+ comp, bgcolor, clip);
+ }else{
+ int w = srcSurf.getWidth();
+ int h = srcSurf.getHeight();
+ BufferedImage tmp = new BufferedImage(w, h,
+ BufferedImage.TYPE_INT_RGB);
+ Surface tmpSurf = Surface.getImageSurface(tmp);
+ long tmpSurfStruct = tmpSurf.getSurfaceDataPtr();
+ Object tmpData = tmpSurf.getData();
+ int tmpClip[] = new int[]{5, 0, 0, srcSurf.getWidth(),
+ srcSurf.getHeight()};
+
+ blt(0, 0, srcSurfStruct, srcData, 0, 0,
+ tmpSurfStruct, tmpData, w, h,
+ AlphaComposite.SRC_OVER,
+ 1.0f, tmpClip, srcSurf.invalidated());
+ srcSurf.validate();
+ JavaBlitter.inst.blit(srcX, srcY, tmpSurf, dstX, dstY,
+ dstSurf, width, height,
+ comp, bgcolor, clip);
+ }
+ }
+ }
+
+ }
+
+ private native void bltBG(int srcX, int srcY, long srsSurfDataPtr,
+ Object srcData, int dstX, int dstY, long dstSurfDataPtr,
+ Object dstData, int width, int height, int bgcolor,
+ int compType, float alpha, int clip[], boolean invalidated);
+
+ private native void blt(int srcX, int srcY, long srsSurfDataPtr,
+ Object srcData, int dstX, int dstY, long dstSurfDataPtr,
+ Object dstData, int width, int height, int compType,
+ float alpha, int clip[], boolean invalidated);
+
+ private native void xor(int srcX, int srcY, long srsSurfDataPtr,
+ Object srcData, int dstX, int dstY, long dstSurfDataPtr,
+ Object dstData, int width, int height, int xorcolor,
+ int clip[], boolean invalidated);
+
+
+}
diff --git a/awt/org/apache/harmony/awt/gl/render/NullBlitter.java b/awt/org/apache/harmony/awt/gl/render/NullBlitter.java
new file mode 100644
index 0000000..9032e4e
--- /dev/null
+++ b/awt/org/apache/harmony/awt/gl/render/NullBlitter.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Igor V. Stolyarov
+ * @version $Revision$
+ * Created on 07.12.2005
+ *
+ */
+package org.apache.harmony.awt.gl.render;
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.geom.AffineTransform;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+import org.apache.harmony.awt.gl.Surface;
+
+
+public class NullBlitter implements Blitter {
+
+ static Blitter inst = new NullBlitter();
+ public static Blitter getInstance(){
+ return inst;
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, AffineTransform sysxform,
+ AffineTransform xform, Composite comp, Color bgcolor,
+ MultiRectArea clip) {
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, AffineTransform sysxform,
+ Composite comp, Color bgcolor, MultiRectArea clip) {
+ }
+
+ public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
+ Surface dstSurf, int width, int height, Composite comp,
+ Color bgcolor, MultiRectArea clip) {
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/im/InputMethodContext.java b/awt/org/apache/harmony/awt/im/InputMethodContext.java
new file mode 100644
index 0000000..45ed11f
--- /dev/null
+++ b/awt/org/apache/harmony/awt/im/InputMethodContext.java
@@ -0,0 +1,563 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.im;
+
+//???AWT
+import java.awt.AWTEvent;
+import java.awt.Component;
+//import java.awt.KeyboardFocusManager;
+import java.awt.Rectangle;
+//import java.awt.Window;
+import java.awt.event.FocusEvent;
+import java.awt.event.InputMethodEvent;
+import java.awt.event.KeyEvent;
+import java.awt.font.TextHitInfo;
+import java.awt.im.InputContext;
+import java.awt.im.InputMethodRequests;
+import java.awt.im.spi.InputMethod;
+import java.awt.im.spi.InputMethodDescriptor;
+import java.lang.Character.Subset;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedCharacterIterator.Attribute;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+//???AWT
+//import javax.swing.JFrame;
+
+import org.apache.harmony.awt.wtk.NativeIM;
+
+/**
+ * Implementation of InputMethodContext
+ * interface, also provides all useful
+ * functionality of InputContext
+ *
+ */
+public class InputMethodContext extends InputContext implements
+ java.awt.im.spi.InputMethodContext {
+
+ //???AWT
+ private InputMethod inputMethod; // current IM
+ private Component client; // current "active" client component
+ //???AWT: private CompositionWindow composeWindow; // composition Window
+ private final Map<InputMethodDescriptor, InputMethod> imInstances; // Map<InputMethodDescriptor, InputMethod>
+ private final Map<Locale, InputMethod> localeIM; // Map<Locale, InputMethod> last user-selected IM for locale
+ private final Set<InputMethod> notifyIM; // set of IMs to notify of client window bounds changes
+
+ /**
+ * a flag indicating that IM should be notified of client window
+ * position/visibility changes as soon as it is activated(new client
+ * appears)
+ */
+ private boolean pendingClientNotify;
+ private Component nextComp; // component to gain focus after endComposition()
+ //???AWT: private final Set<Window> imWindows; // set of all IM windows created by this instance
+ private final NativeIM nativeIM;
+
+
+
+ public InputMethodContext() {
+ notifyIM = new HashSet<InputMethod>();
+//???AWT: imWindows = new HashSet<Window>();
+ imInstances = new HashMap<InputMethodDescriptor, InputMethod>();
+ localeIM = new HashMap<Locale, InputMethod>();
+ selectInputMethod(Locale.US); // not default?
+ nativeIM = (NativeIM) inputMethod;
+ }
+
+ //???AWT
+ /*
+ @Override
+ public void dispatchEvent(AWTEvent event) {
+ int id = event.getID();
+ if ((id >= FocusEvent.FOCUS_FIRST) && (id <=FocusEvent.FOCUS_LAST)) {
+ dispatchFocusEvent((FocusEvent) event);
+ } else {
+ // handle special KEY_PRESSED
+ // event to show IM selection menu
+ if (id == KeyEvent.KEY_PRESSED) {
+ KeyEvent ke = (KeyEvent) event;
+ IMManager.selectIM(ke, this,
+ IMManager.getWindow(ke.getComponent()));
+ }
+ // dispatch all input events to the current IM:
+ if (inputMethod != null) {
+ inputMethod.dispatchEvent(event);
+ }
+ }
+ }
+
+ private void dispatchFocusEvent(FocusEvent fe) {
+ switch (fe.getID()) {
+ case FocusEvent.FOCUS_LOST:
+ if (inputMethod != null) {
+ inputMethod.deactivate(fe.isTemporary());
+ }
+ break;
+ case FocusEvent.FOCUS_GAINED:
+
+ Component comp = fe.getComponent();
+ if (imWindows.contains(comp)) {
+ // prevent activating when IM windows
+ // attached to this context gain focus
+ return;
+ }
+ InputMethodContext lastActive = IMManager.getLastActiveIMC();
+ if ((lastActive != this) && (lastActive != null)) {
+ lastActive.hideWindows();
+ }
+ if (inputMethod != null) {
+ activateIM(inputMethod);
+ if (!getCompositionWindow().isEmpty()) {
+ IMManager.showCompositionWindow(composeWindow);
+ }
+ if (client == comp) {
+ if (nextComp != null) {
+ // temporarily got focus to
+ // end composition
+ endComposition();
+
+ // transfer focus to new client
+ client = nextComp;
+ nextComp = null;
+ client.requestFocusInWindow();
+ }
+ } else if ((client != null) && getCompositionWindow().isVisible()) {
+ // temporarily return focus back
+ // to previous client to be able
+ // to end composition
+ nextComp = comp;
+ client.requestFocusInWindow();
+ } else {
+ client = comp;
+ }
+ }
+ if (pendingClientNotify) {
+ notifyClientWindowChange(IMManager.getWindow(comp).getBounds());
+ }
+ break;
+ }
+
+ }
+
+ private void activateIM(InputMethod im) {
+ im.activate();
+ if ((nativeIM != null) && (im != nativeIM)) {
+ // when Java IM is active
+ // native input method editor must be
+ // explicitly disabled
+ nativeIM.disableIME();
+ }
+ IMManager.setLastActiveIMC(this);
+ }
+
+ @SuppressWarnings("deprecation")
+ private void hideWindows() {
+ if (inputMethod != null) {
+ inputMethod.hideWindows();
+ }
+ if (composeWindow != null) {
+ composeWindow.hide();
+ }
+ }
+
+ private void createCompositionWindow() {
+ composeWindow = new CompositionWindow(client);
+ }
+
+ private CompositionWindow getCompositionWindow() {
+ if (composeWindow == null) {
+ createCompositionWindow();
+ }
+ composeWindow.setClient(client);
+ return composeWindow;
+ }
+ */
+
+ /**
+ * Gets input method requests for the current client
+ * irrespective of input style.
+ * @return input method requests of composition window if
+ * client is passive,
+ * otherwise input method requests of client
+ */
+ private InputMethodRequests getIMRequests() {
+ InputMethodRequests imRequests = null;
+
+ if (client != null) {
+ imRequests = client.getInputMethodRequests();
+ //???AWT
+ /*
+ if (imRequests == null) {
+ imRequests = getCompositionWindow().getInputMethodRequests();
+ }
+ */
+ }
+
+ return imRequests;
+ }
+
+ /**
+ * Gets input method requests for the current client & input style.
+ * @return input method requests of composition window if
+ * input style is "below-the-spot"(or client is passive),
+ * otherwise client input method requests
+ */
+ private InputMethodRequests getStyleIMRequests() {
+ //???AWT
+ /*
+ if (IMManager.belowTheSpot()) {
+ return getCompositionWindow().getInputMethodRequests();
+ }
+ */
+ return getIMRequests();
+ }
+
+ @Override
+ public void dispose() {
+ if (inputMethod != null) {
+ closeIM(inputMethod);
+ inputMethod.dispose();
+ }
+ notifyIM.clear();
+ super.dispose();
+ }
+
+ @Override
+ public void endComposition() {
+ if (inputMethod != null) {
+ inputMethod.endComposition();
+ }
+ super.endComposition();
+ }
+
+ @Override
+ public Object getInputMethodControlObject() {
+ if (inputMethod != null) {
+ return inputMethod.getControlObject();
+ }
+ return super.getInputMethodControlObject();
+ }
+
+ @Override
+ public Locale getLocale() {
+ if (inputMethod != null) {
+ return inputMethod.getLocale();
+ }
+ return super.getLocale();
+ }
+
+ @Override
+ public boolean isCompositionEnabled() {
+ if (inputMethod != null) {
+ return inputMethod.isCompositionEnabled();
+ }
+ return super.isCompositionEnabled();
+ }
+
+ @Override
+ public void reconvert() {
+ if (inputMethod != null) {
+ inputMethod.reconvert();
+ }
+ super.reconvert();
+ }
+
+ //???AWT
+ /*
+ @Override
+ public void removeNotify(Component client) {
+ if ((inputMethod != null) && (client == this.client)) {
+ inputMethod.removeNotify();
+ client = null;
+ // set flag indicating that IM should be notified
+ // as soon as it is activated(new client appears)
+ pendingClientNotify = true;
+ }
+
+ super.removeNotify(client);
+ }
+ */
+
+ @Override
+ public boolean selectInputMethod(Locale locale) {
+
+ if ((inputMethod != null) && inputMethod.setLocale(locale)) {
+ return true;
+ }
+ // first
+ // take last user-selected IM for locale
+ InputMethod newIM = localeIM.get(locale);
+
+ // if not found search through IM descriptors
+ // and take already created instance if exists
+ // or create, store new IM instance in descriptor->instance map
+ //???AWT
+ /*
+ if (newIM == null) {
+ try {
+ newIM = getIMInstance(IMManager.getIMDescriptors().iterator(),
+ locale);
+ } catch (Exception e) {
+ // ignore exceptions - just return false
+ }
+ }
+ */
+
+ return switchToIM(locale, newIM);
+ }
+
+ private boolean switchToIM(Locale locale, InputMethod newIM) {
+ //???AWT
+ /*
+ if (newIM != null) {
+ closeIM(inputMethod);
+ client = KeyboardFocusManager.
+ getCurrentKeyboardFocusManager().getFocusOwner();
+ initIM(newIM, locale);
+ inputMethod = newIM;
+
+ return true;
+ }
+ */
+ return false;
+ }
+
+ /**
+ * Is called when IM is selected from UI
+ */
+ void selectIM(InputMethodDescriptor imd, Locale locale) {
+ try {
+ switchToIM(locale, getIMInstance(imd));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Gets input method instance for the given
+ * locale from the given list of descriptors
+ * @param descriptors iterator of the list of IM descriptors
+ * @param locale the locale to be supported by the IM
+ * @return input method instance
+ * @throws Exception
+ */
+ private InputMethod getIMInstance(Iterator<InputMethodDescriptor> descriptors,
+ Locale locale) throws Exception {
+ while (descriptors.hasNext()) {
+ InputMethodDescriptor desc = descriptors.next();
+ Locale[] locs = desc.getAvailableLocales();
+ for (Locale element : locs) {
+ if (locale.equals(element)) {
+ return getIMInstance(desc);
+ }
+ }
+ }
+ return null;
+ }
+
+ private InputMethod getIMInstance(InputMethodDescriptor imd) throws Exception {
+ InputMethod im = imInstances.get(imd);
+ if (im == null) {
+ im = imd.createInputMethod();
+ im.setInputMethodContext(this);
+ imInstances.put(imd, im);
+ }
+ return im;
+ }
+
+ private void initIM(InputMethod im, Locale locale) {
+ if (im == null) {
+ return;
+ }
+ im.setLocale(locale);
+ im.setCharacterSubsets(null);
+ //???AWT: activateIM(im);
+ try {
+ im.setCompositionEnabled(inputMethod != null ?
+ inputMethod.isCompositionEnabled() : true);
+ } catch (UnsupportedOperationException uoe) {
+
+ }
+
+ }
+
+ private void closeIM(InputMethod im) {
+ if (im == null) {
+ return;
+ }
+ if (im.isCompositionEnabled()) {
+ im.endComposition();
+ }
+
+ im.deactivate(true);
+ im.hideWindows();
+
+ }
+
+ @Override
+ public void setCharacterSubsets(Subset[] subsets) {
+ if (inputMethod != null) {
+ inputMethod.setCharacterSubsets(subsets);
+ }
+ super.setCharacterSubsets(subsets);
+ }
+
+ @Override
+ public void setCompositionEnabled(boolean enable) {
+ if (inputMethod != null) {
+ inputMethod.setCompositionEnabled(enable);
+ }
+ super.setCompositionEnabled(enable);
+ }
+
+ //???AWT
+ /*
+ public JFrame createInputMethodJFrame(String title,
+ boolean attachToInputContext) {
+ JFrame jf = new IMJFrame(title, attachToInputContext ? this : null);
+ imWindows.add(jf);
+ return jf;
+ }
+
+ public Window createInputMethodWindow(String title,
+ boolean attachToInputContext) {
+ Window w = new IMWindow(title, attachToInputContext ? this : null);
+ imWindows.add(w);
+ return w;
+ }
+ */
+
+ @SuppressWarnings("deprecation")
+ public void dispatchInputMethodEvent(int id,
+ AttributedCharacterIterator text,
+ int committedCharacterCount,
+ TextHitInfo caret,
+ TextHitInfo visiblePosition) {
+ if (client == null) {
+ return;
+ }
+ //???AWT
+ /*
+ InputMethodEvent ime = new InputMethodEvent(client, id, text,
+ committedCharacterCount,
+ caret, visiblePosition);
+
+
+ if ((client.getInputMethodRequests() != null) &&
+ !IMManager.belowTheSpot()) {
+
+ client.dispatchEvent(ime);
+ } else {
+
+ // show/hide composition window if necessary
+ if (committedCharacterCount < text.getEndIndex()) {
+ IMManager.showCompositionWindow(getCompositionWindow());
+ } else {
+ getCompositionWindow().hide();
+ }
+ composeWindow.getActiveClient().dispatchEvent(ime);
+ }
+ */
+
+ }
+
+ public void enableClientWindowNotification(InputMethod inputMethod,
+ boolean enable) {
+ if (enable) {
+ notifyIM.add(inputMethod);
+ //???AWT
+ /*
+ if (client != null) {
+ notifyClientWindowChange(IMManager.getWindow(client).getBounds());
+ } else {
+ pendingClientNotify = true;
+ }
+ */
+ } else {
+ notifyIM.remove(inputMethod);
+ }
+
+ }
+
+ public AttributedCharacterIterator cancelLatestCommittedText(
+ Attribute[] attributes) {
+ return getIMRequests().cancelLatestCommittedText(attributes);
+ }
+
+ public AttributedCharacterIterator getCommittedText(int beginIndex,
+ int endIndex,
+ Attribute[] attributes) {
+ return getIMRequests().getCommittedText(beginIndex, endIndex,
+ attributes);
+ }
+
+ public int getCommittedTextLength() {
+ return getIMRequests().getCommittedTextLength();
+ }
+
+ public int getInsertPositionOffset() {
+ return getIMRequests().getInsertPositionOffset();
+ }
+
+ public TextHitInfo getLocationOffset(int x, int y) {
+ InputMethodRequests imr = getStyleIMRequests();
+ if (imr != null) {
+ return imr.getLocationOffset(x, y);
+ }
+ return null;
+ }
+
+ public AttributedCharacterIterator getSelectedText(Attribute[] attributes) {
+ return getIMRequests().getSelectedText(attributes);
+ }
+
+ public Rectangle getTextLocation(TextHitInfo offset) {
+ return getStyleIMRequests().getTextLocation(offset);
+ }
+
+ /**
+ * To be called by AWT when client Window's bounds/visibility/state
+ * change
+ */
+ public void notifyClientWindowChange(Rectangle bounds) {
+ if (notifyIM.contains(inputMethod)) {
+ inputMethod.notifyClientWindowChange(bounds);
+ }
+ pendingClientNotify = false;
+ }
+
+ public final InputMethod getInputMethod() {
+ return inputMethod;
+ }
+
+ public final Component getClient() {
+ return client;
+ }
+
+ public final NativeIM getNativeIM() {
+ return nativeIM;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/internal/nls/Messages.java b/awt/org/apache/harmony/awt/internal/nls/Messages.java
new file mode 100644
index 0000000..96762c9
--- /dev/null
+++ b/awt/org/apache/harmony/awt/internal/nls/Messages.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL.
+ * All changes made to this file manually will be overwritten
+ * if this tool runs again. Better make changes in the template file.
+ */
+
+package org.apache.harmony.awt.internal.nls;
+
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+// BEGIN android-deleted
+/*
+ * For Android, this module is a separate library and not part of the
+ * boot classpath, so its resources won't be found on the boot classpath
+ * as is assumed by MsgHelp.getString(). We instead use a local MsgHelp
+ * which bottoms out in a call to the useful part of its lower-level
+ * namesake.
+ */
+//import org.apache.harmony.kernel.vm.VM;
+//import org.apache.harmony.luni.util.MsgHelp;
+// END android-deleted
+
+/**
+ * This class retrieves strings from a resource bundle and returns them,
+ * formatting them with MessageFormat when required.
+ * <p>
+ * It is used by the system classes to provide national language support, by
+ * looking up messages in the <code>
+ * org.apache.harmony.awt.internal.nls.messages
+ * </code>
+ * resource bundle. Note that if this file is not available, or an invalid key
+ * is looked up, or resource bundle support is not available, the key itself
+ * will be returned as the associated message. This means that the <em>KEY</em>
+ * should a reasonable human-readable (english) string.
+ *
+ */
+public class Messages {
+
+ // BEGIN android-deleted
+ //private static final String sResource =
+ // "org.apache.harmony.awt.internal.nls.messages";
+ // END android-deleted
+
+ /**
+ * Retrieves a message which has no arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg) {
+ return MsgHelp.getString(msg);
+ }
+
+ /**
+ * Retrieves a message which takes 1 argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * Object the object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg) {
+ return getString(msg, new Object[] { arg });
+ }
+
+ /**
+ * Retrieves a message which takes 1 integer argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * int the integer to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, int arg) {
+ return getString(msg, new Object[] { Integer.toString(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 1 character argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * char the character to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, char arg) {
+ return getString(msg, new Object[] { String.valueOf(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 2 arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg1
+ * Object an object to insert in the formatted output.
+ * @param arg2
+ * Object another object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg1, Object arg2) {
+ return getString(msg, new Object[] { arg1, arg2 });
+ }
+
+ /**
+ * Retrieves a message which takes several arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param args
+ * Object[] the objects to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object[] args) {
+ return MsgHelp.getString(msg, args);
+ }
+}
diff --git a/awt/org/apache/harmony/awt/internal/nls/MsgHelp.java b/awt/org/apache/harmony/awt/internal/nls/MsgHelp.java
new file mode 100644
index 0000000..b57fe11
--- /dev/null
+++ b/awt/org/apache/harmony/awt/internal/nls/MsgHelp.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This implementation is based on the class of the same name in
+ * org.apache.harmony.luni.util.
+ */
+
+package org.apache.harmony.awt.internal.nls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+import java.util.Locale;
+import java.util.PropertyResourceBundle;
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+/**
+ * This class contains helper methods for loading resource bundles and
+ * formatting external message strings.
+ */
+public final class MsgHelp {
+ /** name of the resource for this class */
+ private static final String RESOURCE_NAME =
+ "/org/apache/harmony/awt/internal/nls/messages.properties";
+
+ /** the resource bundle for this class */
+ private static final ResourceBundle THE_BUNDLE;
+
+ static {
+ ResourceBundle rb = null;
+
+ try {
+ InputStream in = MsgHelp.class.getResourceAsStream(
+ RESOURCE_NAME);
+ rb = new PropertyResourceBundle(in);
+ } catch (IOException ex) {
+ Logger.global.warning("Couldn't read resource bundle: " +
+ ex);
+ } catch (RuntimeException ex) {
+ // Shouldn't happen, but deal at least somewhat gracefully.
+ Logger.global.warning("Couldn't find resource bundle: " +
+ ex);
+ }
+
+ THE_BUNDLE = rb;
+ }
+
+ public static String getString(String msg) {
+ if (THE_BUNDLE == null) {
+ return msg;
+ }
+ try {
+ return THE_BUNDLE.getString(msg);
+ } catch (MissingResourceException e) {
+ return "Missing message: " + msg;
+ }
+ }
+
+ static public String getString(String msg, Object[] args) {
+ String format = msg;
+ if (THE_BUNDLE != null) {
+ try {
+ format = THE_BUNDLE.getString(msg);
+ } catch (MissingResourceException e) {
+ }
+ }
+
+ return org.apache.harmony.luni.util.MsgHelp.format(format, args);
+ }
+}
diff --git a/awt/org/apache/harmony/awt/state/MenuItemState.java b/awt/org/apache/harmony/awt/state/MenuItemState.java
new file mode 100644
index 0000000..b13e50b
--- /dev/null
+++ b/awt/org/apache/harmony/awt/state/MenuItemState.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.state;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+/**
+ * State of menu item
+ */
+
+public interface MenuItemState {
+
+ String getText();
+ Rectangle getTextBounds();
+ void setTextBounds(int x, int y, int w, int h);
+
+ String getShortcut();
+ Rectangle getShortcutBounds();
+ void setShortcutBounds(int x, int y, int w, int h);
+
+ Rectangle getItemBounds();
+ void setItemBounds(int x, int y, int w, int h);
+
+ boolean isMenu();
+ boolean isChecked();
+ boolean isEnabled();
+
+ boolean isCheckBox();
+ boolean isSeparator();
+
+ Dimension getMenuSize();
+}
diff --git a/awt/org/apache/harmony/awt/state/MenuState.java b/awt/org/apache/harmony/awt/state/MenuState.java
new file mode 100644
index 0000000..564a49a
--- /dev/null
+++ b/awt/org/apache/harmony/awt/state/MenuState.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.state;
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+
+/**
+ * State of pop-up or drop-down menu
+ */
+
+public interface MenuState {
+ int getWidth();
+ int getHeight();
+ Point getLocation();
+
+ void setSize(int w, int h);
+
+ Font getFont();
+ boolean isFontSet();
+ FontMetrics getFontMetrics(Font f);
+
+ int getItemCount();
+ int getSelectedItemIndex();
+
+ MenuItemState getItem(int index);
+}
diff --git a/awt/org/apache/harmony/awt/state/State.java b/awt/org/apache/harmony/awt/state/State.java
new file mode 100644
index 0000000..4b8706d
--- /dev/null
+++ b/awt/org/apache/harmony/awt/state/State.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.state;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Rectangle;
+
+/**
+ * State of the component
+ */
+public interface State {
+
+ boolean isEnabled();
+ boolean isVisible();
+ boolean isFocused();
+
+ Font getFont();
+ boolean isFontSet();
+ FontMetrics getFontMetrics();
+
+ Color getBackground();
+ boolean isBackgroundSet();
+
+ Color getTextColor();
+ boolean isTextColorSet();
+
+ Rectangle getBounds();
+ Dimension getSize();
+
+ Dimension getDefaultMinimumSize();
+ void setDefaultMinimumSize(Dimension size);
+
+ long getWindowId();
+}
diff --git a/awt/org/apache/harmony/awt/wtk/CreationParams.java b/awt/org/apache/harmony/awt/wtk/CreationParams.java
new file mode 100644
index 0000000..63c581d
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/CreationParams.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+/**
+ * This class describes cross-platform NativeWindow creation params
+ * See also WindowFactory.createWindow
+ */
+public class CreationParams {
+ /**
+ * Initial state is maximized verticaly
+ */
+ public final long MAXIMIZED_VERT = 1;
+ /**
+ * Initial state is maximized horizontaly
+ */
+ public final long MAXIMIZED_HORIZ = 2;
+ /**
+ * Initial state is maximized both
+ * horizontaly and verticaly
+ */
+ public final long MAXIMIZED = 3;
+
+ /**
+ * The top-level window that has all possible decorations,
+ * has no owner and is displayed in taskbar
+ */
+ public final static int DECOR_TYPE_FRAME = 1;
+ /**
+ * The dialog window
+ */
+ public final static int DECOR_TYPE_DIALOG = 2;
+ /**
+ * The transient undecorated pop-up window
+ */
+ public final static int DECOR_TYPE_POPUP = 3;
+ /**
+ * The undecoraded pop-up window
+ */
+ public final static int DECOR_TYPE_UNDECOR = 4;
+ /**
+ * Non-MDI child window
+ */
+ public final static int DECOR_TYPE_NONE = 0;
+
+ /**
+ * Initial x.
+ */
+ public int x = 0;
+ /**
+ * Initial y.
+ */
+ public int y = 0;
+ /**
+ * Initial width.
+ */
+ public int w = 1;
+ /**
+ * Initial height.
+ */
+ public int h = 1;
+ /**
+ * The decoration type of the top-level window. The possible values are:
+ * DECOR_TYPE_FRAME, DECOR_TYPE_DIALOG, DECOR_TYPE_POPUP and DECOR_TYPE_UNDECOR
+ */
+ public int decorType = DECOR_TYPE_NONE;
+ /**
+ * Window is child of parent, otherwise it's
+ * toplevel(child of desktop) window owned by parent.
+ */
+ public boolean child = false;
+ /**
+ * Window is resizable
+ */
+ public boolean resizable = true;
+ /**
+ * The window has no decorations
+ */
+ public boolean undecorated = false;
+ /**
+ * Initial visibility state.
+ */
+ public boolean visible = false;
+ /**
+ * Window is ALWAYS topmost in Z order.
+ */
+ public boolean topmost = false;
+ /**
+ * Window is disabled.
+ */
+ public boolean disabled = false;
+ /**
+ * Window initially iconified.
+ */
+ public boolean iconified = false;
+ /**
+ * Bitwise OR of MAXIMIZED_* constants.
+ * Means if window is initially maximized.
+ */
+ public int maximizedState = 0;
+ /**
+ * Tells that window position should be determined by native windowing system
+ */
+ public boolean locationByPlatform = false;
+ /**
+ * Id of parent or owner window, see child field
+ * For non-child window without owner equals 0.
+ */
+ public long parentId = 0;
+ /**
+ * Name wich is displayed on titlebar, taskbar and visible
+ * for system requests.
+ */
+ public String name = null;
+} \ No newline at end of file
diff --git a/awt/org/apache/harmony/awt/wtk/CursorFactory.java b/awt/org/apache/harmony/awt/wtk/CursorFactory.java
new file mode 100644
index 0000000..35e7d33
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/CursorFactory.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Dimension;
+import java.awt.Image;
+
+/**
+ * Provides factory for NativeCursor
+ */
+public abstract class CursorFactory {
+ protected NativeCursor[] systemCursors = {
+ null, null, null, null,
+ null, null, null, null,
+ null, null, null, null,
+ null, null,
+ };
+ /**
+ * Creates and returns NativeCursor for predefined
+ * Java Cursor
+ *
+ * @param type - type of predefined Java Cursor
+ * @return created cursor
+ */
+ public abstract NativeCursor createCursor(int type);
+
+ /**
+ * Gets a cached instance of system(predefined) native cursor
+ * or creates a new one. This is a platform-independent method.
+ *
+ * @param type - type of predefined Java Cursor
+ * @return created cursor
+ */
+ public NativeCursor getCursor(int type) {
+ if (type >= 0 && type < systemCursors.length) {
+ NativeCursor cursor = systemCursors[type];
+ if (cursor == null) {
+ cursor = createCursor(type);
+ systemCursors[type] = cursor;
+ }
+ return cursor;
+ }
+ return null;
+ }
+ /**
+ * Creates and returns custom NativeCursor from image
+ *
+ * @param img - image(source) to create cursor from
+ * @param xHotSpot - x coordinate of the hotspot relative to the source's origin
+ * @param yHotSpot - y coordinate of the hotspot relative to the source's origin
+ * @return created cursor
+ */
+ public abstract NativeCursor createCustomCursor(Image img, int xHotSpot, int yHotSpot);
+
+ /**
+ * Query native system for the best cursor size closest to specified dimensions
+ * @param prefWidth - preferred width
+ * @param prefHeight - preferred height
+ * @return closest supported dimensions to ones specified
+ */
+ public abstract Dimension getBestCursorSize(int prefWidth, int prefHeight);
+
+ /**
+ * @return maximum number of colors supported by custom cursors
+ */
+ public abstract int getMaximumCursorColors();
+}
diff --git a/awt/org/apache/harmony/awt/wtk/GraphicsFactory.java b/awt/org/apache/harmony/awt/wtk/GraphicsFactory.java
new file mode 100644
index 0000000..0d7c84f
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/GraphicsFactory.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov, Alexey A. Petrenko, Oleg V. Khaschansky
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.peer.FontPeer;
+import org.apache.harmony.awt.gl.MultiRectArea;
+import org.apache.harmony.awt.gl.font.FontManager;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+
+/**
+ * GraphicsFactory interface defines methods for Graphics2D
+ * and font stuff instances factories.
+ */
+public interface GraphicsFactory {
+ static final FontMetrics cacheFM[] = new FontMetrics[10];
+
+ /**
+ * This method creates Graphics2D instance for specified native window.
+ *
+ * @param win Native window to draw
+ * @param translateX Translation along X axis
+ * @param translateY Translation along Y axis
+ * @param clip Clipping area for a new Graphics2D instance
+ * @return New Graphics2D instance for specified native window
+ * @deprecated
+ */
+ @Deprecated
+ Graphics2D getGraphics2D(NativeWindow win, int translateX, int translateY, MultiRectArea clip);
+
+ /**
+ * This method creates Graphics2D instance for specified native window.
+ *
+ * @param win Native window to draw
+ * @param translateX Translation along X axis
+ * @param translateY Translation along Y axis
+ * @param width Width of drawing area
+ * @param height Height of drawing area
+ * @return New Graphics2D instance for specified native window
+ */
+ Graphics2D getGraphics2D(NativeWindow win, int translateX, int translateY, int width, int height);
+ // ???AWT: not standard harmony
+ Graphics2D getGraphics2D(Canvas c, Paint p);
+
+ /**
+ * Creates instance of GraphicsEnvironment for specified WindowFactory
+ *
+ * @param wf WindowFactory
+ * @return New instance of GraphicsEnvironment
+ */
+ GraphicsEnvironment createGraphicsEnvironment(WindowFactory wf);
+
+ // Font methods
+ FontMetrics getFontMetrics(Font font);
+ FontManager getFontManager();
+ FontPeer getFontPeer(Font font);
+ Font embedFont(String fontFilePath);
+}
diff --git a/awt/org/apache/harmony/awt/wtk/KeyInfo.java b/awt/org/apache/harmony/awt/wtk/KeyInfo.java
new file mode 100644
index 0000000..1f8a29a
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/KeyInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.event.KeyEvent;
+
+/**
+ * Keystroke information
+ */
+
+public final class KeyInfo {
+
+ public int vKey;
+ public int keyLocation;
+ public final StringBuffer keyChars;
+
+ public static final int DEFAULT_VKEY = KeyEvent.VK_UNDEFINED;
+ public static final int DEFAULT_LOCATION = KeyEvent.KEY_LOCATION_STANDARD;
+
+ public KeyInfo() {
+ vKey = DEFAULT_VKEY;
+ keyLocation = DEFAULT_LOCATION;
+ keyChars = new StringBuffer();
+ }
+
+ public void setKeyChars(char ch) {
+ keyChars.setLength(0);
+ keyChars.append(ch);
+ }
+
+ public void setKeyChars(StringBuffer sb) {
+ keyChars.setLength(0);
+ keyChars.append(sb);
+ }
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeCursor.java b/awt/org/apache/harmony/awt/wtk/NativeCursor.java
new file mode 100644
index 0000000..2c6eb1e
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeCursor.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+/**
+ * The interface provides access to platform dependent functionality
+ * for the class java.awt.Cursor.
+ */
+public interface NativeCursor {
+ /**
+ * Sets the current cursor shape
+ * to this cursor when a pointer is inside
+ * @param winID - window(currently used only on X11)
+ */
+ void setCursor(long winID);
+ /**
+ * Destroys the native resource associated with
+ * this cursor
+ */
+ void destroyCursor();
+
+ /**
+ * @return Native handle associated with this cursor
+ */
+ long getId();
+
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeEvent.java b/awt/org/apache/harmony/awt/wtk/NativeEvent.java
new file mode 100644
index 0000000..1471c1a
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeEvent.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Mikhail Danilov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+
+
+/**
+ * The interface describing cross-platform translation of system
+ * messages.
+ *
+ * <p/>Some messages can appear only on specific platform,
+ * but they still can have cross-platform interpretation if the
+ * application should be aware of them and can react using
+ * cross-platform API.
+ *
+ */
+public abstract class NativeEvent {
+
+ /**
+ * Message has no common cross-platform
+ * interpretation and should be skipped.
+ */
+ public static final int ID_PLATFORM = 0;
+
+ /**
+ * Window bounds have changed.
+ */
+ public static final int ID_BOUNDS_CHANGED = -1;
+
+ /**
+ * Window decoration size has changed.
+ */
+ public static final int ID_INSETS_CHANGED = -2;
+
+ /**
+ * Window was just created (WM_CREATE on Windows)
+ */
+ public static final int ID_CREATED = -3;
+
+ /**
+ * Mouse grab was canceled by the native system
+ */
+ public static final int ID_MOUSE_GRAB_CANCELED = -4;
+
+ /**
+ * System color scheme or visual theme was changed
+ */
+ public static final int ID_THEME_CHANGED = -5;
+
+ protected long windowId;
+ protected int eventId;
+ protected long otherWindowId;
+
+ protected Point screenPos;
+ protected Point localPos;
+ protected Rectangle windowRect;
+
+ protected int modifiers;
+ protected int mouseButton;
+ protected int wheelRotation;
+
+ protected KeyInfo keyInfo = new KeyInfo();
+
+ protected int windowState = -1;
+ protected long time;
+
+ /**
+ * Returns the system window id of the event recipient.
+ * @return HWND on Windows, xwindnow on X
+ */
+ public long getWindowId() {
+ return windowId;
+ }
+
+ /**
+ * Returns cross-platform event id
+ * should be one of ID_* constants or
+ * id constants from java.awt.AWTEvent subclasess
+ * @return cross-platform event id
+ */
+ public int getEventId() {
+ return eventId;
+ }
+
+ /**
+ * Returns the position of cursor when event occured relative to
+ * top-left corner of recipient window
+ * @return position of cursor in local coordinates
+ */
+ public Point getLocalPos() {
+ return localPos;
+ }
+
+ /**
+ * Returns the position of cursor when event occured
+ * in screen coordinates.
+ * @return position of cursor in screen coordinates
+ */
+ public Point getScreenPos() {
+ return screenPos;
+ }
+
+ /**
+ * The recipient window bounds when the event occured
+ * @return window bounds
+ */
+ public Rectangle getWindowRect() {
+ return windowRect;
+ }
+
+ /**
+ * Returns the state of keyboard and mouse buttons when the event
+ * occured if event from mouse or keyboard, for other events can
+ * return junk values. The value is bitwise OR of
+ * java.awt.event.InputEvent *_DOWN constants.
+ *
+ * Method is aware of system mouse button swap for left-hand
+ * mouse and return swapped values.
+ * @return bitwise OR of java.awt.event.InputEvent *_DOWN constants
+ */
+ public int getInputModifiers() {
+ return modifiers;
+ }
+
+ /**
+ * Returns the iconified/maximized state of recipient window if
+ * event is state related, for other events can junk values.
+ * The value has the same meaning as Frame.getExtendedState
+ * It's bitwise OR of ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT
+ * @return bitwise OR of ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT
+ */
+ public int getWindowState() {
+ return windowState;
+ }
+
+ /**
+ * The same meaning as java.awt.event.getKeyCode
+ * @return java.awt.event VK_* constant
+ */
+ public int getVKey() {
+ return (keyInfo != null) ? keyInfo.vKey : KeyInfo.DEFAULT_VKEY;
+ }
+
+ /**
+ * The same meaning as java.awt.event.getKeyLocation
+ * @return java.awt.event KEY_LOCATION_* constant
+ */
+ public int getKeyLocation() {
+ return (keyInfo != null) ? keyInfo.keyLocation : KeyInfo.DEFAULT_LOCATION;
+ }
+
+ /**
+ * Return the string of characters associated with the event
+ * Has meaning only for KEY_PRESSED as should be translated to
+ * serie of KEY_TYPED events. For dead keys and input methods
+ * one key press can generate multiple key chars.
+ * @return string of characters
+ */
+ public StringBuffer getKeyChars() {
+ if (keyInfo == null) {
+ return null;
+ }
+ if (keyInfo.vKey == KeyEvent.VK_ENTER) {
+ keyInfo.keyChars.setLength(0);
+ keyInfo.setKeyChars('\n');
+ }
+ return keyInfo.keyChars;
+ }
+
+ public char getLastChar() {
+ if (keyInfo == null || keyInfo.keyChars.length() == 0) {
+ return KeyEvent.CHAR_UNDEFINED;
+ }
+ return keyInfo.keyChars.charAt(keyInfo.keyChars.length()-1);
+ }
+
+ /**
+ * Returns the number of mouse button which changed it's state,
+ * otherwise 0.
+ * Left button is 1, middle button is 2, right button is 3.
+ *
+ * Method is aware of system mouse button swap for left-hand
+ * mouse and return swapped values.
+ * @return mouse button number
+ */
+ public int getMouseButton() {
+ return mouseButton;
+ }
+
+ /**
+ * Returns time when the message was received
+ * @return time in milliseconds
+ */
+ public long getTime() {
+ return time;
+ }
+
+ /**
+ * For the focus event contains the oposite window.
+ * This means it lost focus if recipient gains it,
+ * or will gain focus if recipient looses it.
+ * @return HWND on Windows, xwindnow on X
+ */
+ public long getOtherWindowId() {
+ return otherWindowId;
+ }
+
+ /**
+ * Returns the "dirty" area of the window as set of non-intersecting
+ * rectangles. This area is to be painted.
+ * @return non-empty array of null if empty
+ */
+ public abstract MultiRectArea getClipRects();
+
+ /**
+ * Returns the "dirty" area of the window as one rectangle.
+ * This area is to be painted.
+ * @return non-null Rectangle
+ */
+ public abstract Rectangle getClipBounds();
+
+ /**
+ * Returns the window insets. Insets is area which belongs to
+ * window somehow but is outside of it's client area,
+ * it usually contains system provided border and titlebar.
+ * @return non-null java.awt.Insets
+ */
+ public abstract Insets getInsets();
+
+ /**
+ * Returns true if event is popup menu trigger.
+ * @return boolean flag
+ */
+ public abstract boolean getTrigger();
+
+ /**
+ * Returns the number of "clicks" the mouse wheel was rotated.
+ * @return negative values if the mouse wheel was rotated up/away from the user,
+ * and positive values if the mouse wheel was rotated down/ towards the user
+ */
+ public int getWheelRotation() {
+ return wheelRotation;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeEventQueue.java b/awt/org/apache/harmony/awt/wtk/NativeEventQueue.java
new file mode 100644
index 0000000..0738cd1
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeEventQueue.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Mikhail Danilov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.util.LinkedList;
+
+
+/**
+ * Describes the cross-platform native event queue interface
+ *
+ * <p/> The implementation constructor should remember thread it was
+ * created. All other methods would be called obly from this thread,
+ * except awake().
+ */
+public abstract class NativeEventQueue {
+
+ private ShutdownWatchdog shutdownWatchdog;
+ private class EventMonitor {}
+ private final Object eventMonitor = new EventMonitor();
+ private final LinkedList<NativeEvent> eventQueue = new LinkedList<NativeEvent>();
+
+ public static abstract class Task {
+ public volatile Object returnValue;
+
+ public abstract void perform();
+ }
+
+ /**
+ * Blocks current thread until native event queue is not empty
+ * or awaken from other thread by awake().
+ *
+ * <p/>Should be called only on tread which
+ * will process native events.
+ *
+ * @return if event loop should be stopped
+ */
+ public abstract boolean waitEvent();
+
+ /**
+ * Determines whether or not the native event queue is empty.
+ * An queue is empty if it contains no messages waiting.
+ *
+ * @return true if the queue is empty; false otherwise
+ */
+ public boolean isEmpty() {
+ synchronized(eventQueue) {
+ return eventQueue.isEmpty();
+ }
+ }
+
+ public NativeEvent getNextEvent() {
+ synchronized (eventQueue) {
+ if (eventQueue.isEmpty()) {
+ shutdownWatchdog.setNativeQueueEmpty(true);
+ return null;
+ }
+ return eventQueue.remove(0);
+ }
+ }
+
+ protected void addEvent(NativeEvent event) {
+ synchronized (eventQueue) {
+ eventQueue.add(event);
+ shutdownWatchdog.setNativeQueueEmpty(false);
+ }
+ synchronized (eventMonitor) {
+ eventMonitor.notify();
+ }
+ }
+
+ public final Object getEventMonitor() {
+ return eventMonitor;
+ }
+
+ public abstract void awake();
+
+ /**
+ * Gets AWT system window ID.
+ *
+ * @return AWT system window ID
+ */
+ public abstract long getJavaWindow();
+
+ /**
+ * Add NativeEvent to the queue
+ */
+ public abstract void dispatchEvent();
+
+ public abstract void performTask(Task task);
+
+ public abstract void performLater(Task task);
+
+ public final void setShutdownWatchdog(ShutdownWatchdog watchdog) {
+ synchronized (eventQueue) {
+ shutdownWatchdog = watchdog;
+ }
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeEventThread.java b/awt/org/apache/harmony/awt/wtk/NativeEventThread.java
new file mode 100644
index 0000000..d50add4
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeEventThread.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+
+/**
+ * NativeEventThread
+ */
+public class NativeEventThread extends Thread {
+
+ public interface Init {
+ WTK init();
+ }
+
+ NativeEventQueue nativeQueue;
+ Init init;
+
+ private WTK wtk;
+
+ public NativeEventThread() {
+ super("AWT-NativeEventThread"); //$NON-NLS-1$
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ synchronized (this) {
+ try {
+ wtk = init.init();
+ nativeQueue = wtk.getNativeEventQueue();
+ } finally {
+ notifyAll();
+ }
+ }
+
+ runModalLoop();
+ }
+
+ void runModalLoop() {
+ while (nativeQueue.waitEvent()) {
+ nativeQueue.dispatchEvent();
+ }
+ }
+
+ public void start(Init init) {
+ synchronized (this) {
+ this.init = init;
+ super.start();
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public WTK getWTK() {
+ return wtk;
+ }
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeIM.java b/awt/org/apache/harmony/awt/wtk/NativeIM.java
new file mode 100644
index 0000000..1626f4a
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeIM.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.AWTEvent;
+import java.awt.AWTException;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.im.spi.InputMethod;
+import java.awt.im.spi.InputMethodContext;
+import java.awt.im.spi.InputMethodDescriptor;
+import java.lang.Character.Subset;
+import java.util.Locale;
+
+/**
+ * A cross-platform interface for native input
+ * method sub-system functionality.
+ */
+public abstract class NativeIM implements InputMethod, InputMethodDescriptor {
+ protected InputMethodContext imc;
+
+ public void activate() {
+
+ }
+
+ public void deactivate(boolean isTemporary) {
+
+ }
+
+ public void dispatchEvent(AWTEvent event) {
+
+ }
+
+ public void dispose() {
+
+ }
+
+ public void endComposition() {
+
+ }
+
+ public Object getControlObject() {
+ return null;
+ }
+
+ public Locale getLocale() {
+ return null;
+ }
+
+ public void hideWindows() {
+
+ }
+
+ public boolean isCompositionEnabled() {
+ return false;
+ }
+
+ public void notifyClientWindowChange(Rectangle bounds) {
+
+ }
+
+ public void reconvert() {
+
+ }
+
+ public void removeNotify() {
+
+ }
+
+ public void setCharacterSubsets(Subset[] subsets) {
+
+ }
+
+ public void setCompositionEnabled(boolean enable) {
+
+ }
+
+ public void setInputMethodContext(InputMethodContext context) {
+ imc = context;
+ }
+
+ public boolean setLocale(Locale locale) {
+ return false;
+ }
+
+ public Locale[] getAvailableLocales() throws AWTException {
+ return new Locale[]{Locale.getDefault(), Locale.ENGLISH};
+ //return new Locale[]{Locale.getDefault(), Locale.US};
+ }
+
+ public InputMethod createInputMethod() throws Exception {
+ return this;
+ }
+
+ public String getInputMethodDisplayName(Locale inputLocale,
+ Locale displayLanguage) {
+ return "System input methods"; //$NON-NLS-1$
+ }
+
+ public Image getInputMethodIcon(Locale inputLocale) {
+ return null;
+ }
+
+ public boolean hasDynamicLocaleList() {
+ return false;
+ }
+
+ public abstract void disableIME();
+
+// public abstract void disableIME(long id);
+
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java b/awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java
new file mode 100644
index 0000000..0696975
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Point;
+
+/**
+ * The interface provides access to platform dependent functionality
+ * for classes java.awt.PointerInfo & java.awt.MouseInfo.
+ */
+public interface NativeMouseInfo {
+
+ /**
+ * Returns the Point that represents
+ * the coordinates of the pointer on the screen.
+ */
+ Point getLocation();
+
+ /**
+ * Returns the number of buttons on the mouse.
+ * If no mouse is installed returns -1.
+ */
+ int getNumberOfButtons();
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeRobot.java b/awt/org/apache/harmony/awt/wtk/NativeRobot.java
new file mode 100644
index 0000000..0b354d0
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeRobot.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Dmitry A. Durnev
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+/**
+ * A cross-platform interface for java.awt.Robot implementation
+ */
+public interface NativeRobot {
+
+ /**
+ * @see java.awt.Robot#createScreenCapture(Rectangle)
+ * @param screenRect rectangle to capture in screen coordinates
+ * @return the captured image or null if
+ * capture failed.
+ */
+ BufferedImage createScreenCapture(Rectangle screenRect);
+
+ /**
+ * @see java.awt.Robot#getPixelColor(int, int)
+ */
+ Color getPixel(int x, int y);
+
+ /**
+ * Generate a native system keyboard input event.
+ * @param keycode A Java virtual key code
+ * @param press A key is pressed if true, released otherwise
+ * @see java.awt.Robot#keyPress(int)
+ * @throws IllegalArgumentException if keycode is invalid in the native system
+ */
+ void keyEvent(int keycode, boolean press);
+
+ /**
+ * Generate a native system mouse button(s) press or release event.
+ * @param buttons A mask of Java mouse button flags
+ * @param press buttons are pressed if true, released otherwise
+ * @see java.awt.Robot#mousePress(int)
+ */
+ void mouseButton(int buttons, boolean press);
+
+ /**
+ * Generate a native system mouse motion event.
+ *
+ * @see java.awt.Robot#mouseMove(int, int)
+ */
+ void mouseMove(int x, int y);
+
+ /**
+ * Generate a native system mouse wheel event.
+ *
+ * @see java.awt.Robot#mouseWheel(int)
+ */
+ void mouseWheel(int wheelAmt);
+}
diff --git a/awt/org/apache/harmony/awt/wtk/NativeWindow.java b/awt/org/apache/harmony/awt/wtk/NativeWindow.java
new file mode 100644
index 0000000..73fd6c0
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/NativeWindow.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Mikhail Danilov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+
+import org.apache.harmony.awt.gl.MultiRectArea;
+
+
+/**
+ * Provides cross-platform way to manipulate native window.
+ *
+ * Results of methods are reported through native messages.
+ */
+public interface NativeWindow {
+ /**
+ * Returns system id of the associated window
+ * @return HWND on Windows, xwindow on X
+ */
+ long getId();
+
+ /**
+ * Shows/hides window
+ * @param v - new visibility
+ */
+ void setVisible(boolean v);
+
+ /**
+ * Means only size should be changed
+ */
+ static final int BOUNDS_NOMOVE = 1;
+
+ /**
+ * Means only position should be changed
+ */
+ static final int BOUNDS_NOSIZE = 2;
+
+ /**
+ * Tries to set desired window bounds. It's not gurantied the
+ * property will have the desired value. The value change
+ * should be reported by system event (as for other properties).
+ *
+ * <p/> If child, position is relative to parent window.
+ * @param x - desired x
+ * @param y - desired y
+ * @param w - desired width
+ * @param h - desired height
+ * @param boundsMask - bitwise OR of BOUNDS_* constants.
+ * Governs the new bounds interpretation.
+ */
+ void setBounds(int x, int y, int w, int h, int boundsMask);
+
+ /**
+ * Returns last notified window bounds. This means the last bounds
+ * reported by system event.
+ *
+ * <p/> If child, position is relative to parent window.
+ * @return last notified window bounds
+ */
+ Rectangle getBounds();
+
+ /**
+ * Returns last notified insets. This means the last insets
+ * reported by system event. Insets are margins around client area
+ * ocupied by system provided decor, ususally border and titlebar.
+ * @return last notified insets
+ */
+ Insets getInsets();
+
+ /**
+ * Enables/disables processing of input (key, mouse) event
+ * by window. If disabled input events are ignored.
+ * @param value - if enabled
+ */
+ void setEnabled(boolean value);
+
+ /**
+ * Sets the "focusable" window state.
+ * @param value - if true makes window focusable
+ */
+ void setFocusable(boolean value);
+
+ /**
+ *
+ * @return current focusable window state
+ */
+ boolean isFocusable();
+
+ /**
+ * Tries to set application input focus to the window or clear
+ * current focus from focused window.
+ *
+ * <p/> For toplevel windows it's not gurantied focus will land in
+ * desired window even if function returns true. Focus traversal should be tracked
+ * by processing system events.
+ *
+ * @param focus - if true sets focus, else clears focus
+ * @return if success
+ */
+ boolean setFocus(boolean focus);
+
+ /**
+ * Destroys the asscoiated window.
+ * Attempts to use it thereafter can result in
+ * unpredictable bechavior.
+ */
+ void dispose();
+
+ /**
+ * Changes window Z-order to place this window under, If w is null
+ * places places this window on the top. Z-order is per parent.
+ * Toplevels a children of desktop in terms of Z-order.
+ * @param w - window to place under.
+ */
+ void placeAfter(NativeWindow w);
+
+ /**
+ * Places window on top of Z-order
+ */
+ void toFront();
+
+ /**
+ * Places window on bottom of Z-order
+ */
+ void toBack();
+
+ /**
+ * Makes the window resizable/not resizable by user
+ * @param value - if resizable
+ */
+ void setResizable(boolean value);
+
+ /**
+ * Sets the window caption
+ * @param title - caption text
+ */
+ void setTitle(String title);
+
+ /**
+ * Activate the mouse event capturing
+ */
+ void grabMouse();
+
+ /**
+ * Deactivate mouse event capturing
+ */
+ void ungrabMouse();
+
+ /**
+ * Set extended state for top-level window.
+ *
+ * @param state - new state, bitmask of ICONIFIED, MAXIMIZED_BOTH, etc.
+ */
+ void setState(int state);
+
+ /**
+ * Set the image to be displayed in the minimized icon for
+ * top-level [decorated] window.
+ * @param image the icon image to be displayed
+ */
+ void setIconImage(Image image);
+
+ /**
+ * Makes window top-most if value is true,
+ * non-topmost(normal) otherwise.
+ */
+ void setAlwaysOnTop(boolean value);
+
+ /**
+ * Set desired [top-level] window bounds when being in maximized state.
+ * Fields set to Integer.MAX_VALUE are ignored[system-supplied values are
+ * used instead]
+ */
+ void setMaximizedBounds(Rectangle bounds);
+
+ /**
+ * Get absolute position on the screen
+ */
+ Point getScreenPos();
+
+ /**
+ * Set a window "packed" flag:
+ * the flag indicates that if insets change
+ * client area shouldn't be resized, but frame
+ * must be resized instead
+ */
+ void setPacked(boolean packed);
+
+ /**
+ * Make window an "input method window" by setting
+ * special window style, e. g. small title bar, no
+ * close, minimize/maximize buttons. For internal
+ * use by input method framework.
+ *
+ */
+ void setIMStyle();
+
+ MultiRectArea getObscuredRegion(Rectangle part);
+}
diff --git a/awt/org/apache/harmony/awt/wtk/ShutdownThread.java b/awt/org/apache/harmony/awt/wtk/ShutdownThread.java
new file mode 100644
index 0000000..701eb46
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/ShutdownThread.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Michael Danilov, Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+public final class ShutdownThread extends Thread {
+
+ public static final class Watchdog {
+ }
+
+ public ShutdownThread() {
+ setName("AWT-Shutdown"); //$NON-NLS-1$
+ setDaemon(false);
+ }
+
+ private boolean shouldStop = false;
+
+ @Override
+ public void run() {
+ synchronized (this) {
+ notifyAll(); // synchronize the startup
+
+ while (true) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+
+ if (shouldStop) {
+ notifyAll(); // synchronize the shutdown
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void start() {
+ synchronized (this) {
+ super.start();
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // awt.26=Shutdown thread was interrupted while starting
+ throw new RuntimeException(
+ Messages.getString("awt.26")); //$NON-NLS-1$
+ }
+ }
+ }
+
+ public void shutdown() {
+ synchronized (this) {
+ shouldStop = true;
+ notifyAll();
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // awt.27=Shutdown thread was interrupted while stopping
+ throw new RuntimeException(
+ Messages.getString("awt.27")); //$NON-NLS-1$
+ }
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java b/awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java
new file mode 100644
index 0000000..6efa519
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+/**
+ * Shutdown Watchdog
+ */
+public final class ShutdownWatchdog {
+
+ private boolean nativeQueueEmpty = true;
+ private boolean awtQueueEmpty = true;
+ private boolean windowListEmpty = true;
+
+ private boolean forcedShutdown = false;
+
+ private ShutdownThread thread;
+
+ public synchronized void setNativeQueueEmpty(boolean empty) {
+ nativeQueueEmpty = empty;
+ checkShutdown();
+ }
+
+ public synchronized void setAwtQueueEmpty(boolean empty) {
+ awtQueueEmpty = empty;
+ checkShutdown();
+ }
+
+ public synchronized void setWindowListEmpty(boolean empty) {
+ windowListEmpty = empty;
+ checkShutdown();
+ }
+
+ public synchronized void forceShutdown() {
+ forcedShutdown = true;
+ shutdown();
+ }
+
+ public synchronized void start() {
+ keepAlive();
+ }
+
+ private void checkShutdown() {
+ if (canShutdown()) {
+ shutdown();
+ } else {
+ keepAlive();
+ }
+ }
+
+ private boolean canShutdown() {
+ return (nativeQueueEmpty && awtQueueEmpty && windowListEmpty) ||
+ forcedShutdown;
+ }
+
+ private void keepAlive() {
+ if (thread == null) {
+ thread = new ShutdownThread();
+ thread.start();
+ }
+ }
+
+ private void shutdown() {
+ if (thread != null) {
+ thread.shutdown();
+ thread = null;
+ }
+ }
+}
diff --git a/awt/org/apache/harmony/awt/wtk/Synchronizer.java b/awt/org/apache/harmony/awt/wtk/Synchronizer.java
new file mode 100644
index 0000000..3eeaa0b
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/Synchronizer.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Mikhail Danilov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.util.Hashtable;
+import java.util.LinkedList;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+
+/**
+ * Class synchronizer is to protect AWT state integrity in multithreading environment.
+ * It is supposed to have a child class per native platform.
+ * The only instance is created on the first use of one of the core AWT classes.
+ * Registers WTK on the dispatch thread startup.
+ * It is just a special kind of mutex.
+ *
+ */
+
+public class Synchronizer {
+ //TODO: think about java.util.concurrent use for faster blocking/awaking operations
+ //TODO: think about all synchronized methods. Is there need to synchronize everything?
+
+ /**
+ * This field holds the counter of lock operation.
+ * To free synchronizer unlock method must be called $acquestCounter times.
+ * Equals to 0 when synchronizer is free.
+ */
+ protected int acquestCounter;
+
+ /**
+ * This field holds the owner of synchronizer.
+ * Owner of synchronizer is a last thread that successfully locked synchronizer and
+ * still havn't freed it. Equals to null when synchronizer is free.
+ */
+ protected Thread owner;
+
+ /**
+ * This field holds the wait queue.
+ * Wait queue is a queue where thread wait for synchronizer access.
+ * Empty when synchronizer is free.
+ */
+ protected final LinkedList<Thread> waitQueue = new LinkedList<Thread>();
+
+ /**
+ * The event dispatch thread
+ */
+ protected Thread dispatchThread;
+
+ private final Hashtable<Thread, Integer> storedStates = new Hashtable<Thread, Integer>();
+
+ /**
+ * Acquire the lock for this synchronizer. Nested lock is supported.
+ * If the mutex is already locked by another thread, the current thread will be put
+ * into wait queue until the lock becomes available.
+ * All user threads are served in FIFO order. Dispatch thread has higher priority.
+ * Supposed to be used in Toolkit.lockAWT() only.
+ */
+ public void lock() {
+ synchronized (this) {
+ Thread curThread = Thread.currentThread();
+
+ if (acquestCounter == 0) {
+ acquestCounter = 1;
+ owner = curThread;
+ } else {
+ if (owner == curThread) {
+ acquestCounter++;
+ } else {
+ if (curThread == dispatchThread) {
+ waitQueue.addFirst(curThread);
+ } else {
+ waitQueue.addLast(curThread);
+ }
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ if (owner != curThread) {
+ waitQueue.remove(curThread);
+ // awt.1F=Waiting for resource access thread interrupted not from unlock method.
+ throw new RuntimeException(Messages
+ .getString("awt.1F")); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Release the lock for this synchronizer.
+ * If wait queue is not empty the first waiting thread acquires the lock.
+ * Supposed to be used in Toolkit.unlockAWT() only.
+ */
+ public void unlock() {
+ synchronized (this) {
+ if (acquestCounter == 0) {
+ // awt.20=Can't unlock not locked resource.
+ throw new RuntimeException(Messages.getString("awt.20")); //$NON-NLS-1$
+ }
+ if (owner != Thread.currentThread()) {
+ // awt.21=Not owner can't unlock resource.
+ throw new RuntimeException(Messages.getString("awt.21")); //$NON-NLS-1$
+ }
+
+ acquestCounter--;
+ if (acquestCounter == 0) {
+ if (waitQueue.size() > 0) {
+ acquestCounter = 1;
+ owner = waitQueue.removeFirst();
+ owner.interrupt();
+ } else {
+ owner = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Stores state of this synchronizer and frees it.
+ * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with
+ * lockAndRestoreState().
+ * Do not call it directly.
+ */
+ public void storeStateAndFree() {
+ synchronized (this) {
+ Thread curThread = Thread.currentThread();
+
+ if (owner != curThread) {
+ // awt.22=Not owner can't free resource.
+ throw new RuntimeException(Messages.getString("awt.22")); //$NON-NLS-1$
+ }
+ if (storedStates.containsKey(curThread)) {
+ // awt.23=One thread can't store state several times in a row.
+ throw new RuntimeException(Messages.getString("awt.23")); //$NON-NLS-1$
+ }
+
+ storedStates.put(curThread, new Integer(acquestCounter));
+ acquestCounter = 1;
+ unlock();
+ }
+ }
+
+ /**
+ * Locks this synchronizer and restores it's state.
+ * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with
+ * storeStateAndFree().
+ * Do not call it directly.
+ */
+ public void lockAndRestoreState() {
+ synchronized (this) {
+ Thread curThread = Thread.currentThread();
+
+ if (owner == curThread) {
+ // awt.24=Owner can't overwrite resource state. Lock operations may be lost.
+ throw new RuntimeException(
+ Messages.getString("awt.24")); //$NON-NLS-1$
+ }
+ if (!storedStates.containsKey(curThread)) {
+ // awt.25=No state stored for current thread.
+ throw new RuntimeException(Messages.getString("awt.25")); //$NON-NLS-1$
+ }
+
+ lock();
+ acquestCounter = storedStates.get(curThread).intValue();
+ storedStates.remove(curThread);
+ }
+ }
+
+ /**
+ * Sets references to WTK and event dispatch thread.
+ * Called on toolkit startup.
+ *
+ * @param wtk - reference to WTK instance
+ * @param dispatchThread - reference to event dispatch thread
+ */
+ public void setEnvironment(WTK wtk, Thread dispatchThread) {
+ synchronized (this) {
+ this.dispatchThread = dispatchThread;
+ }
+ }
+
+}
diff --git a/awt/org/apache/harmony/awt/wtk/SystemProperties.java b/awt/org/apache/harmony/awt/wtk/SystemProperties.java
new file mode 100644
index 0000000..6b59f0e
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/SystemProperties.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Font;
+import java.awt.font.TextAttribute;
+import java.awt.im.InputMethodHighlight;
+import java.util.Map;
+
+/**
+ * NativeProperties
+ */
+
+public interface SystemProperties {
+
+ /**
+ * Get current value of a system color
+ * @param index - one of java.awt.SystemColor constants
+ * @return ARGB value of requested system color
+ */
+ int getSystemColorARGB(int index);
+
+ /**
+ * Get default font for GUI elements such as menus and buttons
+ * @return the font object
+ */
+ Font getDefaultFont();
+
+ /**
+ * Fill the given Map with system properties
+ */
+ void init(Map<String, ?> desktopProperties);
+
+ /**
+ * Fills the given map with system-dependent visual text
+ * attributes for the abstract description
+ * of the given input method highlight
+ * @see java.awt.Toolkit.mapInputMethodHighlight()
+ */
+ void mapInputMethodHighlight(InputMethodHighlight highlight, Map<TextAttribute, ?> map);
+}
diff --git a/awt/org/apache/harmony/awt/wtk/WTK.java b/awt/org/apache/harmony/awt/wtk/WTK.java
new file mode 100644
index 0000000..4162fbd
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/WTK.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Pavel Dolgov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.GraphicsDevice;
+
+
+public abstract class WTK {
+
+ public abstract GraphicsFactory getGraphicsFactory();
+ public abstract NativeEventQueue getNativeEventQueue();
+ public abstract WindowFactory getWindowFactory();
+
+ /**
+ * Returns platform specific implementation of the interface
+ * org.apache.harmony.awt.wtk.CursorFactory.
+ * @return implementation of CursorFactory
+ */
+ public abstract CursorFactory getCursorFactory();
+
+ /**
+ * Returns platform specific implementation of the interface
+ * org.apache.harmony.awt.wtk.NativeMouseInfo.
+ * @return implementation of NativeMouseInfo
+ */
+ public abstract NativeMouseInfo getNativeMouseInfo();
+
+ public abstract SystemProperties getSystemProperties();
+
+ /**
+ * Returns platform specific implementation of the interface
+ * org.apache.harmony.awt.wtk.NativeRobot.
+ * @return implementation of NativeRobot
+ */
+ public abstract NativeRobot getNativeRobot(GraphicsDevice screen);
+
+ /**
+ * Returns platform specific implementation of the abstract
+ * class org.apache.harmony.awt.wtk.NativeIM.
+ * @return implementation of NativeIM
+ */
+ public abstract NativeIM getNativeIM();
+}
diff --git a/awt/org/apache/harmony/awt/wtk/WindowFactory.java b/awt/org/apache/harmony/awt/wtk/WindowFactory.java
new file mode 100644
index 0000000..23604da
--- /dev/null
+++ b/awt/org/apache/harmony/awt/wtk/WindowFactory.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Mikhail Danilov
+ * @version $Revision$
+ */
+package org.apache.harmony.awt.wtk;
+
+import java.awt.Dimension;
+import java.awt.Point;
+
+/**
+ * Provides factory for NativeWindow
+ */
+public interface WindowFactory {
+ /**
+ * Creates and returns NativeWindow with desired
+ * creation params
+ *
+ * @param p - initial window properties
+ * @return created window
+ */
+ NativeWindow createWindow(CreationParams p);
+ /**
+ * Create NativeWindow instance connected to existing native resource
+ * @param nativeWindowId - id of existing window
+ * @return created NativeWindow instance
+ */
+ NativeWindow attachWindow(long nativeWindowId);
+ /**
+ * Returns NativeWindow instance if created by this instance of
+ * WindowFactory, otherwise null
+ *
+ * @param id - HWND on Windows xwindow on X
+ * @return NativeWindow or null if unknown
+ */
+ NativeWindow getWindowById(long id);
+ /**
+ * Returns NativeWindow instance of the top-level window
+ * that contains a specified point and was
+ * created by this instance of WindowFactory
+ * @param p - Point to check
+ * @return NativeWindow or null if the point is
+ * not within a window created by this WindowFactory
+ */
+ NativeWindow getWindowFromPoint(Point p);
+
+ /**
+ * Returns whether native system supports the state for windows.
+ * This method tells whether the UI concept of, say, maximization or iconification is supported.
+ * It will always return false for "compound" states like Frame.ICONIFIED|Frame.MAXIMIZED_VERT.
+ * In other words, the rule of thumb is that only queries with a single frame state
+ * constant as an argument are meaningful.
+ *
+ * @param state - one of named frame state constants.
+ * @return true is this frame state is supported by this Toolkit implementation, false otherwise.
+ */
+ boolean isWindowStateSupported(int state);
+
+ /**
+ * @see org.apache.harmony.awt.ComponentInternals
+ */
+ void setCaretPosition(int x, int y);
+
+ /**
+ * Request size of arbitrary native window
+ * @param id - window ID
+ * @return window size
+ */
+ Dimension getWindowSizeById(long id);
+} \ No newline at end of file