Merged gcj-eclipse branch to trunk.

From-SVN: r120621
This commit is contained in:
Tom Tromey 2007-01-09 19:58:05 +00:00
parent c648dedbde
commit 97b8365caf
17478 changed files with 606493 additions and 100744 deletions

View file

@ -96,6 +96,11 @@ public abstract class AWTEvent extends EventObject
*/
protected boolean consumed;
/**
* Used for implementing a simple linked list in EventQueue.
*/
transient AWTEvent queueNext;
/**
* Who knows? It's in the serial version.
*

View file

@ -1175,16 +1175,17 @@ public class AWTEventMulticaster
* @throws IllegalArgumentException if type is Void.TYPE
* @since 1.4
*/
public static EventListener[] getListeners(EventListener l, Class type)
public static <T extends EventListener> T[] getListeners(EventListener l,
Class<T> type)
{
ArrayList list = new ArrayList();
ArrayList<EventListener> list = new ArrayList<EventListener>();
if (l instanceof AWTEventMulticaster)
((AWTEventMulticaster) l).getListeners(list, type);
else if (type.isInstance(l))
list.add(l);
EventListener[] r = (EventListener[]) Array.newInstance(type, list.size());
list.toArray(r);
return r;
return (T[]) r;
}
/**

View file

@ -1,5 +1,5 @@
/* AWTKeyStroke.java -- an immutable key stroke
Copyright (C) 2002, 2004, 2005 Free Software Foundation
Copyright (C) 2002, 2004, 2005 Free Software Foundation
This file is part of GNU Classpath.
@ -65,6 +65,7 @@ import java.util.StringTokenizer;
* no-arg constructor (of any accessibility).
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
* @see #getAWTKeyStroke(char)
* @since 1.4
* @status updated to 1.4
@ -85,13 +86,15 @@ public class AWTKeyStroke implements Serializable
* under the assumption that garbage collection of a new keystroke is
* easy when we find the old one that it matches in the cache.
*/
private static final LinkedHashMap cache = new LinkedHashMap(11, 0.75f, true)
private static final LinkedHashMap<AWTKeyStroke,AWTKeyStroke> cache =
new LinkedHashMap<AWTKeyStroke,AWTKeyStroke>(11, 0.75f, true)
{
/** The largest the keystroke cache can grow. */
private static final int MAX_CACHE_SIZE = 2048;
/** Prune stale entries. */
protected boolean removeEldestEntry(Map.Entry eldest)
protected boolean removeEldestEntry(Map.Entry<AWTKeyStroke,AWTKeyStroke>
eldest)
{ // XXX - FIXME Use Map.Entry, not just Entry as gcj 3.1 workaround.
return size() > MAX_CACHE_SIZE;
}
@ -114,7 +117,7 @@ public class AWTKeyStroke implements Serializable
*
* @see #getAWTKeyStroke(String)
*/
static final HashMap vktable = new HashMap();
static final HashMap<String,Object> vktable = new HashMap<String,Object>();
static
{
// Using reflection saves the hassle of keeping this in sync with KeyEvent,
@ -229,7 +232,7 @@ public class AWTKeyStroke implements Serializable
* @throws IllegalArgumentException subclass doesn't have no-arg constructor
* @throws ClassCastException subclass doesn't extend AWTKeyStroke
*/
protected static void registerSubclass(final Class subclass)
protected static void registerSubclass(final Class<?> subclass)
{
if (subclass == null)
throw new IllegalArgumentException();
@ -252,7 +255,8 @@ public class AWTKeyStroke implements Serializable
throws NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
Constructor c = subclass.getDeclaredConstructor(null);
Constructor<?> c =
subclass.getDeclaredConstructor((Class<?>[])null);
c.setAccessible(true);
// Create a new instance, to make sure that we can, and
// to cause any ClassCastException.
@ -595,7 +599,7 @@ public class AWTKeyStroke implements Serializable
*/
protected Object readResolve() throws ObjectStreamException
{
AWTKeyStroke s = (AWTKeyStroke) cache.get(this);
AWTKeyStroke s = cache.get(this);
if (s != null)
return s;
cache.put(this, this);

View file

@ -43,6 +43,7 @@ import gnu.java.awt.java2d.LineSegment;
import gnu.java.awt.java2d.QuadSegment;
import gnu.java.awt.java2d.Segment;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
@ -486,8 +487,157 @@ public class BasicStroke implements Stroke
private Shape dashedStroke(PathIterator pi)
{
GeneralPath out = new GeneralPath();
return out;
// The choice of (flatnessSq == width / 3) is made to be consistent with
// the flattening in CubicSegment.getDisplacedSegments
FlatteningPathIterator flat = new FlatteningPathIterator(pi,
Math.sqrt(width / 3));
// Holds the endpoint of the current segment (or piece of a segment)
double[] coords = new double[2];
// Holds end of the last segment
double x, y, x0, y0;
x = x0 = y = y0 = 0;
// Various useful flags
boolean pathOpen = false;
boolean dashOn = true;
boolean offsetting = (phase != 0);
// How far we are into the current dash
double distance = 0;
int dashIndex = 0;
// And variables to hold the final output
GeneralPath output = new GeneralPath();
Segment[] p;
// Iterate over the FlatteningPathIterator
while (! flat.isDone())
{
switch (flat.currentSegment(coords))
{
case PathIterator.SEG_MOVETO:
x0 = x = coords[0];
y0 = y = coords[1];
if (pathOpen)
{
capEnds();
convertPath(output, start);
start = end = null;
pathOpen = false;
}
break;
case PathIterator.SEG_LINETO:
boolean segmentConsumed = false;
while (! segmentConsumed)
{
// Find the total remaining length of this segment
double segLength = Math.sqrt((x - coords[0]) * (x - coords[0])
+ (y - coords[1])
* (y - coords[1]));
boolean spanBoundary = true;
double[] segmentEnd = null;
// The current segment fits entirely inside the current dash
if ((offsetting && distance + segLength <= phase)
|| distance + segLength <= dash[dashIndex])
{
spanBoundary = false;
}
// Otherwise, we need to split the segment in two, as this
// segment spans a dash boundry
else
{
segmentEnd = (double[]) coords.clone();
// Calculate the remaining distance in this dash,
// and coordinates of the dash boundary
double reqLength;
if (offsetting)
reqLength = phase - distance;
else
reqLength = dash[dashIndex] - distance;
coords[0] = x + ((coords[0] - x) * reqLength / segLength);
coords[1] = y + ((coords[1] - y) * reqLength / segLength);
}
if (offsetting || ! dashOn)
{
// Dash is off, or we are in offset - treat this as a
// moveTo
x0 = x = coords[0];
y0 = y = coords[1];
if (pathOpen)
{
capEnds();
convertPath(output, start);
start = end = null;
pathOpen = false;
}
}
else
{
// Dash is on - treat this as a lineTo
p = (new LineSegment(x, y, coords[0], coords[1])).getDisplacedSegments(width / 2.0);
if (! pathOpen)
{
start = p[0];
end = p[1];
pathOpen = true;
}
else
addSegments(p);
x = coords[0];
y = coords[1];
}
// Update variables depending on whether we spanned a
// dash boundary or not
if (! spanBoundary)
{
distance += segLength;
segmentConsumed = true;
}
else
{
if (offsetting)
offsetting = false;
dashOn = ! dashOn;
distance = 0;
coords = segmentEnd;
if (dashIndex + 1 == dash.length)
dashIndex = 0;
else
dashIndex++;
// Since the value of segmentConsumed is still false,
// the next run of the while loop will complete the segment
}
}
break;
// This is a flattened path, so we don't need to deal with curves
}
flat.next();
}
if (pathOpen)
{
capEnds();
convertPath(output, start);
}
return output;
}
/**
@ -611,9 +761,13 @@ public class BasicStroke implements Stroke
p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
dx = p1[0] - p0[0];
dy = p1[1] - p0[1];
l = Math.sqrt(dx * dx + dy * dy);
dx = (2.0/3.0)*width*dx/l;
dy = (2.0/3.0)*width*dy/l;
if (dx != 0 && dy != 0)
{
l = Math.sqrt(dx * dx + dy * dy);
dx = (2.0/3.0)*width*dx/l;
dy = (2.0/3.0)*width*dy/l;
}
c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy);
c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy);
a.add(new CubicSegment(a.last.P2, c1, c2, b.P1));

View file

@ -352,11 +352,11 @@ removeActionListener(ActionListener listener)
*
* @since 1.3
*/
public EventListener[] getListeners(Class listenerType)
public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
if (listenerType == ActionListener.class)
return getActionListeners();
return (EventListener[]) Array.newInstance(listenerType, 0);
return (T[]) getActionListeners();
return (T[]) Array.newInstance(listenerType, 0);
}
/*************************************************************************/

View file

@ -225,6 +225,8 @@ public class CardLayout implements LayoutManager2, Serializable
*/
public Dimension maximumLayoutSize (Container target)
{
if (target == null)
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
// The JCL says that this returns Integer.MAX_VALUE for both
// dimensions. But that just seems wrong to me.
return getSize (target, MAX);
@ -361,7 +363,7 @@ public class CardLayout implements LayoutManager2, Serializable
*/
public String toString ()
{
return getClass ().getName () + "[" + hgap + "," + vgap + "]";
return getClass ().getName () + "[hgap=" + hgap + ",vgap=" + vgap + "]";
}
/**
@ -401,11 +403,11 @@ public class CardLayout implements LayoutManager2, Serializable
{
if (comps[i].isVisible ())
{
if (what == NEXT)
if (choice == i)
{
choice = i + 1;
if (choice == num)
choice = 0;
// Do nothing if we're already looking at the right
// component.
return;
}
else if (what == PREV)
{
@ -413,17 +415,20 @@ public class CardLayout implements LayoutManager2, Serializable
if (choice < 0)
choice = num - 1;
}
else if (choice == i)
else if (what == NEXT)
{
// Do nothing if we're already looking at the right
// component.
return;
choice = i + 1;
if (choice == num)
choice = 0;
}
comps[i].setVisible (false);
if (choice >= 0)
break;
}
} else
{
comps[i].setVisible(true);
}
}
if (choice >= 0 && choice < num)

View file

@ -318,11 +318,11 @@ paramString()
* @exception ClassCastException If listenerType doesn't specify a class or
* interface that implements java.util.EventListener.
*/
public EventListener[] getListeners (Class listenerType)
public <T extends EventListener> T[] getListeners (Class<T> listenerType)
{
if (listenerType == ItemListener.class)
return AWTEventMulticaster.getListeners (item_listeners, listenerType);
return super.getListeners (listenerType);
}

View file

@ -255,8 +255,8 @@ public class Choice extends Component
/**
* Adds the specified item to this choice box.
*
* This method is oboslete since Java 2 platform 1.1. Please use @see add
* instead.
* This method is oboslete since Java 2 platform 1.1. Please use
* {@link #add(String)} instead.
*
* @param item The item to add.
*
@ -320,9 +320,6 @@ public class Choice extends Component
*/
public synchronized void remove(int index)
{
if ((index < 0) || (index > getItemCount()))
throw new IllegalArgumentException("Bad index: " + index);
pItems.removeElementAt(index);
if (peer != null)
@ -332,14 +329,14 @@ public class Choice extends Component
selectedIndex = -1;
else
{
if( selectedIndex > index )
selectedIndex--;
else if( selectedIndex == index )
selectedIndex = 0;
if( selectedIndex > index )
selectedIndex--;
else if( selectedIndex == index )
selectedIndex = 0;
if( peer != null )
((ChoicePeer)peer).select( selectedIndex );
}
if( peer != null )
((ChoicePeer)peer).select( selectedIndex );
}
}
/**
@ -523,11 +520,11 @@ public class Choice extends Component
*
* @since 1.3
*/
public EventListener[] getListeners (Class listenerType)
public <T extends EventListener> T[] getListeners (Class<T> listenerType)
{
if (listenerType == ItemListener.class)
return AWTEventMulticaster.getListeners (item_listeners, listenerType);
return super.getListeners (listenerType);
}

View file

@ -534,14 +534,31 @@ public class Color implements Paint, Serializable
{
// Do not inline getRGB() to this.value, because of SystemColor.
int value = getRGB();
int red = (value & RED_MASK) >> 16;
int green = (value & GREEN_MASK) >> 8;
int blue = value & BLUE_MASK;
// We have to special case 0-2 because they won't scale by division.
red = red < 3 ? 3 : (int) Math.min(255, red / BRIGHT_SCALE);
green = green < 3 ? 3 : (int) Math.min(255, green / BRIGHT_SCALE);
blue = blue < 3 ? 3 : (int) Math.min(255, blue / BRIGHT_SCALE);
return new Color(red, green, blue, 255);
int[] hues = new int[3];
hues[0] = (value & RED_MASK) >> 16;
hues[1] = (value & GREEN_MASK) >> 8;
hues[2] = value & BLUE_MASK;
// (0,0,0) is a special case.
if (hues[0] == 0 && hues[1] == 0 && hues[2] ==0)
{
hues[0] = 3;
hues[1] = 3;
hues[2] = 3;
}
else
{
for (int index = 0; index < 3; index++)
{
if (hues[index] > 2)
hues[index] = (int) Math.min(255, hues[index]/0.7f);
if (hues[index] == 1 || hues[index] == 2)
hues[index] = 4;
}
}
return new Color(hues[0], hues[1], hues[2], 255);
}
/**

File diff suppressed because it is too large Load diff

View file

@ -39,11 +39,11 @@ exception statement from your version. */
package java.awt;
import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.peer.ComponentPeer;
import java.awt.peer.ContainerPeer;
import java.awt.peer.LightweightPeer;
@ -69,10 +69,11 @@ import javax.accessibility.Accessible;
*
* @author original author unknown
* @author Eric Blake (ebb9@email.byu.edu)
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
*
* @since 1.0
*
* @status still missing 1.4 support
* @status still missing 1.4 support, some generics from 1.5
*/
public class Container extends Component
{
@ -86,8 +87,6 @@ public class Container extends Component
Component[] component;
LayoutManager layoutMgr;
Dimension maxSize;
/**
* @since 1.4
*/
@ -208,10 +207,12 @@ public class Container extends Component
*/
public Insets insets()
{
if (peer == null)
return new Insets (0, 0, 0, 0);
return ((ContainerPeer) peer).getInsets ();
Insets i;
if (peer == null || peer instanceof LightweightPeer)
i = new Insets (0, 0, 0, 0);
else
i = ((ContainerPeer) peer).getInsets ();
return i;
}
/**
@ -324,23 +325,6 @@ public class Container extends Component
// we are.
if (comp.parent != null)
comp.parent.remove(comp);
comp.parent = this;
if (peer != null)
{
// Notify the component that it has a new parent.
comp.addNotify();
if (comp.isLightweight ())
{
enableEvents (comp.eventMask);
if (!isLightweight ())
enableEvents (AWTEvent.PAINT_EVENT_MASK);
}
}
// Invalidate the layout of the added component and its ancestors.
comp.invalidate();
if (component == null)
component = new Component[4]; // FIXME, better initial size?
@ -365,6 +349,9 @@ public class Container extends Component
++ncomponents;
}
// Give the new component a parent.
comp.parent = this;
// Update the counter for Hierarchy(Bounds)Listeners.
int childHierarchyListeners = comp.numHierarchyListeners;
if (childHierarchyListeners > 0)
@ -375,6 +362,18 @@ public class Container extends Component
updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK,
childHierarchyListeners);
// Invalidate the layout of this container.
if (valid)
invalidate();
// Create the peer _after_ the component has been added, so that
// the peer gets to know about the component hierarchy.
if (peer != null)
{
// Notify the component that it has a new parent.
comp.addNotify();
}
// Notify the layout manager.
if (layoutMgr != null)
{
@ -394,13 +393,15 @@ public class Container extends Component
// We previously only sent an event when this container is showing.
// Also, the event was posted to the event queue. A Mauve test shows
// that this event is not delivered using the event queue and it is
// also sent when the container is not showing.
ContainerEvent ce = new ContainerEvent(this,
ContainerEvent.COMPONENT_ADDED,
comp);
ContainerListener[] listeners = getContainerListeners();
for (int i = 0; i < listeners.length; i++)
listeners[i].componentAdded(ce);
// also sent when the container is not showing.
if (containerListener != null
|| (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0)
{
ContainerEvent ce = new ContainerEvent(this,
ContainerEvent.COMPONENT_ADDED,
comp);
dispatchEvent(ce);
}
// Notify hierarchy listeners.
comp.fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, comp,
@ -417,17 +418,15 @@ public class Container extends Component
{
synchronized (getTreeLock ())
{
if (index < 0 || index >= ncomponents)
throw new ArrayIndexOutOfBoundsException();
Component r = component[index];
if (peer != null)
r.removeNotify();
ComponentListener[] list = r.getComponentListeners();
for (int j = 0; j < list.length; j++)
r.removeComponentListener(list[j]);
r.removeNotify();
System.arraycopy(component, index + 1, component, index,
ncomponents - index - 1);
component[--ncomponents] = null;
if (layoutMgr != null)
layoutMgr.removeLayoutComponent(r);
// Update the counter for Hierarchy(Bounds)Listeners.
int childHierarchyListeners = r.numHierarchyListeners;
@ -439,20 +438,23 @@ public class Container extends Component
updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK,
-childHierarchyListeners);
invalidate();
if (layoutMgr != null)
layoutMgr.removeLayoutComponent(r);
r.parent = null;
if (isShowing ())
System.arraycopy(component, index + 1, component, index,
ncomponents - index - 1);
component[--ncomponents] = null;
if (valid)
invalidate();
if (containerListener != null
|| (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0)
{
// Post event to notify of removing the component.
ContainerEvent ce = new ContainerEvent(this,
ContainerEvent.COMPONENT_REMOVED,
r);
getToolkit().getSystemEventQueue().postEvent(ce);
ContainerEvent.COMPONENT_REMOVED,
r);
dispatchEvent(ce);
}
// Notify hierarchy listeners.
@ -496,36 +498,51 @@ public class Container extends Component
// super.removeAll() ).
// By doing it this way, user code cannot prevent the correct
// removal of components.
for ( int index = 0; index < ncomponents; index++)
while (ncomponents > 0)
{
Component r = component[index];
ncomponents--;
Component r = component[ncomponents];
component[ncomponents] = null;
ComponentListener[] list = r.getComponentListeners();
for (int j = 0; j < list.length; j++)
r.removeComponentListener(list[j]);
r.removeNotify();
if (peer != null)
r.removeNotify();
if (layoutMgr != null)
layoutMgr.removeLayoutComponent(r);
r.parent = null;
if (isShowing ())
// Send ContainerEvent if necessary.
if (containerListener != null
|| (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0)
{
// Post event to notify of removing the component.
ContainerEvent ce
= new ContainerEvent(this,
ContainerEvent.COMPONENT_REMOVED,
r);
getToolkit().getSystemEventQueue().postEvent(ce);
dispatchEvent(ce);
}
}
// Update the counter for Hierarchy(Bounds)Listeners.
int childHierarchyListeners = r.numHierarchyListeners;
if (childHierarchyListeners > 0)
updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK,
-childHierarchyListeners);
int childHierarchyBoundsListeners = r.numHierarchyBoundsListeners;
if (childHierarchyBoundsListeners > 0)
updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK,
-childHierarchyListeners);
// Send HierarchyEvent if necessary.
fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, r, this,
HierarchyEvent.PARENT_CHANGED);
}
if (valid)
invalidate();
ncomponents = 0;
}
}
@ -590,11 +607,20 @@ public class Container extends Component
*/
public void validate()
{
synchronized (getTreeLock ())
ComponentPeer p = peer;
if (! valid && p != null)
{
if (! isValid() && peer != null)
ContainerPeer cPeer = null;
if (p instanceof ContainerPeer)
cPeer = (ContainerPeer) peer;
synchronized (getTreeLock ())
{
if (cPeer != null)
cPeer.beginValidate();
validateTree();
valid = true;
if (cPeer != null)
cPeer.endValidate();
}
}
}
@ -602,24 +628,20 @@ public class Container extends Component
/**
* Recursively invalidates the container tree.
*/
void invalidateTree()
private final void invalidateTree()
{
synchronized (getTreeLock())
{
super.invalidate(); // Clean cached layout state.
for (int i = 0; i < ncomponents; i++)
{
Component comp = component[i];
comp.invalidate();
if (comp instanceof Container)
((Container) comp).invalidateTree();
else if (comp.valid)
comp.invalidate();
}
if (layoutMgr != null && layoutMgr instanceof LayoutManager2)
{
LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
lm2.invalidateLayout(this);
}
if (valid)
invalidate();
}
}
@ -629,40 +651,36 @@ public class Container extends Component
*/
protected void validateTree()
{
if (valid)
return;
ContainerPeer cPeer = null;
if (peer != null && ! (peer instanceof LightweightPeer))
if (!valid)
{
cPeer = (ContainerPeer) peer;
cPeer.beginValidate();
}
for (int i = 0; i < ncomponents; ++i)
{
Component comp = component[i];
if (comp.getPeer () == null)
comp.addNotify();
}
doLayout ();
for (int i = 0; i < ncomponents; ++i)
{
Component comp = component[i];
if (! comp.isValid())
ContainerPeer cPeer = null;
if (peer instanceof ContainerPeer)
{
if (comp instanceof Container)
cPeer = (ContainerPeer) peer;
cPeer.beginLayout();
}
doLayout ();
for (int i = 0; i < ncomponents; ++i)
{
Component comp = component[i];
if (comp instanceof Container && ! (comp instanceof Window)
&& ! comp.valid)
{
((Container) comp).validateTree();
}
else
{
component[i].validate();
comp.validate();
}
}
if (cPeer != null)
{
cPeer = (ContainerPeer) peer;
cPeer.endLayout();
}
}
/* children will call invalidate() when they are layed out. It
@ -670,19 +688,15 @@ public class Container extends Component
until after the children have been layed out. */
valid = true;
if (cPeer != null)
cPeer.endValidate();
}
public void setFont(Font f)
{
if( (f != null && (font == null || !font.equals(f)))
|| f == null)
Font oldFont = getFont();
super.setFont(f);
Font newFont = getFont();
if (newFont != oldFont && (oldFont == null || ! oldFont.equals(newFont)))
{
super.setFont(f);
// FIXME: Although it might make more sense to invalidate only
// those children whose font == null, Sun invalidates all children.
// So we'll do the same.
invalidateTree();
}
}
@ -784,8 +798,9 @@ public class Container extends Component
LayoutManager l = layoutMgr;
if (l instanceof LayoutManager2)
maxSize = ((LayoutManager2) l).maximumLayoutSize(this);
else
else {
maxSize = super.maximumSizeImpl();
}
size = maxSize;
}
}
@ -920,8 +935,8 @@ public class Container extends Component
*/
public void paintComponents(Graphics g)
{
paint(g);
visitChildren(g, GfxPaintAllVisitor.INSTANCE, true);
if (isShowing())
visitChildren(g, GfxPaintAllVisitor.INSTANCE, false);
}
/**
@ -943,7 +958,12 @@ public class Container extends Component
*/
public synchronized void addContainerListener(ContainerListener listener)
{
containerListener = AWTEventMulticaster.add(containerListener, listener);
if (listener != null)
{
containerListener = AWTEventMulticaster.add(containerListener,
listener);
newEventsOnly = true;
}
}
/**
@ -985,10 +1005,10 @@ public class Container extends Component
*
* @since 1.3
*/
public EventListener[] getListeners(Class listenerType)
public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
if (listenerType == ContainerListener.class)
return getContainerListeners();
return (T[]) getContainerListeners();
return super.getListeners(listenerType);
}
@ -1247,8 +1267,14 @@ public class Container extends Component
{
synchronized (getTreeLock ())
{
for (int i = 0; i < ncomponents; ++i)
component[i].removeNotify();
int ncomps = ncomponents;
Component[] comps = component;
for (int i = ncomps - 1; i >= 0; --i)
{
Component comp = comps[i];
if (comp != null)
comp.removeNotify();
}
super.removeNotify();
}
}
@ -1345,7 +1371,8 @@ public class Container extends Component
*
* @since 1.4
*/
public void setFocusTraversalKeys(int id, Set keystrokes)
public void setFocusTraversalKeys(int id,
Set<? extends AWTKeyStroke> keystrokes)
{
if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
@ -1433,7 +1460,8 @@ public class Container extends Component
if (focusTraversalKeys == null)
focusTraversalKeys = new Set[4];
keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes));
keystrokes =
Collections.unmodifiableSet(new HashSet<AWTKeyStroke>(keystrokes));
firePropertyChange (name, focusTraversalKeys[id], keystrokes);
focusTraversalKeys[id] = keystrokes;
@ -1451,7 +1479,7 @@ public class Container extends Component
*
* @since 1.4
*/
public Set getFocusTraversalKeys (int id)
public Set<AWTKeyStroke> getFocusTraversalKeys (int id)
{
if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
@ -1866,6 +1894,7 @@ public class Container extends Component
bounds.height);
try
{
g2.setFont(comp.getFont());
visitor.visit(comp, g2);
}
finally
@ -1874,20 +1903,40 @@ public class Container extends Component
}
}
/**
* Overridden to dispatch events to lightweight descendents.
*
* @param e the event to dispatch.
*/
void dispatchEventImpl(AWTEvent e)
{
boolean dispatched =
LightweightDispatcher.getInstance().dispatchEvent(e);
if (! dispatched)
LightweightDispatcher dispatcher = LightweightDispatcher.getInstance();
if (! isLightweight() && dispatcher.dispatchEvent(e))
{
if ((e.id <= ContainerEvent.CONTAINER_LAST
&& e.id >= ContainerEvent.CONTAINER_FIRST)
&& (containerListener != null
|| (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0))
processEvent(e);
else
super.dispatchEventImpl(e);
// Some lightweight descendent got this event dispatched. Consume
// it and let the peer handle it.
e.consume();
ComponentPeer p = peer;
if (p != null)
p.handleEvent(e);
}
else
{
super.dispatchEventImpl(e);
}
}
/**
* This is called by the lightweight dispatcher to avoid recursivly
* calling into the lightweight dispatcher.
*
* @param e the event to dispatch
*
* @see LightweightDispatcher#redispatch(MouseEvent, Component, int)
*/
void dispatchNoLightweight(AWTEvent e)
{
super.dispatchEventImpl(e);
}
/**
@ -2004,6 +2053,43 @@ public class Container extends Component
parent.updateHierarchyListenerCount(type, delta);
}
/**
* Notifies interested listeners about resizing or moving the container.
* This performs the super behaviour (sending component events) and
* additionally notifies any hierarchy bounds listeners on child components.
*
* @param resized true if the component has been resized, false otherwise
* @param moved true if the component has been moved, false otherwise
*/
void notifyReshape(boolean resized, boolean moved)
{
// Notify component listeners.
super.notifyReshape(resized, moved);
if (ncomponents > 0)
{
// Notify hierarchy bounds listeners.
if (resized)
{
for (int i = 0; i < getComponentCount(); i++)
{
Component child = getComponent(i);
child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_RESIZED,
this, parent, 0);
}
}
if (moved)
{
for (int i = 0; i < getComponentCount(); i++)
{
Component child = getComponent(i);
child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_MOVED,
this, parent, 0);
}
}
}
}
private void addNotifyContainerChildren()
{
synchronized (getTreeLock ())
@ -2011,12 +2097,6 @@ public class Container extends Component
for (int i = ncomponents; --i >= 0; )
{
component[i].addNotify();
if (component[i].isLightweight ())
{
enableEvents(component[i].eventMask);
if (peer != null && !isLightweight ())
enableEvents (AWTEvent.PAINT_EVENT_MASK);
}
}
}
}

View file

@ -96,6 +96,11 @@ public class Dialog extends Window
*/
private EventQueue eq2 = null;
/**
* The number used to generate the name returned by getName.
*/
private static transient long next_dialog_number;
/**
* Initializes a new instance of <code>Dialog</code> with the specified
* parent, that is resizable and not modal, and which has no title.
@ -190,6 +195,7 @@ public class Dialog extends Window
visible = false;
setLayout(new BorderLayout());
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
/**
@ -273,6 +279,7 @@ public class Dialog extends Window
visible = false;
setLayout(new BorderLayout());
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
/**
@ -530,5 +537,19 @@ public class Dialog extends Window
accessibleContext = new AccessibleAWTDialog();
return accessibleContext;
}
/**
* Generate a unique name for this <code>Dialog</code>.
*
* @return A unique name for this <code>Dialog</code>.
*/
String generateName()
{
return "dialog" + getUniqueLong();
}
private static synchronized long getUniqueLong()
{
return next_dialog_number++;
}
}

View file

@ -38,10 +38,16 @@ exception statement from your version. */
package java.awt;
import gnu.java.awt.LowPriorityEvent;
import gnu.java.awt.peer.NativeEventLoopRunningEvent;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.InputMethodEvent;
import java.awt.event.InvocationEvent;
import java.awt.event.PaintEvent;
import java.awt.peer.ComponentPeer;
import java.awt.peer.LightweightPeer;
import java.lang.reflect.InvocationTargetException;
import java.util.EmptyStackException;
@ -61,11 +67,47 @@ import java.util.EmptyStackException;
*/
public class EventQueue
{
private static final int INITIAL_QUEUE_DEPTH = 8;
private AWTEvent[] queue = new AWTEvent[INITIAL_QUEUE_DEPTH];
/**
* Indicates events that are processed with normal priority. This is normally
* all events except PaintEvents.
*/
private static final int NORM_PRIORITY = 0;
private int next_in = 0; // Index where next event will be added to queue
private int next_out = 0; // Index of next event to be removed from queue
/**
* Indicates events that are processed with lowes priority. This is normally
* all PaintEvents and LowPriorityEvents.
*/
private static final int LOW_PRIORITY = 1;
/**
* Implements the actual queue. EventQueue has 2 internal queues for
* different priorities:
* 1 PaintEvents are always dispatched with low priority.
* 2. All other events are dispatched with normal priority.
*
* This makes sure that the actual painting (output) is performed _after_ all
* available input has been processed and that the paint regions are
* coalesced as much as possible.
*/
private class Queue
{
/**
* The first item in the queue. This is where events are popped from.
*/
AWTEvent queueHead;
/**
* The last item. This is where events are posted to.
*/
AWTEvent queueTail;
}
/**
* The three internal event queues.
*
* @see Queue
*/
private Queue[] queues;
private EventQueue next;
private EventQueue prev;
@ -73,31 +115,25 @@ public class EventQueue
private long lastWhen = System.currentTimeMillis();
private EventDispatchThread dispatchThread = new EventDispatchThread(this);
private boolean shutdown = false;
private boolean nativeLoopRunning = false;
synchronized private void setShutdown (boolean b)
private boolean isShutdown ()
{
shutdown = b;
}
synchronized boolean isShutdown ()
{
if (shutdown)
return true;
// This is the exact self-shutdown condition specified in J2SE:
// http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html
// FIXME: check somewhere that the native queue is empty
if (peekEvent() == null)
{
Frame[] frames = Frame.getFrames();
for (int i = 0; i < frames.length; ++i)
if (frames[i].isDisplayable())
return false;
return true;
}
return false;
if (nativeLoopRunning)
return false;
if (peekEvent() != null)
return false;
Frame[] frames = Frame.getFrames();
for (int i = 0; i < frames.length; ++i)
if (frames[i].isDisplayable())
return false;
return true;
}
/**
@ -105,6 +141,9 @@ public class EventQueue
*/
public EventQueue()
{
queues = new Queue[2];
queues[NORM_PRIORITY] = new Queue();
queues[LOW_PRIORITY] = new Queue();
}
/**
@ -122,30 +161,66 @@ public class EventQueue
if (next != null)
return next.getNextEvent();
while (next_in == next_out)
{
// We are not allowed to return null from this method, yet it
// is possible that we actually have run out of native events
// in the enclosing while() loop, and none of the native events
// happened to cause AWT events. We therefore ought to check
// the isShutdown() condition here, before risking a "native
// wait". If we check it before entering this function we may
// wait forever for events after the shutdown condition has
// arisen.
AWTEvent res = getNextEventImpl(true);
while (res == null)
{
if (isShutdown())
throw new InterruptedException();
{
// Explicitly set dispathThread to null. If we don't do
// this, there is a race condition where dispatchThread
// can be != null even after the event dispatch thread has
// stopped running. If that happens, then the
// dispatchThread == null check in postEventImpl will
// fail, and a new event dispatch thread will not be
// created, leaving invokeAndWaits waiting indefinitely.
dispatchThread = null;
// Interrupt the event dispatch thread.
throw new InterruptedException();
}
wait();
res = getNextEventImpl(true);
}
AWTEvent res = queue[next_out];
if (++next_out == queue.length)
next_out = 0;
return res;
}
/**
* Fetches and possibly removes the next event from the internal queues.
* This method returns immediately. When all queues are empty, this returns
* <code>null</code>:
*
* @param remove <true> when the event should be removed from the queue,
* <code>false</code> otherwise
*
* @return the next event or <code>null</code> when all internal queues
* are empty
*/
private AWTEvent getNextEventImpl(boolean remove)
{
AWTEvent next = null;
for (int i = 0; i < queues.length && next == null; i++)
{
Queue q = queues[i];
if (q.queueHead != null)
{
// Got an event, remove it.
next = q.queueHead;
if (remove)
{
// Unlink event from the queue.
q.queueHead = next.queueNext;
if (q.queueHead == null)
q.queueTail = null;
next.queueNext = null;
}
}
}
return next;
}
/**
* Returns the next event in the queue without removing it from the queue.
* This method will block until an event is available or until the thread
@ -160,10 +235,7 @@ public class EventQueue
if (next != null)
return next.peekEvent();
if (next_in != next_out)
return queue[next_out];
else
return null;
return getNextEventImpl(false);
}
/**
@ -184,14 +256,18 @@ public class EventQueue
if (next != null)
return next.peekEvent(id);
int i = next_out;
while (i != next_in)
AWTEvent evt = null;
for (int i = 0; i < queues.length && evt == null; i++)
{
AWTEvent qevt = queue[i];
if (qevt.id == id)
return qevt;
Queue q = queues[i];
evt = q.queueHead;
while (evt != null && evt.id != id)
evt = evt.queueNext;
// At this point we either have found an event (evt != null -> exit
// for loop), or we have found no event (evt == null -> search next
// internal queue).
}
return null;
return evt;
}
/**
@ -201,7 +277,42 @@ public class EventQueue
*
* @exception NullPointerException If event is null.
*/
public synchronized void postEvent(AWTEvent evt)
public void postEvent(AWTEvent evt)
{
postEventImpl(evt);
}
/**
* Sorts events to their priority and calls
* {@link #postEventImpl(AWTEvent, int)}.
*
* @param evt the event to post
*/
private synchronized final void postEventImpl(AWTEvent evt)
{
int priority = NORM_PRIORITY;
if (evt instanceof PaintEvent || evt instanceof LowPriorityEvent)
priority = LOW_PRIORITY;
// TODO: Maybe let Swing RepaintManager events also be processed with
// low priority.
if (evt instanceof NativeEventLoopRunningEvent)
{
nativeLoopRunning = ((NativeEventLoopRunningEvent) evt).isRunning();
notify();
return;
}
postEventImpl(evt, priority);
}
/**
* Actually performs the event posting. This is needed because the
* RI doesn't use the public postEvent() method when transferring events
* between event queues in push() and pop().
*
* @param evt the event to post
* @param priority the priority of the event
*/
private final void postEventImpl(AWTEvent evt, int priority)
{
if (evt == null)
throw new NullPointerException();
@ -212,52 +323,71 @@ public class EventQueue
return;
}
/* Check for any events already on the queue with the same source
and ID. */
int i = next_out;
while (i != next_in)
Object source = evt.getSource();
Queue q = queues[priority];
if (source instanceof Component)
{
AWTEvent qevt = queue[i];
Object src;
if (qevt.id == evt.id
&& (src = qevt.getSource()) == evt.getSource()
&& src instanceof Component)
// For PaintEvents, ask the ComponentPeer to coalesce the event
// when the component is heavyweight.
Component comp = (Component) source;
ComponentPeer peer = comp.peer;
if (peer != null && evt instanceof PaintEvent
&& ! (peer instanceof LightweightPeer))
peer.coalescePaintEvent((PaintEvent) evt);
// Check for any events already on the queue with the same source
// and ID.
AWTEvent previous = null;
for (AWTEvent qevt = q.queueHead; qevt != null; qevt = qevt.queueNext)
{
/* If there are, call coalesceEvents on the source component
to see if they can be combined. */
Component srccmp = (Component) src;
AWTEvent coalesced_evt = srccmp.coalesceEvents(qevt, evt);
if (coalesced_evt != null)
Object src = qevt.getSource();
if (qevt.id == evt.id && src == comp)
{
/* Yes. Replace the existing event with the combined event. */
queue[i] = coalesced_evt;
return;
// If there are, call coalesceEvents on the source component
// to see if they can be combined.
Component srccmp = (Component) src;
AWTEvent coalescedEvt = srccmp.coalesceEvents(qevt, evt);
if (coalescedEvt != null)
{
// Yes. Replace the existing event with the combined event.
if (qevt != coalescedEvt)
{
if (previous != null)
{
assert previous.queueNext == qevt;
previous.queueNext = coalescedEvt;
}
else
{
assert q.queueHead == qevt;
q.queueHead = coalescedEvt;
}
coalescedEvt.queueNext = qevt.queueNext;
if (q.queueTail == qevt)
q.queueTail = coalescedEvt;
qevt.queueNext = null;
}
return;
}
}
break;
previous = qevt;
}
if (++i == queue.length)
i = 0;
}
queue[next_in] = evt;
if (++next_in == queue.length)
next_in = 0;
if (next_in == next_out)
if (q.queueHead == null)
{
/* Queue is full. Extend it. */
AWTEvent[] oldQueue = queue;
queue = new AWTEvent[queue.length * 2];
int len = oldQueue.length - next_out;
System.arraycopy(oldQueue, next_out, queue, 0, len);
if (next_out != 0)
System.arraycopy(oldQueue, 0, queue, len, next_out);
next_out = 0;
next_in = oldQueue.length;
// We have an empty queue. Set this event both as head and as tail.
q.queueHead = evt;
q.queueTail = evt;
}
else
{
// Note: queueTail should not be null here.
q.queueTail.queueNext = evt;
q.queueTail = evt;
}
if (dispatchThread == null || !dispatchThread.isAlive())
{
dispatchThread = new EventDispatchThread(this);
@ -287,15 +417,15 @@ public class EventQueue
throw new Error("Can't call invokeAndWait from event dispatch thread");
EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
Thread current = Thread.currentThread();
Object notifyObject = new Object();
InvocationEvent ie =
new InvocationEvent(eq, runnable, current, true);
InvocationEvent ie =
new InvocationEvent(eq, runnable, notifyObject, true);
synchronized (current)
synchronized (notifyObject)
{
eq.postEvent(ie);
current.wait();
notifyObject.wait();
}
Exception exception;
@ -387,17 +517,26 @@ public class EventQueue
if (dispatchThread == null)
dispatchThread = new EventDispatchThread(this);
int i = next_out;
while (i != next_in)
synchronized (newEventQueue)
{
newEventQueue.postEvent(queue[i]);
next_out = i;
if (++i == queue.length)
i = 0;
// The RI transfers the events without calling the new eventqueue's
// push(), but using getNextEvent().
while (peekEvent() != null)
{
try
{
newEventQueue.postEventImpl(getNextEvent());
}
catch (InterruptedException ex)
{
// What should we do with this?
ex.printStackTrace();
}
}
newEventQueue.prev = this;
}
next = newEventQueue;
newEventQueue.prev = this;
}
/** Transfer any pending events from this queue back to the parent queue that
@ -408,36 +547,49 @@ public class EventQueue
*/
protected void pop() throws EmptyStackException
{
if (prev == null)
throw new EmptyStackException();
/* The order is important here, we must get the prev lock first,
or deadlock could occur as callers usually get here following
prev's next pointer, and thus obtain prev's lock before trying
to get this lock. */
synchronized (prev)
EventQueue previous = prev;
if (previous == null)
throw new EmptyStackException();
synchronized (previous)
{
prev.next = next;
if (next != null)
next.prev = prev;
synchronized (this)
{
int i = next_out;
while (i != next_in)
EventQueue nextQueue = next;
if (nextQueue != null)
{
prev.postEvent(queue[i]);
next_out = i;
if (++i == queue.length)
i = 0;
nextQueue.pop();
}
// Empty the queue so it can be reused
next_in = 0;
next_out = 0;
else
{
previous.next = null;
setShutdown(true);
dispatchThread = null;
this.notifyAll();
// The RI transfers the events without calling the new eventqueue's
// push(), so this should be OK and most effective.
while (peekEvent() != null)
{
try
{
previous.postEventImpl(getNextEvent());
}
catch (InterruptedException ex)
{
// What should we do with this?
ex.printStackTrace();
}
}
prev = null;
// Tell our EventDispatchThread that it can end
// execution.
if (dispatchThread != null)
{
dispatchThread.interrupt();
dispatchThread = null;
}
}
}
}
}

View file

@ -95,6 +95,11 @@ private FilenameFilter filter;
*/
private int mode;
/**
* The number used to generate the name returned by getName.
*/
private static transient long next_file_dialog_number;
/*************************************************************************/
/*
@ -300,7 +305,11 @@ getFile()
public synchronized void
setFile(String file)
{
this.file = file;
if ("".equals(file))
this.file = null;
else
this.file = file;
if (peer != null)
{
FileDialogPeer f = (FileDialogPeer) peer;
@ -366,5 +375,22 @@ paramString()
",mode=" + mode + "," + super.paramString());
}
/**
* Generate a unique name for this <code>FileDialog</code>.
*
* @return A unique name for this <code>FileDialog</code>.
*/
String
generateName()
{
return "filedlg" + getUniqueLong();
}
private static synchronized long
getUniqueLong()
{
return next_file_dialog_number++;
}
} // class FileDialog

View file

@ -337,7 +337,10 @@ public class FlowLayout implements LayoutManager, Serializable
Insets ins = parent.getInsets ();
w += (num + 1) * hgap + ins.left + ins.right;
if (num == 0)
w += 2 * hgap + ins.left + ins.right;
else
w += (num + 1) * hgap + ins.left + ins.right;
h += 2 * vgap + ins.top + ins.bottom;
return new Dimension (w, h);

View file

@ -44,6 +44,7 @@ import gnu.java.awt.peer.ClasspathFontPeer;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
@ -351,7 +352,7 @@ public class Font implements Serializable
this.name = peer.getName(this);
}
public Font(Map attrs)
public Font(Map<? extends AttributedCharacterIterator.Attribute, ?> attrs)
{
this(null, attrs);
}
@ -797,7 +798,7 @@ public class Font implements Serializable
*
* @since 1.2
*/
public Font deriveFont(Map attributes)
public Font deriveFont(Map<? extends AttributedCharacterIterator.Attribute, ?> attributes)
{
return peer.deriveFont(this, attributes);
}
@ -811,7 +812,7 @@ public class Font implements Serializable
* @see java.text.AttributedCharacterIterator.Attribute
* @see java.awt.font.TextAttribute
*/
public Map getAttributes()
public Map<TextAttribute, ?> getAttributes()
{
return peer.getAttributes(this);
}
@ -890,7 +891,7 @@ public class Font implements Serializable
*
* @see java.awt.font.TextAttribute
*/
public static Font getFont(Map attributes)
public static Font getFont(Map<? extends AttributedCharacterIterator.Attribute, ?> attributes)
{
return getFontFromToolkit(null, attributes);
}
@ -1086,7 +1087,8 @@ public class Font implements Serializable
*/
public Rectangle2D getStringBounds(String str, FontRenderContext frc)
{
return getStringBounds(str, 0, str.length() - 1, frc);
char[] chars = str.toCharArray();
return getStringBounds(chars, 0, chars.length, frc);
}
/**
@ -1114,8 +1116,8 @@ public class Font implements Serializable
public Rectangle2D getStringBounds(String str, int begin,
int limit, FontRenderContext frc)
{
return peer.getStringBounds(this, new StringCharacterIterator(str), begin,
limit, frc);
String sub = str.substring(begin, limit);
return getStringBounds(sub, frc);
}
/**
@ -1143,7 +1145,16 @@ public class Font implements Serializable
public Rectangle2D getStringBounds(CharacterIterator ci, int begin,
int limit, FontRenderContext frc)
{
return peer.getStringBounds(this, ci, begin, limit, frc);
int start = ci.getBeginIndex();
int end = ci.getEndIndex();
char[] chars = new char[limit - start];
ci.setIndex(start);
for (int index = 0; index < chars.length; index++)
{
chars[index] = ci.current();
ci.next();
}
return getStringBounds(chars, 0, chars.length, frc);
}
/**
@ -1171,9 +1182,10 @@ public class Font implements Serializable
public Rectangle2D getStringBounds(char[] chars, int begin,
int limit, FontRenderContext frc)
{
return peer.getStringBounds(this,
new StringCharacterIterator(new String(chars)),
begin, limit, frc);
String str = new String(chars, begin, limit - begin);
TextLayout layout = new TextLayout(str, this, frc);
return new Rectangle2D.Float(0, -layout.getAscent(), layout.getAdvance(),
layout.getDescent() + layout.getLeading());
}
/**

View file

@ -340,13 +340,16 @@ public class Frame extends Window implements MenuContainer
parent.remove(menuBar);
menuBar.setParent(this);
if (peer != null)
{
if (menuBar != null)
menuBar.addNotify();
invalidateTree();
((FramePeer) peer).setMenuBar(menuBar);
}
// Create local copy for thread safety.
FramePeer p = (FramePeer) peer;
if (p != null)
{
if (menuBar != null)
menuBar.addNotify();
if (valid)
invalidate();
p.setMenuBar(menuBar);
}
}
}
@ -485,7 +488,10 @@ public class Frame extends Window implements MenuContainer
private static void noteFrame(Frame f)
{
weakFrames.add(new WeakReference(f));
synchronized (weakFrames)
{
weakFrames.add(new WeakReference(f));
}
}
public static Frame[] getFrames()
@ -533,8 +539,7 @@ public class Frame extends Window implements MenuContainer
public int getState()
{
// FIXME: State might have changed in the peer... Must check.
return (state & ICONIFIED) != 0 ? ICONIFIED : NORMAL;
return (getExtendedState() & ICONIFIED) != 0 ? ICONIFIED : NORMAL;
}
/**
@ -542,7 +547,13 @@ public class Frame extends Window implements MenuContainer
*/
public void setExtendedState(int state)
{
this.state = state;
if (getToolkit().isFrameStateSupported(state))
{
this.state = state;
FramePeer p = (FramePeer) peer;
if (p != null)
p.setState(state);
}
}
/**
@ -550,6 +561,9 @@ public class Frame extends Window implements MenuContainer
*/
public int getExtendedState()
{
FramePeer p = (FramePeer) peer;
if (p != null)
state = p.getState();
return state;
}

View file

@ -183,7 +183,7 @@ public abstract class Graphics2D extends Graphics
* @see #getComposite()
*/
public abstract void setComposite(Composite comp);
/**
* Sets the paint to be used for subsequent drawing operations.
*
@ -227,14 +227,14 @@ public abstract class Graphics2D extends Graphics
*
* @see #addRenderingHints(Map)
*/
public abstract void setRenderingHints(Map hints);
public abstract void setRenderingHints(Map<?,?> hints);
/**
* Adds/updates the rendering hint.
*
* @param hints the hints to add or update.
*/
public abstract void addRenderingHints(Map hints);
public abstract void addRenderingHints(Map<?,?> hints);
/**
* Returns the current rendering hints.

View file

@ -46,6 +46,7 @@ import java.util.Hashtable;
/**
* @author Michael Koch (konqueror@gmx.de)
* @author Jeroen Frijters (jeroen@frijters.net)
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
*/
public class GridBagLayout
implements Serializable, LayoutManager2
@ -62,8 +63,8 @@ public class GridBagLayout
// REMAINDER constraints.
// Constraints kept in comptable are never modified, and constraints
// kept in internalcomptable can be modified internally only.
protected Hashtable comptable;
private Hashtable internalcomptable;
protected Hashtable<Component,GridBagConstraints> comptable;
private Hashtable<Component,GridBagConstraints> internalcomptable;
protected GridBagLayoutInfo layoutInfo;
protected GridBagConstraints defaultConstraints;
@ -74,8 +75,8 @@ public class GridBagLayout
public GridBagLayout ()
{
this.comptable = new Hashtable();
this.internalcomptable = new Hashtable();
this.comptable = new Hashtable<Component,GridBagConstraints>();
this.internalcomptable = new Hashtable<Component,GridBagConstraints>();
this.defaultConstraints= new GridBagConstraints();
}
@ -319,6 +320,16 @@ public class GridBagLayout
return new Point (col, row);
}
/**
* Return a string representation of this GridBagLayout.
*
* @return a string representation
*/
public String toString()
{
return getClass().getName();
}
/**
* Move and resize a rectangle according to a set of grid bag
* constraints. The x, y, width and height fields of the
@ -489,16 +500,18 @@ public class GridBagLayout
// Guaranteed to contain the last component added to the given row
// or column, whose gridwidth/height is not REMAINDER.
HashMap lastInRow = new HashMap();
HashMap lastInCol = new HashMap();
HashMap<Integer,Component> lastInRow = new HashMap<Integer,Component>();
HashMap<Integer,Component> lastInCol = new HashMap<Integer,Component>();
Component[] components = parent.getComponents();
// Components sorted by gridwidths/heights,
// smallest to largest, with REMAINDER and RELATIVE at the end.
// These are useful when determining sizes and weights.
ArrayList sortedByWidth = new ArrayList(components.length);
ArrayList sortedByHeight = new ArrayList(components.length);
ArrayList<Component> sortedByWidth =
new ArrayList<Component>(components.length);
ArrayList<Component> sortedByHeight =
new ArrayList<Component>(components.length);
// STEP 1: first we figure out how many rows/columns
for (int i = 0; i < components.length; i++)
@ -763,7 +776,7 @@ public class GridBagLayout
// STEP 3: Determine sizes and weights for columns.
for (int i = 0; i < sortedByWidth.size(); i++)
{
Component component = (Component) sortedByWidth.get(i);
Component component = sortedByWidth.get(i);
// If component is not visible we dont have to care about it.
if (!component.isVisible())
@ -877,7 +890,8 @@ public class GridBagLayout
* width. Otherwise, sort by height.
* FIXME: Use a better sorting algorithm.
*/
private void sortBySpan (Component component, int span, ArrayList list, boolean sortByWidth)
private void sortBySpan (Component component, int span,
ArrayList<Component> list, boolean sortByWidth)
{
if (span == GridBagConstraints.REMAINDER
|| span == GridBagConstraints.RELATIVE)

View file

@ -289,7 +289,7 @@ public class GridLayout implements LayoutManager, Serializable
public String toString ()
{
return (getClass ().getName () + "["
+ ",hgap=" + hgap + ",vgap=" + vgap
+ "hgap=" + hgap + ",vgap=" + vgap
+ ",rows=" + rows + ",cols=" + cols
+ "]");
}

View file

@ -1,5 +1,5 @@
/* KeyboardFocusManager.java -- manage component focusing via the keyboard
Copyright (C) 2002, 2004 Free Software Foundation
Copyright (C) 2002, 2004, 2005 Free Software Foundation
This file is part of GNU Classpath.
@ -555,7 +555,9 @@ public abstract class KeyboardFocusManager
* @see #UP_CYCLE_TRAVERSAL_KEYS
* @see #DOWN_CYCLE_TRAVERSAL_KEYS
*/
public void setDefaultFocusTraversalKeys (int id, Set keystrokes)
public void setDefaultFocusTraversalKeys (int id,
Set<? extends AWTKeyStroke>
keystrokes)
{
if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
@ -627,7 +629,7 @@ public abstract class KeyboardFocusManager
* @see #UP_CYCLE_TRAVERSAL_KEYS
* @see #DOWN_CYCLE_TRAVERSAL_KEYS
*/
public Set getDefaultFocusTraversalKeys (int id)
public Set<AWTKeyStroke> getDefaultFocusTraversalKeys (int id)
{
if (id < FORWARD_TRAVERSAL_KEYS || id > DOWN_CYCLE_TRAVERSAL_KEYS)
throw new IllegalArgumentException ();
@ -989,9 +991,9 @@ public abstract class KeyboardFocusManager
* @return A list of explicitly registered key event dispatchers.
* @see KeyboardFocusManager#addKeyEventDispatcher(java.awt.KeyEventDispatcher)
*/
protected List getKeyEventDispatchers ()
protected List<KeyEventDispatcher> getKeyEventDispatchers ()
{
return (List) keyEventDispatchers.clone ();
return (List<KeyEventDispatcher>) keyEventDispatchers.clone ();
}
/**
@ -1046,9 +1048,9 @@ public abstract class KeyboardFocusManager
* @return A list of explicitly registered key event post processors.
* @see KeyboardFocusManager#addKeyEventPostProcessor(java.awt.KeyEventPostProcessor)
*/
protected List getKeyEventPostProcessors ()
protected List<KeyEventPostProcessor> getKeyEventPostProcessors ()
{
return (List) keyEventPostProcessors.clone ();
return (List<KeyEventPostProcessor>) keyEventPostProcessors.clone ();
}
/**

View file

@ -38,7 +38,10 @@ exception statement from your version. */
package java.awt;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.peer.LightweightPeer;
import java.util.WeakHashMap;
/**
@ -49,7 +52,7 @@ import java.util.WeakHashMap;
*
* @author Roman Kennke (kennke@aicas.com)
*/
class LightweightDispatcher
final class LightweightDispatcher
{
/**
@ -59,26 +62,17 @@ class LightweightDispatcher
*/
private static WeakHashMap instances = new WeakHashMap();
/**
* The component that is the start of a mouse dragging. All MOUSE_DRAGGED
* events that follow the initial press must have the source set to this,
* as well as the MOUSE_RELEASED event following the dragging.
*/
private Component dragTarget;
/**
* Stores the button number which started the drag operation. This is needed
* because we want to handle only one drag operation and only the button that
* started the dragging should be able to stop it (by a button release).
*/
private int dragButton;
/**
* The last mouse event target. If the target changes, additional
* MOUSE_ENTERED and MOUSE_EXITED events must be dispatched.
*/
private Component lastTarget;
/**
* The current mouseEventTarget.
*/
private Component mouseEventTarget;
/**
* Returns an instance of LightweightDispatcher for the current thread's
* thread group.
@ -113,9 +107,9 @@ class LightweightDispatcher
*
* @param event the event
*/
public boolean dispatchEvent(AWTEvent event)
public boolean dispatchEvent(final AWTEvent event)
{
if (event instanceof MouseEvent && event.getSource() instanceof Window)
if (event instanceof MouseEvent)
{
MouseEvent mouseEvent = (MouseEvent) event;
return handleMouseEvent(mouseEvent);
@ -130,151 +124,49 @@ class LightweightDispatcher
* @param ev the mouse event
* @return whether or not we found a lightweight that handled the event.
*/
private boolean handleMouseEvent(MouseEvent ev)
private boolean handleMouseEvent(final MouseEvent ev)
{
Window window = (Window) ev.getSource();
// Find the target for the mouse event. We first seach the deepest
// component at the specified location. The we go up to its parent and
// try to find a neighbor of the deepest component that is suitable as
// mouse event target (it must be showing, at that location and have either
// a MouseListener or MouseMotionListener installed). If no such component
// is found, then we walk up the container hierarchy and find the next
// container that has a MouseListener or MouseMotionListener installed.
Component deepest = window.findComponentAt(ev.getX(), ev.getY());
if (deepest == null)
return false;
Container parent = deepest.getParent();
Point loc = ev.getPoint();
loc = convertPointToChild(window, loc, parent);
Component target = null;
if (parent != null)
Container container = (Container) ev.getSource();
Component target = findTarget(container, ev.getX(), ev.getY());
trackEnterExit(target, ev);
int id = ev.getID();
// Dont update the mouseEventTarget when dragging. Also, MOUSE_CLICKED
// must be dispatched to the original target of MOUSE_PRESSED, so don't
// update in this case either.
if (! isDragging(ev) && id != MouseEvent.MOUSE_CLICKED)
mouseEventTarget = (target != container) ? target : null;
if (mouseEventTarget != null)
{
target = findTarget(parent, loc);
while (target == null && parent != null)
switch (id)
{
if (parent.mouseListener != null
|| parent.mouseMotionListener != null
|| (parent.eventMask
& (AWTEvent.MOUSE_EVENT_MASK
| AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0)
{
target = parent;
}
else
parent = parent.getParent();
}
}
if (target == null || target.isLightweight())
{
// Dispatch additional MOUSE_EXITED and MOUSE_ENTERED if event target
// is different from the last event target.
if (target != lastTarget)
{
if (lastTarget != null)
{
Point p1 = convertPointToChild(window, ev.getPoint(),
lastTarget);
MouseEvent mouseExited =
new MouseEvent(lastTarget, MouseEvent.MOUSE_EXITED,
ev.getWhen(), ev.getModifiers(), p1.x, p1.y,
ev.getClickCount(), ev.isPopupTrigger());
//System.err.println("event: " + mouseExited);
lastTarget.dispatchEvent(mouseExited);
}
// If a target exists dispatch the MOUSE_ENTERED event.
// Experimenting shows that the MOUSE_ENTERED is also dispatched
// when the mouse is dragging.
if (target != null)
{
Point p = convertPointToChild(window, ev.getPoint(), target);
MouseEvent mouseEntered =
new MouseEvent(target,
MouseEvent.MOUSE_ENTERED, ev.getWhen(),
ev.getModifiers(), p.x, p.y, ev.getClickCount(),
ev.isPopupTrigger());
//System.err.println("event: " + mouseEntered);
target.dispatchEvent(mouseEntered);
}
}
switch (ev.getID())
{
case MouseEvent.MOUSE_PRESSED:
// Handle the start of a drag operation or discard the event if
// one is already in progress. This prevents focus changes with the
// other mouse buttons when one is used for dragging.
if (dragTarget == null)
{
lastTarget = dragTarget = target;
// Save the button that started the drag operation.
dragButton = ev.getButton();
}
else
return false;
case MouseEvent.MOUSE_ENTERED:
case MouseEvent.MOUSE_EXITED:
// This is already handled in trackEnterExit().
break;
case MouseEvent.MOUSE_PRESSED:
case MouseEvent.MOUSE_RELEASED:
// Stop the drag operation only when the button that started
// it was released.
if (dragTarget != null && dragButton == ev.getButton())
{
// Only post MOUSE_RELEASED to dragTarget (set in
// MOUSE_PRESSED) when the dragTarget is actually visible.
// Otherwise post the event to the normal target.
if (dragTarget.isVisible())
target = dragTarget;
dragTarget = null;
}
lastTarget = target;
case MouseEvent.MOUSE_MOVED:
redispatch(ev, mouseEventTarget, id);
break;
case MouseEvent.MOUSE_CLICKED:
// When we receive a MOUSE_CLICKED, we set the target to the
// previous target, which must have been a MOUSE_RELEASED event.
// This is necessary for the case when the MOUSE_RELEASED has
// caused the original target (like an internal component) go
// away.
// This line is the reason why it is not possible to move the
// 'lastTarget = target' assignment before the switch-statement.
target = lastTarget;
// MOUSE_CLICKED must be dispatched to the original target of
// MOUSE_PRESSED.
if (target == mouseEventTarget)
redispatch(ev, mouseEventTarget, id);
break;
case MouseEvent.MOUSE_DRAGGED:
// We consider only dragTarget for redispatching the event still
// we have to act in a way that the newly found target component
// was handled.
lastTarget = target;
target = dragTarget;
if (isDragging(ev))
redispatch(ev, mouseEventTarget, id);
break;
default:
// Only declare current target as the old value in all other
// cases.
lastTarget = target;
break;
}
if (target != null)
{
Point targetCoordinates = convertPointToChild(window,
ev.getPoint(),
target);
int dx = targetCoordinates.x - ev.getX();
int dy = targetCoordinates.y - ev.getY();
ev.translatePoint(dx, dy);
ev.setSource(target);
target.dispatchEvent(ev);
// We reset the event, so that the normal event dispatching is not
// influenced by this modified event.
ev.setSource(window);
ev.translatePoint(-dx, -dy);
case MouseEvent.MOUSE_WHEEL:
redispatch(ev, mouseEventTarget, id);
}
return true;
ev.consume();
}
else
return false;
return ev.isConsumed();
}
/**
@ -290,58 +182,180 @@ class LightweightDispatcher
* @return the actual receiver of the mouse event, or null, if no such
* component has been found
*/
private Component findTarget(Container c, Point loc)
private Component findTarget(final Container c, final int x, final int y)
{
int numComponents = c.getComponentCount();
Component target = null;
if (c != null)
// First we check the children of the container.
// Note: It is important that we use the package private Container
// fields ncomponents and component here. There are applications
// that override getComponentCount()
// and getComponent() to hide internal components, which makes
// the LightweightDispatcher not work correctly in these cases.
// As a positive sideeffect this is slightly more efficient.
int nChildren = c.ncomponents;
for (int i = 0; i < nChildren && target == null; i++)
{
for (int i = 0; i < numComponents; i++)
Component child = c.component[i];
int childX = x - child.x;
int childY = y - child.y;
if (child != null && child.visible
&& child.peer instanceof LightweightPeer
&& child.contains(childX, childY))
{
Component child = c.getComponent(i);
if (child.isShowing())
// Check if there's a deeper possible target.
if (child instanceof Container)
{
if (child.contains(loc.x - child.getX(), loc.y - child.getY())
&& (child.mouseListener != null
|| child.mouseMotionListener != null
|| (child.eventMask
& (AWTEvent.MOUSE_EVENT_MASK
| AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0))
{
target = child;
break;
}
Component deeper = findTarget((Container) child,
childX, childY);
if (deeper != null)
target = deeper;
}
// Check if the child itself is interested in mouse events.
else if (isMouseListening(child))
target = child;
}
}
// Check the container itself, if we didn't find a target yet.
if (target == null && c.contains(x, y) && isMouseListening(c))
target = c;
return target;
}
/**
* Converts a point in the parent's coordinate system to a child coordinate
* system. The resulting point is stored in the same Point object and
* returned.
* Checks if the specified component would be interested in a mouse event.
*
* @param parent the parent component
* @param p the point
* @param child the child component
* @param c the component to check
*
* @return the translated point
* @return <code>true</code> if the component has mouse listeners installed,
* <code>false</code> otherwise
*/
private Point convertPointToChild(Component parent, Point p,
Component child)
private boolean isMouseListening(final Component c)
{
int offX = 0;
int offY = 0;
Component comp = child;
while (comp != null && comp != parent)
// Note: It is important to NOT check if the component is listening
// for a specific event (for instance, mouse motion events). The event
// gets dispatched to the component if the component is listening
// for ANY mouse event, even when the component is not listening for the
// specific type of event. There are applications that depend on this
// (sadly).
return c.mouseListener != null
|| c.mouseMotionListener != null
|| c.mouseWheelListener != null
|| (c.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0
|| (c.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0
|| (c.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0;
}
/**
* Tracks MOUSE_ENTERED and MOUSE_EXIT as well as MOUSE_MOVED and
* MOUSE_DRAGGED and creates synthetic MOUSE_ENTERED and MOUSE_EXITED for
* lightweight component.s
*
* @param target the current mouse event target
* @param ev the mouse event
*/
private void trackEnterExit(final Component target, final MouseEvent ev)
{
int id = ev.getID();
if (target != lastTarget)
{
offX += comp.getX();
offY += comp.getY();
comp = comp.getParent();
if (lastTarget != null)
redispatch(ev, lastTarget, MouseEvent.MOUSE_EXITED);
if (id == MouseEvent.MOUSE_EXITED)
ev.consume();
if (target != null)
redispatch(ev, target, MouseEvent.MOUSE_ENTERED);
if (id == MouseEvent.MOUSE_ENTERED)
ev.consume();
lastTarget = target;
}
p.x -= offX;
p.y -= offY;
return p;
}
/**
* Redispatches the specified mouse event to the specified target with the
* specified id.
*
* @param ev the mouse event
* @param target the new target
* @param id the new id
*/
private void redispatch(MouseEvent ev, Component target, int id)
{
Component source = ev.getComponent();
if (target != null)
{
// Translate coordinates.
int x = ev.getX();
int y = ev.getY();
for (Component c = target; c != null && c != source; c = c.getParent())
{
x -= c.x;
y -= c.y;
}
// Retarget event.
MouseEvent retargeted;
if (id == MouseEvent.MOUSE_WHEEL)
{
MouseWheelEvent mwe = (MouseWheelEvent) ev;
retargeted = new MouseWheelEvent(target, id, ev.getWhen(),
ev.getModifiers()
| ev.getModifiersEx(), x, y,
ev.getClickCount(),
ev.isPopupTrigger(),
mwe.getScrollType(),
mwe.getScrollAmount(),
mwe.getWheelRotation());
}
else
{
retargeted = new MouseEvent(target, id, ev.getWhen(),
ev.getModifiers() | ev.getModifiersEx(),
x, y, ev.getClickCount(),
ev.isPopupTrigger(), ev.getButton());
}
if (target == source)
((Container) target).dispatchNoLightweight(retargeted);
else
target.dispatchEvent(retargeted);
}
}
/**
* Determines if we are in the middle of a drag operation, that is, if
* any of the buttons is held down.
*
* @param ev the mouse event to check
*
* @return <code>true</code> if we are in the middle of a drag operation,
* <code>false</code> otherwise
*/
private boolean isDragging(MouseEvent ev)
{
int mods = ev.getModifiersEx();
int id = ev.getID();
if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED)
{
switch (ev.getButton())
{
case MouseEvent.BUTTON1:
mods ^= InputEvent.BUTTON1_DOWN_MASK;
break;
case MouseEvent.BUTTON2:
mods ^= InputEvent.BUTTON2_DOWN_MASK;
break;
case MouseEvent.BUTTON3:
mods ^= InputEvent.BUTTON3_DOWN_MASK;
break;
}
}
return (mods & (InputEvent.BUTTON1_DOWN_MASK
| InputEvent.BUTTON2_DOWN_MASK
| InputEvent.BUTTON3_DOWN_MASK)) != 0;
}
}

File diff suppressed because it is too large Load diff

View file

@ -54,38 +54,28 @@ import javax.accessibility.AccessibleRole;
public class Menu extends MenuItem implements MenuContainer, Serializable
{
/*
* Static Variables
*/
/**
* The number used to generate the name returned by getName.
*/
private static transient long next_menu_number;
/**
* The number used to generate the name returned by getName.
*/
private static transient long next_menu_number;
// Serialization Constant
private static final long serialVersionUID = -8809584163345499784L;
// Serialization Constant
private static final long serialVersionUID = -8809584163345499784L;
/**
* @serial The actual items in the menu
*/
private Vector items = new Vector();
/*************************************************************************/
/**
* @serial Flag indicating whether or not this menu is a tear off
*/
private boolean tearOff;
/*
* Instance Variables
*/
/**
* @serial The actual items in the menu
*/
private Vector items = new Vector();
/**
* @serial Flag indicating whether or not this menu is a tear off
*/
private boolean tearOff;
/**
* @serial Indicates whether or not this is a help menu.
*/
private boolean isHelpMenu;
/**
* @serial Indicates whether or not this is a help menu.
*/
private boolean isHelpMenu;
/*
* @serial Unused in this implementation, but present in Sun's
@ -93,371 +83,316 @@ private boolean isHelpMenu;
*/
private int menuSerializedDataVersion = 1;
static final transient String separatorLabel = "-";
static final transient String separatorLabel = "-";
/*************************************************************************/
/**
* Initializes a new instance of <code>Menu</code> with no label and that
* is not a tearoff;
*
* @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
*/
public Menu()
{
}
/*
* Constructors
*/
/**
* Initializes a new instance of <code>Menu</code> that is not a tearoff and
* that has the specified label.
*
* @param label The menu label.
*
* @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
*/
public Menu(String label)
{
this(label, false);
}
/**
* Initializes a new instance of <code>Menu</code> with no label and that
* is not a tearoff;
*
* @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
*/
public
Menu()
{
}
/**
* Initializes a new instance of <code>Menu</code> with the specified
* label and tearoff status.
*
* @param label The label for this menu
* @param isTearOff <code>true</code> if this menu is a tear off menu,
* <code>false</code> otherwise.
*
* @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
*/
public Menu(String label, boolean isTearOff)
{
super(label);
/*************************************************************************/
tearOff = isTearOff;
/**
* Initializes a new instance of <code>Menu</code> that is not a tearoff and
* that has the specified label.
*
* @param label The menu label.
*
* @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
*/
public
Menu(String label)
{
this(label, false);
}
if (label.equals("Help"))
isHelpMenu = true;
/*************************************************************************/
if (GraphicsEnvironment.isHeadless())
throw new HeadlessException();
}
/**
* Initializes a new instance of <code>Menu</code> with the specified
* label and tearoff status.
*
* @param label The label for this menu
* @param isTearOff <code>true</code> if this menu is a tear off menu,
* <code>false</code> otherwise.
*
* @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
*/
public
Menu(String label, boolean isTearOff)
{
super(label);
/**
* Tests whether or not this menu is a tearoff.
*
* @return <code>true</code> if this menu is a tearoff, <code>false</code>
* otherwise.
*/
public boolean isTearOff()
{
return(tearOff);
}
tearOff = isTearOff;
/**
* Returns the number of items in this menu.
*
* @return The number of items in this menu.
*/
public int getItemCount()
{
return countItems();
}
if (label.equals("Help"))
isHelpMenu = true;
if (GraphicsEnvironment.isHeadless())
throw new HeadlessException ();
}
/*************************************************************************/
/*
* Instance Methods
*/
/**
* Tests whether or not this menu is a tearoff.
*
* @return <code>true</code> if this menu is a tearoff, <code>false</code>
* otherwise.
*/
public boolean
isTearOff()
{
return(tearOff);
}
/*************************************************************************/
/**
* Returns the number of items in this menu.
*
* @return The number of items in this menu.
*/
public int
getItemCount()
{
return countItems ();
}
/**
* Returns the number of items in this menu.
*
* @return The number of items in this menu.
*
* @deprecated As of JDK 1.1, replaced by getItemCount().
*/
public int countItems ()
{
return items.size ();
}
/**
* Returns the number of items in this menu.
*
* @return The number of items in this menu.
*
* @deprecated As of JDK 1.1, replaced by getItemCount().
*/
public int countItems()
{
return items.size();
}
/*************************************************************************/
/**
* Returns the item at the specified index.
*
* @param index the item index.
*
* @return The item at the specified index.
*
* @exception ArrayIndexOutOfBoundsException If the index value is not valid.
*/
public MenuItem getItem(int index)
{
return((MenuItem) items.elementAt(index));
}
/**
* Returns the item at the specified index.
*
* @return The item at the specified index.
*
* @exception ArrayIndexOutOfBoundsException If the index value is not valid.
*/
public MenuItem
getItem(int index)
{
return((MenuItem)items.elementAt(index));
}
/**
* Adds the specified item to this menu. If it was previously part of
* another menu, it is first removed from that menu.
*
* @param item The new item to add.
*
* @return The item that was added.
*/
public MenuItem add(MenuItem item)
{
MenuContainer parent = item.getParent();
if (parent != null)
parent.remove(item);
/*************************************************************************/
items.addElement(item);
item.setParent(this);
/**
* Adds the specified item to this menu. If it was previously part of
* another menu, it is first removed from that menu.
*
* @param item The new item to add.
*
* @return The item that was added.
*/
public MenuItem
add(MenuItem item)
{
MenuContainer parent = item.getParent();
if (parent != null)
parent.remove(item);
if (peer != null)
{
item.addNotify();
MenuPeer mp = (MenuPeer) peer;
mp.addItem(item);
}
items.addElement(item);
item.setParent(this);
return item;
}
if (peer != null)
{
item.addNotify();
MenuPeer mp = (MenuPeer) peer;
mp.addItem(item);
}
/**
* Add an item with the specified label to this menu.
*
* @param label The label of the menu item to add.
*/
public void add(String label)
{
add(new MenuItem(label));
}
return item;
}
/**
* Inserts the specified menu item into this menu at the specified index. If
* the index is greater than or equal to the number of items already in the
* menu, the new item is added as the last item in the menu.
*
* @param item The menu item to add (<code>null</code> not permitted).
* @param index The index of the menu item (>= 0).
*
* @throws IllegalArgumentException if the index is less than zero.
* @throws NullPointerException if <code>item</code> is <code>null</code>.
*/
public void insert(MenuItem item, int index)
{
if (index < 0)
throw new IllegalArgumentException("Index is less than zero");
/*************************************************************************/
int count = getItemCount();
/**
* Add an item with the specified label to this menu.
*
* @param label The label of the menu item to add.
*/
public void
add(String label)
{
add(new MenuItem(label));
}
/*************************************************************************/
/**
* Inserts the specified menu item into this menu at the specified index.
*
* @param item The menu item to add.
* @param index The index of the menu item.
*
* @exception IllegalArgumentException If the index is less than zero.
* @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
*/
public void
insert(MenuItem item, int index)
{
if (index < 0)
throw new IllegalArgumentException("Index is less than zero");
int count = getItemCount ();
if (index >= count)
add(item);
else
{
MenuContainer parent = item.getParent();
if (parent != null)
parent.remove(item);
if (index >= count)
add(item);
else
{
MenuContainer parent = item.getParent();
if (parent != null)
parent.remove(item);
items.insertElementAt(item, index);
item.setParent(this);
items.insertElementAt(item, index);
item.setParent(this);
MenuPeer peer = (MenuPeer) getPeer();
if (peer == null)
return;
MenuPeer peer = (MenuPeer) getPeer();
if (peer == null)
return;
for (int i = count - 1; i >= index; i--)
peer.delItem(i);
for (int i = count - 1; i >= index; i--)
peer.delItem(i);
item.addNotify();
peer.addItem(item);
item.addNotify();
peer.addItem(item);
for (int i = index; i < count; i++)
peer.addItem((MenuItem) items.elementAt (i));
}
// bear in mind that count is the number of items *before* the new
// item was added
for (int i = index + 1; i <= count; i++)
peer.addItem((MenuItem) items.elementAt(i));
}
}
/*************************************************************************/
/**
* Inserts an item with the specified label into this menu at the specified index.
*
* @param label The label of the item to add.
* @param index The index of the menu item.
*
* @exception IllegalArgumentException If the index is less than zero.
* @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
*/
public void
insert(String label, int index)
{
insert(new MenuItem(label), index);
}
/*************************************************************************/
/**
* Adds a separator bar at the current menu location.
*/
public void
addSeparator()
{
add(new MenuItem(separatorLabel));
}
/*************************************************************************/
/**
* Inserts a separator bar at the specified index value.
*
* @param index The index at which to insert a separator bar.
*
* @exception IllegalArgumentException If the index is less than zero.
* @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
*/
public void
insertSeparator(int index)
{
insert(new MenuItem(separatorLabel), index);
}
/*************************************************************************/
/**
* Deletes the item at the specified index from this menu.
*
* @param index The index of the item to remove.
*
* @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
*/
public synchronized void
remove(int index)
{
MenuItem item = (MenuItem) items.remove(index);
MenuPeer mp = (MenuPeer) getPeer();
if (mp != null)
{
mp.delItem(index);
item.removeNotify();
}
item.setParent(null);
}
/*************************************************************************/
/**
* Removes the specifed item from the menu. If the specified component
* does not exist, this method does nothing.
*
* @param item The component to remove.
*/
public void
remove(MenuComponent item)
{
int index = items.indexOf(item);
if (index == -1)
return;
remove(index);
}
/*************************************************************************/
/**
* Removes all the elements from this menu.
*/
public synchronized void
removeAll()
{
int count = getItemCount();
for(int i = 0; i < count; i++)
{
// We must always remove item 0.
remove(0);
}
}
/*************************************************************************/
/**
* Creates the native peer for this object.
*/
public void
addNotify()
{
MenuPeer peer = (MenuPeer) getPeer();
if (peer == null)
{
peer = getToolkit().createMenu(this);
setPeer(peer);
}
Enumeration e = items.elements();
while (e.hasMoreElements())
{
MenuItem mi = (MenuItem)e.nextElement();
mi.addNotify();
peer.addItem(mi);
}
super.addNotify ();
}
/*************************************************************************/
/**
* Destroys the native peer for this object.
*/
public void
removeNotify()
{
Enumeration e = items.elements();
while (e.hasMoreElements())
/**
* Inserts an item with the specified label into this menu at the specified
* index. If the index is greater than or equal to the number of items
* already in the menu, the new item is added as the last item in the menu.
*
* @param label The label of the item to add.
* @param index The index of the menu item (>= 0).
*
* @throws IllegalArgumentException If the index is less than zero.
*/
public void insert(String label, int index)
{
MenuItem mi = (MenuItem) e.nextElement();
mi.removeNotify();
insert(new MenuItem(label), index);
}
super.removeNotify();
}
/*************************************************************************/
/**
* Adds a separator bar at the current menu location.
*/
public void addSeparator()
{
add(new MenuItem(separatorLabel));
}
/**
* Returns a debugging string for this menu.
*
* @return A debugging string for this menu.
*/
public String
paramString()
{
return (",tearOff=" + tearOff + ",isHelpMenu=" + isHelpMenu
+ super.paramString());
}
/**
* Inserts a separator bar at the specified index value.
*
* @param index The index at which to insert a separator bar.
*
* @exception IllegalArgumentException If the index is less than zero.
* @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
*/
public void insertSeparator(int index)
{
insert(new MenuItem(separatorLabel), index);
}
/**
* Deletes the item at the specified index from this menu.
*
* @param index The index of the item to remove.
*
* @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
*/
public synchronized void remove(int index)
{
MenuItem item = (MenuItem) items.remove(index);
MenuPeer mp = (MenuPeer) getPeer();
if (mp != null)
{
mp.delItem(index);
item.removeNotify();
}
item.setParent(null);
}
/**
* Removes the specifed item from the menu. If the specified component
* does not exist, this method does nothing.
*
* @param item The component to remove.
*/
public void remove(MenuComponent item)
{
int index = items.indexOf(item);
if (index == -1)
return;
remove(index);
}
/**
* Removes all the elements from this menu.
*/
public synchronized void removeAll()
{
int count = getItemCount();
for(int i = 0; i < count; i++)
{
// We must always remove item 0.
remove(0);
}
}
/**
* Creates the native peer for this object.
*/
public void addNotify()
{
MenuPeer peer = (MenuPeer) getPeer();
if (peer == null)
{
peer = getToolkit().createMenu(this);
setPeer(peer);
}
Enumeration e = items.elements();
while (e.hasMoreElements())
{
MenuItem mi = (MenuItem)e.nextElement();
mi.addNotify();
peer.addItem(mi);
}
super.addNotify();
}
/**
* Destroys the native peer for this object.
*/
public void removeNotify()
{
Enumeration e = items.elements();
while (e.hasMoreElements())
{
MenuItem mi = (MenuItem) e.nextElement();
mi.removeNotify();
}
super.removeNotify();
}
/**
* Returns a debugging string for this menu.
*
* @return A debugging string for this menu.
*/
public String paramString()
{
return (",tearOff=" + tearOff + ",isHelpMenu=" + isHelpMenu
+ super.paramString());
}
/**
* Basic Accessibility class for Menu. Details get provided in derived

View file

@ -272,7 +272,7 @@ public class MenuBar extends MenuComponent
*
* @return a list of all shortcuts for the menus in this menu bar
*/
public synchronized Enumeration shortcuts()
public synchronized Enumeration<MenuShortcut> shortcuts()
{
Vector shortcuts = new Vector();
Enumeration e = menus.elements();

View file

@ -523,11 +523,11 @@ removeActionListener(ActionListener l)
* ClassClassException is thrown.
* @since 1.3
*/
public EventListener[] getListeners(Class listenerType)
public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
if (listenerType == ActionListener.class)
return getActionListeners();
return (EventListener[]) Array.newInstance(listenerType, 0);
return (T[]) getActionListeners();
return (T[]) Array.newInstance(listenerType, 0);
}
/*************************************************************************/

View file

@ -38,6 +38,8 @@ exception statement from your version. */
package java.awt;
import java.awt.event.KeyEvent;
/**
* This class implements a keyboard accelerator for a menu item.
*
@ -70,6 +72,8 @@ private int key;
*/
private boolean usesShift;
private String keyName;
/*************************************************************************/
/**
@ -99,6 +103,7 @@ MenuShortcut(int key, boolean usesShift)
{
this.key = key;
this.usesShift = usesShift;
setKeyName(key);
}
/*************************************************************************/
@ -181,7 +186,11 @@ equals(Object obj)
public String
toString()
{
return(getClass().getName() + "[" + paramString () + "]");
String temp = "Ctrl+";
if (usesShift)
temp = temp + "Shift+";
temp = temp + keyName;
return temp;
}
public int
@ -204,4 +213,224 @@ paramString()
return "key=" + key + ",usesShift=" + usesShift;
}
} // class MenuShortcut
private void
setKeyName(int key)
{
if (key == '\n')
keyName = "Enter";
else if (key == '\b')
keyName = "Backspace";
else if (key == '\t')
keyName = "Tab";
else if (key == ' ')
keyName = "Space";
else if (key == ',')
keyName = "Comma";
else if (key == '.')
keyName = "Period";
else if (key == '/')
keyName = "Slash";
else if (key == '\\')
keyName = "Back Slash";
else if (key == ';')
keyName = "Semicolon";
else if (key == '=')
keyName = "Equals";
else if (key == '[')
keyName = "Open Bracket";
else if (key == ']')
keyName = "Close Bracket";
else if (key == '0')
keyName = "0";
else if (key == '1')
keyName = "1";
else if (key == '2')
keyName = "2";
else if (key == '3')
keyName = "3";
else if (key == '4')
keyName = "4";
else if (key == '5')
keyName = "5";
else if (key == '6')
keyName = "6";
else if (key == '7')
keyName = "7";
else if (key == '8')
keyName = "8";
else if (key == '9')
keyName = "9";
else if (key == 'A')
keyName = "A";
else if (key == 'B')
keyName = "B";
else if (key == 'C')
keyName = "C";
else if (key == 'D')
keyName = "D";
else if (key == 'E')
keyName = "E";
else if (key == 'F')
keyName = "F";
else if (key == 'G')
keyName = "G";
else if (key == 'H')
keyName = "H";
else if (key == 'I')
keyName = "I";
else if (key == 'J')
keyName = "J";
else if (key == 'K')
keyName = "K";
else if (key == 'L')
keyName = "L";
else if (key == 'M')
keyName = "M";
else if (key == 'N')
keyName = "N";
else if (key == 'O')
keyName = "O";
else if (key == 'P')
keyName = "P";
else if (key == 'Q')
keyName = "Q";
else if (key == 'R')
keyName = "R";
else if (key == 'S')
keyName = "S";
else if (key == 'T')
keyName = "T";
else if (key == 'U')
keyName = "U";
else if (key == 'V')
keyName = "V";
else if (key == 'W')
keyName = "W";
else if (key == 'X')
keyName = "X";
else if (key == 'Y')
keyName = "Y";
else if (key == 'Z')
keyName = "Z";
else if (key == 3)
keyName = "Cancel";
else if (key == 12)
keyName = "Clear";
else if (key == 16)
keyName = "Shift";
else if (key == 17)
keyName = "Ctrl";
else if (key == 18)
keyName = "Alt";
else if (key == 19)
keyName = "Pause";
else if (key == 20)
keyName = "Caps Lock";
else if (key == 21)
keyName = "Kana";
else if (key == 24)
keyName = "Final";
else if (key == 25)
keyName = "Kanji";
else if (key == 27)
keyName = "Escape";
else if (key == 28)
keyName = "Convert";
else if (key == 29)
keyName = "No Convert";
else if (key == 30)
keyName = "Accept";
else if (key == 31)
keyName = "Mode Change";
else if (key == 33)
keyName = "Page Up";
else if (key == 34)
keyName = "Page Down";
else if (key == 35)
keyName = "End";
else if (key == 36)
keyName = "Home";
else if (key == 37)
keyName = "Left";
else if (key == 38)
keyName = "Up";
else if (key == 39)
keyName = "Right";
else if (key == 40)
keyName = "Down";
else if (key == 96)
keyName = "NumPad-0";
else if (key == 97)
keyName = "NumPad-1";
else if (key == 98)
keyName = "NumPad-2";
else if (key == 99)
keyName = "NumPad-3";
else if (key == 100)
keyName = "NumPad-4";
else if (key == 101)
keyName = "NumPad-5";
else if (key == 102)
keyName = "NumPad-6";
else if (key == 103)
keyName = "NumPad-7";
else if (key == 104)
keyName = "NumPad-8";
else if (key == 105)
keyName = "NumPad-9";
else if (key == 106)
keyName = "NumPad *";
else if (key == 107)
keyName = "NumPad +";
else if (key == 108)
keyName = "NumPad ,";
else if (key == 109)
keyName = "NumPad -";
else if (key == 110)
keyName = "NumPad .";
else if (key == 111)
keyName = "NumPad /";
else if (key == 112)
keyName = "F1";
else if (key == 113)
keyName = "F2";
else if (key == 114)
keyName = "F3";
else if (key == 115)
keyName = "F4";
else if (key == 116)
keyName = "F5";
else if (key == 117)
keyName = "F6";
else if (key == 118)
keyName = "F7";
else if (key == 119)
keyName = "F8";
else if (key == 120)
keyName = "F9";
else if (key == 121)
keyName = "F10";
else if (key == 122)
keyName = "F11";
else if (key == 123)
keyName = "F12";
else if (key == 127)
keyName = "Delete";
else if (key == 144)
keyName = "Num Lock";
else if (key == 145)
keyName = "Scroll Lock";
else if (key == 154)
keyName = "Print Screen";
else if (key == 155)
keyName = "Insert";
else if (key == 156)
keyName = "Help";
else if (key == 157)
keyName = "Meta";
else if (key == 192)
keyName = "Back Quote";
else if (key == 222)
keyName = "Quote";
}
} // class MenuShortcut

View file

@ -1,5 +1,5 @@
/* Rectangle.java -- represents a graphics rectangle
Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation
Copyright (C) 1999, 2000, 2001, 2002, 2006, Free Software Foundation
This file is part of GNU Classpath.
@ -119,7 +119,6 @@ public class Rectangle extends Rectangle2D implements Shape, Serializable
* coordinates of the specified rectangle.
*
* @param r the rectangle to copy from
* @throws NullPointerException if r is null
* @since 1.1
*/
public Rectangle(Rectangle r)
@ -168,7 +167,6 @@ public class Rectangle extends Rectangle2D implements Shape, Serializable
*
* @param p the upper left corner of the rectangle
* @param d the width and height of the rectangle
* @throws NullPointerException if p or d is null
*/
public Rectangle(Point p, Dimension d)
{
@ -185,7 +183,7 @@ public class Rectangle extends Rectangle2D implements Shape, Serializable
* @param p the upper left corner of the rectangle
*/
public Rectangle(Point p)
{
{
x = p.x;
y = p.y;
}
@ -198,7 +196,7 @@ public class Rectangle extends Rectangle2D implements Shape, Serializable
* @param d the width and height of the rectangle
*/
public Rectangle(Dimension d)
{
{
width = d.width;
height = d.height;
}
@ -299,8 +297,10 @@ public class Rectangle extends Rectangle2D implements Shape, Serializable
}
/**
* Updates this rectangle to have the specified dimensions, as rounded to
* integers.
* Updates this rectangle to have the specified dimensions, rounded to the
* integer precision used by this class (the values are rounded "outwards" so
* that the stored rectangle completely encloses the specified double
* precision rectangle).
*
* @param x the new X coordinate of the upper left hand corner
* @param y the new Y coordinate of the upper left hand corner
@ -310,10 +310,10 @@ public class Rectangle extends Rectangle2D implements Shape, Serializable
*/
public void setRect(double x, double y, double width, double height)
{
this.x = (int) x;
this.y = (int) y;
this.width = (int) width;
this.height = (int) height;
this.x = (int) Math.floor(x);
this.y = (int) Math.floor(y);
this.width = (int) Math.ceil(x + width) - this.x;
this.height = (int) Math.ceil(y + height) - this.y;
}
/**

View file

@ -54,7 +54,8 @@ import java.util.Set;
* @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
* @author Eric Blake (ebb9@email.byu.edu)
*/
public class RenderingHints implements Map, Cloneable
public class RenderingHints
implements Map<Object,Object>, Cloneable
{
/**
* The base class used to represent keys.
@ -550,7 +551,7 @@ public class RenderingHints implements Map, Cloneable
* @param init a map containing a collection of hints (<code>null</code>
* permitted).
*/
public RenderingHints(Map init)
public RenderingHints(Map<Key,?> init)
{
if (init != null)
putAll(init);
@ -704,7 +705,7 @@ public class RenderingHints implements Map, Cloneable
* @throws IllegalArgumentException if the map contains a value that is
* not compatible with its key.
*/
public void putAll(Map m)
public void putAll(Map<?,?> m)
{
// preprocess map to generate appropriate exceptions
Iterator iterator = m.keySet().iterator();
@ -723,7 +724,7 @@ public class RenderingHints implements Map, Cloneable
*
* @return A set of keys.
*/
public Set keySet()
public Set<Object> keySet()
{
return hintMap.keySet();
}
@ -735,7 +736,7 @@ public class RenderingHints implements Map, Cloneable
*
* @return A collection of values.
*/
public Collection values()
public Collection<Object> values()
{
return hintMap.values();
}
@ -745,7 +746,7 @@ public class RenderingHints implements Map, Cloneable
*
* @return A set of entries.
*/
public Set entrySet()
public Set<Map.Entry<Object,Object>> entrySet()
{
return Collections.unmodifiableSet(hintMap.entrySet());
}

View file

@ -338,10 +338,15 @@ getVScrollbarWidth()
* Returns the current scroll position of the viewport.
*
* @return The current scroll position of the viewport.
*
* @throws NullPointerException if the scrollpane does have a child.
*/
public Point
getScrollPosition()
{
if (getComponentCount() == 0)
throw new NullPointerException();
int x = 0;
int y = 0;
@ -380,20 +385,35 @@ setScrollPosition(Point scrollPosition) throws IllegalArgumentException
* @param x The new X coordinate of the scroll position.
* @param y The new Y coordinate of the scroll position.
*
* @throws NullPointerException if scrollpane does not have a child.
*
* @exception IllegalArgumentException If the specified value is outside
* the legal scrolling range.
*/
public void
setScrollPosition(int x, int y)
{
if (getComponentCount() == 0)
throw new NullPointerException("child is null");
if (x > (int) (getComponent(0).getWidth() - getViewportSize().getWidth()))
x = (int) (getComponent(0).getWidth() - getViewportSize().getWidth());
if (y > (int) (getComponent(0).getHeight() - getViewportSize().getHeight()))
y = (int) (getComponent(0).getHeight() - getViewportSize().getHeight());
if (x < 0)
x = 0;
if (y < 0)
y = 0;
Adjustable h = getHAdjustable();
Adjustable v = getVAdjustable();
if (h != null)
h.setValue(x);
if (v != null)
v.setValue(y);
ScrollPanePeer spp = (ScrollPanePeer)getPeer();
if (spp != null)
spp.setScrollPosition(x, y);
@ -414,7 +434,7 @@ addNotify()
super.addNotify();
Component[] list = getComponents();
if (list != null && list.length > 0 && ! (list[0] instanceof Panel))
if (list != null && list.length > 0 && list[0].isLightweight())
{
Panel panel = new Panel();
panel.setLayout(new BorderLayout());
@ -453,9 +473,7 @@ removeNotify()
if ((list != null) && (list.length > 0))
remove(list[0]);
super.addImpl(component, constraints, -1);
doLayout();
super.addImpl(component, constraints, index);
}
/*************************************************************************/
@ -507,6 +525,8 @@ layout()
p.y = dim.height;
setScrollPosition (p);
list[0].setLocation(new Point());
}
}
@ -518,11 +538,12 @@ layout()
* not have layout managers.
*
* @param layoutManager Ignored
* @throws AWTError Always throws this error when called.
*/
public final void
setLayout(LayoutManager layoutManager)
{
return;
throw new AWTError("ScrollPane controls layout");
}
/*************************************************************************/
@ -553,16 +574,37 @@ paramString()
+ getX() + ","
+ getY() + ","
+ getWidth() + "x" + getHeight() + ","
+ "ScrollPosition=(" + scrollPosition.getX() + ","
+ scrollPosition.getY() + "),"
+ getIsValidString() + ","
+ "ScrollPosition=(" + scrollPosition.x + ","
+ scrollPosition.y + "),"
+ "Insets=(" + insets.top + ","
+ insets.left + ","
+ insets.bottom + ","
+ insets.right + "),"
+ "ScrollbarDisplayPolicy=" + getScrollbarDisplayPolicy() + ","
+ "ScrollbarDisplayPolicy=" + getScrollbarDisplayPolicyString() + ","
+ "wheelScrollingEnabled=" + isWheelScrollingEnabled();
}
private String
getScrollbarDisplayPolicyString()
{
if (getScrollbarDisplayPolicy() == 0)
return "as-needed";
else if (getScrollbarDisplayPolicy() == 1)
return "always";
else
return "never";
}
private String
getIsValidString()
{
if (isValid())
return "valid";
else
return "invalid";
}
/**
* Tells whether or not an event is enabled.
*

View file

@ -145,14 +145,26 @@ public class ScrollPaneAdjustable
this.blockIncrement = blockIncrement;
}
public void setMaximum (int maximum)
/**
* This method should never be called.
*
* @param maximum The maximum value to be set.
* @throws AWTError Always throws this error when called.
*/
public void setMaximum (int maximum) throws AWTError
{
this.maximum = maximum;
throw new AWTError("Can be set by scrollpane only");
}
/**
* This method should never be called.
*
* @param minimum The minimum value to be set.
* @throws AWTError Always throws this error when called.
*/
public void setMinimum (int minimum)
{
this.minimum = minimum;
throw new AWTError("Can be set by scrollpane only");
}
public void setUnitIncrement (int unitIncrement)
@ -171,20 +183,36 @@ public class ScrollPaneAdjustable
maximum = value;
}
/**
* This method should never be called.
*
* @param visibleAmount The visible amount to be set.
* @throws AWTError Always throws this error when called.
*/
public void setVisibleAmount (int visibleAmount)
{
this.visibleAmount = visibleAmount;
throw new AWTError("Can be set by scrollpane only");
}
public String paramString ()
{
return ("scrollpane=" + sp + ", orientation=" + orientation
+ ", value=" + value + ", minimum=" + minimum
+ ", maximum=" + maximum + ", visibleAmount=" + visibleAmount
+ ", unitIncrement=" + unitIncrement
+ ", blockIncrement=" + blockIncrement);
return paramStringHelper()
+ ",[" + getMinimum() + ".." + getMaximum()
+ "],val=" + getValue()
+ ",vis=" + getVisibleAmount()
+ ",unit=" + getUnitIncrement()
+ ",block=" + getBlockIncrement()
+ ",isAdjusting=" + valueIsAdjusting;
}
private String paramStringHelper()
{
if (getOrientation() == HORIZONTAL)
return "horizontal";
else
return "vertical";
}
public String toString()
{
return getClass().getName() + "[" + paramString() + "]";
@ -209,5 +237,6 @@ public class ScrollPaneAdjustable
{
this.valueIsAdjusting = valueIsAdjusting;
}
} // class ScrollPaneAdjustable

View file

@ -341,17 +341,22 @@ public class Scrollbar extends Component implements Accessible, Adjustable
public synchronized void setValues(int value, int visibleAmount,
int minimum, int maximum)
{
if (maximum < minimum)
maximum = minimum;
if (visibleAmount <= 0)
visibleAmount = 1;
if (maximum <= minimum)
maximum = minimum + 1;
if (value < minimum)
value = minimum;
if (value > maximum)
value = maximum;
if (visibleAmount > maximum - minimum)
visibleAmount = maximum - minimum;
// According to documentation, the actual maximum
// value is (maximum - visibleAmount)
if (value > maximum - visibleAmount)
value = maximum - visibleAmount;
ScrollbarPeer peer = (ScrollbarPeer) getPeer();
if (peer != null
@ -362,30 +367,7 @@ public class Scrollbar extends Component implements Accessible, Adjustable
this.value = value;
this.visibleAmount = visibleAmount;
this.minimum = minimum;
this.maximum = maximum;
int range = maximum - minimum;
if (lineIncrement > range)
{
if (range == 0)
lineIncrement = 1;
else
lineIncrement = range;
if (peer != null)
peer.setLineIncrement(lineIncrement);
}
if (pageIncrement > range)
{
if (range == 0)
pageIncrement = 1;
else
pageIncrement = range;
if (peer != null)
peer.setPageIncrement(pageIncrement);
}
this.maximum = maximum;
}
/**
@ -437,19 +419,13 @@ public class Scrollbar extends Component implements Accessible, Adjustable
{
if (lineIncrement < 0)
throw new IllegalArgumentException("Unit increment less than zero.");
int range = maximum - minimum;
if (lineIncrement > range)
{
if (range == 0)
lineIncrement = 1;
else
lineIncrement = range;
}
if (lineIncrement == this.lineIncrement)
if (lineIncrement == 0)
lineIncrement = 1;
if (lineIncrement == this.lineIncrement)
return;
this.lineIncrement = lineIncrement;
ScrollbarPeer peer = (ScrollbarPeer) getPeer();
@ -507,15 +483,9 @@ public class Scrollbar extends Component implements Accessible, Adjustable
if (pageIncrement < 0)
throw new IllegalArgumentException("Block increment less than zero.");
int range = maximum - minimum;
if (pageIncrement > range)
{
if (range == 0)
pageIncrement = 1;
else
pageIncrement = range;
}
if (pageIncrement == 0)
pageIncrement = 1;
if (pageIncrement == this.pageIncrement)
return;
@ -647,7 +617,7 @@ public class Scrollbar extends Component implements Accessible, Adjustable
* @exception ClassCastException If listenerType doesn't specify a class or
* interface that implements java.util.EventListener.
*/
public EventListener[] getListeners(Class listenerType)
public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
if (listenerType == AdjustmentListener.class)
return AWTEventMulticaster.getListeners(adjustment_listeners,

View file

@ -284,11 +284,7 @@ public class TextArea extends TextComponent implements java.io.Serializable
}
/**
* Retrieve the minimum size for this text area, considering the
* text area's current row and column values. A text area's minimum
* size depends on the number of rows and columns of text it would
* prefer to display, and on the size of the font in which the text
* would be displayed.
* Retrieve the minimum size for this text area.
*
* @return The minimum size for this text field.
*/
@ -298,11 +294,8 @@ public class TextArea extends TextComponent implements java.io.Serializable
}
/**
* Retrieve the minimum size that this text area would have if its
* row and column values were equal to those specified. A text
* area's minimum size depends on the number of rows and columns of
* text it would prefer to display, and on the size of the font in
* which the text would be displayed.
* Retrieve the minimum size for this text area. If the minimum
* size has been set, then rows and columns are used in the calculation.
*
* @param rows The number of rows to use in the minimum size
* calculation.
@ -317,12 +310,8 @@ public class TextArea extends TextComponent implements java.io.Serializable
}
/**
* Retrieve the minimum size for this text area, considering the
* text area's current row and column values. A text area's minimum
* size depends on the number of rows and columns of text it would
* prefer to display, and on the size of the font in which the text
* would be displayed.
*
* Retrieve the minimum size for this text area.
*
* @return The minimum size for this text area.
*
* @deprecated This method is deprecated in favor of
@ -334,11 +323,8 @@ public class TextArea extends TextComponent implements java.io.Serializable
}
/**
* Retrieve the minimum size that this text area would have if its
* row and column values were equal to those specified. A text
* area's minimum size depends on the number of rows and columns of
* text it would prefer to display, and on the size of the font in
* which the text would be displayed.
* Retrieve the minimum size for this text area. If the minimum
* size has been set, then rows and columns are used in the calculation.
*
* @param rows The number of rows to use in the minimum size
* calculation.
@ -352,21 +338,18 @@ public class TextArea extends TextComponent implements java.io.Serializable
*/
public Dimension minimumSize (int rows, int columns)
{
if (isMinimumSizeSet())
return new Dimension(minSize);
TextAreaPeer peer = (TextAreaPeer) getPeer ();
// Sun returns Dimension (0,0) in this case.
if (peer == null)
return new Dimension (0, 0);
return new Dimension (getWidth(), getHeight());
return peer.getMinimumSize (rows, columns);
}
/**
* Retrieve the preferred size for this text area, considering the
* text area's current row and column values. A text area's preferred
* size depends on the number of rows and columns of text it would
* prefer to display, and on the size of the font in which the text
* would be displayed.
* Retrieve the preferred size for this text area.
*
* @return The preferred size for this text field.
*/
@ -376,11 +359,8 @@ public class TextArea extends TextComponent implements java.io.Serializable
}
/**
* Retrieve the preferred size that this text area would have if its
* row and column values were equal to those specified. A text
* area's preferred size depends on the number of rows and columns
* of text it would prefer to display, and on the size of the font
* in which the text would be displayed.
* Retrieve the preferred size for this text area. If the preferred
* size has been set, then rows and columns are used in the calculation.
*
* @param rows The number of rows to use in the preferred size
* calculation.
@ -395,11 +375,7 @@ public class TextArea extends TextComponent implements java.io.Serializable
}
/**
* Retrieve the preferred size for this text area, considering the
* text area's current row and column values. A text area's preferred
* size depends on the number of rows and columns of text it would
* prefer to display, and on the size of the font in which the text
* would be displayed.
* Retrieve the preferred size for this text area.
*
* @return The preferred size for this text field.
*
@ -412,11 +388,8 @@ public class TextArea extends TextComponent implements java.io.Serializable
}
/**
* Retrieve the preferred size that this text area would have if its
* row and column values were equal to those specified. A text
* area's preferred size depends on the number of rows and columns
* of text it would prefer to display, and on the size of the font
* in which the text would be displayed.
* Retrieve the preferred size for this text area. If the preferred
* size has been set, then rows and columns are used in the calculation.
*
* @param rows The number of rows to use in the preferred size
* calculation.
@ -430,11 +403,12 @@ public class TextArea extends TextComponent implements java.io.Serializable
*/
public Dimension preferredSize (int rows, int columns)
{
if (isPreferredSizeSet())
return new Dimension(prefSize);
TextAreaPeer peer = (TextAreaPeer) getPeer ();
// Sun returns Dimension (0,0) in this case.
if (peer == null)
return new Dimension (0, 0);
return new Dimension (getWidth(), getHeight());
return peer.getPreferredSize (rows, columns);
}

View file

@ -391,7 +391,9 @@ public class TextComponent extends Component
*/
public synchronized void setSelectionStart(int selectionStart)
{
select(selectionStart, getSelectionEnd());
select(selectionStart,
(getSelectionEnd() < selectionStart)
? selectionStart : getSelectionEnd());
}
/**
@ -610,7 +612,7 @@ public class TextComponent extends Component
* @exception ClassCastException If listenerType doesn't specify a class or
* interface that implements java.util.EventListener.
*/
public EventListener[] getListeners(Class listenerType)
public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
if (listenerType == TextListener.class)
return AWTEventMulticaster.getListeners(textListener, listenerType);

View file

@ -264,9 +264,12 @@ public class TextField extends TextComponent
*/
public Dimension minimumSize(int columns)
{
if (isMinimumSizeSet())
return new Dimension(minSize);
TextFieldPeer peer = (TextFieldPeer) getPeer ();
if (peer == null)
return null; // FIXME: What do we do if there is no peer?
return new Dimension(getWidth(), getHeight());
return peer.getMinimumSize (columns);
}
@ -316,10 +319,13 @@ public class TextField extends TextComponent
*/
public Dimension preferredSize(int columns)
{
if (isPreferredSizeSet())
return new Dimension(prefSize);
TextFieldPeer peer = (TextFieldPeer) getPeer ();
if (peer == null)
return new Dimension (0, 0);
return new Dimension (getWidth(), getHeight());
return peer.getPreferredSize (columns);
}
@ -422,7 +428,7 @@ public class TextField extends TextComponent
*
* @since 1.3
*/
public EventListener[] getListeners (Class listenerType)
public <T extends EventListener> T[] getListeners (Class<T> listenerType)
{
if (listenerType == ActionListener.class)
return AWTEventMulticaster.getListeners (action_listeners, listenerType);

View file

@ -41,6 +41,7 @@ package java.awt;
import gnu.classpath.SystemProperties;
import gnu.java.awt.peer.GLightweightPeer;
import gnu.java.awt.peer.headless.HeadlessToolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
@ -51,6 +52,7 @@ import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.AWTEventListener;
import java.awt.event.AWTEventListenerProxy;
import java.awt.event.KeyEvent;
import java.awt.font.TextAttribute;
import java.awt.im.InputMethodHighlight;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
@ -86,6 +88,7 @@ import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
@ -119,7 +122,8 @@ public abstract class Toolkit
/** The toolkit properties. */
private static Properties props = new Properties();
protected final Map desktopProperties = new Properties();
protected final Map<String,Object> desktopProperties =
new Hashtable<String,Object>();
protected final PropertyChangeSupport desktopPropsSupport
= new PropertyChangeSupport(this);
@ -130,6 +134,11 @@ public abstract class Toolkit
*/
AWTEventListenerProxy[] awtEventListeners;
/**
* The shared peer for all lightweight components.
*/
private GLightweightPeer lightweightPeer;
/**
* Default constructor for subclasses.
*/
@ -379,7 +388,9 @@ public abstract class Toolkit
*/
protected LightweightPeer createComponent(Component target)
{
return new GLightweightPeer(target);
if (lightweightPeer == null)
lightweightPeer = new GLightweightPeer();
return lightweightPeer;
}
/**
@ -540,10 +551,11 @@ public abstract class Toolkit
*
* @throws AWTError If the toolkit cannot be loaded.
*/
public static Toolkit getDefaultToolkit()
public static synchronized Toolkit getDefaultToolkit()
{
if (toolkit != null)
return toolkit;
String toolkit_name = SystemProperties.getProperty("awt.toolkit",
default_toolkit_name);
try
@ -573,8 +585,18 @@ public abstract class Toolkit
}
catch (Throwable t)
{
AWTError e = new AWTError("Cannot load AWT toolkit: " + toolkit_name);
throw (AWTError) e.initCause(t);
// Check for the headless property.
if (GraphicsEnvironment.isHeadless())
{
toolkit = new HeadlessToolkit();
return toolkit;
}
else
{
AWTError e = new AWTError("Cannot load AWT toolkit: "
+ toolkit_name);
throw (AWTError) e.initCause(t);
}
}
}
@ -964,8 +986,8 @@ public abstract class Toolkit
/**
* @since 1.3
*/
public DragGestureRecognizer
createDragGestureRecognizer(Class recognizer, DragSource ds,
public <T extends DragGestureRecognizer> T
createDragGestureRecognizer(Class<T> recognizer, DragSource ds,
Component comp, int actions,
DragGestureListener l)
{
@ -1252,7 +1274,8 @@ public abstract class Toolkit
/**
* @since 1.3
*/
public abstract Map mapInputMethodHighlight(InputMethodHighlight highlight);
public abstract Map<TextAttribute,?>
mapInputMethodHighlight(InputMethodHighlight highlight);
/**
* Initializes the accessibility framework. In particular, this loads the

View file

@ -1,5 +1,5 @@
/* Window.java --
Copyright (C) 1999, 2000, 2002, 2003, 2004, 2006 Free Software Foundation
Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation
This file is part of GNU Classpath.
@ -140,7 +140,7 @@ public class Window extends Container implements Accessible
this();
graphicsConfiguration = gc;
}
/**
* Initializes a new instance of <code>Window</code> with the specified
* parent. The window will initially be invisible.
@ -250,13 +250,13 @@ public class Window extends Container implements Accessible
/**
* Shows on-screen this window and any of its owned windows for whom
* isVisible returns true.
* @specnote: Deprecated starting in 1.5.
*/
@Deprecated
public void show()
{
synchronized (getTreeLock())
{
if (parent != null && ! parent.isDisplayable())
parent.addNotify();
if (peer == null)
addNotify();
@ -298,11 +298,24 @@ public class Window extends Container implements Accessible
if (initialFocusOwner != null)
initialFocusOwner.requestFocusInWindow();
// Post WINDOW_OPENED from here.
if (windowListener != null
|| (eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0)
{
WindowEvent ev = new WindowEvent(this,
WindowEvent.WINDOW_OPENED);
Toolkit tk = Toolkit.getDefaultToolkit();
tk.getSystemEventQueue().postEvent(ev);
}
shown = true;
}
}
}
/**
* @specnote: Deprecated starting in 1.5.
*/
@Deprecated
public void hide()
{
// Hide visible owned windows.
@ -349,9 +362,15 @@ public class Window extends Container implements Accessible
component[i].removeNotify();
this.removeNotify();
// Post a WINDOW_CLOSED event.
WindowEvent we = new WindowEvent(this, WindowEvent.WINDOW_CLOSED);
getToolkit().getSystemEventQueue().postEvent(we);
// Post WINDOW_CLOSED from here.
if (windowListener != null
|| (eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0)
{
WindowEvent ev = new WindowEvent(this,
WindowEvent.WINDOW_CLOSED);
Toolkit tk = Toolkit.getDefaultToolkit();
tk.getSystemEventQueue().postEvent(ev);
}
}
}
@ -479,7 +498,11 @@ public class Window extends Container implements Accessible
*/
public synchronized void addWindowListener(WindowListener listener)
{
windowListener = AWTEventMulticaster.add(windowListener, listener);
if (listener != null)
{
newEventsOnly = true;
windowListener = AWTEventMulticaster.add(windowListener, listener);
}
}
/**
@ -536,7 +559,12 @@ public class Window extends Container implements Accessible
*/
public void addWindowFocusListener (WindowFocusListener wfl)
{
windowFocusListener = AWTEventMulticaster.add (windowFocusListener, wfl);
if (wfl != null)
{
newEventsOnly = true;
windowFocusListener = AWTEventMulticaster.add (windowFocusListener,
wfl);
}
}
/**
@ -546,7 +574,12 @@ public class Window extends Container implements Accessible
*/
public void addWindowStateListener (WindowStateListener wsl)
{
windowStateListener = AWTEventMulticaster.add (windowStateListener, wsl);
if (wsl != null)
{
newEventsOnly = true;
windowStateListener = AWTEventMulticaster.add (windowStateListener,
wsl);
}
}
/**
@ -577,42 +610,21 @@ public class Window extends Container implements Accessible
*
* @since 1.3
*/
public EventListener[] getListeners(Class listenerType)
public <T extends EventListener> T[] getListeners(Class<T> listenerType)
{
if (listenerType == WindowListener.class)
return getWindowListeners();
return (T[]) getWindowListeners();
return super.getListeners(listenerType);
}
void dispatchEventImpl(AWTEvent e)
{
// Make use of event id's in order to avoid multiple instanceof tests.
if (e.id <= WindowEvent.WINDOW_LAST
&& e.id >= WindowEvent.WINDOW_FIRST
&& (windowListener != null
|| windowFocusListener != null
|| windowStateListener != null
|| (eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0))
processEvent(e);
else
if (e.getID() == ComponentEvent.COMPONENT_RESIZED)
{
if (peer != null && (e.id == ComponentEvent.COMPONENT_RESIZED
|| e.id == ComponentEvent.COMPONENT_MOVED))
{
Rectangle bounds = peer.getBounds();
x = bounds.x;
y = bounds.y;
height = bounds.height;
width = bounds.width;
if (e.id == ComponentEvent.COMPONENT_RESIZED)
{
invalidate();
validate();
}
}
super.dispatchEventImpl(e);
invalidate();
validate();
}
super.dispatchEventImpl(e);
}
/**
@ -626,7 +638,28 @@ public class Window extends Container implements Accessible
protected void processEvent(AWTEvent evt)
{
if (evt instanceof WindowEvent)
processWindowEvent((WindowEvent) evt);
{
WindowEvent we = (WindowEvent) evt;
switch (evt.getID())
{
case WindowEvent.WINDOW_OPENED:
case WindowEvent.WINDOW_CLOSED:
case WindowEvent.WINDOW_CLOSING:
case WindowEvent.WINDOW_ICONIFIED:
case WindowEvent.WINDOW_DEICONIFIED:
case WindowEvent.WINDOW_ACTIVATED:
case WindowEvent.WINDOW_DEACTIVATED:
processWindowEvent(we);
break;
case WindowEvent.WINDOW_GAINED_FOCUS:
case WindowEvent.WINDOW_LOST_FOCUS:
processWindowFocusEvent(we);
break;
case WindowEvent.WINDOW_STATE_CHANGED:
processWindowStateEvent(we);
break;
}
}
else
super.processEvent(evt);
}
@ -641,54 +674,35 @@ public class Window extends Container implements Accessible
*/
protected void processWindowEvent(WindowEvent evt)
{
int id = evt.getID();
if (id == WindowEvent.WINDOW_GAINED_FOCUS
|| id == WindowEvent.WINDOW_LOST_FOCUS)
processWindowFocusEvent (evt);
else if (id == WindowEvent.WINDOW_STATE_CHANGED)
processWindowStateEvent (evt);
else
if (windowListener != null)
{
if (windowListener != null)
{
switch (evt.getID())
{
case WindowEvent.WINDOW_ACTIVATED:
windowListener.windowActivated(evt);
break;
case WindowEvent.WINDOW_CLOSED:
windowListener.windowClosed(evt);
break;
case WindowEvent.WINDOW_CLOSING:
windowListener.windowClosing(evt);
break;
case WindowEvent.WINDOW_DEACTIVATED:
windowListener.windowDeactivated(evt);
break;
case WindowEvent.WINDOW_DEICONIFIED:
windowListener.windowDeiconified(evt);
break;
case WindowEvent.WINDOW_ICONIFIED:
windowListener.windowIconified(evt);
break;
case WindowEvent.WINDOW_OPENED:
windowListener.windowOpened(evt);
break;
default:
break;
}
}
switch (evt.getID())
{
case WindowEvent.WINDOW_ACTIVATED:
windowListener.windowActivated(evt);
break;
case WindowEvent.WINDOW_CLOSED:
windowListener.windowClosed(evt);
break;
case WindowEvent.WINDOW_CLOSING:
windowListener.windowClosing(evt);
break;
case WindowEvent.WINDOW_DEACTIVATED:
windowListener.windowDeactivated(evt);
break;
case WindowEvent.WINDOW_DEICONIFIED:
windowListener.windowDeiconified(evt);
break;
case WindowEvent.WINDOW_ICONIFIED:
windowListener.windowIconified(evt);
break;
case WindowEvent.WINDOW_OPENED:
windowListener.windowOpened(evt);
break;
}
}
}
/**
* Identifies if this window is active. The active window is a Frame or
* Dialog that has focus or owns the active window.
@ -1233,6 +1247,42 @@ public class Window extends Container implements Accessible
return "win" + getUniqueLong();
}
/**
* Overridden to handle WindowEvents.
*
* @return <code>true</code> when the specified event type is enabled,
* <code>false</code> otherwise
*/
boolean eventTypeEnabled(int type)
{
boolean enabled = false;
switch (type)
{
case WindowEvent.WINDOW_OPENED:
case WindowEvent.WINDOW_CLOSED:
case WindowEvent.WINDOW_CLOSING:
case WindowEvent.WINDOW_ICONIFIED:
case WindowEvent.WINDOW_DEICONIFIED:
case WindowEvent.WINDOW_ACTIVATED:
case WindowEvent.WINDOW_DEACTIVATED:
enabled = ((eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0)
|| windowListener != null;
break;
case WindowEvent.WINDOW_GAINED_FOCUS:
case WindowEvent.WINDOW_LOST_FOCUS:
enabled = ((eventMask & AWTEvent.WINDOW_FOCUS_EVENT_MASK) != 0)
|| windowFocusListener != null;
break;
case WindowEvent.WINDOW_STATE_CHANGED:
enabled = ((eventMask & AWTEvent.WINDOW_STATE_EVENT_MASK) != 0)
|| windowStateListener != null;
break;
default:
enabled = super.eventTypeEnabled(type);
}
return enabled;
}
private static synchronized long getUniqueLong()
{
return next_window_number++;

View file

@ -38,14 +38,13 @@ exception statement from your version. */
package java.awt.datatransfer;
import gnu.classpath.NotImplementedException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OptionalDataException;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
@ -76,8 +75,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
* deals with bytes not chars. Use <code>getRederForText()</code>.
*/
public static final DataFlavor plainTextFlavor =
new DataFlavor(java.io.InputStream.class,
"text/plain; charset=unicode",
new DataFlavor("text/plain; charset=unicode; class=java.io.InputStream",
"plain unicode text");
/**
@ -94,8 +92,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
* element of the list being a <code>java.io.File</code>.
*/
public static final DataFlavor javaFileListFlavor =
new DataFlavor(java.util.List.class,
"application/x-java-file-list; class=java.util.List",
new DataFlavor("application/x-java-file-list; class=java.util.List",
"Java File List");
/**
@ -132,10 +129,10 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
// The MIME type for this flavor
private final String mimeType;
private MimeType mimeType;
// The representation class for this flavor
private final Class representationClass;
private Class<?> representationClass;
// The human readable name of this flavor
private String humanPresentableName;
@ -156,8 +153,8 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*
* @exception ClassNotFoundException If the class cannot be loaded.
*/
protected static final Class tryToLoadClass(String className,
ClassLoader classLoader)
protected static final Class<?> tryToLoadClass(String className,
ClassLoader classLoader)
throws ClassNotFoundException
{
// Bootstrap
@ -198,62 +195,6 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
throw new ClassNotFoundException(className);
}
private static Class getRepresentationClassFromMimeThrows(String mimeString,
ClassLoader classLoader)
throws ClassNotFoundException
{
String classname = getParameter("class", mimeString);
if (classname != null)
return tryToLoadClass(classname, classLoader);
else
return java.io.InputStream.class;
}
// Same as above, but wraps any ClassNotFoundExceptions
private static Class getRepresentationClassFromMime(String mimeString,
ClassLoader classLoader)
{
try
{
return getRepresentationClassFromMimeThrows(mimeString, classLoader);
}
catch(ClassNotFoundException cnfe)
{
IllegalArgumentException iae;
iae = new IllegalArgumentException("mimeString: "
+ mimeString
+ " classLoader: "
+ classLoader);
iae.initCause(cnfe);
throw iae;
}
}
/**
* Returns the value of the named MIME type parameter, or <code>null</code>
* if the parameter does not exist. Given the parameter name and the mime
* string.
*
* @param paramName The name of the parameter.
* @param mimeString The mime string from where the name should be found.
*
* @return The value of the parameter or null.
*/
private static String getParameter(String paramName, String mimeString)
{
int idx = mimeString.indexOf(paramName + "=");
if (idx == -1)
return(null);
String value = mimeString.substring(idx + paramName.length() + 1);
idx = value.indexOf(";");
if (idx == -1)
return(value);
else
return(value.substring(0, idx));
}
/**
* XXX - Currently returns <code>plainTextFlavor</code>.
*/
@ -321,32 +262,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public DataFlavor()
{
mimeType = null;
representationClass = null;
humanPresentableName = null;
}
/**
* Private constructor.
*/
private DataFlavor(Class representationClass,
String mimeType,
String humanPresentableName)
{
this.representationClass = representationClass;
this.mimeType = mimeType;
// Do some simple validity checks
String type = getPrimaryType() + "/" + getSubType();
if (type.indexOf(' ') != -1
|| type.indexOf('=') != -1
|| type.indexOf(';') != -1)
throw new IllegalArgumentException(mimeType);
if (humanPresentableName != null)
this.humanPresentableName = humanPresentableName;
else
this.humanPresentableName = mimeType;
// Used for deserialization only, nothing to do here.
}
/**
@ -359,13 +275,23 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
* @param representationClass The representation class for this object.
* @param humanPresentableName The display name of the object.
*/
public DataFlavor(Class representationClass, String humanPresentableName)
public DataFlavor(Class<?> representationClass, String humanPresentableName)
{
this(representationClass,
"application/x-java-serialized-object"
+ "; class="
+ representationClass.getName(),
humanPresentableName);
if (representationClass == null)
throw new NullPointerException("representationClass must not be null");
try
{
mimeType = new MimeType(javaSerializedObjectMimeType);
}
catch (MimeTypeParseException ex)
{
// Must not happen as we use a constant string.
assert false;
}
if (humanPresentableName == null)
humanPresentableName = javaSerializedObjectMimeType;
this.humanPresentableName = humanPresentableName;
this.representationClass = representationClass;
}
/**
@ -390,8 +316,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
ClassLoader classLoader)
throws ClassNotFoundException
{
this(getRepresentationClassFromMimeThrows(mimeType, classLoader),
mimeType, humanPresentableName);
init(mimeType, humanPresentableName, classLoader);
}
/**
@ -412,8 +337,17 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public DataFlavor(String mimeType, String humanPresentableName)
{
this(getRepresentationClassFromMime (mimeType, null),
mimeType, humanPresentableName);
try
{
init(mimeType, humanPresentableName, getClass().getClassLoader());
}
catch (ClassNotFoundException ex)
{
IllegalArgumentException iae =
new IllegalArgumentException("Class not found: " + ex.getMessage());
iae.initCause(ex);
throw iae;
}
}
/**
@ -432,8 +366,54 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public DataFlavor(String mimeType) throws ClassNotFoundException
{
this(getRepresentationClassFromMimeThrows(mimeType, null),
mimeType, null);
init(mimeType, null, getClass().getClassLoader());
}
/**
* Called by various constructors to initialize this object.
*
* @param mime the mime string
* @param humanPresentableName the human presentable name
* @param loader the class loader to use for loading the representation
* class
*/
private void init(String mime, String humanPresentableName,
ClassLoader loader)
throws ClassNotFoundException
{
if (mime == null)
throw new NullPointerException("The mime type must not be null");
try
{
mimeType = new MimeType(mime);
}
catch (MimeTypeParseException ex)
{
IllegalArgumentException iae =
new IllegalArgumentException("Invalid mime type");
iae.initCause(ex);
throw iae;
}
String className = mimeType.getParameter("class");
if (className == null)
{
if (mimeType.getBaseType().equals(javaSerializedObjectMimeType))
throw new IllegalArgumentException("Serialized object type must have"
+ " a representation class parameter");
else
representationClass = java.io.InputStream.class;
}
else
representationClass = tryToLoadClass(className, loader);
mimeType.addParameter("class", representationClass.getName());
if (humanPresentableName == null)
{
humanPresentableName = mimeType.getParameter("humanPresentableName");
if (humanPresentableName == null)
humanPresentableName = mimeType.getBaseType();
}
this.humanPresentableName = humanPresentableName;
}
/**
@ -443,7 +423,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public String getMimeType()
{
return(mimeType);
return(mimeType.toString());
}
/**
@ -451,7 +431,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*
* @return The representation class for this flavor.
*/
public Class getRepresentationClass()
public Class<?> getRepresentationClass()
{
return(representationClass);
}
@ -473,11 +453,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public String getPrimaryType()
{
int idx = mimeType.indexOf("/");
if (idx == -1)
return(mimeType);
return(mimeType.substring(0, idx));
return(mimeType.getPrimaryType());
}
/**
@ -487,15 +463,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public String getSubType()
{
int start = mimeType.indexOf("/");
if (start == -1)
return "";
int end = mimeType.indexOf(";", start + 1);
if (end == -1)
return mimeType.substring(start + 1);
else
return mimeType.substring(start + 1, end);
return mimeType.getSubType();
}
/**
@ -511,7 +479,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
if ("humanPresentableName".equals(paramName))
return getHumanPresentableName();
return getParameter(paramName, mimeType);
return mimeType.getParameter(paramName);
}
/**
@ -537,16 +505,22 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public boolean isMimeTypeEqual(String mimeType)
{
String mime = getMimeType();
int i = mime.indexOf(";");
if (i != -1)
mime = mime.substring(0, i);
i = mimeType.indexOf(";");
if (i != -1)
mimeType = mimeType.substring(0, i);
return mime.equals(mimeType);
if (mimeType == null)
throw new NullPointerException("mimeType must not be null");
boolean equal = false;
try
{
if (this.mimeType != null)
{
MimeType other = new MimeType(mimeType);
equal = this.mimeType.matches(other);
}
}
catch (MimeTypeParseException ex)
{
// Return false in this case.
}
return equal;
}
/**
@ -571,7 +545,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public boolean isMimeTypeSerializedObject()
{
return mimeType.startsWith(javaSerializedObjectMimeType);
return isMimeTypeEqual(javaSerializedObjectMimeType);
}
/**
@ -617,8 +591,8 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public boolean isFlavorSerializedObjectType()
{
// FIXME: What is the diff between this and isMimeTypeSerializedObject?
return(mimeType.startsWith(javaSerializedObjectMimeType));
return isRepresentationClassSerializable()
&& isMimeTypeEqual(javaSerializedObjectMimeType);
}
/**
@ -629,7 +603,9 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public boolean isFlavorRemoteObjectType()
{
return(mimeType.startsWith(javaRemoteObjectMimeType));
return isRepresentationClassRemote()
&& isRepresentationClassSerializable()
&& isMimeTypeEqual(javaRemoteObjectMimeType);
}
/**
@ -770,7 +746,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*/
public int hashCode()
{
return mimeType.toLowerCase().hashCode() ^ representationClass.hashCode();
return mimeType.toString().hashCode() ^ representationClass.hashCode();
}
/**
@ -822,9 +798,17 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
* @exception IOException If an error occurs.
*/
public void writeExternal(ObjectOutput stream)
throws IOException, NotImplementedException
throws IOException
{
// FIXME: Implement me
if (mimeType != null)
{
mimeType.addParameter("humanPresentableName", humanPresentableName);
stream.writeObject(mimeType);
mimeType.removeParameter("humanPresentableName");
}
else
stream.writeObject(null);
stream.writeObject(representationClass);
}
@ -838,9 +822,34 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
* cannot be found.
*/
public void readExternal(ObjectInput stream)
throws IOException, ClassNotFoundException, NotImplementedException
throws IOException, ClassNotFoundException
{
// FIXME: Implement me
mimeType = (MimeType) stream.readObject();
String className = null;
if (mimeType != null)
{
humanPresentableName =
mimeType.getParameter("humanPresentableName");
mimeType.removeParameter("humanPresentableName");
className = mimeType.getParameter("class");
if (className == null)
throw new IOException("No class in mime type");
}
try
{
representationClass = (Class) stream.readObject();
}
catch (OptionalDataException ex)
{
if (ex.eof && ex.length == 0)
{
if (className != null)
representationClass = tryToLoadClass(className,
getClass().getClassLoader());
}
else
throw ex;
}
}
/**
@ -861,7 +870,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
*
* @since 1.3
*/
public final Class getDefaultRepresentationClass()
public final Class<?> getDefaultRepresentationClass()
{
return java.io.InputStream.class;
}

View file

@ -58,7 +58,7 @@ public interface FlavorMap
*
* @return A <code>Map</code> of native data types.
*/
Map getNativesForFlavors (DataFlavor[] flavors);
Map<DataFlavor, String> getNativesForFlavors (DataFlavor[] flavors);
/**
* Maps the specified native type names to <code>DataFlavor</code>'s.
@ -71,5 +71,5 @@ public interface FlavorMap
*
* @return A <code>Map</code> of data flavors.
*/
Map getFlavorsForNatives (String[] natives);
Map<String, DataFlavor> getFlavorsForNatives (String[] natives);
}

View file

@ -59,7 +59,7 @@ public interface FlavorTable extends FlavorMap
* @param flavor the flavor to look up, or null to return all natives
* @return the sorted list of natives
*/
List getNativesForFlavor(DataFlavor flavor);
List<String> getNativesForFlavor(DataFlavor flavor);
/**
* Returns a list of flavors corresponding to the given String native. The
@ -69,5 +69,5 @@ public interface FlavorTable extends FlavorMap
* @param name the native name to look up, or null to return all flavors
* @return the sorted list of flavors
*/
List getFlavorsForNative(String name);
List<DataFlavor> getFlavorsForNative(String name);
}

View file

@ -0,0 +1,281 @@
/* MimeType.java -- A helper class for mime handling in DataFlavor
Copyright (C) 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
Linking this library statically or dynamically with other modules is
making a combined work based on this library. Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package java.awt.datatransfer;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
/**
* A helper class for mime handling in DataFlavor.
*
* A Mauve test for DataFlavor.writeExternal() shows that a non-public
* class java.awt.datatransfer.MimeType gets serialized. This class
* is mainly here for serialization compatibility. Of course,
* now that we have it here, we can just as well implement some
* mime handling facility here.
*/
class MimeType
implements Externalizable
{
/**
* The primary type.
*/
private String primaryType;
/**
* The subtype.
*/
private String subType;
/**
* Additional parameters to be appended to the mime string.
*/
private HashMap parameters;
/**
* This is only here for deserialization.
*/
public MimeType()
{
parameters = new HashMap();
}
/**
* Creates a new MimeType object.
*
* @param mime the mime type
*/
MimeType(String mime)
throws MimeTypeParseException
{
this();
parse(mime);
}
/**
* Adds a mime parameter.
*
* @param param the parameter key
* @param value the parameter value
*/
void addParameter(String param, String value)
{
parameters.put(param, value);
}
/**
* Removes the parameter with the specified key.
*
* @param param the parameter to remove
*/
void removeParameter(String param)
{
parameters.remove(param);
}
/**
* Returns the parameter for the <code>key</code>.
*
* @param key the parameter key
*
* @return the parameter for the <code>key</code>
*/
String getParameter(String key)
{
return (String) parameters.get(key);
}
/**
* Returns the primary type.
*
* @return the primary type
*/
String getPrimaryType()
{
return primaryType;
}
String getSubType()
{
return subType;
}
/**
* Returns the base type of this mime type. This is the primary
* type plus the subtype, separated by '/'.
*
* @return the base type of this mime type
*/
String getBaseType()
{
return primaryType + '/' + subType;
}
/**
* Returns <code>true</code> if this mime type and another mime type
* match. This will be true when their primary types are equal, and their
* subtypes are equal (or when either subtype is * ).
*
* @param other the other mime type
*
* @return <code>true</code> if the mime types match, <code>false</code>
* otherwise
*/
boolean matches(MimeType other)
{
boolean match = false;
if (other != null)
{
match = primaryType.equals(other.primaryType)
&& (subType.equals("*") || other.subType.equals("*")
|| subType.equals(other.subType));
}
return match;
}
/**
* Serializes the mime type.
*
* @param in the input stream to read from
*
* @throws ClassNotFoundException not thrown here
* @throws IOException when something goes wrong on the input stream,
* or when the mime type can't be parsed
*/
public void readExternal(ObjectInput in)
throws ClassNotFoundException, IOException
{
String mime = in.readUTF();
parameters.clear();
try
{
parse(mime);
}
catch (MimeTypeParseException ex)
{
IOException ioEx = new IOException();
ioEx.initCause(ex);
throw ioEx;
}
}
/**
* Serializes this mime type.
*
* @param out the output stream
*
* @throws IOException when something goes wrong on the output stream
*/
public void writeExternal(ObjectOutput out)
throws IOException
{
out.writeUTF(toString());
}
/**
* Creates a string representation of this mime type.
*
* @return a string representation of this mime type
*/
public String toString()
{
StringBuilder s = new StringBuilder();
s.append(primaryType);
s.append('/');
s.append(subType);
if (parameters.size() > 0)
{
Set entries = parameters.entrySet();
for (Iterator i = entries.iterator(); i.hasNext();)
{
s.append("; ");
Map.Entry entry = (Map.Entry) i.next();
s.append(entry.getKey());
s.append('=');
s.append(entry.getValue());
}
}
return s.toString();
}
/**
* Parses the specified mime type string and initializes the fields
* of this object.
*
* @param mime the mime type string
*/
private void parse(String mime)
throws MimeTypeParseException
{
// FIXME: Maybe implement more sophisticated mime string parsing according
// to RFC 2045 and 2046.
StringTokenizer tokenizer = new StringTokenizer(mime);
try
{
primaryType = tokenizer.nextToken("/");
subType = tokenizer.nextToken("/;");
}
catch (NoSuchElementException ex)
{
throw new MimeTypeParseException("Expected / separator");
}
// Add any parameters.
while (tokenizer.hasMoreTokens())
{
String keyValuePair = tokenizer.nextToken(";");
int i = keyValuePair.indexOf('=');
if (i == -1)
throw new MimeTypeParseException("Expected = as parameter separator");
String key = keyValuePair.substring(0, i).trim();
String value = keyValuePair.substring(i + 1).trim();
parameters.put(key, value);
}
}
}

View file

@ -98,9 +98,9 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
*
* @return A <code>Map</code> of native data types to data flavors.
*/
public Map getNativesForFlavors (DataFlavor[] flavors)
public Map<DataFlavor, String> getNativesForFlavors (DataFlavor[] flavors)
{
return new HashMap();
return new HashMap<DataFlavor, String>();
}
/**
@ -114,9 +114,9 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
*
* @return A <code>Map</code> of data flavors to native type names.
*/
public Map getFlavorsForNatives (String[] natives)
public Map<String, DataFlavor> getFlavorsForNatives (String[] natives)
{
return new HashMap();
return new HashMap<String, DataFlavor>();
}
/**
@ -263,13 +263,13 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
* specified native and a DataFlavor whose MIME type is a decoded
* version of the native.
*/
public List getFlavorsForNative (String nat)
public List<DataFlavor> getFlavorsForNative (String nat)
throws NotImplementedException
{
throw new Error ("Not implemented");
}
public List getNativesForFlavor (DataFlavor flav)
public List<String> getNativesForFlavor (DataFlavor flav)
throws NotImplementedException
{
throw new Error ("Not implemented");

View file

@ -59,7 +59,7 @@ public class DragGestureEvent extends EventObject
private Component component;
private final Point origin;
private final int action;
private List events;
private List<InputEvent> events;
private DragGestureRecognizer dgr;
/**
@ -71,15 +71,15 @@ public class DragGestureEvent extends EventObject
* @throws IllegalArgumentException - if input parameters are null
*/
public DragGestureEvent(DragGestureRecognizer dgr, int action, Point origin,
List events)
{
List<? extends InputEvent> events)
{
super(dgr);
if (origin == null || events == null || dgr == null)
throw new IllegalArgumentException();
this.origin = origin;
this.action = action;
this.events = events;
this.events = (List<InputEvent>) events;
this.dgr = dgr;
this.component = dgr.getComponent();
this.dragSource = dgr.getDragSource();
@ -130,7 +130,7 @@ public class DragGestureEvent extends EventObject
*
* @return an iterator representation of the List of events.
*/
public Iterator iterator()
public Iterator<InputEvent> iterator()
{
return events.iterator();
}
@ -155,7 +155,7 @@ public class DragGestureEvent extends EventObject
{
return events.toArray(array);
}
/**
* Gets the user's preferred action.
*

View file

@ -1,5 +1,5 @@
/* DragGestureRecognizer.java --
Copyright (C) 2002,2006 Free Software Foundation, Inc.
Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@ -38,8 +38,6 @@ exception statement from your version. */
package java.awt.dnd;
import gnu.classpath.NotImplementedException;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.InputEvent;
@ -52,6 +50,8 @@ import java.util.TooManyListenersException;
/**
* STUBBED
* @author Michael Koch (konqueror@gmx.de)
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
* @since 1.2
*/
public abstract class DragGestureRecognizer implements Serializable
@ -65,7 +65,7 @@ public abstract class DragGestureRecognizer implements Serializable
protected Component component;
protected transient DragGestureListener dragGestureListener;
protected int sourceActions;
protected ArrayList events = new ArrayList();
protected ArrayList<InputEvent> events = new ArrayList<InputEvent>();
protected DragGestureRecognizer(DragSource ds, Component c, int sa,
DragGestureListener dgl)
@ -127,11 +127,12 @@ public abstract class DragGestureRecognizer implements Serializable
return events.size() > 0 ? (InputEvent) events.get(0) : null;
}
/**
* Resets the recognizer. If a gesture is currently recognize, discard it.
*/
public void resetRecognizer()
throws NotImplementedException
{
events = new ArrayList();
// FIXME: Not implemented fully.
events.clear();
}
/**
@ -164,6 +165,7 @@ public abstract class DragGestureRecognizer implements Serializable
if(dragGestureListener != null)
dragGestureListener.dragGestureRecognized
(new DragGestureEvent(this, dragAction, p, events));
resetRecognizer();
}
protected void appendEvent(InputEvent e)

View file

@ -105,16 +105,15 @@ public class DragSource implements Serializable
ds = null;
throw new HeadlessException();
}
if (ds == null)
ds = new DragSource();
return ds;
}
public static boolean isDragImageSupported()
throws NotImplementedException
{
// FIXME: Implement this
// In all cases, Sun returns false here.
return false;
}
@ -140,8 +139,6 @@ public class DragSource implements Serializable
// This function sends the same message to the context, which then forwards
// it to the peer, passing itself as a parameter. Now, the native system has
// access to the Transferable through the context.
// FIXME: Add check to determine if dragging.
try
{
@ -228,15 +225,16 @@ public class DragSource implements Serializable
{
return flavorMap;
}
public DragGestureRecognizer createDragGestureRecognizer(Class recognizer,
Component c,
int actions,
DragGestureListener dgl)
public <T extends DragGestureRecognizer> T
createDragGestureRecognizer(Class<T> recognizer,
Component c,
int actions,
DragGestureListener dgl)
{
return Toolkit.getDefaultToolkit().createDragGestureRecognizer(recognizer,
this, c,
actions, dgl);
return (T) Toolkit.getDefaultToolkit().createDragGestureRecognizer(recognizer,
this, c,
actions, dgl);
}
public DragGestureRecognizer createDefaultDragGestureRecognizer(Component c,
@ -299,23 +297,23 @@ public class DragSource implements Serializable
/**
* @since 1.4
*/
public EventListener[] getListeners (Class listenerType)
public <T extends EventListener> T[] getListeners (Class<T> listenerType)
{
if (listenerType == DragSourceListener.class)
return DnDEventMulticaster.getListeners (dragSourceListener,
listenerType);
listenerType);
if (listenerType == DragSourceMotionListener.class)
return DnDEventMulticaster.getListeners (dragSourceMotionListener,
listenerType);
listenerType);
// Return an empty EventListener array.
return new EventListener [0];
return (T[]) new EventListener [0];
}
/**
* TODO
* @return
* @return TODO
*
* @since 1.5
*/
@ -323,6 +321,6 @@ public class DragSource implements Serializable
throws NotImplementedException
{
// FIXME: Not implemented.
return 4;
return 8;
}
} // class DragSource

View file

@ -38,8 +38,6 @@ exception statement from your version. */
package java.awt.dnd;
import gnu.classpath.NotImplementedException;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Image;
@ -268,7 +266,8 @@ public class DragSourceContext
for (int i = 0; i < dsl.length; i++)
dsl[i].dragExit(e);
updateCurrentCursor(0, 0, DEFAULT);
updateCurrentCursor(DnDConstants.ACTION_NONE, DnDConstants.ACTION_NONE,
DEFAULT);
}
/**
@ -340,26 +339,45 @@ public class DragSourceContext
* @param status - the status of the cursor (constant).
*/
protected void updateCurrentCursor(int dropOp, int targetAct, int status)
throws NotImplementedException
{
// FIXME: Not implemented fully
if (!useCustomCursor)
if (! useCustomCursor)
{
Cursor cursor = null;
Cursor newCursor = null;
switch (status)
{
case ENTER:
break;
case CHANGED:
break;
case OVER:
break;
default:
break;
targetAct = DnDConstants.ACTION_NONE;
case ENTER:
case CHANGED:
case OVER:
int action = dropOp & targetAct;
if (action == DnDConstants.ACTION_NONE)
{
if ((dropOp & DnDConstants.ACTION_LINK) != 0)
newCursor = DragSource.DefaultLinkNoDrop;
else if ((dropOp & DnDConstants.ACTION_MOVE) != 0)
newCursor = DragSource.DefaultMoveNoDrop;
else
newCursor = DragSource.DefaultCopyNoDrop;
}
else
{
if ((dropOp & DnDConstants.ACTION_LINK) != 0)
newCursor = DragSource.DefaultLinkDrop;
else if ((dropOp & DnDConstants.ACTION_MOVE) != 0)
newCursor = DragSource.DefaultMoveDrop;
else
newCursor = DragSource.DefaultCopyDrop;
}
}
this.cursor = cursor;
peer.setCursor(cursor);
if (cursor == null || ! cursor.equals(newCursor))
{
cursor = newCursor;
DragSourceContextPeer p = peer;
if (p != null)
p.setCursor(cursor);
}
}
}
} // class DragSourceContext

View file

@ -38,13 +38,14 @@ exception statement from your version. */
package java.awt.dnd;
import gnu.classpath.NotImplementedException;
import java.awt.Component;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.FlavorMap;
import java.awt.datatransfer.SystemFlavorMap;
import java.awt.dnd.peer.DropTargetPeer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@ -54,6 +55,8 @@ import java.io.Serializable;
import java.util.EventListener;
import java.util.TooManyListenersException;
import javax.swing.Timer;
/**
* @author Michael Koch
* @since 1.2
@ -69,30 +72,87 @@ public class DropTarget
protected static class DropTargetAutoScroller
implements ActionListener
{
/**
* The threshold that keeps the autoscroller running.
*/
private static final int HYSTERESIS = 10;
/**
* The initial timer delay.
*/
private static final int DELAY = 100;
private Component component;
private Point point;
/**
* The timer that triggers autoscrolling.
*/
private Timer timer;
/**
* The outer region of the scroller. This is the component's size.
*/
private Rectangle outer;
/**
* The inner region of the scroller. This is the component size without
* the autoscroll insets.
*/
private Rectangle inner;
protected DropTargetAutoScroller (Component c, Point p)
{
component = c;
point = p;
timer = new Timer(DELAY, this);
timer.setCoalesce(true);
timer.start();
}
protected void updateLocation (Point newLocn)
{
Point previous = point;
point = newLocn;
if (Math.abs(point.x - previous.x) > HYSTERESIS
|| Math.abs(point.y - previous.y) > HYSTERESIS)
{
if (timer.isRunning())
timer.stop();
}
else
{
if (! timer.isRunning())
timer.start();
}
}
protected void stop ()
throws NotImplementedException
{
// FIXME: implement this
timer.start();
}
public void actionPerformed (ActionEvent e)
throws NotImplementedException
{
// FIXME: implement this
Autoscroll autoScroll = (Autoscroll) component;
// First synchronize the inner and outer rectangles.
Insets i = autoScroll.getAutoscrollInsets();
int width = component.getWidth();
int height = component.getHeight();
if (width != outer.width || height != outer.height)
outer.setBounds(0, 0, width, height);
if (inner.x != i.left || inner.y != i.top)
inner.setLocation(i.left, i.top);
int inWidth = width - i.left - i.right;
int inHeight = height - i.top - i.bottom;
if (inWidth != inner.width || inHeight != inner.height)
inner.setSize(inWidth, inHeight);
// Scroll if the outer rectangle contains the location, but the
// inner doesn't.
if (outer.contains(point) && ! inner.contains(point))
autoScroll.autoscroll(point);
}
}
@ -113,7 +173,7 @@ public class DropTarget
*/
public DropTarget ()
{
this (null, 0, null, true, null);
this (null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null);
}
/**
@ -124,7 +184,7 @@ public class DropTarget
*/
public DropTarget (Component c, DropTargetListener dtl)
{
this (c, 0, dtl, true, null);
this (c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null);
}
/**
@ -164,7 +224,11 @@ public class DropTarget
setComponent(c);
setDefaultActions(i);
dropTargetListener = dtl;
flavorMap = fm;
if (fm == null)
flavorMap = SystemFlavorMap.getDefaultFlavorMap();
else
flavorMap = fm;
setActive (b);
@ -177,6 +241,8 @@ public class DropTarget
*/
public void setComponent (Component c)
{
if (component != null)
clearAutoscroll();
component = c;
}
@ -207,6 +273,8 @@ public class DropTarget
public void setActive (boolean active)
{
this.active = active;
if (! active)
clearAutoscroll();
}
public boolean isActive()
@ -225,8 +293,14 @@ public class DropTarget
public void addDropTargetListener (DropTargetListener dtl)
throws TooManyListenersException
{
if (dtl == null)
return;
if (dtl.equals(this))
throw new IllegalArgumentException();
if (dropTargetListener != null)
throw new TooManyListenersException ();
throw new TooManyListenersException();
dropTargetListener = dtl;
}
@ -239,30 +313,47 @@ public class DropTarget
public void dragEnter(DropTargetDragEvent dtde)
{
if (dropTargetListener != null)
dropTargetListener.dragEnter(dtde);
if (active)
{
if (dropTargetListener != null)
dropTargetListener.dragEnter(dtde);
initializeAutoscrolling(dtde.getLocation());
}
}
public void dragOver(DropTargetDragEvent dtde)
{
if (dropTargetListener != null)
dropTargetListener.dragOver(dtde);
if (active)
{
if (dropTargetListener != null)
dropTargetListener.dragOver(dtde);
updateAutoscroll(dtde.getLocation());
}
}
public void dropActionChanged(DropTargetDragEvent dtde)
{
if (dropTargetListener != null)
dropTargetListener.dropActionChanged(dtde);
if (active)
{
if (dropTargetListener != null)
dropTargetListener.dropActionChanged(dtde);
updateAutoscroll(dtde.getLocation());
}
}
public void dragExit(DropTargetEvent dte)
{
if (dropTargetListener != null)
dropTargetListener.dragExit(dte);
if (active)
{
if (dropTargetListener != null)
dropTargetListener.dragExit(dte);
clearAutoscroll();
}
}
public void drop(DropTargetDropEvent dtde)
{
clearAutoscroll();
if (dropTargetListener != null)
dropTargetListener.drop(dtde);
}
@ -321,15 +412,13 @@ public class DropTarget
protected DropTarget.DropTargetAutoScroller createDropTargetAutoScroller
(Component c, Point p)
{
if (autoscroller == null)
autoscroller = new DropTarget.DropTargetAutoScroller (c, p);
return autoscroller;
return new DropTarget.DropTargetAutoScroller (c, p);
}
protected void initializeAutoscrolling(Point p)
{
createDropTargetAutoScroller (component, p);
if (component instanceof Autoscroll) // Checks for null too.
autoscroller = createDropTargetAutoScroller (component, p);
}
protected void updateAutoscroll(Point dragCursorLocn)
@ -340,6 +429,10 @@ public class DropTarget
protected void clearAutoscroll()
{
autoscroller = null;
if (autoscroller != null)
{
autoscroller.stop();
autoscroller = null;
}
}
} // class DropTarget

View file

@ -1,5 +1,5 @@
/* DropTargetContext.java --
Copyright (C) 2002, 2003, 2004, 2006, Free Software Foundation
Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation
This file is part of GNU Classpath.
@ -49,6 +49,7 @@ import java.util.List;
/**
* @author Michael Koch (konqueror@gmx.de)
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
* @since 1.2
*/
public class DropTargetContext implements Serializable
@ -128,51 +129,51 @@ public class DropTargetContext implements Serializable
*
* @exception InvalidDnDOperationException If a drop is not outstanding.
*/
public void dropComplete(boolean success)
public void dropComplete (boolean success)
{
if (dtcp != null)
dtcp.dropComplete(success);
}
protected void acceptDrag(int dragOperation)
protected void acceptDrag (int dragOperation)
{
if (dtcp != null)
dtcp.acceptDrag(dragOperation);
}
protected void rejectDrag()
protected void rejectDrag ()
{
if (dtcp != null)
dtcp.rejectDrag();
}
protected void acceptDrop(int dropOperation)
protected void acceptDrop (int dropOperation)
{
if (dtcp != null)
dtcp.acceptDrop(dropOperation);
}
protected void rejectDrop()
protected void rejectDrop ()
{
if (dtcp != null)
dtcp.rejectDrop();
}
protected DataFlavor[] getCurrentDataFlavors()
protected DataFlavor[] getCurrentDataFlavors ()
{
if (dtcp != null)
dtcp.getTransferDataFlavors();
return null;
}
protected List getCurrentDataFlavorsAsList()
protected List<DataFlavor> getCurrentDataFlavorsAsList ()
{
return Arrays.asList(getCurrentDataFlavors());
return Arrays.asList(getCurrentDataFlavors ());
}
protected boolean isDataFlavorSupported(DataFlavor flavor)
protected boolean isDataFlavorSupported (DataFlavor flavor)
{
return getCurrentDataFlavorsAsList().contains(flavor);
return getCurrentDataFlavorsAsList().contains (flavor);
}
/**

View file

@ -108,7 +108,7 @@ public class DropTargetDragEvent extends DropTargetEvent
return context.getCurrentDataFlavors ();
}
public List getCurrentDataFlavorsAsList ()
public List<DataFlavor> getCurrentDataFlavorsAsList ()
{
return context.getCurrentDataFlavorsAsList ();
}
@ -147,7 +147,6 @@ public class DropTargetDragEvent extends DropTargetEvent
*/
public Transferable getTransferable()
{
// FIXME: Not implemented
return null;
return context.getTransferable();
}
} // class DropTargetDragEvent

View file

@ -1,5 +1,5 @@
/* DropTargetDropEvent.java --
Copyright (C) 2002 Free Software Foundation, Inc.
Copyright (C) 2002, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@ -123,7 +123,7 @@ public class DropTargetDropEvent extends DropTargetEvent
return context.getCurrentDataFlavors();
}
public List getCurrentDataFlavorsAsList()
public List<DataFlavor> getCurrentDataFlavorsAsList()
{
return context.getCurrentDataFlavorsAsList();
}

View file

@ -41,6 +41,10 @@ import java.util.EventObject;
public class DropTargetEvent extends EventObject
{
/**
* Serialization identifier for Sun 1.5 compatability
*/
private static final long serialVersionUID = 2821229066521922993L;
protected DropTargetContext context;

View file

@ -1,5 +1,5 @@
/* ComponentEvent.java -- notification of events for components
Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc.
Copyright (C) 1999, 2002, 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@ -114,24 +114,27 @@ public class ComponentEvent extends AWTEvent
*/
public String paramString()
{
StringBuffer s = new StringBuffer();
// Unlike Sun, we don't throw NullPointerException or ClassCastException
// when source was illegally changed.
switch (id)
{
case COMPONENT_MOVED:
return "COMPONENT_MOVED "
+ (source instanceof Component
? ((Component) source).getBounds() : (Object) "");
case COMPONENT_RESIZED:
return "COMPONENT_RESIZED "
+ (source instanceof Component
? ((Component) source).getBounds() : (Object) "");
case COMPONENT_SHOWN:
return "COMPONENT_SHOWN";
case COMPONENT_HIDDEN:
return "COMPONENT_HIDDEN";
default:
return "unknown type";
}
if (id == COMPONENT_MOVED)
s.append("COMPONENT_MOVED ");
else if (id == COMPONENT_RESIZED)
s.append("COMPONENT_RESIZED ");
else if (id == COMPONENT_SHOWN)
s.append("COMPONENT_SHOWN ");
else if (id == COMPONENT_HIDDEN)
s.append("COMPONENT_HIDDEN ");
else
return "unknown type";
s.append("(").append(getComponent().getX()).append(",")
.append(getComponent().getY()).append(" ")
.append(getComponent().getWidth()).append("x")
.append(getComponent().getHeight()).append(")");
return s.toString();
}
} // class ComponentEvent

View file

@ -117,8 +117,12 @@ public class FontRenderContext
*/
public int hashCode ()
{
// FIXME: check what SUN does here.
return affineTransform == null ? 0 : affineTransform.hashCode ();
int code = ( isAntiAliased ? 1 : 0 ) + ( usesFractionalMetrics ? 2 : 0 );
if( affineTransform != null && !affineTransform.isIdentity() )
code ^= affineTransform.hashCode();
return code;
}
public boolean isAntiAliased ()

View file

@ -81,6 +81,9 @@ public final class TextHitInfo
public boolean equals(TextHitInfo hitInfo)
{
if (hitInfo == null)
return false;
return (charIndex == hitInfo.getCharIndex ())
&& (leadingEdge == hitInfo.isLeadingEdge ());
}
@ -97,7 +100,7 @@ public final class TextHitInfo
public static TextHitInfo beforeOffset(int offset)
{
return new TextHitInfo (offset, false);
return new TextHitInfo ((offset - 1), false);
}
public static TextHitInfo afterOffset(int offset)

File diff suppressed because it is too large Load diff

View file

@ -1401,10 +1401,10 @@ public class AffineTransform implements Cloneable, Serializable
* documented, but appears to be the same as:
* <pre>
* long l = Double.doubleToLongBits(getScaleX());
* l = l * 31 + Double.doubleToLongBits(getShearY());
* l = l * 31 + Double.doubleToLongBits(getShearX());
* l = l * 31 + Double.doubleToLongBits(getScaleY());
* l = l * 31 + Double.doubleToLongBits(getTranslateX());
* l = l * 31 + Double.doubleToLongBits(getShearY());
* l = l * 31 + Double.doubleToLongBits(getScaleY());
* l = l * 31 + Double.doubleToLongBits(getTranslateY());
* return (int) ((l >> 32) ^ l);
* </pre>
@ -1413,12 +1413,12 @@ public class AffineTransform implements Cloneable, Serializable
*/
public int hashCode()
{
long l = Double.doubleToLongBits(m00);
l = l * 31 + Double.doubleToLongBits(m10);
l = l * 31 + Double.doubleToLongBits(m01);
l = l * 31 + Double.doubleToLongBits(m11);
l = l * 31 + Double.doubleToLongBits(m02);
l = l * 31 + Double.doubleToLongBits(m12);
long l = Double.doubleToLongBits(m00);
l = l * 31 + Double.doubleToLongBits(m01);
l = l * 31 + Double.doubleToLongBits(m02);
l = l * 31 + Double.doubleToLongBits(m10);
l = l * 31 + Double.doubleToLongBits(m11);
l = l * 31 + Double.doubleToLongBits(m12);
return (int) ((l >> 32) ^ l);
}

View file

@ -774,14 +774,9 @@ public abstract class Arc2D extends RectangularShape
y = a.getY();
w = a.getWidth();
h = a.getHeight();
double start = a.getAngleStart() * (Math.PI / 180);
double extent = a.getAngleExtent() * (Math.PI / 180);
double start = Math.toRadians(a.getAngleStart());
double extent = Math.toRadians(a.getAngleExtent());
if (extent < 0)
{
extent = -extent;
start = 2 * Math.PI - extent + start;
}
this.start = start;
this.extent = extent;
@ -790,11 +785,11 @@ public abstract class Arc2D extends RectangularShape
limit = -1;
else if (extent == 0)
limit = type;
else if (extent <= Math.PI / 2.0)
else if (Math.abs(extent) <= Math.PI / 2.0)
limit = type + 1;
else if (extent <= Math.PI)
else if (Math.abs(extent) <= Math.PI)
limit = type + 2;
else if (extent <= 3.0 * (Math.PI / 2.0))
else if (Math.abs(extent) <= 3.0 * (Math.PI / 2.0))
limit = type + 3;
else
limit = type + 4;
@ -909,9 +904,20 @@ public abstract class Arc2D extends RectangularShape
double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0);
double quad = (Math.PI / 2.0);
double curr_begin = start + (current - 1) * quad;
double curr_extent = Math.min((start + extent) - curr_begin, quad);
double portion_of_a_quadrant = curr_extent / quad;
double curr_begin;
double curr_extent;
if (extent > 0)
{
curr_begin = start + (current - 1) * quad;
curr_extent = Math.min((start + extent) - curr_begin, quad);
}
else
{
curr_begin = start - (current - 1) * quad;
curr_extent = Math.max((start + extent) - curr_begin, -quad);
}
double portion_of_a_quadrant = Math.abs(curr_extent / quad);
double x0 = xmid + rx * Math.cos(curr_begin);
double y0 = ymid - ry * Math.sin(curr_begin);
@ -932,7 +938,11 @@ public abstract class Arc2D extends RectangularShape
// will *subtract* the y value of this control vector from our first
// point.
cvec[0] = 0;
cvec[1] = len;
if (extent > 0)
cvec[1] = len;
else
cvec[1] = -len;
trans.scale(rx, ry);
trans.rotate(angle);
trans.transform(cvec, 0, cvec, 0, 1);
@ -942,7 +952,11 @@ public abstract class Arc2D extends RectangularShape
// control vector #2 would, ideally, be sticking out and to the
// right, in a first quadrant arc segment. again, subtraction of y.
cvec[0] = 0;
cvec[1] = -len;
if (extent > 0)
cvec[1] = -len;
else
cvec[1] = len;
trans.rotate(curr_extent);
trans.transform(cvec, 0, cvec, 0, 1);
coords[2] = x1 + cvec[0];

View file

@ -86,7 +86,7 @@ public final class GeneralPath implements Shape, Cloneable
public static final int WIND_EVEN_ODD
= java.awt.geom.PathIterator.WIND_EVEN_ODD;
/** Same constant as {@link PathIterator.WIND_NON_ZERO}. */
/** Same constant as {@link PathIterator#WIND_NON_ZERO}. */
public static final int WIND_NON_ZERO
= java.awt.geom.PathIterator.WIND_NON_ZERO;
@ -140,7 +140,11 @@ public final class GeneralPath implements Shape, Cloneable
/**
* Constructs a GeneralPath with a specific winding rule
* and the default initial capacity (20).
* @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD)
* @param rule the winding rule ({@link #WIND_NON_ZERO} or
* {@link #WIND_EVEN_ODD})
*
* @throws IllegalArgumentException if <code>rule</code> is not one of the
* listed values.
*/
public GeneralPath(int rule)
{
@ -151,8 +155,12 @@ public final class GeneralPath implements Shape, Cloneable
* Constructs a GeneralPath with a specific winding rule
* and the initial capacity. The initial capacity should be
* the approximate number of path segments to be used.
* @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD)
* @param rule the winding rule ({@link #WIND_NON_ZERO} or
* {@link #WIND_EVEN_ODD})
* @param capacity the inital capacity, in path segments
*
* @throws IllegalArgumentException if <code>rule</code> is not one of the
* listed values.
*/
public GeneralPath(int rule, int capacity)
{
@ -169,7 +177,10 @@ public final class GeneralPath implements Shape, Cloneable
/**
* Constructs a GeneralPath from an arbitrary shape object.
* The Shapes PathIterator path and winding rule will be used.
* @param s the shape
*
* @param s the shape (<code>null</code> not permitted).
*
* @throws NullPointerException if <code>shape</code> is <code>null</code>.
*/
public GeneralPath(Shape s)
{
@ -183,6 +194,9 @@ public final class GeneralPath implements Shape, Cloneable
/**
* Adds a new point to a path.
*
* @param x the x-coordinate.
* @param y the y-coordinate.
*/
public void moveTo(float x, float y)
{
@ -263,6 +277,11 @@ public final class GeneralPath implements Shape, Cloneable
* Appends the segments of a Shape to the path. If <code>connect</code> is
* true, the new path segments are connected to the existing one with a line.
* The winding rule of the Shape is ignored.
*
* @param s the shape (<code>null</code> not permitted).
* @param connect whether to connect the new shape to the existing path.
*
* @throws NullPointerException if <code>s</code> is <code>null</code>.
*/
public void append(Shape s, boolean connect)
{
@ -276,7 +295,7 @@ public final class GeneralPath implements Shape, Cloneable
* PathIterator#SEG_LINETO} segment.
*
* @param iter the PathIterator specifying which segments shall be
* appended.
* appended (<code>null</code> not permitted).
*
* @param connect <code>true</code> for substituting the initial
* {@link PathIterator#SEG_MOVETO} segment by a {@link
@ -327,6 +346,8 @@ public final class GeneralPath implements Shape, Cloneable
/**
* Returns the path&#x2019;s current winding rule.
*
* @return {@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}.
*/
public int getWindingRule()
{
@ -338,6 +359,8 @@ public final class GeneralPath implements Shape, Cloneable
* considered &#x2019;inside&#x2019; or &#x2019;outside&#x2019; the path
* on drawing. Valid rules are WIND_EVEN_ODD for an even-odd winding rule,
* or WIND_NON_ZERO for a non-zero winding rule.
*
* @param rule the rule ({@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}).
*/
public void setWindingRule(int rule)
{
@ -348,6 +371,8 @@ public final class GeneralPath implements Shape, Cloneable
/**
* Returns the current appending point of the path.
*
* @return The point.
*/
public Point2D getCurrentPoint()
{
@ -367,6 +392,8 @@ public final class GeneralPath implements Shape, Cloneable
/**
* Applies a transform to the path.
*
* @param xform the transform (<code>null</code> not permitted).
*/
public void transform(AffineTransform xform)
{
@ -706,6 +733,8 @@ public final class GeneralPath implements Shape, Cloneable
/**
* Helper method - ensure the size of the data arrays,
* otherwise, reallocate new ones twice the size
*
* @param size the minimum array size.
*/
private void ensureSize(int size)
{

View file

@ -326,15 +326,12 @@ public abstract class RectangularShape implements Shape, Cloneable
/**
* Returns a bounding box for this shape, in integer format. Notice that you
* may get a tighter bound with getBounds2D. If the frame is empty, the
* box is the default empty box at the origin.
* may get a tighter bound with getBounds2D.
*
* @return a bounding box
*/
public Rectangle getBounds()
{
if (isEmpty())
return new Rectangle();
double x = getX();
double y = getY();
double maxx = Math.ceil(x + getWidth());

View file

@ -1,5 +1,5 @@
/* RoundRectangle2D.java -- represents a rectangle with rounded corners
Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation
Copyright (C) 2000, 2002, 2003, 2004, 2006, Free Software Foundation
This file is part of GNU Classpath.
@ -37,7 +37,6 @@ exception statement from your version. */
package java.awt.geom;
import java.util.NoSuchElementException;
/** This class implements a rectangle with rounded corners.
@ -46,13 +45,29 @@ import java.util.NoSuchElementException;
*/
public abstract class RoundRectangle2D extends RectangularShape
{
/** Return the arc height of this round rectangle. */
/**
* Return the arc height of this round rectangle. The arc height and width
* control the roundness of the corners of the rectangle.
*
* @return The arc height.
*
* @see #getArcWidth()
*/
public abstract double getArcHeight();
/** Return the arc width of this round rectangle. */
/**
* Return the arc width of this round rectangle. The arc width and height
* control the roundness of the corners of the rectangle.
*
* @return The arc width.
*
* @see #getArcHeight()
*/
public abstract double getArcWidth();
/** Set the values of this round rectangle
/**
* Set the values of this round rectangle.
*
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
@ -63,14 +78,16 @@ public abstract class RoundRectangle2D extends RectangularShape
public abstract void setRoundRect(double x, double y, double w, double h,
double arcWidth, double arcHeight);
/** Create a RoundRectangle2D. This is protected because this class
/**
* Create a RoundRectangle2D. This is protected because this class
* is abstract and cannot be instantiated.
*/
protected RoundRectangle2D()
{
}
/** Return true if this object contains the specified point.
/**
* Return true if this object contains the specified point.
* @param x The x coordinate
* @param y The y coordinate
*/
@ -106,7 +123,8 @@ public abstract class RoundRectangle2D extends RectangularShape
return dx * dx + dy * dy <= 1.0;
}
/** Return true if this object contains the specified rectangle
/**
* Return true if this object contains the specified rectangle
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
@ -120,176 +138,185 @@ public abstract class RoundRectangle2D extends RectangularShape
&& contains(x + w, y));
}
/** Return a new path iterator which iterates over this rectangle.
/**
* Return a new path iterator which iterates over this rectangle.
*
* @param at An affine transform to apply to the object
*/
public PathIterator getPathIterator(final AffineTransform at)
public PathIterator getPathIterator(final AffineTransform at)
{
final double minx = getX();
final double miny = getY();
final double maxx = minx + getWidth();
final double maxy = miny + getHeight();
final double arcwidth = getArcWidth();
final double archeight = getArcHeight();
return new PathIterator()
double arcW = Math.min(getArcWidth(), getWidth());
double arcH = Math.min(getArcHeight(), getHeight());
// check for special cases...
if (arcW <= 0 || arcH <= 0)
{
/** We iterate counterclockwise around the rectangle, starting in the
* upper right. This variable tracks our current point, which
* can be on either side of a given corner. */
private int current = 0;
Rectangle2D r = new Rectangle2D.Double(getX(), getY(), getWidth(),
getHeight());
return r.getPathIterator(at);
}
else if (arcW >= getWidth() && arcH >= getHeight())
{
Ellipse2D e = new Ellipse2D.Double(getX(), getY(), getWidth(),
getHeight());
return e.getPathIterator(at);
}
// otherwise return the standard case...
return new PathIterator()
{
double x = getX();
double y = getY();
double w = getWidth();
double h = getHeight();
double arcW = Math.min(getArcWidth(), w);
double arcH = Math.min(getArcHeight(), h);
Arc2D.Double arc = new Arc2D.Double();
PathIterator corner;
int step = -1;
/** Child path iterator, used for corners. */
private PathIterator corner;
public int currentSegment(double[] coords)
{
if (corner != null) // steps 1, 3, 5 and 7
{
int r = corner.currentSegment(coords);
if (r == SEG_MOVETO)
r = SEG_LINETO;
return r;
}
if (step == -1)
{
// move to the start position
coords[0] = x + w - arcW / 2;
coords[1] = y;
}
else if (step == 0)
{
// top line
coords[0] = x + arcW / 2;
coords[1] = y;
}
else if (step == 2)
{
// left line
coords[0] = x;
coords[1] = y + h - arcH / 2;
}
else if (step == 4)
{
// bottom line
coords[0] = x + w - arcW / 2;
coords[1] = y + h;
}
else if (step == 6)
{
// right line
coords[0] = x + w;
coords[1] = y + arcH / 2;
}
if (at != null)
at.transform(coords, 0, coords, 0, 1);
return step == -1 ? SEG_MOVETO : SEG_LINETO;
}
/** This is used when rendering the corners. We re-use the arc
* for each corner. */
private Arc2D arc = new Arc2D.Double();
public int currentSegment(float[] coords) {
if (corner != null) // steps 1, 3, 5 and 7
{
int r = corner.currentSegment(coords);
if (r == SEG_MOVETO)
r = SEG_LINETO;
return r;
}
if (step == -1)
{
// move to the start position
coords[0] = (float) (x + w - arcW / 2);
coords[1] = (float) y;
}
else if (step == 0)
{
// top line
coords[0] = (float) (x + arcW / 2);
coords[1] = (float) y;
}
else if (step == 2)
{
// left line
coords[0] = (float) x;
coords[1] = (float) (y + h - arcH / 2);
}
else if (step == 4)
{
// bottom line
coords[0] = (float) (x + w - arcW / 2);
coords[1] = (float) (y + h);
}
else if (step == 6)
{
// right line
coords[0] = (float) (x + w);
coords[1] = (float) (y + arcH / 2);
}
if (at != null)
at.transform(coords, 0, coords, 0, 1);
return step == -1 ? SEG_MOVETO : SEG_LINETO;
}
/** Temporary array used by getPoint. */
private double[] temp = new double[2];
public int getWindingRule() {
return WIND_NON_ZERO;
}
public int getWindingRule()
{
return WIND_NON_ZERO;
}
public boolean isDone() {
return step >= 8;
}
public boolean isDone()
{
return current > 9;
}
private void getPoint(int val)
{
switch (val)
{
case 0:
case 8:
temp[0] = maxx;
temp[1] = miny + archeight;
break;
case 7:
temp[0] = maxx;
temp[1] = maxy - archeight;
break;
case 6:
temp[0] = maxx - arcwidth;
temp[1] = maxy;
break;
case 5:
temp[0] = minx + arcwidth;
temp[1] = maxy;
break;
case 4:
temp[0] = minx;
temp[1] = maxy - archeight;
break;
case 3:
temp[0] = minx;
temp[1] = miny + archeight;
break;
case 2:
temp[0] = minx + arcwidth;
temp[1] = miny;
break;
case 1:
temp[0] = maxx - arcwidth;
temp[1] = miny;
break;
}
}
public void next()
{
if (current >= 8)
++current;
else if (corner != null)
{
// We're iterating through the corner. Work on the child
// iterator; if it finishes, reset and move to the next
// point along the rectangle.
corner.next();
if (corner.isDone())
{
corner = null;
++current;
}
}
else
{
// Make an arc between this point on the rectangle and
// the next one, and then iterate over this arc.
getPoint(current);
double x1 = temp[0];
double y1 = temp[1];
getPoint(current + 1);
Rectangle2D.Double r = new Rectangle2D.Double(Math.min(x1,
temp[0]),
Math.min(y1,
temp[1]),
Math.abs(x1
- temp[0]),
Math.abs(y1
- temp[1]));
arc.setArc(r, (current >> 1) * 90.0, 90.0, Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
}
public int currentSegment(float[] coords)
{
if (corner != null)
{
int r = corner.currentSegment(coords);
if (r == SEG_MOVETO)
r = SEG_LINETO;
return r;
}
if (current < 9)
{
getPoint(current);
coords[0] = (float) temp[0];
coords[1] = (float) temp[1];
}
else if (current == 9)
return SEG_CLOSE;
else
throw new NoSuchElementException("rect iterator out of bounds");
if (at != null)
at.transform(coords, 0, coords, 0, 1);
return current == 0 ? SEG_MOVETO : SEG_LINETO;
}
public int currentSegment(double[] coords)
{
if (corner != null)
{
int r = corner.currentSegment(coords);
if (r == SEG_MOVETO)
r = SEG_LINETO;
return r;
}
if (current < 9)
{
getPoint(current);
coords[0] = temp[0];
coords[1] = temp[1];
}
else if (current == 9)
return SEG_CLOSE;
else
throw new NoSuchElementException("rect iterator out of bounds");
if (at != null)
at.transform(coords, 0, coords, 0, 1);
return current == 0 ? SEG_MOVETO : SEG_LINETO;
}
};
public void next()
{
if (corner != null)
{
corner.next();
if (corner.isDone())
{
corner = null;
step++;
}
}
else
{
step++;
if (step == 1)
{
// create top left corner
arc.setArc(x, y, arcW, arcH, 90, 90, Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
else if (step == 3)
{
// create bottom left corner
arc.setArc(x, y + h - arcH, arcW, arcH, 180, 90,
Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
else if (step == 5)
{
// create bottom right corner
arc.setArc(x + w - arcW, y + h - arcH, arcW, arcH, 270, 90,
Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
else if (step == 7)
{
// create top right corner
arc.setArc(x + w - arcW, y, arcW, arcH, 0, 90, Arc2D.OPEN);
corner = arc.getPathIterator(at);
}
}
}
};
}
/** Return true if the given rectangle intersects this shape.
/**
* Return true if the given rectangle intersects this shape.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
@ -302,7 +329,8 @@ public abstract class RoundRectangle2D extends RectangularShape
|| contains(x + w, y));
}
/** Set the boundary of this round rectangle.
/**
* Set the boundary of this round rectangle.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
@ -314,7 +342,8 @@ public abstract class RoundRectangle2D extends RectangularShape
setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
}
/** Set the values of this round rectangle to be the same as those
/**
* Set the values of this round rectangle to be the same as those
* of the argument.
* @param rr The round rectangle to copy
*/
@ -324,8 +353,10 @@ public abstract class RoundRectangle2D extends RectangularShape
rr.getArcWidth(), rr.getArcHeight());
}
/** A subclass of RoundRectangle which keeps its parameters as
* doubles. */
/**
* A subclass of RoundRectangle which keeps its parameters as
* doubles.
*/
public static class Double extends RoundRectangle2D
{
/** The height of the corner arc. */
@ -346,12 +377,15 @@ public abstract class RoundRectangle2D extends RectangularShape
/** The height of this object. */
public double height;
/** Construct a new instance, with all parameters set to 0. */
/**
* Construct a new instance, with all parameters set to 0.
*/
public Double()
{
}
/** Construct a new instance with the given arguments.
/**
* Construct a new instance with the given arguments.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
@ -422,8 +456,10 @@ public abstract class RoundRectangle2D extends RectangularShape
}
} // class Double
/** A subclass of RoundRectangle which keeps its parameters as
* floats. */
/**
* A subclass of RoundRectangle which keeps its parameters as
* floats.
*/
public static class Float extends RoundRectangle2D
{
/** The height of the corner arc. */
@ -444,12 +480,15 @@ public abstract class RoundRectangle2D extends RectangularShape
/** The height of this object. */
public float height;
/** Construct a new instance, with all parameters set to 0. */
/**
* Construct a new instance, with all parameters set to 0.
*/
public Float()
{
}
/** Construct a new instance with the given arguments.
/**
* Construct a new instance with the given arguments.
* @param x The x coordinate
* @param y The y coordinate
* @param w The width
@ -508,6 +547,18 @@ public abstract class RoundRectangle2D extends RectangularShape
return width <= 0 || height <= 0;
}
/**
* Sets the dimensions for this rounded rectangle.
*
* @param x the x-coordinate of the top left corner.
* @param y the y-coordinate of the top left corner.
* @param w the width of the rectangle.
* @param h the height of the rectangle.
* @param arcWidth the arc width.
* @param arcHeight the arc height.
*
* @see #setRoundRect(double, double, double, double, double, double)
*/
public void setRoundRect(float x, float y, float w, float h,
float arcWidth, float arcHeight)
{

View file

@ -1,5 +1,5 @@
/* InputContext.java -- provides the context for text input
Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@ -76,6 +76,7 @@ import java.util.Locale;
* java.awt.im.spi.InputMethodDescriptor.
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
* @see Component#getInputContext()
* @see Component#enableInputMethods(boolean)
* @since 1.2
@ -86,7 +87,9 @@ public class InputContext
/**
* The list of installed input method descriptors.
*/
private static final ArrayList descriptors = new ArrayList();
private static final ArrayList<InputMethodDescriptor> descriptors
= new ArrayList<InputMethodDescriptor>();
static
{
Enumeration e;
@ -123,7 +126,7 @@ public class InputContext
{
if (line.charAt(0) != '#')
{
Class c = Class.forName(line);
Class<?> c = Class.forName(line);
descriptors.add((InputMethodDescriptor) c.newInstance());
}
line = in.readLine().trim();
@ -143,7 +146,8 @@ public class InputContext
private InputMethod im;
/** Map of locales to the most recently selected input method. */
private final HashMap recent = new HashMap();
private final HashMap<Locale,InputMethod> recent
= new HashMap<Locale,InputMethod>();
/** The list of acceptable character subsets. */
private Character.Subset[] subsets;

View file

@ -41,6 +41,7 @@ import java.awt.Toolkit;
import java.text.Annotation;
import java.text.AttributedCharacterIterator;
import java.util.Map;
import java.awt.font.TextAttribute;
/**
* This describes the highlight attributes of text composed in an input method.
@ -95,7 +96,7 @@ public class InputMethodHighlight
private final int variation;
/** The unmodifiable map of rendering styles. */
private final Map style;
private final Map<TextAttribute, ?> style;
/**
* Create an input method highlight style, with variation 0 and null style
@ -134,7 +135,7 @@ public class InputMethodHighlight
* @since 1.3
*/
public InputMethodHighlight(boolean selected, int state, int variation,
Map style)
Map<TextAttribute, ?> style)
{
if (state != RAW_TEXT && state != CONVERTED_TEXT)
throw new IllegalArgumentException();
@ -181,7 +182,7 @@ public class InputMethodHighlight
* @return the style map
* @since 1.3
*/
public Map getStyle()
public Map<TextAttribute, ?> getStyle()
{
return style;
}

View file

@ -1,5 +1,5 @@
/* InputMethodContext.java -- communication between an input method and client
Copyright (C) 2002, 2004 Free Software Foundation, Inc.
Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@ -53,6 +53,7 @@ import javax.swing.JFrame;
* {@link InputMethod#setInputMethodContext(InputMethodContext)}.
*
* @author Eric Blake (ebb9@email.byu.edu)
* @author Andrew John Hughes (gnu_andrew@member.fsf.org)
* @since 1.3
* @status updated to 1.4
*/

View file

@ -1,6 +1,6 @@
/* AffineTransformOp.java -- This class performs affine
transformation between two images or rasters in 2 dimensions.
Copyright (C) 2004 Free Software Foundation
Copyright (C) 2004, 2006 Free Software Foundation
This file is part of GNU Classpath.
@ -39,6 +39,7 @@ exception statement from your version. */
package java.awt.image;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
@ -48,10 +49,14 @@ import java.awt.geom.Rectangle2D;
import java.util.Arrays;
/**
* This class performs affine transformation between two images or
* rasters in 2 dimensions.
* AffineTransformOp performs matrix-based transformations (translations,
* scales, flips, rotations, and shears).
*
* If interpolation is required, nearest neighbour, bilinear, and bicubic
* methods are available.
*
* @author Olga Rodimina (rodimina@redhat.com)
* @author Francis Kung (fkung@redhat.com)
*/
public class AffineTransformOp implements BufferedImageOp, RasterOp
{
@ -74,6 +79,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
*
* @param xform AffineTransform that will applied to the source image
* @param interpolationType type of interpolation used
* @throws ImagingOpException if the transform matrix is noninvertible
*/
public AffineTransformOp (AffineTransform xform, int interpolationType)
{
@ -102,6 +108,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
*
* @param xform AffineTransform that will applied to the source image
* @param hints rendering hints that will be used during transformation
* @throws ImagingOpException if the transform matrix is noninvertible
*/
public AffineTransformOp (AffineTransform xform, RenderingHints hints)
{
@ -112,185 +119,165 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
}
/**
* Creates empty BufferedImage with the size equal to that of the
* transformed image and correct number of bands. The newly created
* Creates a new BufferedImage with the size equal to that of the
* transformed image and the correct number of bands. The newly created
* image is created with the specified ColorModel.
* If the ColorModel is equal to null, then image is created
* with the ColorModel of the source image.
* If a ColorModel is not specified, an appropriate ColorModel is used.
*
* @param src source image
* @param destCM color model for the destination image
* @return new compatible destination image
* @param src the source image.
* @param destCM color model for the destination image (can be null).
* @return a new compatible destination image.
*/
public BufferedImage createCompatibleDestImage (BufferedImage src,
ColorModel destCM)
{
if (destCM != null)
return new BufferedImage(destCM,
createCompatibleDestRaster(src.getRaster()),
src.isAlphaPremultiplied(), null);
// if destCm is not specified, use color model of the source image
if (destCM == null)
destCM = src.getColorModel ();
return new BufferedImage (destCM,
createCompatibleDestRaster (src.getRaster ()),
src.isAlphaPremultiplied (),
null);
// This behaviour was determined by Mauve testcases, and is compatible
// with the reference implementation
if (src.getType() == BufferedImage.TYPE_INT_ARGB_PRE
|| src.getType() == BufferedImage.TYPE_4BYTE_ABGR
|| src.getType() == BufferedImage.TYPE_4BYTE_ABGR_PRE)
return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
else
return new BufferedImage(src.getWidth(), src.getHeight(),
BufferedImage.TYPE_INT_ARGB);
}
/**
* Creates empty WritableRaster with the size equal to the transformed
* source raster and correct number of bands
* Creates a new WritableRaster with the size equal to the transformed
* source raster and correct number of bands .
*
* @param src source raster
* @throws RasterFormatException if resulting width or height of raster is 0
* @return new compatible raster
* @param src the source raster.
* @throws RasterFormatException if resulting width or height of raster is 0.
* @return a new compatible raster.
*/
public WritableRaster createCompatibleDestRaster (Raster src)
{
Rectangle rect = (Rectangle) getBounds2D (src);
Rectangle2D rect = getBounds2D(src);
// throw RasterFormatException if resulting width or height of the
// transformed raster is 0
if (rect.getWidth () == 0 || rect.getHeight () == 0)
if (rect.getWidth() == 0 || rect.getHeight() == 0)
throw new RasterFormatException("width or height is 0");
return src.createCompatibleWritableRaster ((int) rect.getWidth (),
(int) rect.getHeight ());
return src.createCompatibleWritableRaster((int) rect.getWidth(),
(int) rect.getHeight());
}
/**
* Transforms source image using transform specified at the constructor.
* The resulting transformed image is stored in the destination image.
* The resulting transformed image is stored in the destination image if one
* is provided; otherwise a new BufferedImage is created and returned.
*
* @param src source image
* @param dst destination image
* @return transformed source image
* @throws IllegalArgumentException if the source and destination image are
* the same
* @return transformed source image.
*/
public final BufferedImage filter (BufferedImage src, BufferedImage dst)
{
if (dst == src)
throw new IllegalArgumentException ("src image cannot be the same as the dst image");
// If the destination image is null, then BufferedImage is
// created with ColorModel of the source image
throw new IllegalArgumentException("src image cannot be the same as "
+ "the dst image");
// If the destination image is null, then use a compatible BufferedImage
if (dst == null)
dst = createCompatibleDestImage(src, src.getColorModel ());
dst = createCompatibleDestImage(src, null);
// FIXME: Must check if color models of src and dst images are the same.
// If it is not, then source image should be converted to color model
// of the destination image
Graphics2D gr = (Graphics2D) dst.createGraphics ();
gr.setRenderingHints (hints);
gr.drawImage (src, transform, null);
Graphics2D gr = (Graphics2D) dst.createGraphics();
gr.setRenderingHints(hints);
gr.drawImage(src, transform, null);
return dst;
}
/**
* Transforms source raster using transform specified at the constructor.
* The resulting raster is stored in the destination raster.
* The resulting raster is stored in the destination raster if it is not
* null, otherwise a new raster is created and returned.
*
* @param src source raster
* @param dst destination raster
* @return transformed raster
* @throws IllegalArgumentException if the source and destination are not
* compatible
* @return transformed raster.
*/
public final WritableRaster filter (Raster src, WritableRaster dst)
public final WritableRaster filter(Raster src, WritableRaster dst)
{
// Initial checks
if (dst == src)
throw new IllegalArgumentException("src image cannot be the same as"
+ " the dst image");
+ " the dst image");
if (dst == null)
dst = createCompatibleDestRaster(src);
if (src.getNumBands() != dst.getNumBands())
throw new IllegalArgumentException("src and dst must have same number"
+ " of bands");
+ " of bands");
double[] dpts = new double[dst.getWidth() * 2];
double[] pts = new double[dst.getWidth() * 2];
for (int x = 0; x < dst.getWidth(); x++)
{
dpts[2 * x] = x + dst.getMinX();
dpts[2 * x + 1] = x;
}
Rectangle srcbounds = src.getBounds();
if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
{
for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
{
try {
transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
} catch (NoninvertibleTransformException e) {
// Can't happen since the constructor traps this
e.printStackTrace();
}
for (int x = 0; x < dst.getWidth(); x++)
{
if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
continue;
dst.setDataElements(x + dst.getMinX(), y,
src.getDataElements((int)pts[2 * x],
(int)pts[2 * x + 1],
null));
}
}
}
else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
{
double[] tmp = new double[4 * src.getNumBands()];
for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
// Optimization for rasters that can be represented in the RGB colormodel:
// wrap the rasters in images, and let Cairo do the transformation
if (ColorModel.getRGBdefault().isCompatibleSampleModel(src.getSampleModel())
&& ColorModel.getRGBdefault().isCompatibleSampleModel(dst.getSampleModel()))
{
try {
transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
} catch (NoninvertibleTransformException e) {
// Can't happen since the constructor traps this
e.printStackTrace();
}
for (int x = 0; x < dst.getWidth(); x++)
{
if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
continue;
int xx = (int)pts[2 * x];
int yy = (int)pts[2 * x + 1];
double dx = (pts[2 * x] - xx);
double dy = (pts[2 * x + 1] - yy);
// TODO write this more intelligently
if (xx == src.getMinX() + src.getWidth() - 1 ||
yy == src.getMinY() + src.getHeight() - 1)
{
// bottom or right edge
Arrays.fill(tmp, 0);
src.getPixel(xx, yy, tmp);
}
else
{
// Normal case
src.getPixels(xx, yy, 2, 2, tmp);
for (int b = 0; b < src.getNumBands(); b++)
tmp[b] = dx * dy * tmp[b]
+ (1 - dx) * dy * tmp[b + src.getNumBands()]
+ dx * (1 - dy) * tmp[b + 2 * src.getNumBands()]
+ (1 - dx) * (1 - dy) * tmp[b + 3 * src.getNumBands()];
}
dst.setPixel(x, y, tmp);
}
WritableRaster src2 = Raster.createWritableRaster(src.getSampleModel(),
src.getDataBuffer(),
new Point(src.getMinX(),
src.getMinY()));
BufferedImage iSrc = new BufferedImage(ColorModel.getRGBdefault(),
src2, false, null);
BufferedImage iDst = new BufferedImage(ColorModel.getRGBdefault(), dst,
false, null);
return filter(iSrc, iDst).getRaster();
}
}
else
{
// Bicubic
throw new UnsupportedOperationException("not implemented yet");
}
// Otherwise, we need to do the transformation in java code...
// Create arrays to hold all the points
double[] dstPts = new double[dst.getHeight() * dst.getWidth() * 2];
double[] srcPts = new double[dst.getHeight() * dst.getWidth() * 2];
// Populate array with all points in the *destination* raster
int i = 0;
for (int x = 0; x < dst.getWidth(); x++)
{
for (int y = 0; y < dst.getHeight(); y++)
{
dstPts[i++] = x;
dstPts[i++] = y;
}
}
Rectangle srcbounds = src.getBounds();
// Use an inverse transform to map each point in the destination to
// a point in the source. Note that, while all points in the destination
// matrix are integers, this is not necessarily true for points in the
// source (hence why interpolation is required)
try
{
AffineTransform inverseTx = transform.createInverse();
inverseTx.transform(dstPts, 0, srcPts, 0, dstPts.length / 2);
}
catch (NoninvertibleTransformException e)
{
// Shouldn't happen since the constructor traps this
throw new ImagingOpException(e.getMessage());
}
// Different interpolation methods...
if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
filterNearest(src, dst, dstPts, srcPts);
else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
filterBilinear(src, dst, dstPts, srcPts);
else // bicubic
filterBicubic(src, dst, dstPts, srcPts);
return dst;
}
@ -314,27 +301,22 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
*/
public final Rectangle2D getBounds2D (Raster src)
{
// determine new size for the transformed raster.
// Need to calculate transformed coordinates of the lower right
// corner of the raster. The upper left corner is always (0,0)
double x2 = (double) src.getWidth () + src.getMinX ();
double y2 = (double) src.getHeight () + src.getMinY ();
Point2D p2 = getPoint2D (new Point2D.Double (x2,y2), null);
Rectangle2D rect = new Rectangle (0, 0, (int) p2.getX (), (int) p2.getY ());
return rect.getBounds ();
return transform.createTransformedShape(src.getBounds()).getBounds2D();
}
/**
* Returns interpolation type used during transformations
* Returns interpolation type used during transformations.
*
* @return interpolation type
*/
public final int getInterpolationType ()
{
if(hints.containsValue (RenderingHints.VALUE_INTERPOLATION_BILINEAR))
if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
return TYPE_BILINEAR;
else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC))
return TYPE_BICUBIC;
else
return TYPE_NEAREST_NEIGHBOR;
}
@ -355,7 +337,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
/**
* Returns rendering hints that are used during transformation.
*
* @return rendering hints
* @return the rendering hints used in this Op.
*/
public final RenderingHints getRenderingHints ()
{
@ -366,10 +348,261 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
* Returns transform used in transformation between source and destination
* image.
*
* @return transform
* @return the transform used in this Op.
*/
public final AffineTransform getTransform ()
{
return transform;
}
/**
* Perform nearest-neighbour filtering
*
* @param src the source raster
* @param dst the destination raster
* @param dpts array of points on the destination raster
* @param pts array of corresponding points on the source raster
*/
private void filterNearest(Raster src, WritableRaster dst, double[] dpts,
double[] pts)
{
Rectangle srcbounds = src.getBounds();
// For all points on the destination raster, copy the value from the
// corrosponding (rounded) source point
for (int i = 0; i < dpts.length; i += 2)
{
int srcX = (int) Math.round(pts[i]) + src.getMinX();
int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
if (srcbounds.contains(srcX, srcY))
dst.setDataElements((int) dpts[i] + dst.getMinX(),
(int) dpts[i + 1] + dst.getMinY(),
src.getDataElements(srcX, srcY, null));
}
}
/**
* Perform bilinear filtering
*
* @param src the source raster
* @param dst the destination raster
* @param dpts array of points on the destination raster
* @param pts array of corresponding points on the source raster
*/
private void filterBilinear(Raster src, WritableRaster dst, double[] dpts,
double[] pts)
{
Rectangle srcbounds = src.getBounds();
Object xyarr = null;
Object xp1arr = null;
Object yp1arr = null;
Object xyp1arr = null;
double xy;
double xp1;
double yp1;
double xyp1;
double[] result = new double[src.getNumBands()];
// For all points in the destination raster, use bilinear interpolation
// to find the value from the corrosponding source points
for (int i = 0; i < dpts.length; i += 2)
{
int srcX = (int) Math.round(pts[i]) + src.getMinX();
int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
if (srcbounds.contains(srcX, srcY))
{
// Corner case at the bottom or right edge; use nearest neighbour
if (pts[i] >= src.getWidth() - 1
|| pts[i + 1] >= src.getHeight() - 1)
dst.setDataElements((int) dpts[i] + dst.getMinX(),
(int) dpts[i + 1] + dst.getMinY(),
src.getDataElements(srcX, srcY, null));
// Standard case, apply the bilinear formula
else
{
int x = (int) Math.floor(pts[i] + src.getMinX());
int y = (int) Math.floor(pts[i + 1] + src.getMinY());
double xdiff = pts[i] + src.getMinX() - x;
double ydiff = pts[i + 1] + src.getMinY() - y;
// Get surrounding pixels used in interpolation... optimized
// to use the smallest datatype possible.
if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
|| src.getTransferType() == DataBuffer.TYPE_FLOAT)
{
xyarr = src.getPixel(x, y, (double[])xyarr);
xp1arr = src.getPixel(x+1, y, (double[])xp1arr);
yp1arr = src.getPixel(x, y+1, (double[])yp1arr);
xyp1arr = src.getPixel(x+1, y+1, (double[])xyp1arr);
}
else
{
xyarr = src.getPixel(x, y, (int[])xyarr);
xp1arr = src.getPixel(x+1, y, (int[])xp1arr);
yp1arr = src.getPixel(x, y+1, (int[])yp1arr);
xyp1arr = src.getPixel(x+1, y+1, (int[])xyp1arr);
}
// using
// array[] pixels = src.getPixels(x, y, 2, 2, pixels);
// instead of doing four individual src.getPixel() calls
// should be faster, but benchmarking shows that it's not...
// Run interpolation for each band
for (int j = 0; j < src.getNumBands(); j++)
{
// Pull individual sample values out of array
if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
|| src.getTransferType() == DataBuffer.TYPE_FLOAT)
{
xy = ((double[])xyarr)[j];
xp1 = ((double[])xp1arr)[j];
yp1 = ((double[])yp1arr)[j];
xyp1 = ((double[])xyp1arr)[j];
}
else
{
xy = ((int[])xyarr)[j];
xp1 = ((int[])xp1arr)[j];
yp1 = ((int[])yp1arr)[j];
xyp1 = ((int[])xyp1arr)[j];
}
// If all four samples are identical, there's no need to
// calculate anything
if (xy == xp1 && xy == yp1 && xy == xyp1)
result[j] = xy;
// Run bilinear interpolation formula
else
result[j] = (xy * (1-xdiff) + xp1 * xdiff)
* (1-ydiff)
+ (yp1 * (1-xdiff) + xyp1 * xdiff)
* ydiff;
}
dst.setPixel((int)dpts[i] + dst.getMinX(),
(int)dpts[i+1] + dst.getMinY(),
result);
}
}
}
}
/**
* Perform bicubic filtering
* based on http://local.wasp.uwa.edu.au/~pbourke/colour/bicubic/
*
* @param src the source raster
* @param dst the destination raster
* @param dpts array of points on the destination raster
* @param pts array of corresponding points on the source raster
*/
private void filterBicubic(Raster src, WritableRaster dst, double[] dpts,
double[] pts)
{
Rectangle srcbounds = src.getBounds();
double[] result = new double[src.getNumBands()];
Object pixels = null;
// For all points on the destination raster, perform bicubic interpolation
// from corrosponding source points
for (int i = 0; i < dpts.length; i += 2)
{
if (srcbounds.contains((int) Math.round(pts[i]) + src.getMinX(),
(int) Math.round(pts[i + 1]) + src.getMinY()))
{
int x = (int) Math.floor(pts[i] + src.getMinX());
int y = (int) Math.floor(pts[i + 1] + src.getMinY());
double dx = pts[i] + src.getMinX() - x;
double dy = pts[i + 1] + src.getMinY() - y;
Arrays.fill(result, 0);
for (int m = - 1; m < 3; m++)
for (int n = - 1; n < 3; n++)
{
// R(x) = ( P(x+2)^3 - 4 P(x+1)^3 + 6 P(x)^3 - 4 P(x-1)^3 ) / 6
double r1 = 0;
double r2 = 0;
// Calculate R(m - dx)
double rx = m - dx + 2;
r1 += rx * rx * rx;
rx = m - dx + 1;
if (rx > 0)
r1 -= 4 * rx * rx * rx;
rx = m - dx;
if (rx > 0)
r1 += 6 * rx * rx * rx;
rx = m - dx - 1;
if (rx > 0)
r1 -= 4 * rx * rx * rx;
r1 /= 6;
// Calculate R(dy - n);
rx = dy - n + 2;
if (rx > 0)
r2 += rx * rx * rx;
rx = dy - n + 1;
if (rx > 0)
r2 -= 4 * rx * rx * rx;
rx = dy - n;
if (rx > 0)
r2 += 6 * rx * rx * rx;
rx = dy - n - 1;
if (rx > 0)
r2 -= 4 * rx * rx * rx;
r2 /= 6;
// Calculate F(i+m, j+n) R(m - dx) R(dy - n)
// Check corner cases
int srcX = x + m;
if (srcX >= src.getMinX() + src.getWidth())
srcX = src.getMinX() + src.getWidth() - 1;
else if (srcX < src.getMinX())
srcX = src.getMinX();
int srcY = y + n;
if (srcY >= src.getMinY() + src.getHeight())
srcY = src.getMinY() + src.getHeight() - 1;
else if (srcY < src.getMinY())
srcY = src.getMinY();
// Calculate once for each band, using the smallest
// datatype possible
if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
|| src.getTransferType() == DataBuffer.TYPE_FLOAT)
{
pixels = src.getPixel(srcX, srcY, (double[])pixels);
for (int j = 0; j < result.length; j++)
result[j] += ((double[])pixels)[j] * r1 * r2;
}
else
{
pixels = src.getPixel(srcX, srcY, (int[])pixels);
for (int j = 0; j < result.length; j++)
result[j] += ((int[])pixels)[j] * r1 * r2;
}
}
// Put it all together
dst.setPixel((int)dpts[i] + dst.getMinX(),
(int)dpts[i+1] + dst.getMinY(),
result);
}
}
}
}

View file

@ -1,4 +1,5 @@
/* Copyright (C) 2004 Free Software Foundation
/* BandCombineOp.java - perform a combination on the bands of a raster
Copyright (C) 2004, 2006 Free Software Foundation
This file is part of GNU Classpath.
@ -36,10 +37,10 @@ exception statement from your version. */
package java.awt.image;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
/**
* Filter Raster pixels by applying a matrix.
@ -53,6 +54,9 @@ import java.awt.geom.Rectangle2D;
* for the destination. Therefore the destination Raster must contain the
* same number of bands as the number of rows in the filter matrix.
*
* This Op assumes that samples are integers; floating point sample types will
* be rounded to their nearest integer value during filtering.
*
* @author Jerry Quinn (jlquinn@optonline.net)
*/
public class BandCombineOp implements RasterOp
@ -65,52 +69,74 @@ public class BandCombineOp implements RasterOp
*
* @param matrix The matrix to filter pixels with.
* @param hints Rendering hints to apply. Ignored.
* @throws ArrayIndexOutOfBoundsException if the matrix is invalid
*/
public BandCombineOp(float[][] matrix, RenderingHints hints)
{
this.matrix = matrix;
this.matrix = new float[matrix.length][];
int width = matrix[0].length;
for (int i = 0; i < matrix.length; i++)
{
this.matrix[i] = new float[width + 1];
for (int j = 0; j < width; j++)
this.matrix[i][j] = matrix[i][j];
// The reference implementation pads the array with a trailing zero...
this.matrix[i][width] = 0;
}
this.hints = hints;
}
/**
* Filter Raster pixels through a matrix.
*
* Applies the Op matrix to source pixes to produce dest pixels. Each row
* of the matrix is multiplied by the src pixel components to produce the
* dest pixel. If matrix is one more than the number of bands in the src,
* the last element is implicitly multiplied by 1, i.e. added to the sum
* for that dest component.
*
* If dest is null, a suitable Raster is created. This implementation uses
* createCompatibleDestRaster.
* Filter Raster pixels through a matrix. Applies the Op matrix to source
* pixes to produce dest pixels. Each row of the matrix is multiplied by the
* src pixel components to produce the dest pixel. If matrix is one more than
* the number of bands in the src, the last element is implicitly multiplied
* by 1, i.e. added to the sum for that dest component. If dest is null, a
* suitable Raster is created. This implementation uses
* createCompatibleDestRaster.
*
* @param src The source Raster.
* @param dest The destination Raster, or null.
* @returns The destination Raster or an allocated Raster.
* @param dest The destination Raster, or null.
* @throws IllegalArgumentException if the destination raster is incompatible
* with the source raster.
* @return The filtered Raster.
* @see java.awt.image.RasterOp#filter(java.awt.image.Raster,
*java.awt.image.WritableRaster)
* java.awt.image.WritableRaster)
*/
public WritableRaster filter(Raster src, WritableRaster dest) {
if (dest == null)
dest = createCompatibleDestRaster(src);
else if (dest.getNumBands() != src.getNumBands()
|| dest.getTransferType() != src.getTransferType())
throw new IllegalArgumentException("Destination raster is incompatible with source raster");
// Filter the pixels
float[] spix = new float[matrix[0].length];
float[] dpix = new float[matrix.length];
int[] spix = new int[matrix[0].length - 1];
int[] spix2 = new int[matrix[0].length - 1];
int[] dpix = new int[matrix.length];
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
{
// In case matrix rows have implicit translation
spix[spix.length - 1] = 1.0f;
src.getPixel(x, y, spix);
for (int i = 0; i < matrix.length; i++)
{
dpix[i] = 0;
for (int j = 0; j < matrix[0].length; j++)
dpix[i] += spix[j] * matrix[i][j];
// In case matrix rows have implicit translation
spix[spix.length - 1] = 1;
src.getPixel(x, y, spix);
// Do not re-calculate if pixel is identical to the last one
// (ie, blocks of the same colour)
if (!Arrays.equals(spix, spix2))
{
System.arraycopy(spix, 0, spix2, 0, spix.length);
for (int i = 0; i < matrix.length; i++)
{
dpix[i] = 0;
for (int j = 0; j < matrix[0].length - 1; j++)
dpix[i] += spix[j] * (int)matrix[i][j];
}
}
dest.setPixel(x, y, dpix);
}
dest.setPixel(x, y, dpix);
}
return dest;
}
@ -125,28 +151,48 @@ public class BandCombineOp implements RasterOp
/**
* Creates a new WritableRaster that can be used as the destination for this
* Op. This implementation creates a Banded Raster with data type FLOAT.
* @see
*java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
* Op. The number of bands in the source raster must equal the number of rows
* in the op matrix, which must also be equal to either the number of columns
* or (columns - 1) in the matrix.
*
* @param src The source raster.
* @return A compatible raster.
* @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
* @throws IllegalArgumentException if the raster is incompatible with the
* matrix.
*/
public WritableRaster createCompatibleDestRaster(Raster src)
{
return Raster.createBandedRaster(DataBuffer.TYPE_FLOAT, src.getWidth(),
src.getHeight(), matrix.length,
new Point(src.getMinX(), src.getMinY()));
// Destination raster must have same number of bands as source
if (src.getNumBands() != matrix.length)
throw new IllegalArgumentException("Number of rows in matrix specifies an "
+ "incompatible number of bands");
// We use -1 and -2 because we previously padded the rows with a trailing 0
if (src.getNumBands() != matrix[0].length - 1
&& src.getNumBands() != matrix[0].length - 2)
throw new IllegalArgumentException("Incompatible number of bands: "
+ "the number of bands in the raster must equal the number of "
+ "columns in the matrix, optionally minus one");
return src.createCompatibleWritableRaster();
}
/** Return corresponding destination point for source point.
/**
* Return corresponding destination point for source point. Because this is
* not a geometric operation, it simply returns a copy of the source.
*
* LookupOp will return the value of src unchanged.
* @param src The source point.
* @param dst The destination point.
* @return dst The destination point.
* @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D,
*java.awt.geom.Point2D)
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
if (dst == null) return (Point2D)src.clone();
if (dst == null)
return (Point2D)src.clone();
dst.setLocation(src);
return dst;
}
@ -159,7 +205,11 @@ public class BandCombineOp implements RasterOp
return hints;
}
/** Return the matrix for this Op. */
/**
* Return the matrix used in this operation.
*
* @return The matrix used in this operation.
*/
public final float[][] getMatrix()
{
return matrix;

View file

@ -38,6 +38,7 @@ exception statement from your version. */
package java.awt.image;
import gnu.java.awt.Buffers;
import gnu.java.awt.ComponentDataBlitOp;
import java.awt.Graphics;
@ -79,26 +80,36 @@ public class BufferedImage extends Image
TYPE_BYTE_BINARY = 12,
TYPE_BYTE_INDEXED = 13;
static final int[] bits3 = { 8, 8, 8 };
static final int[] bits4 = { 8, 8, 8, 8 };
static final int[] bits1byte = { 8 };
static final int[] bits1ushort = { 16 };
/**
* Vector of TileObservers (or null)
*/
Vector tileObservers;
static final int[] masks_int = { 0x00ff0000,
0x0000ff00,
0x000000ff,
DataBuffer.TYPE_INT };
static final int[] masks_565 = { 0xf800,
0x07e0,
0x001f,
DataBuffer.TYPE_USHORT};
static final int[] masks_555 = { 0x7c00,
0x03e0,
0x001f,
DataBuffer.TYPE_USHORT};
/**
* The image's WritableRaster
*/
WritableRaster raster;
/**
* The associated ColorModel
*/
ColorModel colorModel;
/**
* The image's properties (or null)
*/
Hashtable properties;
/**
* Whether alpha is premultiplied
*/
boolean isPremultiplied;
/**
* The predefined type, if any.
*/
int type;
Vector observers;
/**
* Creates a new <code>BufferedImage</code> with the specified width, height
* and type. Valid <code>type</code> values are:
@ -119,155 +130,181 @@ public class BufferedImage extends Image
* <li>{@link #TYPE_BYTE_INDEXED}</li>
* </ul>
*
* @param w the width (must be > 0).
* @param h the height (must be > 0).
* @param width the width (must be > 0).
* @param height the height (must be > 0).
* @param type the image type (see the list of valid types above).
*
* @throws IllegalArgumentException if <code>w</code> or <code>h</code> is
* less than or equal to zero.
* @throws IllegalArgumentException if <code>width</code> or
* <code>height</code> is less than or equal to zero.
* @throws IllegalArgumentException if <code>type</code> is not one of the
* specified values.
*/
public BufferedImage(int w, int h, int type)
public BufferedImage(int width, int height, int type)
{
SampleModel sm = null;
ColorModel cm = null;
boolean alpha = false;
boolean premultiplied = false;
switch (type)
boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE ||
type == BufferedImage.TYPE_4BYTE_ABGR_PRE);
switch( type )
{
case TYPE_4BYTE_ABGR_PRE:
case TYPE_INT_ARGB_PRE:
premultiplied = true;
// fall through
case TYPE_INT_ARGB:
case TYPE_4BYTE_ABGR:
alpha = true;
}
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
switch (type)
{
case TYPE_INT_RGB:
case TYPE_INT_ARGB:
case TYPE_INT_ARGB_PRE:
case TYPE_USHORT_565_RGB:
case TYPE_USHORT_555_RGB:
int[] masks = null;
switch (type)
{
case TYPE_INT_RGB:
case TYPE_INT_ARGB:
case TYPE_INT_ARGB_PRE:
masks = masks_int;
break;
case TYPE_USHORT_565_RGB:
masks = masks_565;
break;
case TYPE_USHORT_555_RGB:
masks = masks_555;
break;
}
cm = new DirectColorModel(cs,
32, // 32 bits in an int
masks[0], // r
masks[1], // g
masks[2], // b
alpha ? 0xff000000 : 0,
premultiplied,
masks[3] // data type
);
case BufferedImage.TYPE_INT_RGB:
sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
width, height,
new int[]{ 0x00FF0000,
0x0000FF00,
0x000000FF } ) ;
cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff );
break;
case TYPE_INT_BGR:
String msg =
"FIXME: Programmer is confused. Why (and how) does a " +
"TYPE_INT_BGR image use ComponentColorModel to store " +
"8-bit values? Is data type TYPE_INT or TYPE_BYTE. What " +
"is the difference between TYPE_INT_BGR and TYPE_3BYTE_BGR?";
throw new UnsupportedOperationException(msg);
case TYPE_3BYTE_BGR:
case TYPE_4BYTE_ABGR:
case TYPE_4BYTE_ABGR_PRE:
case TYPE_BYTE_GRAY:
case TYPE_USHORT_GRAY:
int[] bits = null;
int dataType = DataBuffer.TYPE_BYTE;
switch (type) {
case TYPE_3BYTE_BGR:
bits = bits3;
break;
case TYPE_4BYTE_ABGR:
case TYPE_4BYTE_ABGR_PRE:
bits = bits4;
break;
case TYPE_BYTE_GRAY:
bits = bits1byte;
cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
break;
case TYPE_USHORT_GRAY:
bits = bits1ushort;
cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
dataType = DataBuffer.TYPE_USHORT;
break;
}
cm = new ComponentColorModel(cs, bits, alpha, premultiplied,
alpha ?
Transparency.TRANSLUCENT:
Transparency.OPAQUE,
dataType);
case BufferedImage.TYPE_3BYTE_BGR:
sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
width, height,
3, width * 3,
new int[]{ 2, 1, 0 } );
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
false, false,
BufferedImage.OPAQUE,
DataBuffer.TYPE_BYTE);
break;
case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
width, height,
new int[]{ 0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000 } );
if (premultiplied)
cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB),
32, 0xff0000, 0xff00, 0xff, 0xff000000,
true,
Buffers.smallestAppropriateTransferType(32));
else
cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 );
break;
case TYPE_BYTE_BINARY:
byte[] vals = { 0, (byte) 0xff };
cm = new IndexColorModel(8, 2, vals, vals, vals);
case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
width, height,
4, 4*width,
new int[]{3, 2, 1, 0});
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
true, premultiplied,
BufferedImage.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
break;
case TYPE_BYTE_INDEXED:
String msg2 = "type not implemented yet";
throw new UnsupportedOperationException(msg2);
// FIXME: build color-cube and create color model
case BufferedImage.TYPE_INT_BGR:
sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
width, height,
new int[]{ 0x000000FF,
0x0000FF00,
0x00FF0000 } ) ;
cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 );
break;
case BufferedImage.TYPE_USHORT_565_RGB:
sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
width, height,
new int[]{ 0xF800,
0x7E0,
0x1F } ) ;
cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F );
break;
case BufferedImage.TYPE_USHORT_555_RGB:
sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
width, height,
new int[]{ 0x7C00,
0x3E0,
0x1F } ) ;
cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F );
break;
case BufferedImage.TYPE_BYTE_INDEXED:
cm = createDefaultIndexedColorModel( false );
case BufferedImage.TYPE_BYTE_GRAY:
sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
width, height,
1, width, new int[]{ 0 } );
break;
case BufferedImage.TYPE_USHORT_GRAY:
sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT,
width, height,
1, width, new int[]{ 0 } );
break;
case BufferedImage.TYPE_BYTE_BINARY:
cm = createDefaultIndexedColorModel( true );
sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
width, height, 1);
break;
default:
throw new IllegalArgumentException("Unknown image type " + type);
sm = null;
}
if( sm == null )
throw new IllegalArgumentException("Unknown predefined image type.");
init(cm,
cm.createCompatibleWritableRaster(w, h),
premultiplied,
null, // no properties
type
);
if( cm == null ) // only for the grayscale types
{
int buftype;
int[] bits = new int[1];
if( type == BufferedImage.TYPE_BYTE_GRAY )
{
buftype = DataBuffer.TYPE_BYTE;
bits[0] = 8;
}
else
{
buftype = DataBuffer.TYPE_USHORT;
bits[0] = 16;
}
ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY );
cm = new ComponentColorModel( graySpace, bits, false, false,
Transparency.OPAQUE, buftype );
}
init( cm,
Raster.createWritableRaster(sm, new Point( 0, 0 ) ),
premultiplied,
null, // no properties
type );
}
public BufferedImage(int w, int h, int type,
IndexColorModel indexcolormodel)
{
if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED))
throw new IllegalArgumentException("type must be binary or indexed");
throw new IllegalArgumentException("Type must be TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED");
if( indexcolormodel.getMapSize() > 16 && type == TYPE_BYTE_BINARY )
throw new IllegalArgumentException("Type TYPE_BYTE_BINARY cannot have a larger than 16-color palette.");
if( indexcolormodel.getMapSize() > 256 )
throw new IllegalArgumentException("Byte type cannot have a larger than 256-color palette.");
init(indexcolormodel,
indexcolormodel.createCompatibleWritableRaster(w, h),
false, // not premultiplied (guess)
null, // no properties
type);
init( indexcolormodel,
indexcolormodel.createCompatibleWritableRaster(w, h),
indexcolormodel.isAlphaPremultiplied(),
null, // no properties
type );
}
public BufferedImage(ColorModel colormodel,
WritableRaster writableraster,
boolean premultiplied,
Hashtable properties)
Hashtable<?,?> properties)
{
init(colormodel, writableraster, premultiplied, properties,
TYPE_CUSTOM);
// TODO: perhaps try to identify type?
}
WritableRaster raster;
ColorModel colorModel;
Hashtable properties;
boolean isPremultiplied;
int type;
private void init(ColorModel cm,
WritableRaster writableraster,
boolean premultiplied,
@ -280,12 +317,48 @@ public class BufferedImage extends Image
isPremultiplied = premultiplied;
this.type = type;
}
//public void addTileObserver(TileObserver tileobserver) {}
/**
* Creates the default palettes for the predefined indexed color types
* (256-color or black-and-white)
*
* @param binary - If <code>true</code>, a black and white palette,
* otherwise a default 256-color palette is returned.
*/
private IndexColorModel createDefaultIndexedColorModel( boolean binary )
{
if( binary )
{
byte[] t = new byte[]{ 0, (byte)255 };
return new IndexColorModel( 1, 2, t, t, t );
}
byte[] r = new byte[256];
byte[] g = new byte[256];
byte[] b = new byte[256];
int index = 0;
for( int i = 0; i < 6; i++ )
for( int j = 0; j < 6; j++ )
for( int k = 0; k < 6; k++ )
{
r[ index ] = (byte)(i * 51);
g[ index ] = (byte)(j * 51);
b[ index ] = (byte)(k * 51);
index++;
}
while( index < 256 )
{
r[ index ] = g[ index ] = b[ index ] =
(byte)(18 + (index - 216) * 6);
index++;
}
return new IndexColorModel( 8, 256, r, g, b );
}
public void coerceData(boolean premultiplied)
{
colorModel = colorModel.coerceData(raster, premultiplied);
isPremultiplied = premultiplied;
}
public WritableRaster copyData(WritableRaster dest)
@ -555,7 +628,7 @@ public class BufferedImage extends Image
};
}
public Vector getSources()
public Vector<RenderedImage> getSources()
{
return null;
}
@ -726,10 +799,10 @@ public class BufferedImage extends Image
*/
public void addTileObserver (TileObserver to)
{
if (observers == null)
observers = new Vector ();
if (tileObservers == null)
tileObservers = new Vector ();
observers.add (to);
tileObservers.add (to);
}
/**
@ -741,10 +814,10 @@ public class BufferedImage extends Image
*/
public void removeTileObserver (TileObserver to)
{
if (observers == null)
if (tileObservers == null)
return;
observers.remove (to);
tileObservers.remove (to);
}
/**

View file

@ -38,7 +38,10 @@ exception statement from your version. */
package java.awt.image;
import gnu.java.awt.Buffers;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
@ -47,9 +50,9 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
/**
* ColorConvertOp is a filter for converting an image from one colorspace to
* another colorspace. The filter can convert the image through a sequence
* of colorspaces or just from source to destination.
* ColorConvertOp is a filter for converting images or rasters between
* colorspaces, either through a sequence of colorspaces or just from source to
* destination.
*
* Color conversion is done on the color components without alpha. Thus
* if a BufferedImage has alpha premultiplied, this is divided out before
@ -63,24 +66,22 @@ import java.awt.geom.Rectangle2D;
*/
public class ColorConvertOp implements BufferedImageOp, RasterOp
{
private ColorSpace srccs;
private ColorSpace dstcs;
private RenderingHints hints;
private ICC_Profile[] profiles;
private ICC_Profile[] profiles = null;
private ColorSpace[] spaces;
private boolean rasterValid;
/**
* Convert BufferedImage through a ColorSpace.
* Convert a BufferedImage through a ColorSpace.
*
* This filter version is only valid for BufferedImages. The source image
* is converted to cspace. If the destination is not null, it is then
* converted to the destination colorspace. Normally this filter will only
* be used with a null destination.
* Objects created with this constructor can be used to convert
* BufferedImage's to a destination ColorSpace. Attempts to convert Rasters
* with this constructor will result in an IllegalArgumentException when the
* filter(Raster, WritableRaster) method is called.
*
* @param cspace The target color space.
* @param hints Rendering hints to use in conversion, or null.
* @param hints Rendering hints to use in conversion, if any (may be null)
* @throws NullPointerException if the ColorSpace is null.
*/
public ColorConvertOp(ColorSpace cspace, RenderingHints hints)
{
@ -88,9 +89,27 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
throw new NullPointerException();
spaces = new ColorSpace[]{cspace};
this.hints = hints;
rasterValid = false;
}
/**
* Convert from a source colorspace to a destination colorspace.
*
* This constructor takes two ColorSpace arguments as the source and
* destination color spaces. It is usually used with the
* filter(Raster, WritableRaster) method, in which case the source colorspace
* is assumed to correspond to the source Raster, and the destination
* colorspace with the destination Raster.
*
* If used with BufferedImages that do not match the source or destination
* colorspaces specified here, there is an implicit conversion from the
* source image to the source ColorSpace, or the destination ColorSpace to
* the destination image.
*
* @param srcCspace The source ColorSpace.
* @param dstCspace The destination ColorSpace.
* @param hints Rendering hints to use in conversion, if any (may be null).
* @throws NullPointerException if any ColorSpace is null.
*/
public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace,
RenderingHints hints)
{
@ -101,61 +120,77 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
}
/**
* Convert from a source image destination image color space.
* Convert from a source colorspace to a destinatino colorspace.
*
* This constructor builds a ColorConvertOp from an array of ICC_Profiles.
* The source image will be converted through the sequence of color spaces
* The source will be converted through the sequence of color spaces
* defined by the profiles. If the sequence of profiles doesn't give a
* well-defined conversion, throws IllegalArgumentException.
* well-defined conversion, an IllegalArgumentException is thrown.
*
* NOTE: Sun's docs don't clearly define what a well-defined conversion is
* - or perhaps someone smarter can come along and sort it out.
*
* For BufferedImages, when the first and last profiles match the
* requirements of the source and destination color space respectively, the
* corresponding conversion is unnecessary. TODO: code this up. I don't
* yet understand how you determine this.
* If used with BufferedImages that do not match the source or destination
* colorspaces specified here, there is an implicit conversion from the
* source image to the source ColorSpace, or the destination ColorSpace to
* the destination image.
*
* For Rasters, the first and last profiles must have the same number of
* bands as the source and destination Rasters, respectively. If this is
* not the case, or there fewer than 2 profiles, an IllegalArgumentException
* will be thrown.
*
* @param profiles
* @param hints
* @param profiles An array of ICC_Profile's to convert through.
* @param hints Rendering hints to use in conversion, if any (may be null).
* @throws NullPointerException if the profile array is null.
* @throws IllegalArgumentException if the array is not a well-defined
* conversion.
*/
public ColorConvertOp(ICC_Profile[] profiles, RenderingHints hints)
{
if (profiles == null)
throw new NullPointerException();
this.hints = hints;
this.profiles = profiles;
// TODO: Determine if this is well-defined.
// Create colorspace array with space for src and dest colorspace
// Note that the ICC_ColorSpace constructor will throw an
// IllegalArgumentException if the profile is invalid; thus we check
// for a "well defined conversion"
spaces = new ColorSpace[profiles.length];
for (int i = 0; i < profiles.length; i++)
spaces[i] = new ICC_ColorSpace(profiles[i]);
}
/** Convert from source image color space to destination image color space.
/**
* Convert from source color space to destination color space.
*
* Only valid for BufferedImage objects, this Op converts from the source
* color space to the destination color space. The destination can't be
* null for this operation.
* image's color space to the destination image's color space.
*
* @param hints Rendering hints to use during conversion, or null.
* The destination in the filter(BufferedImage, BufferedImage) method cannot
* be null for this operation, and it also cannot be used with the
* filter(Raster, WritableRaster) method.
*
* @param hints Rendering hints to use in conversion, if any (may be null).
*/
public ColorConvertOp(RenderingHints hints)
{
this.hints = hints;
srccs = null;
dstcs = null;
rasterValid = false;
this.hints = hints;
spaces = new ColorSpace[0];
}
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage,
java.awt.image.BufferedImage)
/**
* Converts the source image using the conversion path specified in the
* constructor. The resulting image is stored in the destination image if one
* is provided; otherwise a new BufferedImage is created and returned.
*
* The source and destination BufferedImage (if one is supplied) must have
* the same dimensions.
*
* @param src The source image.
* @param dst The destination image.
* @throws IllegalArgumentException if the rasters and/or color spaces are
* incompatible.
* @return The transformed image.
*/
public final BufferedImage filter(BufferedImage src, BufferedImage dst)
{
@ -163,129 +198,241 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
// For now we just suck it up and create intermediate buffers.
if (dst == null && spaces.length == 0)
throw new IllegalArgumentException();
throw new IllegalArgumentException("Not enough color space information "
+ "to complete conversion.");
if (dst != null
&& (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth()))
throw new IllegalArgumentException("Source and destination images have "
+ "different dimensions");
// Make sure input isn't premultiplied by alpha
if (src.isAlphaPremultiplied())
{
BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel());
copyimage(src, tmp);
tmp.coerceData(false);
src = tmp;
}
{
BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel());
copyimage(src, tmp);
tmp.coerceData(false);
src = tmp;
}
ColorModel scm = src.getColorModel();
// Convert through defined intermediate conversions
BufferedImage tmp;
for (int i = 0; i < spaces.length; i++)
{
BufferedImage tmp = createCompatibleDestImage(src, scm);
copyimage(src, tmp);
src = tmp;
}
{
if (src.getColorModel().getColorSpace().getType() != spaces[i].getType())
{
tmp = createCompatibleDestImage(src,
createCompatibleColorModel(src,
spaces[i]));
copyimage(src, tmp);
src = tmp;
}
}
// Intermediate conversions leave result in src
// No implicit conversion to destination type needed; return result from the
// last intermediate conversions (which was left in src)
if (dst == null)
return src;
// Apply final conversion
copyimage(src, dst);
dst = src;
// Implicit conversion to destination image's color space
else
copyimage(src, dst);
return dst;
}
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel)
/**
* Converts the source raster using the conversion path specified in the
* constructor. The resulting raster is stored in the destination raster if
* one is provided; otherwise a new WritableRaster is created and returned.
*
* This operation is not valid with every constructor of this class; see
* the constructors for details. Further, the source raster must have the
* same number of bands as the source ColorSpace, and the destination raster
* must have the same number of bands as the destination ColorSpace.
*
* The source and destination raster (if one is supplied) must also have the
* same dimensions.
*
* @param src The source raster.
* @param dest The destination raster.
* @throws IllegalArgumentException if the rasters and/or color spaces are
* incompatible.
* @return The transformed raster.
*/
public final WritableRaster filter(Raster src, WritableRaster dest)
{
// Various checks to ensure that the rasters and color spaces are compatible
if (spaces.length < 2)
throw new IllegalArgumentException("Not enough information about " +
"source and destination colorspaces.");
if (spaces[0].getNumComponents() != src.getNumBands()
|| (dest != null && spaces[spaces.length - 1].getNumComponents() != dest.getNumBands()))
throw new IllegalArgumentException("Source or destination raster " +
"contains the wrong number of bands.");
if (dest != null
&& (src.getHeight() != dest.getHeight() || src.getWidth() != dest.getWidth()))
throw new IllegalArgumentException("Source and destination rasters " +
"have different dimensions");
// Need to iterate through each color space.
// spaces[0] corresponds to the ColorSpace of the source raster, and
// spaces[spaces.length - 1] corresponds to the ColorSpace of the
// destination, with any number (or zero) of intermediate conversions.
for (int i = 0; i < spaces.length - 2; i++)
{
WritableRaster tmp = createCompatibleDestRaster(src, spaces[i + 1],
false,
src.getTransferType());
copyraster(src, spaces[i], tmp, spaces[i + 1]);
src = tmp;
}
// The last conversion is done outside of the loop so that we can
// use the dest raster supplied, instead of creating our own temp raster
if (dest == null)
dest = createCompatibleDestRaster(src, spaces[spaces.length - 1], false,
DataBuffer.TYPE_BYTE);
copyraster(src, spaces[spaces.length - 2], dest, spaces[spaces.length - 1]);
return dest;
}
/**
* Creates an empty BufferedImage with the size equal to the source and the
* correct number of bands for the conversion defined in this Op. The newly
* created image is created with the specified ColorModel, or if no ColorModel
* is supplied, an appropriate one is chosen.
*
* @param src The source image.
* @param dstCM A color model for the destination image (may be null).
* @throws IllegalArgumentException if an appropriate colormodel cannot be
* chosen with the information given.
* @return The new compatible destination image.
*/
public BufferedImage createCompatibleDestImage(BufferedImage src,
ColorModel dstCM)
ColorModel dstCM)
{
// FIXME: set properties to those in src
if (dstCM == null && spaces.length == 0)
throw new IllegalArgumentException("Don't know the destination " +
"colormodel");
if (dstCM == null)
{
dstCM = createCompatibleColorModel(src, spaces[spaces.length - 1]);
}
return new BufferedImage(dstCM,
src.getRaster().createCompatibleWritableRaster(),
src.isPremultiplied,
null);
createCompatibleDestRaster(src.getRaster(),
dstCM.getColorSpace(),
src.getColorModel().hasAlpha,
dstCM.getTransferType()),
src.isPremultiplied, null);
}
/**
* Creates a new WritableRaster with the size equal to the source and the
* correct number of bands.
*
* Note, the new Raster will always use a BYTE storage size, regardless of
* the color model or defined destination; this is for compatibility with
* the reference implementation.
*
* @param src The source Raster.
* @throws IllegalArgumentException if there isn't enough colorspace
* information to create a compatible Raster.
* @return The new compatible destination raster.
*/
public WritableRaster createCompatibleDestRaster(Raster src)
{
if (spaces.length < 2)
throw new IllegalArgumentException("Not enough destination colorspace " +
"information");
// Create a new raster with the last ColorSpace in the conversion
// chain, and with no alpha (implied)
return createCompatibleDestRaster(src, spaces[spaces.length-1], false,
DataBuffer.TYPE_BYTE);
}
/**
* Returns the array of ICC_Profiles used to create this Op, or null if the
* Op was created using ColorSpace arguments.
*
* @return The array of ICC_Profiles, or null.
*/
public final ICC_Profile[] getICC_Profiles()
{
return profiles;
}
/** Return the rendering hints for this op. */
/**
* Returns the rendering hints for this op.
*
* @return The rendering hints for this Op, or null.
*/
public final RenderingHints getRenderingHints()
{
return hints;
}
/* (non-Javadoc)
* @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster)
*/
public final WritableRaster filter(Raster src, WritableRaster dest)
{
if (!rasterValid)
throw new IllegalArgumentException();
// Need to iterate through each color space - there must be at least 2
for (int i = 1; i < spaces.length - 1; i++)
{
// FIXME: this is wrong. tmp needs to have the same number of bands as
// spaces[i] has.
WritableRaster tmp = createCompatibleDestRaster(src);
copyraster(src, spaces[i - 1], tmp, spaces[i]);
src = tmp;
}
// FIXME: this is wrong. dst needs to have the same number of bands as
// spaces[i] has.
if (dest == null)
dest = createCompatibleDestRaster(src);
copyraster(src, spaces[spaces.length - 2],
dest, spaces[spaces.length - 1]);
return dest;
}
/* (non-Javadoc)
* @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
*/
public WritableRaster createCompatibleDestRaster(Raster src)
{
return src.createCompatibleWritableRaster();
}
/** Return corresponding destination point for source point.
/**
* Returns the corresponding destination point for a source point.
* Because this is not a geometric operation, the destination and source
* points will be identical.
*
* LookupOp will return the value of src unchanged.
* @param src The source point.
* @param dst The destination point.
* @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D)
* @param dst The transformed destination point.
* @return The transformed destination point.
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
if (dst == null) return (Point2D)src.clone();
if (dst == null)
return (Point2D)src.clone();
dst.setLocation(src);
return dst;
}
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage)
/**
* Returns the corresponding destination boundary of a source boundary.
* Because this is not a geometric operation, the destination and source
* boundaries will be identical.
*
* @param src The source boundary.
* @return The boundaries of the destination.
*/
public final Rectangle2D getBounds2D(BufferedImage src)
{
return src.getRaster().getBounds();
}
/* (non-Javadoc)
* @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster)
/**
* Returns the corresponding destination boundary of a source boundary.
* Because this is not a geometric operation, the destination and source
* boundaries will be identical.
*
* @param src The source boundary.
* @return The boundaries of the destination.
*/
public final Rectangle2D getBounds2D(Raster src)
{
return src.getBounds();
}
// According to Sven de Marothy, we need to copy the src into the dest
// using Graphics2D, in order to use the rendering hints.
/**
* Copy a source image to a destination image, respecting their colorspaces
* and performing colorspace conversions if necessary.
*
* @param src The source image.
* @param dst The destination image.
*/
private void copyimage(BufferedImage src, BufferedImage dst)
{
// This is done using Graphics2D in order to respect the rendering hints.
Graphics2D gg = dst.createGraphics();
// If no hints are set there is no need to call
@ -297,13 +444,23 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
gg.dispose();
}
private void copyraster(Raster src, ColorSpace scs, WritableRaster dst,
ColorSpace dcs)
/**
* Copy a source raster to a destination raster, performing a colorspace
* conversion between the two. The conversion will respect the
* KEY_COLOR_RENDERING rendering hint if one is present.
*
* @param src The source raster.
* @param scs The colorspace of the source raster.
* @dst The destination raster.
* @dcs The colorspace of the destination raster.
*/
private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, ColorSpace dcs)
{
float[] sbuf = new float[src.getNumBands()];
if (hints.get(RenderingHints.KEY_COLOR_RENDERING) ==
RenderingHints.VALUE_COLOR_RENDER_QUALITY)
if (hints != null
&& hints.get(RenderingHints.KEY_COLOR_RENDERING) ==
RenderingHints.VALUE_COLOR_RENDER_QUALITY)
{
// use cie for accuracy
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
@ -321,4 +478,60 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
}
}
/**
* This method creates a color model with the same colorspace and alpha
* settings as the source image. The created color model will always be a
* ComponentColorModel and have a BYTE transfer type.
*
* @param img The source image.
* @param cs The ColorSpace to use.
* @return A color model compatible with the source image.
*/
private ColorModel createCompatibleColorModel(BufferedImage img, ColorSpace cs)
{
// The choice of ComponentColorModel and DataBuffer.TYPE_BYTE is based on
// Mauve testing of the reference implementation.
return new ComponentColorModel(cs,
img.getColorModel().hasAlpha(),
img.isAlphaPremultiplied(),
img.getColorModel().getTransparency(),
DataBuffer.TYPE_BYTE);
}
/**
* This method creates a compatible Raster, given a source raster, colorspace,
* alpha value, and transfer type.
*
* @param src The source raster.
* @param cs The ColorSpace to use.
* @param hasAlpha Whether the raster should include a component for an alpha.
* @param transferType The size of a single data element.
* @return A compatible WritableRaster.
*/
private WritableRaster createCompatibleDestRaster(Raster src, ColorSpace cs,
boolean hasAlpha,
int transferType)
{
// The use of a PixelInterleavedSampleModel weas determined using mauve
// tests, based on the reference implementation
int numComponents = cs.getNumComponents();
if (hasAlpha)
numComponents++;
int[] offsets = new int[numComponents];
for (int i = 0; i < offsets.length; i++)
offsets[i] = i;
DataBuffer db = Buffers.createBuffer(transferType,
src.getWidth() * src.getHeight() * numComponents,
1);
return new WritableRaster(new PixelInterleavedSampleModel(transferType,
src.getWidth(),
src.getHeight(),
numComponents,
numComponents * src.getWidth(),
offsets),
db, new Point(src.getMinX(), src.getMinY()));
}
}

View file

@ -624,40 +624,40 @@ public abstract class ColorModel implements Transparency
return cspace;
}
// Typically overridden
public ColorModel coerceData(WritableRaster raster,
boolean isAlphaPremultiplied)
boolean isAlphaPremultiplied)
{
if (this.isAlphaPremultiplied == isAlphaPremultiplied)
return this;
// This method should always be overridden, but is not abstract.
throw new UnsupportedOperationException();
}
protected void coerceDataWorker(WritableRaster raster,
boolean isAlphaPremultiplied)
{
int w = raster.getWidth();
int h = raster.getHeight();
int x = raster.getMinX();
int y = raster.getMinY();
int size = w*h;
int size = w * h;
int numColors = getNumColorComponents();
int numComponents = getNumComponents();
int alphaScale = (1<<getComponentSize(numColors)) - 1;
int alphaScale = (1 << getComponentSize(numColors)) - 1;
double[] pixels = raster.getPixels(x, y, w, h, (double[]) null);
for (int i=0; i<size; i++)
for (int i = 0; i < size; i++)
{
double alpha = pixels[i*numComponents+numColors]*alphaScale;
for (int c=0; c<numColors; c++)
{
int offset = i*numComponents+c;
if (isAlphaPremultiplied)
pixels[offset] = pixels[offset]/alpha;
else
pixels[offset] = pixels[offset]*alpha;
}
double alpha = pixels[i * numComponents + numColors] / alphaScale;
for (int c = 0; c < numColors; c++)
{
int offset = i * numComponents + c;
if (isAlphaPremultiplied)
pixels[offset] = Math.round(pixels[offset] * alpha);
else
pixels[offset] = Math.round(pixels[offset] / alpha);
}
}
raster.setPixels(0, 0, w, h, pixels);
// FIXME: what can we return?
return null;
raster.setPixels(0, 0, w, h, pixels);
}
/**

View file

@ -42,9 +42,11 @@ import gnu.java.awt.Buffers;
import java.awt.Point;
import java.awt.color.ColorSpace;
import java.util.Arrays;
public class ComponentColorModel extends ColorModel
{
// Find sum of all elements of the array.
private static int sum(int[] values)
{
int sum = 0;
@ -52,6 +54,22 @@ public class ComponentColorModel extends ColorModel
sum += values[i];
return sum;
}
// Create an appropriate array of bits, given a colorspace (ie, number of
// bands), size of the storage data type, and presence of an alpha band.
private static int[] findBits(ColorSpace colorSpace, int transferType,
boolean hasAlpha)
{
int[] bits;
if (hasAlpha)
bits = new int[colorSpace.getNumComponents()+1];
else
bits = new int[colorSpace.getNumComponents()];
Arrays.fill(bits, DataBuffer.getDataTypeSize(transferType));
return bits;
}
public ComponentColorModel(ColorSpace colorSpace, int[] bits,
boolean hasAlpha,
@ -84,8 +102,8 @@ public class ComponentColorModel extends ColorModel
boolean isAlphaPremultiplied,
int transparency, int transferType)
{
this(colorSpace, null, hasAlpha, isAlphaPremultiplied,
transparency, transferType);
this(colorSpace, findBits(colorSpace, transferType, hasAlpha), hasAlpha,
isAlphaPremultiplied, transparency, transferType);
}
public int getRed(int pixel)
@ -288,17 +306,16 @@ public class ComponentColorModel extends ColorModel
public ColorModel coerceData(WritableRaster raster,
boolean isAlphaPremultiplied) {
if (this.isAlphaPremultiplied == isAlphaPremultiplied)
if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
return this;
/* TODO: provide better implementation based on the
assumptions we can make due to the specific type of the
color model. */
super.coerceData(raster, isAlphaPremultiplied);
super.coerceDataWorker(raster, isAlphaPremultiplied);
return new ComponentColorModel(cspace, bits, hasAlpha(),
isAlphaPremultiplied, // argument
transparency, transferType);
return new ComponentColorModel(cspace, hasAlpha, isAlphaPremultiplied,
transparency, transferType);
}
public boolean isCompatibleRaster(Raster raster)

View file

@ -38,7 +38,6 @@ exception statement from your version. */
package java.awt.image;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@ -51,11 +50,13 @@ import java.awt.geom.Rectangle2D;
* with elements in the kernel to compute a new pixel.
*
* Each band in a Raster is convolved and copied to the destination Raster.
* For BufferedImages, convolution is applied to all components. Color
* conversion will be applied if needed.
*
* For BufferedImages, convolution is applied to all components. If the
* source is not premultiplied, the data will be premultiplied before
* convolving. Premultiplication will be undone if the destination is not
* premultiplied. Color conversion will be applied if needed.
* Note that this filter ignores whether the source or destination is alpha
* premultiplied. The reference spec states that data will be premultiplied
* prior to convolving and divided back out afterwards (if needed), but testing
* has shown that this is not the case with their implementation.
*
* @author jlquinn@optonline.net
*/
@ -104,59 +105,83 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
hints = null;
}
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage,
* java.awt.image.BufferedImage)
/**
* Converts the source image using the kernel specified in the
* constructor. The resulting image is stored in the destination image if one
* is provided; otherwise a new BufferedImage is created and returned.
*
* The source and destination BufferedImage (if one is supplied) must have
* the same dimensions.
*
* @param src The source image.
* @param dst The destination image.
* @throws IllegalArgumentException if the rasters and/or color spaces are
* incompatible.
* @return The convolved image.
*/
public final BufferedImage filter(BufferedImage src, BufferedImage dst)
{
if (src == dst)
throw new IllegalArgumentException();
throw new IllegalArgumentException("Source and destination images " +
"cannot be the same.");
if (dst == null)
dst = createCompatibleDestImage(src, src.getColorModel());
// Make sure source image is premultiplied
BufferedImage src1 = src;
if (!src.isPremultiplied)
// The spec says we should do this, but mauve testing shows that Sun's
// implementation does not check this.
/*
if (!src.isAlphaPremultiplied())
{
src1 = createCompatibleDestImage(src, src.getColorModel());
src.copyData(src1.getRaster());
src1.coerceData(true);
}
*/
BufferedImage dst1 = dst;
if (!src.getColorModel().equals(dst.getColorModel()))
if (src1.getColorModel().getColorSpace().getType() != dst.getColorModel().getColorSpace().getType())
dst1 = createCompatibleDestImage(src, src.getColorModel());
filter(src1.getRaster(), dst1.getRaster());
// Since we don't coerceData above, we don't need to divide it back out.
// This is wrong (one mauve test specifically tests converting a non-
// premultiplied image to a premultiplied image, and it shows that Sun
// simply ignores the premultipled flag, contrary to the spec), but we
// mimic it for compatibility.
/*
if (! dst.isAlphaPremultiplied())
dst1.coerceData(false);
*/
// Convert between color models if needed
if (dst1 != dst)
{
// Convert between color models.
// TODO Check that premultiplied alpha is handled correctly here.
Graphics2D gg = dst.createGraphics();
gg.setRenderingHints(hints);
gg.drawImage(dst1, 0, 0, null);
gg.dispose();
}
new ColorConvertOp(hints).filter(dst1, dst);
return dst;
}
/* (non-Javadoc)
* @see
* java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage,
* java.awt.image.ColorModel)
/**
* Creates an empty BufferedImage with the size equal to the source and the
* correct number of bands. The new image is created with the specified
* ColorModel, or if no ColorModel is supplied, an appropriate one is chosen.
*
* @param src The source image.
* @param dstCM A color model for the destination image (may be null).
* @return The new compatible destination image.
*/
public BufferedImage createCompatibleDestImage(BufferedImage src,
ColorModel dstCM)
ColorModel dstCM)
{
// FIXME: set properties to those in src
return new BufferedImage(dstCM,
src.getRaster().createCompatibleWritableRaster(),
src.isPremultiplied, null);
if (dstCM != null)
return new BufferedImage(dstCM,
src.getRaster().createCompatibleWritableRaster(),
src.isAlphaPremultiplied(), null);
return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
}
/* (non-Javadoc)
@ -168,6 +193,8 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
}
/**
* Get the edge condition for this Op.
*
* @return The edge condition.
*/
public int getEdgeCondition()
@ -185,9 +212,22 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
return (Kernel) kernel.clone();
}
/* (non-Javadoc)
* @see java.awt.image.RasterOp#filter(java.awt.image.Raster,
* java.awt.image.WritableRaster)
/**
* Converts the source raster using the kernel specified in the constructor.
* The resulting raster is stored in the destination raster if one is
* provided; otherwise a new WritableRaster is created and returned.
*
* If the convolved value for a sample is outside the range of [0-255], it
* will be clipped.
*
* The source and destination raster (if one is supplied) cannot be the same,
* and must also have the same dimensions.
*
* @param src The source raster.
* @param dest The destination raster.
* @throws IllegalArgumentException if the rasters identical.
* @throws ImagingOpException if the convolution is not possible.
* @return The transformed raster.
*/
public final WritableRaster filter(Raster src, WritableRaster dest)
{
@ -209,6 +249,11 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
int top = kernel.getYOrigin();
int bottom = Math.max(kHeight - top - 1, 0);
// Calculate max sample values for clipping
int[] maxValue = src.getSampleModel().getSampleSize();
for (int i = 0; i < maxValue.length; i++)
maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1;
// process the region that is reachable...
int regionW = src.width - left - right;
int regionH = src.height - top - bottom;
@ -228,7 +273,14 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
v += tmp[tmp.length - i - 1] * kvals[i];
// FIXME: in the above line, I've had to reverse the order of
// the samples array to make the tests pass. I haven't worked
// out why this is necessary.
// out why this is necessary.
// This clipping is is undocumented, but determined by testing.
if (v > maxValue[b])
v = maxValue[b];
else if (v < 0)
v = 0;
dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(),
b, v);
}
@ -310,13 +362,14 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
return src.getBounds();
}
/** Return corresponding destination point for source point.
/**
* Returns the corresponding destination point for a source point. Because
* this is not a geometric operation, the destination and source points will
* be identical.
*
* ConvolveOp will return the value of src unchanged.
* @param src The source point.
* @param dst The destination point.
* @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D,
* java.awt.geom.Point2D)
* @param dst The transformed destination point.
* @return The transformed destination point.
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{

View file

@ -91,11 +91,12 @@ public class CropImageFilter extends ImageFilter
*
* @param props the list of properties associated with this image
*/
public void setProperties(Hashtable props)
public void setProperties(Hashtable<?, ?> props)
{
props.put("filters", "CropImageFilter");
if (consumer != null)
consumer.setProperties(props);
Hashtable<Object, Object> prop2 = (Hashtable<Object, Object>) props;
prop2.put("filters", "CropImageFilter");
if (consumer != null)
consumer.setProperties(prop2);
}
/**

View file

@ -393,20 +393,20 @@ public class DirectColorModel extends PackedColorModel
return Buffers.getData(buffer);
}
public final ColorModel coerceData (WritableRaster raster,
boolean isAlphaPremultiplied)
public ColorModel coerceData (WritableRaster raster,
boolean isAlphaPremultiplied)
{
if (this.isAlphaPremultiplied == isAlphaPremultiplied)
if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
return this;
/* TODO: provide better implementation based on the
assumptions we can make due to the specific type of the
color model. */
super.coerceData(raster, isAlphaPremultiplied);
return new ComponentColorModel(cspace, bits, hasAlpha(),
isAlphaPremultiplied, // argument
transparency, transferType);
super.coerceDataWorker(raster, isAlphaPremultiplied);
return new DirectColorModel(cspace, pixel_bits, getRedMask(),
getGreenMask(), getBlueMask(), getAlphaMask(),
isAlphaPremultiplied, transferType);
}
public boolean isCompatibleRaster(Raster raster)

View file

@ -136,7 +136,7 @@ public interface ImageConsumer
*
* @param props the list of properties associated with this image
*/
void setProperties(Hashtable props);
void setProperties(Hashtable<?,?> props);
/**
* This <code>ColorModel</code> should indicate the model used by

View file

@ -49,180 +49,178 @@ import java.util.Hashtable;
*/
public class ImageFilter implements ImageConsumer, Cloneable
{
/**
* The consumer this filter is filtering an image data stream for.
* It is initialized in the method <code>getFilterInstance</code>.
*/
protected ImageConsumer consumer = null;
/**
* The consumer this filter is filtering an image data stream for.
* It is initialized in the method <code>getFilterInstance</code>.
*/
protected ImageConsumer consumer = null;
/**
* The <code>ImageConsumer</code> can use this method to request
* the pixels be delivered in top-down, left-right order.
* <br>
* The filter can respond in three different ways.
* <ul>
* <li>The default behavior is to forward the request to the
* <code>ImageProducer</code>
* using the method <code>requestTopDownLeftRightResend</code>
* and using the filter as the consumer.</li>
* <li>The filter has the pixels and can retransmit them in the
* top-down, left-right order.</li>
* <li>The filter can do nothing when this method is called.</li>
* </ul>
*/
public void resendTopDownLeftRight(ImageProducer ip)
{
ip.requestTopDownLeftRightResend(this);
}
/**
* The <code>ImageConsumer</code> can use this method to request
* the pixels be delivered in top-down, left-right order.
* <br>
* The filter can respond in three different ways.
* <ul>
* <li>The default behavior is to forward the request to the
* <code>ImageProducer</code>
* using the method <code>requestTopDownLeftRightResend</code>
* and using the filter as the consumer.</li>
* <li>The filter has the pixels and can retransmit them in the
* top-down, left-right order.</li>
* <li>The filter can do nothing when this method is called.</li>
* </ul>
*/
public void resendTopDownLeftRight(ImageProducer ip)
{
ip.requestTopDownLeftRightResend(this);
}
/**
* By default, returns a shallow copy of the object created by
* <code>Object.clone()</code>
*
* @see java.lang.Object#clone ()
*/
public Object clone()
{
try
{
return super.clone();
}
catch (CloneNotSupportedException e)
{
// This should never happen as this class implements the
// Cloneable interface.
throw new InternalError ();
}
}
/**
* By default, returns a shallow copy of the object created by
* <code>Object.clone()</code>
*
* @see java.lang.Object#clone ()
*/
public Object clone()
{
try
{
return super.clone();
}
catch (CloneNotSupportedException e)
{
// This should never happen as this class implements the
// Cloneable interface.
throw new InternalError ();
}
}
/**
* This is the only method which can set the
* <code>ImageConsumer</code> for this filter. By default a clone
* of this filter with the appropriate consumer set is returned.
*
* @see #clone ()
*/
public ImageFilter getFilterInstance(ImageConsumer ic)
{
if ( ic == null )
throw new IllegalArgumentException("null argument for ImageFilter.getFilterInstance(ImageConsumer)");
/**
* This is the only method which can set the
* <code>ImageConsumer</code> for this filter. By default a clone
* of this filter with the appropriate consumer set is returned.
*
* @see #clone ()
*/
public ImageFilter getFilterInstance(ImageConsumer ic)
{
ImageFilter f = (ImageFilter)clone();
f.consumer = ic;
return f;
}
consumer = ic;
ImageFilter f = (ImageFilter)clone();
consumer = null;
return f;
}
/**
* An <code>ImageProducer</code> indicates the size of the image
* being produced using this method. A filter can override this
* method to intercept these calls from the producer in order to
* change either the width or the height before in turn calling
* the consumer's <code>setDimensions</code> method.
*
* @param width the width of the image
* @param height the height of the image
*/
public void setDimensions(int width, int height)
{
consumer.setDimensions(width, height);
}
/**
* An <code>ImageProducer</code> indicates the size of the image
* being produced using this method. A filter can override this
* method to intercept these calls from the producer in order to
* change either the width or the height before in turn calling
* the consumer's <code>setDimensions</code> method.
*
* @param width the width of the image
* @param height the height of the image
*/
public void setDimensions(int width, int height)
{
if (consumer != null)
consumer.setDimensions(width, height);
}
/**
* An <code>ImageProducer</code> can set a list of properties
* associated with this image by using this method.
*
* @param props the list of properties associated with this image
*/
public void setProperties(Hashtable<?,?> props)
{
Hashtable copy = (Hashtable) props.clone();
Object o = copy.get("filters");
if (o == null)
copy.put("filters", toString());
else if (o instanceof String)
copy.put("filters", ((String) o) + toString());
/**
* An <code>ImageProducer</code> can set a list of properties
* associated with this image by using this method.
*
* @param props the list of properties associated with this image
*/
public void setProperties(Hashtable props)
{
props.put("filters", "ImageFilter");
if (consumer != null)
consumer.setProperties(props);
}
consumer.setProperties(copy);
}
/**
* Override this method to process calls to this method from the
* <code>ImageProducer</code>. By default the <code>setColorModel</code>
* method of the consumer is called with the specified <code>model</code>.
*
* @param model the color model to be used most often by setPixels
* @see ColorModel */
public void setColorModel(ColorModel model)
{
if (consumer != null)
consumer.setColorModel(model);
}
/**
* Override this method to process calls to this method from the
* <code>ImageProducer</code>. By default the <code>setColorModel</code>
* method of the consumer is called with the specified <code>model</code>.
*
* @param model the color model to be used most often by setPixels
*
* @see ColorModel
*/
public void setColorModel(ColorModel model)
{
consumer.setColorModel(model);
}
/**
* The <code>ImageProducer</code> should call this method with a
* bit mask of hints from any of <code>RANDOMPIXELORDER</code>,
* <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>,
* <code>SINGLEPASS</code>, <code>SINGLEFRAME</code> from the
* <code>ImageConsumer</code> interface.
*
* @param flags a bit mask of hints
* @see ImageConsumer
*/
public void setHints(int flags)
{
if (consumer != null)
consumer.setHints(flags);
}
/**
* The <code>ImageProducer</code> should call this method with a
* bit mask of hints from any of <code>RANDOMPIXELORDER</code>,
* <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>,
* <code>SINGLEPASS</code>, <code>SINGLEFRAME</code> from the
* <code>ImageConsumer</code> interface.
*
* @param flags a bit mask of hints
* @see ImageConsumer
*/
public void setHints(int flags)
{
consumer.setHints(flags);
}
/**
* This function delivers a rectangle of pixels where any
* pixel(m,n) is stored in the array as a <code>byte</code> at
* index (n * scansize + m + offset).
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code> array
* @param scansize the width to use in extracting pixels from the <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte[] pixels, int offset, int scansize)
{
if (consumer != null)
consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
}
/**
* This function delivers a rectangle of pixels where any
* pixel(m,n) is stored in the array as a <code>byte</code> at
* index (n * scansize + m + offset).
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code> array
* @param scansize the width to use in extracting pixels from the <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte[] pixels, int offset,
int scansize)
{
consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
}
/**
* This function delivers a rectangle of pixels where any
* pixel(m,n) is stored in the array as an <code>int</code> at
* index (n * scansize + m + offset).
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code> array
* @param scansize the width to use in extracting pixels from the <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, int[] pixels, int offset, int scansize)
{
if (consumer != null)
consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
}
/**
* This function delivers a rectangle of pixels where any
* pixel(m,n) is stored in the array as an <code>int</code> at
* index (n * scansize + m + offset).
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code> array
* @param scansize the width to use in extracting pixels from the <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, int[] pixels, int offset,
int scansize)
{
consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
}
/**
* The <code>ImageProducer</code> calls this method to indicate a
* single frame or the entire image is complete. The method is
* also used to indicate an error in loading or producing the
* image.
*/
public void imageComplete(int status)
{
if (consumer != null)
consumer.imageComplete(status);
}
/**
* The <code>ImageProducer</code> calls this method to indicate a
* single frame or the entire image is complete. The method is
* also used to indicate an error in loading or producing the
* image.
*/
public void imageComplete(int status)
{
consumer.imageComplete(status);
}
}

View file

@ -134,10 +134,6 @@ public class IndexColorModel extends ColorModel
if (size < 1)
throw new IllegalArgumentException("size < 1");
map_size = size;
if (0 <= trans && trans < size) {
this.trans = trans;
transparency = BITMASK;
}
rgb = new int[size];
for (int i = 0; i < size; i++)
{
@ -146,6 +142,9 @@ public class IndexColorModel extends ColorModel
| ((greens[i] & 0xff) << 8)
| (blues[i] & 0xff));
}
setTransparentPixel(trans);
// Generate a bigint with 1's for every pixel
validBits = validBits.setBit(size).subtract(BigInteger.ONE);
}
@ -275,8 +274,6 @@ public class IndexColorModel extends ColorModel
throw new IllegalArgumentException("size < 1");
map_size = size;
opaque = !hasAlpha;
if (0 <= trans && trans < size)
this.trans = trans;
rgb = new int[size];
if (hasAlpha)
@ -318,6 +315,8 @@ public class IndexColorModel extends ColorModel
transparency = BITMASK;
}
setTransparentPixel(trans);
// Generate a bigint with 1's for every pixel
validBits = validBits.setBit(size).subtract(BigInteger.ONE);
}
@ -361,9 +360,6 @@ public class IndexColorModel extends ColorModel
throw new IllegalArgumentException("size < 1");
map_size = size;
opaque = !hasAlpha;
if (0 <= trans && trans < size)
this.trans = trans;
rgb = new int[size];
if (!hasAlpha)
for (int i = 0; i < size; i++)
@ -371,6 +367,8 @@ public class IndexColorModel extends ColorModel
else
System.arraycopy(cmap, start, rgb, 0, size);
setTransparentPixel(trans);
// Generate a bigint with 1's for every pixel
validBits = validBits.setBit(size).subtract(BigInteger.ONE);
}
@ -584,12 +582,7 @@ public class IndexColorModel extends ColorModel
*/
public final int getAlpha(int pixel)
{
if (opaque && pixel != trans)
return 255;
if ((pixel == trans && trans != -1) || pixel >= map_size)
return 0;
return (0xFF000000 & rgb[pixel]) >> 24;
return (rgb[pixel] >> 24) & 0xFF;
}
/**
@ -694,4 +687,43 @@ public class IndexColorModel extends ColorModel
return im;
}
/**
* Creates a {@link SampleModel} that is compatible to this color model.
* This will be a {@link MultiPixelPackedSampleModel} for bits/pixel of
* 1, 2 or 4, or a {@link ComponentColorModel} for the other cases.
*
* @param w the width of the sample model to create
* @param h the height of the sample model to create
*
* @return a compatible sample model
*/
public SampleModel createCompatibleSampleModel(int w, int h)
{
SampleModel sm;
if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4)
sm = new MultiPixelPackedSampleModel(transferType, w, h, pixel_bits);
else
sm = new ComponentSampleModel(transferType, w, h, 1, w, new int[]{0});
return sm;
}
/**
* Sets the transparent pixel. This is called by the various constructors.
*
* @param t the transparent pixel
*/
private void setTransparentPixel(int t)
{
if (t >= 0 && t < map_size)
{
rgb[t] &= 0xffffff; // Make the value transparent.
trans = t;
if (transparency == OPAQUE)
{
transparency = BITMASK;
hasAlpha = true;
}
}
}
}

View file

@ -38,7 +38,6 @@ exception statement from your version. */
package java.awt.image;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@ -67,7 +66,8 @@ public class LookupOp implements BufferedImageOp, RasterOp
private LookupTable lut;
private RenderingHints hints;
/** Construct a new LookupOp.
/**
* Construct a new LookupOp using the given LookupTable.
*
* @param lookup LookupTable to use.
* @param hints Rendering hints (can be null).
@ -78,16 +78,40 @@ public class LookupOp implements BufferedImageOp, RasterOp
this.hints = hints;
}
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, java.awt.image.BufferedImage)
/**
* Converts the source image using the lookup table specified in the
* constructor. The resulting image is stored in the destination image if one
* is provided; otherwise a new BufferedImage is created and returned.
*
* The source image cannot use an IndexColorModel, and the destination image
* (if one is provided) must have the same size.
*
* @param src The source image.
* @param dst The destination image.
* @throws IllegalArgumentException if the rasters and/or color spaces are
* incompatible.
* @throws ArrayIndexOutOfBoundsException if a pixel in the source is not
* contained in the LookupTable.
* @return The convolved image.
*/
public final BufferedImage filter(BufferedImage src, BufferedImage dst)
{
if (src.getColorModel() instanceof IndexColorModel)
throw new IllegalArgumentException("LookupOp.filter: IndexColorModel "
+ "not allowed");
if (lut.getNumComponents() != 1
&& lut.getNumComponents() != src.getColorModel().getNumComponents()
&& lut.getNumComponents() != src.getColorModel().getNumColorComponents())
throw new IllegalArgumentException("LookupOp.filter: Incompatible " +
"lookup table and source image");
if (dst == null)
dst = createCompatibleDestImage(src, src.getColorModel());
dst = createCompatibleDestImage(src, null);
else if (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth())
throw new IllegalArgumentException("Source and destination images are " +
"different sizes.");
// Set up for potential colormodel mismatch
BufferedImage tgt;
@ -116,33 +140,35 @@ public class LookupOp implements BufferedImageOp, RasterOp
sr.getPixel(x, y, dbuf);
System.arraycopy(dbuf, 0, tmp, 0, tmpBands);
dr.setPixel(x, y, lut.lookupPixel(tmp, dbuf));
/* The reference implementation does not use LookupTable.lookupPixel,
* but rather it seems to copy the table into a native array. The
* effect of this (a probable bug in their implementation) is that
* an out-of-bounds lookup on a ByteLookupTable will *not* throw an
* out of bounds exception, but will instead return random garbage.
* A bad lookup on a ShortLookupTable, however, will throw an
* exception.
*
* Instead of mimicing this behaviour, we always throw an
* ArrayOutofBoundsException by virtue of using
* LookupTable.lookupPixle.
*/
}
}
else if (lut.getNumComponents() != 1
&&
lut.getNumComponents() != src.getColorModel().getNumComponents())
throw new IllegalArgumentException("LookupOp.filter: "
+ "Incompatible lookup "
+ "table and source image");
// No alpha to ignore
int[] dbuf = new int[src.getColorModel().getNumComponents()];
// Filter the pixels
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
dr.setPixel(x, y, lut.lookupPixel(sr.getPixel(x, y, dbuf), dbuf));
if (tgt != dst)
else
{
// Convert between color models.
// TODO Check that premultiplied alpha is handled correctly here.
Graphics2D gg = dst.createGraphics();
gg.setRenderingHints(hints);
gg.drawImage(tgt, 0, 0, null);
gg.dispose();
// No alpha to ignore
int[] dbuf = new int[src.getColorModel().getNumComponents()];
// Filter the pixels
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
dr.setPixel(x, y, lut.lookupPixel(sr.getPixel(x, y, dbuf), dbuf));
}
if (tgt != dst)
new ColorConvertOp(hints).filter(tgt, dst);
return dst;
}
@ -160,18 +186,27 @@ public class LookupOp implements BufferedImageOp, RasterOp
public BufferedImage createCompatibleDestImage(BufferedImage src,
ColorModel dstCM)
{
// FIXME: set properties to those in src
return new BufferedImage(dstCM,
src.getRaster().createCompatibleWritableRaster(),
src.isPremultiplied, null);
if (dstCM != null)
return new BufferedImage(dstCM,
src.getRaster().createCompatibleWritableRaster(),
src.isAlphaPremultiplied(), null);
// This is a strange exception, done for compatibility with the reference
// (as demonstrated by a mauve testcase)
int imgType = src.getType();
if (imgType == BufferedImage.TYPE_USHORT_GRAY)
imgType = BufferedImage.TYPE_BYTE_GRAY;
return new BufferedImage(src.getWidth(), src.getHeight(), imgType);
}
/** Return corresponding destination point for source point.
/**
* Returns the corresponding destination point for a given source point.
*
* This Op will return the source point unchanged.
*
* LookupOp will return the value of src unchanged.
* @param src The source point.
* @param dst The destination point.
* @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D)
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
@ -182,7 +217,11 @@ public class LookupOp implements BufferedImageOp, RasterOp
return dst;
}
/** Return the LookupTable for this op. */
/**
* Return the LookupTable for this op.
*
* @return The lookup table.
*/
public final LookupTable getTable()
{
return lut;
@ -196,7 +235,8 @@ public class LookupOp implements BufferedImageOp, RasterOp
return hints;
}
/** Filter a raster through a lookup table.
/**
* Filter a raster through a lookup table.
*
* Applies the lookup table for this Rasterop to each pixel of src and
* puts the results in dest. If dest is null, a new Raster is created and
@ -206,8 +246,9 @@ public class LookupOp implements BufferedImageOp, RasterOp
* @param dest The destination raster.
* @return The WritableRaster with the filtered pixels.
* @throws IllegalArgumentException if lookup table has more than one
* component but not the same as src and dest.
* @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster)
* component but not the same as src and dest.
* @throws ArrayIndexOutOfBoundsException if a pixel in the source is not
* contained in the LookupTable.
*/
public final WritableRaster filter(Raster src, WritableRaster dest)
{
@ -216,12 +257,13 @@ public class LookupOp implements BufferedImageOp, RasterOp
dest = createCompatibleDestRaster(src);
else
if (src.getNumBands() != dest.getNumBands())
throw new IllegalArgumentException();
if (lut.getNumComponents() != 1
&& lut.getNumComponents() != src.getNumBands())
throw new IllegalArgumentException();
throw new IllegalArgumentException("Source and destination rasters " +
"are incompatible.");
if (lut.getNumComponents() != 1
&& lut.getNumComponents() != src.getNumBands())
throw new IllegalArgumentException("Lookup table is incompatible with " +
"this raster.");
// Allocate pixel storage.
int[] tmp = new int[src.getNumBands()];
@ -230,6 +272,19 @@ public class LookupOp implements BufferedImageOp, RasterOp
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
dest.setPixel(x, y, lut.lookupPixel(src.getPixel(x, y, tmp), tmp));
/* The reference implementation does not use LookupTable.lookupPixel,
* but rather it seems to copy the table into a native array. The
* effect of this (a probable bug in their implementation) is that
* an out-of-bounds lookup on a ByteLookupTable will *not* throw an
* out of bounds exception, but will instead return random garbage.
* A bad lookup on a ShortLookupTable, however, will throw an
* exception.
*
* Instead of mimicing this behaviour, we always throw an
* ArrayOutofBoundsException by virtue of using
* LookupTable.lookupPixle.
*/
return dest;
}

View file

@ -1,5 +1,5 @@
/* MemoryImageSource.java -- Java class for providing image data
Copyright (C) 1999, 2004 Free Software Foundation, Inc.
Copyright (C) 1999, 2004, 2006, Free Software Foundation, Inc.
This file is part of GNU Classpath.
@ -41,6 +41,9 @@ package java.awt.image;
import java.util.Hashtable;
import java.util.Vector;
/**
* An image producer that delivers image data from an array.
*/
public class MemoryImageSource implements ImageProducer
{
private boolean animated = false;
@ -73,10 +76,19 @@ public class MemoryImageSource implements ImageProducer
}
/**
* Constructs an ImageProducer from memory
* Constructs an ImageProducer from memory.
*
* @param w the image width.
* @param h the image height.
* @param cm the color model.
* @param pix the image data.
* @param off the offset to the first pixel in the array.
* @param scan the number of array elements from a pixel on one row to the
* corresponding pixel on the next row.
* @param props image properties (<code>null</code> permitted).
*/
public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix, int off,
int scan, Hashtable props)
int scan, Hashtable<?,?> props)
{
width = w;
height = h;
@ -106,10 +118,19 @@ public class MemoryImageSource implements ImageProducer
}
/**
Constructs an ImageProducer from memory
*/
* Constructs an ImageProducer from memory
*
* @param w the image width.
* @param h the image height.
* @param cm the color model.
* @param pix the image data.
* @param off the offset to the first pixel in the array.
* @param scan the number of array elements from a pixel on one row to the
* corresponding pixel on the next row.
* @param props image properties (<code>null</code> permitted).
*/
public MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off,
int scan, Hashtable props)
int scan, Hashtable<?,?> props)
{
width = w;
height = h;
@ -122,16 +143,32 @@ public class MemoryImageSource implements ImageProducer
}
/**
* Constructs an ImageProducer from memory using the default RGB ColorModel
* Constructs an ImageProducer from memory using the default RGB ColorModel.
*
* @param w the image width.
* @param h the image height.
* @param pix the image data.
* @param off the offset to the first pixel in the array.
* @param scan the number of array elements from a pixel on one row to the
* corresponding pixel on the next row.
* @param props image properties (<code>null</code> permitted).
*/
public MemoryImageSource(int w, int h, int[] pix, int off, int scan,
Hashtable props)
Hashtable<?,?> props)
{
this(w, h, ColorModel.getRGBdefault(), pix, off, scan, props);
}
/**
* Constructs an ImageProducer from memory using the default RGB ColorModel
* Constructs an ImageProducer from memory using the default RGB ColorModel.
*
* @param w the image width.
* @param h the image height.
* @param pix the image data.
* @param off the offset to the first pixel in the array.
* @param scan the number of array elements from a pixel on one row to the
* corresponding pixel on the next row.
*/
public MemoryImageSource(int w, int h, int[] pix, int off, int scan)
{
@ -141,6 +178,8 @@ public class MemoryImageSource implements ImageProducer
/**
* Used to register an <code>ImageConsumer</code> with this
* <code>ImageProducer</code>.
*
* @param ic the image consumer.
*/
public synchronized void addConsumer(ImageConsumer ic)
{
@ -153,6 +192,8 @@ public class MemoryImageSource implements ImageProducer
/**
* Used to determine if the given <code>ImageConsumer</code> is
* already registered with this <code>ImageProducer</code>.
*
* @param ic the image consumer.
*/
public synchronized boolean isConsumer(ImageConsumer ic)
{
@ -164,6 +205,8 @@ public class MemoryImageSource implements ImageProducer
/**
* Used to remove an <code>ImageConsumer</code> from the list of
* registered consumers for this <code>ImageProducer</code>.
*
* @param ic the image consumer.
*/
public synchronized void removeConsumer(ImageConsumer ic)
{
@ -197,6 +240,8 @@ public class MemoryImageSource implements ImageProducer
* Used to register an <code>ImageConsumer</code> with this
* <code>ImageProducer</code> and then request that this producer
* resend the image data in the order top-down, left-right.
*
* @param ic the image consumer.
*/
public void requestTopDownLeftRightResend(ImageConsumer ic)
{
@ -219,7 +264,7 @@ public class MemoryImageSource implements ImageProducer
* sending animation. If this flag is set then full buffers are sent
* in the newPixels methods instead of just regions.
*
* @param fullbuffers - a flag indicating whether to send the full buffers
* @param fullbuffers a flag indicating whether to send the full buffers
*/
public synchronized void setFullBufferUpdates(boolean fullbuffers)
{
@ -260,6 +305,11 @@ public class MemoryImageSource implements ImageProducer
/**
* Send an animation frame to the image consumers containing the specified
* pixels unless setFullBufferUpdates is set.
*
* @param x the x-coordinate.
* @param y the y-coordinate.
* @param w the width.
* @param h the height.
*/
public synchronized void newPixels(int x, int y, int w, int h)
{
@ -306,6 +356,12 @@ public class MemoryImageSource implements ImageProducer
*
* If framenotify is set then a notification is sent when the frame
* is sent otherwise no status is sent.
*
* @param x the x-coordinate.
* @param y the y-coordinate.
* @param w the width.
* @param h the height.
* @param framenotify send notification?
*/
public synchronized void newPixels(int x, int y, int w, int h,
boolean framenotify)

View file

@ -55,7 +55,7 @@ public class PixelGrabber implements ImageConsumer
ColorModel model = ColorModel.getRGBdefault();
int hints;
Hashtable props;
Hashtable<?,?> props;
int int_pixel_buffer[];
boolean ints_delivered = false;
@ -442,7 +442,7 @@ public class PixelGrabber implements ImageConsumer
* @param props a list of properties associated with the image being
* produced
*/
public synchronized void setProperties(Hashtable props)
public synchronized void setProperties(Hashtable<?,?> props)
{
this.props = props;
}

View file

@ -46,228 +46,220 @@ package java.awt.image;
*/
public abstract class RGBImageFilter extends ImageFilter
{
protected ColorModel origmodel;
protected ColorModel origmodel;
protected ColorModel newmodel;
protected ColorModel newmodel;
/**
Specifies whether to apply the filter to the index entries of the
IndexColorModel. Subclasses should set this to true if the filter
does not depend on the pixel's coordinate.
*/
protected boolean canFilterIndexColorModel = false;
/**
* Specifies whether to apply the filter to the index entries of the
* IndexColorModel. Subclasses should set this to true if the filter
* does not depend on the pixel's coordinate.
*/
protected boolean canFilterIndexColorModel = false;
/**
Construct new RGBImageFilter.
*/
public RGBImageFilter()
{
}
/**
* Construct new RGBImageFilter.
*/
public RGBImageFilter()
{
}
/**
* Sets the ColorModel used to filter with. If the specified ColorModel is IndexColorModel
* and canFilterIndexColorModel is true, we subsitute the ColorModel for a filtered one
* here and in setPixels whenever the original one appears. Otherwise overrides the default
* ColorModel of ImageProducer and specifies the default RGBColorModel
*
* @param model the color model to be used most often by setPixels
* @see ColorModel */
public void setColorModel(ColorModel model)
{
origmodel = model;
newmodel = model;
/**
* Sets the ColorModel used to filter with. If the specified ColorModel is
* IndexColorModel and canFilterIndexColorModel is true, we subsitute the
* ColorModel for a filtered one here and in setPixels whenever the original
* one appears. Otherwise overrides the default ColorModel of ImageProducer
* and specifies the default RGBColorModel
*
* @param model the color model to be used most often by setPixels
*
* @see ColorModel
*/
public void setColorModel(ColorModel model)
{
if ((model instanceof IndexColorModel) && canFilterIndexColorModel)
{
ColorModel newCM = filterIndexColorModel((IndexColorModel) model);
substituteColorModel(model, newCM);
consumer.setColorModel(newmodel);
}
else
{
consumer.setColorModel(ColorModel.getRGBdefault());
}
}
if( ( model instanceof IndexColorModel) && canFilterIndexColorModel ) {
newmodel = filterIndexColorModel( (IndexColorModel) model );
if (consumer != null)
consumer.setColorModel(newmodel);
}
else {
if (consumer != null)
consumer.setColorModel(ColorModel.getRGBdefault());
}
}
/**
Registers a new ColorModel to subsitute for the old ColorModel when
setPixels encounters the a pixel with the old ColorModel. The pixel
remains unchanged except for a new ColorModel.
@param oldcm the old ColorModel
@param newcm the new ColorModel
*/
public void substituteColorModel(ColorModel oldcm,
ColorModel newcm)
{
origmodel = oldcm;
newmodel = newcm;
}
/**
* Registers a new ColorModel to subsitute for the old ColorModel when
* setPixels encounters the a pixel with the old ColorModel. The pixel
* remains unchanged except for a new ColorModel.
*
* @param oldcm the old ColorModel
* @param newcm the new ColorModel
*/
public void substituteColorModel(ColorModel oldcm, ColorModel newcm)
{
origmodel = oldcm;
newmodel = newcm;
}
/**
Filters an IndexColorModel through the filterRGB function. Uses
coordinates of -1 to indicate its filtering an index and not a pixel.
/**
* Filters an IndexColorModel through the filterRGB function. Uses
* coordinates of -1 to indicate its filtering an index and not a pixel.
*
* @param icm an IndexColorModel to filter
*/
public IndexColorModel filterIndexColorModel(IndexColorModel icm)
{
int len = icm.getMapSize();
byte[] reds = new byte[len];
byte[] greens = new byte[len];
byte[] blues = new byte[len];
byte[] alphas = new byte[len];
@param icm an IndexColorModel to filter
*/
public IndexColorModel filterIndexColorModel(IndexColorModel icm)
{
int len = icm.getMapSize(), rgb;
byte reds[] = new byte[len], greens[] = new byte[len], blues[] = new byte[len], alphas[] = new byte[len];
icm.getAlphas( alphas );
icm.getReds( reds );
icm.getGreens( greens );
icm.getBlues( blues );
icm.getAlphas( alphas );
icm.getReds( reds );
icm.getGreens( greens );
icm.getBlues( blues );
for( int i = 0; i < len; i++ )
{
rgb = filterRGB( -1, -1, makeColor ( alphas[i], reds[i], greens[i], blues[i] ) );
alphas[i] = (byte)(( 0xff000000 & rgb ) >> 24);
reds[i] = (byte)(( 0xff0000 & rgb ) >> 16);
greens[i] = (byte)(( 0xff00 & rgb ) >> 8);
blues[i] = (byte)(0xff & rgb);
}
return new IndexColorModel( icm.getPixelSize(), len, reds, greens, blues, alphas );
}
int transparent = icm.getTransparentPixel();
boolean needAlpha = false;
for( int i = 0; i < len; i++ )
{
int rgb = filterRGB(-1, -1, icm.getRGB(i));
alphas[i] = (byte) (rgb >> 24);
if (alphas[i] != ((byte) 0xff) && i != transparent)
needAlpha = true;
reds[i] = (byte) (rgb >> 16);
greens[i] = (byte) (rgb >> 8);
blues[i] = (byte) (rgb);
}
IndexColorModel newIcm;
if (needAlpha)
newIcm = new IndexColorModel(icm.getPixelSize(), len, reds, greens,
blues, alphas);
else
newIcm = new IndexColorModel(icm.getPixelSize(), len, reds, greens,
blues, transparent);
return newIcm;
}
private int makeColor( byte a, byte r, byte g, byte b )
{
return ( 0xff000000 & (a << 24) | 0xff0000 & (r << 16) | 0xff00 & (g << 8) | 0xff & b );
}
/**
* This functions filters a set of RGB pixels through filterRGB.
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the
* <code>pixels</code> array
* @param scansize the width to use in extracting pixels from the
* <code>pixels</code> array
*/
public void filterRGBPixels(int x, int y, int w, int h, int[] pixels,
int offset, int scansize)
{
int index = offset;
for (int yp = 0; yp < h; yp++)
{
for (int xp = 0; xp < w; xp++)
{
pixels[index] = filterRGB(xp + x, yp + y, pixels[index]);
index++;
}
index += scansize - w;
}
consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), pixels, offset,
scansize);
}
/**
This functions filters a set of RGB pixels through filterRGB.
/**
* If the ColorModel is the same ColorModel which as already converted
* then it converts it the converted ColorModel. Otherwise it passes the
* array of pixels through filterRGBpixels.
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code>
* array
* @param scansize the width to use in extracting pixels from the
* <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h, ColorModel model,
byte[] pixels, int offset, int scansize)
{
if (model == origmodel)
{
consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
}
else
{
int[] filtered = new int[w];
int index = offset;
for (int yp = 0; yp < h; yp++)
{
for (int xp = 0; xp < w; xp++)
{
filtered[xp] = model.getRGB((pixels[index] & 0xff));
index++;
}
index += scansize - w;
filterRGBPixels(x, y + yp, w, 1, filtered, 0, w);
}
}
}
@param x the x coordinate of the rectangle
@param y the y coordinate of the rectangle
@param w the width of the rectangle
@param h the height of the rectangle
@param pixels the array of pixel values
@param offset the index of the first pixels in the <code>pixels</code> array
@param scansize the width to use in extracting pixels from the <code>pixels</code> array
*/
public void filterRGBPixels(int x, int y, int w, int h, int[] pixels,
int offset, int scansize)
{
for (int yp = 0; yp < h; yp++)
{
for (int xp = 0; xp < w; xp++)
{
pixels[offset + xp] = filterRGB(xp + x, yp + y, pixels[offset + xp]);
}
offset += scansize;
}
}
/**
* This function delivers a rectangle of pixels where any
* pixel(m,n) is stored in the array as an <code>int</code> at
* index (n * scansize + m + offset).
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code>
* array
* @param scansize the width to use in extracting pixels from the
* <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h, ColorModel model,
int[] pixels, int offset, int scansize)
{
if (model == origmodel)
{
consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
}
else
{
int[] filtered = new int[w];
int index = offset;
for (int yp = 0; yp < h; yp++)
{
for (int xp = 0; xp < w; xp++)
{
filtered[xp] = model.getRGB((pixels[index] & 0xff));
index++;
}
index += scansize - w;
filterRGBPixels(x, y + yp, w, 1, filtered, 0, w);
}
}
}
/**
* If the ColorModel is the same ColorModel which as already converted
* then it converts it the converted ColorModel. Otherwise it passes the
* array of pixels through filterRGBpixels.
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code> array
* @param scansize the width to use in extracting pixels from the <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte[] pixels,
int offset, int scansize)
{
if(model == origmodel && (model instanceof IndexColorModel) && canFilterIndexColorModel)
{
if (consumer != null)
consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
}
else
{
int intPixels[] =
convertColorModelToDefault( x, y, w, h, model, pixels, offset, scansize );
filterRGBPixels( x, y, w, h, intPixels, offset, scansize );
if (consumer != null)
consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), intPixels, offset, scansize);
}
}
/**
* This function delivers a rectangle of pixels where any
* pixel(m,n) is stored in the array as an <code>int</code> at
* index (n * scansize + m + offset).
*
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @param model the <code>ColorModel</code> used to translate the pixels
* @param pixels the array of pixel values
* @param offset the index of the first pixels in the <code>pixels</code> array
* @param scansize the width to use in extracting pixels from the <code>pixels</code> array
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, int[] pixels,
int offset, int scansize)
{
if(model == origmodel && (model instanceof IndexColorModel) && canFilterIndexColorModel)
{
if (consumer != null)
consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
}
else
{
//FIXME: Store the filtered pixels in a separate temporary buffer?
convertColorModelToDefault( x, y, w, h, model, pixels, offset, scansize );
filterRGBPixels( x, y, w, h, pixels, offset, scansize );
if (consumer != null)
consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), pixels, offset, scansize);
}
}
private int[] convertColorModelToDefault(int x, int y, int w, int h,
ColorModel model, byte pixels[],
int offset, int scansize)
{
int intPixels[] = new int[pixels.length];
for (int i = 0; i < pixels.length; i++)
intPixels[i] = makeColorbyDefaultCM(model, pixels[i]);
return intPixels;
}
private void convertColorModelToDefault(int x, int y, int w, int h,
ColorModel model, int pixels[],
int offset, int scansize)
{
for (int i = 0; i < pixels.length; i++)
pixels[i] = makeColorbyDefaultCM(model, pixels[i]);
}
private int makeColorbyDefaultCM(ColorModel model, byte rgb)
{
return makeColor( model.getAlpha( rgb ) * 4, model.getRed( rgb ) * 4, model.getGreen( rgb ) * 4, model.getBlue( rgb ) * 4 );
}
private int makeColorbyDefaultCM(ColorModel model, int rgb)
{
return makeColor( model.getAlpha( rgb ), model.getRed( rgb ), model.getGreen( rgb ), model.getBlue( rgb ) );
}
private int makeColor( int a, int r, int g, int b )
{
return (int)( 0xff000000 & (a << 24) | 0xff0000 & (r << 16) | 0xff00 & (g << 8) | 0xff & b );
}
/**
Filters a single pixel from the default ColorModel.
@param x x-coordinate
@param y y-coordinate
@param rgb color
*/
public abstract int filterRGB(int x,
int y,
int rgb);
/**
* Filters a single pixel from the default ColorModel.
*
* @param x x-coordinate
* @param y y-coordinate
* @param rgb color
*/
public abstract int filterRGB(int x, int y, int rgb);
}

View file

@ -511,9 +511,10 @@ public class Raster
int height, int childMinX, int childMinY,
int[] bandList)
{
/* FIXME: Throw RasterFormatException if child bounds extends
beyond the bounds of this raster. */
if (parentX < minX || parentX + width > minX + this.width
|| parentY < minY || parentY + height > minY + this.height)
throw new RasterFormatException("Child raster extends beyond parent");
SampleModel sm = (bandList == null) ?
sampleModel :
sampleModel.createSubsetSampleModel(bandList);

View file

@ -46,7 +46,7 @@ import java.util.Vector;
*/
public interface RenderedImage
{
Vector getSources();
Vector<RenderedImage> getSources();
Object getProperty(String name);
String[] getPropertyNames();
ColorModel getColorModel();

View file

@ -46,6 +46,7 @@ import java.util.Hashtable;
* exact method is not defined by Sun but some sort of fast Box filter should
* probably be correct.
* <br>
* Currently this filter does nothing and needs to be implemented.
*
* @author C. Brian Jones (cbj@gnu.org)
*/
@ -116,11 +117,11 @@ public class ReplicateScaleFilter extends ImageFilter
}
else if (destWidth < 0)
{
destWidth = (int) (width * ((double) destHeight / srcHeight));
destWidth = width * destHeight / srcHeight;
}
else if (destHeight < 0)
{
destHeight = (int) (height * ((double) destWidth / srcWidth));
destHeight = height * destWidth / srcWidth;
}
if (consumer != null)
@ -133,11 +134,12 @@ public class ReplicateScaleFilter extends ImageFilter
*
* @param props the list of properties associated with this image
*/
public void setProperties(Hashtable props)
public void setProperties(Hashtable<?, ?> props)
{
props.put("filters", "ReplicateScaleFilter");
if (consumer != null)
consumer.setProperties(props);
Hashtable<Object, Object> prop2 = (Hashtable<Object, Object>) props;
prop2.put("filters", "ReplicateScaleFilter");
if (consumer != null)
consumer.setProperties(prop2);
}
/**
@ -157,19 +159,35 @@ public class ReplicateScaleFilter extends ImageFilter
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte[] pixels, int offset, int scansize)
{
double rx = ((double) srcWidth) / destWidth;
double ry = ((double) srcHeight) / destHeight;
int destScansize = (int) Math.round(scansize / rx);
byte[] destPixels = replicatePixels(x, y, w, h,
model, pixels, offset, scansize,
rx, ry, destScansize);
if (consumer != null)
consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry),
(int) Math.ceil(w/rx), (int) Math.ceil(h/ry),
model, destPixels, 0, destScansize);
if (srcrows == null || srccols == null)
setupSources();
int dx1 = (2 * x * destWidth + srcWidth - 1) / (2 * destWidth);
int dy1 = (2 * y * destHeight + srcHeight - 1) / (2 * destHeight);
byte[] pix;
if (outpixbuf != null && outpixbuf instanceof byte[])
{
pix = (byte[]) outpixbuf;
}
else
{
pix = new byte[destWidth];
outpixbuf = pix;
}
int sy, sx;
for (int yy = dy1; (sy = srcrows[yy]) < y + h; yy++)
{
int offs = offset + scansize * (sy - y);
int xx;
for (xx = dx1; (sx = srccols[xx]) < x + w; xx++)
{
pix[xx] = pixels[offs + sx - x];
}
if (xx > dx1)
{
consumer.setPixels(dx1, yy, xx - dx1, 1, model, pix, dx1,
destWidth);
}
}
}
/**
@ -189,59 +207,52 @@ public class ReplicateScaleFilter extends ImageFilter
public void setPixels(int x, int y, int w, int h,
ColorModel model, int[] pixels, int offset, int scansize)
{
double rx = ((double) srcWidth) / destWidth;
double ry = ((double) srcHeight) / destHeight;
int destScansize = (int) Math.round(scansize / rx);
int[] destPixels = replicatePixels(x, y, w, h,
model, pixels, offset, scansize,
rx, ry, destScansize);
if (consumer != null)
consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry),
(int) Math.ceil(w/rx), (int) Math.ceil(h/ry),
model, destPixels, 0, destScansize);
if (srcrows == null || srccols == null)
setupSources();
int dx1 = (2 * x * destWidth + srcWidth - 1) / (2 * destWidth);
int dy1 = (2 * y * destHeight + srcHeight - 1) / (2 * destHeight);
int[] pix;
if (outpixbuf != null && outpixbuf instanceof int[])
{
pix = (int[]) outpixbuf;
}
else
{
pix = new int[destWidth];
outpixbuf = pix;
}
int sy, sx;
for (int yy = dy1; (sy = srcrows[yy]) < y + h; yy++)
{
int offs = offset + scansize * (sy - y);
int xx;
for (xx = dx1; (sx = srccols[xx]) < x + w; xx++)
{
pix[xx] = pixels[offs + sx - x];
}
if (xx > dx1)
{
consumer.setPixels(dx1, yy, xx - dx1, 1, model, pix, dx1,
destWidth);
}
}
}
private byte[] replicatePixels(int srcx, int srcy, int srcw, int srch,
ColorModel model, byte[] srcPixels,
int srcOffset, int srcScansize,
double rx, double ry, int destScansize)
{
byte[] destPixels =
new byte[(int) Math.ceil(srcw/rx) * (int) Math.ceil(srch/ry)];
int a, b;
for (int i = 0; i < destPixels.length; i++)
{
a = (int) ((int) ( ((double) i) / destScansize) * ry) * srcScansize;
b = (int) ((i % destScansize) * rx);
if ((a + b + srcOffset) < srcPixels.length)
destPixels[i] = srcPixels[a + b + srcOffset];
}
return destPixels;
}
private int[] replicatePixels(int srcx, int srcy, int srcw, int srch,
ColorModel model, int[] srcPixels,
int srcOffset, int srcScansize,
double rx, double ry, int destScansize)
{
int[] destPixels =
new int[(int) Math.ceil(srcw/rx) * (int) Math.ceil(srch/ry)];
int a, b;
for (int i = 0; i < destPixels.length; i++)
{
a = (int) ((int) ( ((double) i) / destScansize) * ry) * srcScansize;
b = (int) ((i % destScansize) * rx);
if ((a + b + srcOffset) < srcPixels.length)
destPixels[i] = srcPixels[a + b + srcOffset];
}
return destPixels;
}
/**
* Sets up the srcrows and srccols arrays.
*/
private void setupSources()
{
srcrows = new int[destHeight + 1];
for (int y = 0; y <= destHeight; y++)
{
srcrows[y] = (2 * y * srcHeight + srcHeight) / (2 * destHeight);
}
srccols = new int[destWidth + 1];
for (int x = 0; x <= destWidth; x++)
{
srccols[x] = (2 * x * srcWidth + srcWidth) / (2 * destWidth);
}
}
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2004 Free Software Foundation
/* Copyright (C) 2004, 2006 Free Software Foundation
This file is part of GNU Classpath.
@ -43,7 +43,23 @@ import java.awt.geom.Rectangle2D;
import java.util.Arrays;
/**
* RescaleOp is a filter that changes each pixel by a scaling factor and offset.
*
* For filtering Rasters, either one scaling factor and offset can be specified,
* which will be applied to all bands; or a scaling factor and offset can be
* specified for each band.
*
* For BufferedImages, the scaling may apply to both color and alpha components.
* If only one scaling factor is provided, or if the number of factors provided
* equals the number of color components, the scaling is performed on all color
* components. Otherwise, the scaling is performed on all components including
* alpha. Alpha premultiplication is ignored.
*
* After filtering, if color conversion is necessary, the conversion happens,
* taking alpha premultiplication into account.
*
* @author Jerry Quinn (jlquinn@optonline.net)
* @author Francis Kung (fkung@redhat.com)
*/
public class RescaleOp implements BufferedImageOp, RasterOp
{
@ -51,15 +67,43 @@ public class RescaleOp implements BufferedImageOp, RasterOp
private float[] offsets;
private RenderingHints hints = null;
/**
* Create a new RescaleOp object using the given scale factors and offsets.
*
* The length of the arrays must be equal to the number of bands (or number of
* data or color components) of the raster/image that this Op will be used on,
* otherwise an IllegalArgumentException will be thrown when calling the
* filter method.
*
* @param scaleFactors an array of scale factors.
* @param offsets an array of offsets.
* @param hints any rendering hints to use (can be null).
* @throws NullPointerException if the scaleFactors or offsets array is null.
*/
public RescaleOp(float[] scaleFactors,
float[] offsets,
RenderingHints hints)
{
this.scale = scaleFactors;
this.offsets = offsets;
int length = Math.min(scaleFactors.length, offsets.length);
scale = new float[length];
System.arraycopy(scaleFactors, 0, this.scale, 0, length);
this.offsets = new float[length];
System.arraycopy(offsets, 0, this.offsets, 0, length);
this.hints = hints;
}
/**
* Create a new RescaleOp object using the given scale factor and offset.
*
* The same scale factor and offset will be used on all bands/components.
*
* @param scaleFactor the scale factor to use.
* @param offset the offset to use.
* @param hints any rendering hints to use (can be null).
*/
public RescaleOp(float scaleFactor,
float offset,
RenderingHints hints)
@ -69,22 +113,47 @@ public class RescaleOp implements BufferedImageOp, RasterOp
this.hints = hints;
}
/**
* Returns the scaling factors. This method accepts an optional array, which
* will be used to store the factors if not null (this avoids allocating a
* new array). If this array is too small to hold all the scaling factors,
* the array will be filled and the remaining factors discarded.
*
* @param scaleFactors array to store the scaling factors in (can be null).
* @return an array of scaling factors.
*/
public final float[] getScaleFactors(float[] scaleFactors)
{
if (scaleFactors == null)
scaleFactors = new float[scale.length];
System.arraycopy(scale, 0, scaleFactors, 0, scale.length);
System.arraycopy(scale, 0, scaleFactors, 0, Math.min(scale.length,
scaleFactors.length));
return scaleFactors;
}
/**
* Returns the offsets. This method accepts an optional array, which
* will be used to store the offsets if not null (this avoids allocating a
* new array). If this array is too small to hold all the offsets, the array
* will be filled and the remaining factors discarded.
*
* @param offsets array to store the offsets in (can be null).
* @return an array of offsets.
*/
public final float[] getOffsets(float[] offsets)
{
if (offsets == null)
offsets = new float[this.offsets.length];
System.arraycopy(this.offsets, 0, offsets, 0, this.offsets.length);
System.arraycopy(this.offsets, 0, offsets, 0, Math.min(this.offsets.length,
offsets.length));
return offsets;
}
/**
* Returns the number of scaling factors / offsets.
*
* @return the number of scaling factors / offsets.
*/
public final int getNumFactors()
{
return scale.length;
@ -98,36 +167,74 @@ public class RescaleOp implements BufferedImageOp, RasterOp
return hints;
}
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, java.awt.image.BufferedImage)
/**
* Converts the source image using the scale factors and offsets specified in
* the constructor. The resulting image is stored in the destination image if
* one is provided; otherwise a new BufferedImage is created and returned.
*
* The source image cannot use an IndexColorModel, and the destination image
* (if one is provided) must have the same size.
*
* If the final value of a sample is beyond the range of the color model, it
* will be clipped to the appropriate maximum / minimum.
*
* @param src The source image.
* @param dst The destination image.
* @throws IllegalArgumentException if the rasters and/or color spaces are
* incompatible.
* @return The rescaled image.
*/
public final BufferedImage filter(BufferedImage src, BufferedImage dst)
{
// TODO Make sure premultiplied alpha is handled correctly.
// TODO See that color conversion is handled.
// TODO figure out how to use rendering hints.
if (scale.length != offsets.length)
throw new IllegalArgumentException();
// Initial checks
if (scale.length != 1
&& scale.length != src.getColorModel().getNumComponents()
&& (scale.length != src.getColorModel().getNumColorComponents()))
throw new IllegalArgumentException("Source image has wrong number of "
+ "bands for these scaling factors.");
ColorModel scm = src.getColorModel();
if (dst == null) dst = createCompatibleDestImage(src, null);
if (dst == null)
dst = createCompatibleDestImage(src, null);
else if (src.getHeight() != dst.getHeight()
|| src.getWidth() != dst.getWidth())
throw new IllegalArgumentException("Source and destination images are "
+ "different sizes.");
WritableRaster wsrc = src.getRaster();
WritableRaster wdst = dst.getRaster();
// Share constant across colors except alpha
if (scale.length == 1 || scale.length == scm.getNumColorComponents())
// Prepare for possible colorspace conversion
BufferedImage dst2 = dst;
if (dst.getColorModel().getColorSpace().getType() != src.getColorModel().getColorSpace().getType())
dst2 = createCompatibleDestImage(src, src.getColorModel());
// Figure out how many bands to scale
int numBands = scale.length;
if (scale.length == 1)
numBands = src.getColorModel().getNumColorComponents();
boolean[] bands = new boolean[numBands];
// this assumes the alpha, if present, is the last band
Arrays.fill(bands, true);
// Perform rescaling
filter(src.getRaster(), dst2.getRaster(), bands);
// Copy alpha band if needed (ie if it exists and wasn't scaled)
// NOTE: This assumes the alpha component is the last band!
if (src.getColorModel().hasAlpha()
&& numBands == src.getColorModel().getNumColorComponents())
{
// Construct a raster that doesn't include an alpha band.
int[] subbands = new int[scm.getNumColorComponents()];
for (int i=0; i < subbands.length; i++) subbands[i] = i;
wsrc =
wsrc.createWritableChild(wsrc.minX, wsrc.minY, wsrc.width, wsrc.height,
wsrc.minX, wsrc.minY, subbands);
}
// else all color bands
filter(wsrc, wdst);
dst2.getRaster().setSamples(0, 0, src.getWidth(), src.getHeight(),
numBands,
src.getRaster().getSamples(0, 0,
src.getWidth(),
src.getHeight(),
numBands,
(int[]) null));
}
// Perform colorspace conversion if needed
if (dst != dst2)
new ColorConvertOp(hints).filter(dst2, dst);
return dst;
}
@ -136,50 +243,106 @@ public class RescaleOp implements BufferedImageOp, RasterOp
*/
public final WritableRaster filter(Raster src, WritableRaster dest)
{
if (dest == null) dest = src.createCompatibleWritableRaster();
// Required sanity checks
if (src.numBands != dest.numBands || scale.length != offsets.length)
throw new IllegalArgumentException();
if (scale.length != 1 && scale.length != src.numBands)
throw new IllegalArgumentException();
throw new IllegalArgumentException("Number of rasters is incompatible "
+ "with the number of scaling "
+ "factors provided.");
// Create scaling arrays if needed
float[] lscale = scale;
float[] loff = offsets;
if (scale.length == 1)
{
lscale = new float[src.numBands];
Arrays.fill(lscale, scale[0]);
loff = new float[src.numBands];
Arrays.fill(loff, offsets[0]);
}
if (dest == null)
dest = src.createCompatibleWritableRaster();
else if (src.getHeight() != dest.getHeight()
|| src.getWidth() != dest.getWidth())
throw new IllegalArgumentException("Source and destination rasters are "
+ "different sizes.");
else if (src.numBands != dest.numBands)
throw new IllegalArgumentException("Source and destination rasters "
+ "are incompatible.");
// TODO The efficiency here can be improved for various data storage
// patterns, aka SampleModels.
float[] pixel = new float[src.numBands];
for (int y = src.minY; y < src.height + src.minY; y++)
for (int x = src.minX; x < src.width + src.minX; x++)
{
src.getPixel(x, y, pixel);
for (int b = 0; b < src.numBands; b++)
pixel[b] = pixel[b] * lscale[b] + loff[b];
dest.setPixel(x, y, pixel);
}
// Filter all bands
boolean[] bands = new boolean[src.getNumBands()];
Arrays.fill(bands, true);
return filter(src, dest, bands);
}
/**
* Perform raster-based filtering on a selected number of bands.
*
* The length of the bands array should equal the number of bands; a true
* element indicates filtering should happen on the corresponding band, while
* a false element will skip the band.
*
* The rasters are assumed to be compatible and non-null.
*
* @param src the source raster.
* @param dest the destination raster.
* @param bands an array indicating which bands to filter.
* @throws NullPointerException if any parameter is null.
* @throws ArrayIndexOutOfBoundsException if the bands array is too small.
* @return the destination raster.
*/
private WritableRaster filter(Raster src, WritableRaster dest, boolean[] bands)
{
int[] values = new int[src.getHeight() * src.getWidth()];
float scaleFactor, offset;
// Find max sample value, to be used for clipping later
int[] maxValue = src.getSampleModel().getSampleSize();
for (int i = 0; i < maxValue.length; i++)
maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1;
// TODO: can this be optimized further?
// Filter all samples of all requested bands
for (int band = 0; band < bands.length; band++)
if (bands[band])
{
values = src.getSamples(src.getMinX(), src.getMinY(), src.getWidth(),
src.getHeight(), band, values);
if (scale.length == 1)
{
scaleFactor = scale[0];
offset = offsets[0];
}
else
{
scaleFactor = scale[band];
offset = offsets[band];
}
for (int i = 0; i < values.length; i++)
{
values[i] = (int) (values[i] * scaleFactor + offset);
// Clip if needed
if (values[i] < 0)
values[i] = 0;
if (values[i] > maxValue[band])
values[i] = maxValue[band];
}
dest.setSamples(dest.getMinX(), dest.getMinY(), dest.getWidth(),
dest.getHeight(), band, values);
}
return dest;
}
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel)
/*
* (non-Javadoc)
*
* @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage,
* java.awt.image.ColorModel)
*/
public BufferedImage createCompatibleDestImage(BufferedImage src,
ColorModel dstCM)
{
if (dstCM == null) dstCM = src.getColorModel();
WritableRaster wr = src.getRaster().createCompatibleWritableRaster();
BufferedImage image
= new BufferedImage(dstCM, wr, src.isPremultiplied, null);
return image;
if (dstCM == null)
return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
return new BufferedImage(dstCM,
src.getRaster().createCompatibleWritableRaster(),
src.isAlphaPremultiplied(), null);
}
/* (non-Javadoc)
@ -209,9 +372,13 @@ public class RescaleOp implements BufferedImageOp, RasterOp
/* (non-Javadoc)
* @see java.awt.image.BufferedImageOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D)
*/
public final Point2D getPoint2D(Point2D src, Point2D dst) {
if (dst == null) dst = (Point2D) src.clone();
else dst.setLocation(src);
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
if (dst == null)
dst = (Point2D) src.clone();
else
dst.setLocation(src);
return dst;
}

View file

@ -246,9 +246,7 @@ public abstract class SampleModel
public void setDataElements(int x, int y, int w, int h,
Object obj, DataBuffer data)
{
int size = w * h;
int numDataElements = getNumDataElements();
int dataSize = numDataElements * size;
Object pixelData;
switch (getTransferType())
@ -257,25 +255,34 @@ public abstract class SampleModel
pixelData = new byte[numDataElements];
break;
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
pixelData = new short[numDataElements];
break;
case DataBuffer.TYPE_INT:
pixelData = new int[numDataElements];
break;
case DataBuffer.TYPE_FLOAT:
pixelData = new float[numDataElements];
break;
case DataBuffer.TYPE_DOUBLE:
pixelData = new double[numDataElements];
break;
default:
// Seems like the only sensible thing to do.
throw new ClassCastException();
// The RI silently igores invalid types.
pixelData = null;
}
int inOffset = 0;
for (int yy = y; yy < (y + h); yy++)
int inOffset = 0;
if (pixelData != null)
{
for (int xx = x; xx < (x + w); xx++)
for (int yy=y; yy<(y+h); yy++)
{
System.arraycopy(obj, inOffset, pixelData, 0,
numDataElements);
setDataElements(xx, yy, pixelData, data);
inOffset += numDataElements;
for (int xx=x; xx<(x+w); xx++)
{
System.arraycopy(obj, inOffset, pixelData, 0, numDataElements);
setDataElements(xx, yy, pixelData, data);
inOffset += numDataElements;
}
}
}
}

View file

@ -412,110 +412,31 @@ public class SinglePixelPackedSampleModel extends SampleModel
return (samples & bitMasks[b]) >>> bitOffsets[b];
}
/**
* This method implements a more efficient way to set data elements than the
* default implementation of the super class. It sets the data elements line
* by line instead of pixel by pixel.
*
* @param x The x-coordinate of the data elements in <code>obj</code>.
* @param y The y-coordinate of the data elements in <code>obj</code>.
* @param w The width of the data elements in <code>obj</code>.
* @param h The height of the data elements in <code>obj</code>.
* @param obj The primitive array containing the data elements to set.
* @param data The DataBuffer to store the data elements into.
* @see java.awt.image.SampleModel#setDataElements(int, int, int, int,
* java.lang.Object, java.awt.image.DataBuffer)
*/
public void setDataElements(int x, int y, int w, int h,
Object obj, DataBuffer data)
{
Object pixelData;
switch (getTransferType())
{
case DataBuffer.TYPE_BYTE:
pixelData = ((DataBufferByte) data).getData();
break;
case DataBuffer.TYPE_USHORT:
pixelData = ((DataBufferUShort) data).getData();
break;
case DataBuffer.TYPE_INT:
pixelData = ((DataBufferInt) data).getData();
break;
default:
// Seems like the only sensible thing to do.
throw new ClassCastException();
}
int inOffset = 0;
int dataOffset = scanlineStride*y + x + data.getOffset();
for (int yy=y; yy<(y+h); yy++)
{
System.arraycopy(obj,inOffset,pixelData,dataOffset,w);
dataOffset += scanlineStride;
inOffset += w;
}
}
public void setDataElements(int x, int y, Object obj, DataBuffer data)
{
int offset = scanlineStride*y + x + data.getOffset();
int transferType = getTransferType();
if (getTransferType() != data.getDataType())
{
throw new IllegalArgumentException("transfer type ("+
getTransferType()+"), "+
"does not match data "+
"buffer type (" +
data.getDataType() +
").");
}
try
{
switch (transferType)
{
case DataBuffer.TYPE_BYTE:
{
DataBufferByte out = (DataBufferByte) data;
byte[] in = (byte[]) obj;
out.getData()[offset] = in[0];
return;
}
case DataBuffer.TYPE_USHORT:
{
DataBufferUShort out = (DataBufferUShort) data;
short[] in = (short[]) obj;
out.getData()[offset] = in[0];
return;
}
case DataBuffer.TYPE_INT:
{
DataBufferInt out = (DataBufferInt) data;
int[] in = (int[]) obj;
out.getData()[offset] = in[0];
return;
}
// FIXME: Fill in the other possible types.
default:
throw new InternalError();
}
}
catch (ArrayIndexOutOfBoundsException aioobe)
{
String msg = "While writing data elements" +
", x="+x+", y="+y+
", width="+width+", height="+height+
", scanlineStride="+scanlineStride+
", offset="+offset+
", data.getSize()="+data.getSize()+
", data.getOffset()="+data.getOffset()+
": " +
aioobe;
throw new ArrayIndexOutOfBoundsException(msg);
}
switch (transferType)
{
case DataBuffer.TYPE_BYTE:
{
byte[] in = (byte[]) obj;
data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xff);
break;
}
case DataBuffer.TYPE_USHORT:
{
short[] in = (short[]) obj;
data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xffff);
break;
}
case DataBuffer.TYPE_INT:
{
int[] in = (int[]) obj;
data.setElem(y * scanlineStride + x, in[0]);
break;
}
}
}
/**

View file

@ -136,8 +136,9 @@ public class WritableRaster extends Raster
{
// This mirrors the code from the super class
// FIXME: Throw RasterFormatException if child bounds extends
// beyond the bounds of this raster.
if (parentX < minX || parentX + w > minX + width
|| parentY < minY || parentY + h > minY + height)
throw new RasterFormatException("Child raster extends beyond parent");
SampleModel sm = (bandList == null) ?
sampleModel :
@ -149,6 +150,25 @@ public class WritableRaster extends Raster
sampleModelTranslateY + childMinY - parentY),
this);
}
public Raster createChild(int parentX, int parentY, int width,
int height, int childMinX, int childMinY,
int[] bandList)
{
if (parentX < minX || parentX + width > minX + this.width
|| parentY < minY || parentY + height > minY + this.height)
throw new RasterFormatException("Child raster extends beyond parent");
SampleModel sm = (bandList == null) ?
sampleModel :
sampleModel.createSubsetSampleModel(bandList);
return new WritableRaster(sm, dataBuffer,
new Rectangle(childMinX, childMinY, width, height),
new Point(sampleModelTranslateX + childMinX - parentX,
sampleModelTranslateY + childMinY - parentY),
this);
}
public void setDataElements(int x, int y, Object inData)
{

View file

@ -45,20 +45,20 @@ import java.util.Vector;
public class ParameterBlock implements Cloneable, Serializable
{
private static final long serialVersionUID = -7577115551785240750L;
protected Vector sources;
protected Vector parameters;
protected Vector<Object> sources;
protected Vector<Object> parameters;
public ParameterBlock()
{
this(new Vector(), new Vector());
this(new Vector<Object>(), new Vector<Object>());
}
public ParameterBlock(Vector sources)
public ParameterBlock(Vector<Object> sources)
{
this(sources, new Vector());
this(sources, new Vector<Object>());
}
public ParameterBlock(Vector sources, Vector parameters)
public ParameterBlock(Vector<Object> sources, Vector<Object> parameters)
{
this.sources = sources;
this.parameters = parameters;
@ -80,9 +80,9 @@ public class ParameterBlock implements Cloneable, Serializable
{
ParameterBlock pb = (ParameterBlock) shallowClone();
if (sources != null)
pb.sources = (Vector) sources.clone();
pb.sources = (Vector<Object>) sources.clone();
if (parameters != null)
pb.parameters = (Vector) parameters.clone();
pb.parameters = (Vector<Object>) parameters.clone();
return pb;
}
@ -119,12 +119,12 @@ public class ParameterBlock implements Cloneable, Serializable
return sources.size();
}
public Vector getSources()
public Vector<Object> getSources()
{
return sources;
}
public void setSources(Vector sources)
public void setSources(Vector<Object> sources)
{
this.sources = sources;
}
@ -140,12 +140,12 @@ public class ParameterBlock implements Cloneable, Serializable
return parameters.size();
}
public Vector getParameters()
public Vector<Object> getParameters()
{
return parameters;
}
public void setParameters(Vector parameters)
public void setParameters(Vector<Object> parameters)
{
this.parameters = parameters;
}

View file

@ -46,7 +46,7 @@ public interface RenderableImage
{
String HINTS_OBSERVED = "HINTS_OBSERVED";
Vector getSources();
Vector<RenderableImage> getSources();
Object getProperty(String name);
String[] getPropertyNames();
boolean isDynamic();

View file

@ -55,7 +55,7 @@ public class RenderableImageOp implements RenderableImage
this.block = (ParameterBlock) block.clone();
}
public Vector getSources()
public Vector<RenderableImage> getSources()
{
if (block.sources == null)
return null;