/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.ui.awt;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import org.arakhne.afc.math.continous.object2d.Vector2f;
import org.arakhne.afc.math.generic.Vector2D;
import org.arakhne.afc.ui.MouseCursor;
/** Swing-oriented utilities.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @deprecated see JavaFX API
*/
@Deprecated
public class AwtUtil {
private static FontRenderContext frc = null;
/** Replies the AWT identifier of the mouse cursor.
*
* @param cursor
* @return the AWT identifier of the mouse cursor.
*/
public static int getAWTCursorId(MouseCursor cursor) {
switch(cursor) {
case CROSSHAIR:
return Cursor.CROSSHAIR_CURSOR;
case TEXT:
return Cursor.TEXT_CURSOR;
case WAIT:
return Cursor.WAIT_CURSOR;
case SW_RESIZE:
return Cursor.SW_RESIZE_CURSOR;
case SE_RESIZE:
return Cursor.SE_RESIZE_CURSOR;
case NW_RESIZE:
return Cursor.NW_RESIZE_CURSOR;
case NE_RESIZE:
return Cursor.NE_RESIZE_CURSOR;
case N_RESIZE:
return Cursor.N_RESIZE_CURSOR;
case S_RESIZE:
return Cursor.S_RESIZE_CURSOR;
case W_RESIZE:
return Cursor.W_RESIZE_CURSOR;
case E_RESIZE:
return Cursor.E_RESIZE_CURSOR;
case HAND:
return Cursor.HAND_CURSOR;
case MOVE:
return Cursor.MOVE_CURSOR;
case INVALID:
return Cursor.CUSTOM_CURSOR;
case DEFAULT:
default:
}
return Cursor.DEFAULT_CURSOR;
}
/** Replies the AWT mouse cursor.
*
* @param cursor
* @return the AWT mouse cursor.
*/
public static Cursor getCursor(MouseCursor cursor) {
if (cursor==null || cursor==MouseCursor.DEFAULT)
return Cursor.getDefaultCursor();
if (cursor==MouseCursor.INVALID) {
try {
return Cursor.getSystemCustomCursor("Invalid.32x32"); //$NON-NLS-1$
}
catch (Throwable exception) {
return Cursor.getDefaultCursor();
}
}
return Cursor.getPredefinedCursor(getAWTCursorId(cursor));
}
/** Compute and reply a transparent version of the given color.
*
* @param c
* @return the transparent color.
*/
public static Color makeTransparentColor(Color c) {
int alpha = c.getAlpha() / 2;
return new Color(
c.getRed(), c.getGreen(), c.getBlue(),
alpha);
}
/** Replies the current font render context.
*
* @return the current font render context.
*/
public static FontRenderContext getVectorFontRenderContext() {
synchronized(AwtUtil.class) {
FontRenderContext c = frc;
if (c==null) {
c = new FontRenderContext(new AffineTransform(), true, true);
frc = c;
}
return c;
}
}
/** Move the first specified rectangle to avoid collision
* with the reference.
*
* @param rectangleToMove is the rectangle to move.
* @param reference is the rectangle to avoid collision with.
* @return the displacement vector.
*/
public static Vector2D avoidCollision(Rectangle2D rectangleToMove, Rectangle2D reference) {
double dx1 = reference.getMaxX() - rectangleToMove.getMinX();
double dx2 = rectangleToMove.getMaxX() - reference.getMinX();
double dy1 = reference.getMaxY() - rectangleToMove.getMinY();
double dy2 = rectangleToMove.getMaxY() - reference.getMinY();
double absdx1 = Math.abs(dx1);
double absdx2 = Math.abs(dx2);
double absdy1 = Math.abs(dy1);
double absdy2 = Math.abs(dy2);
double dx = 0;
double dy = 0;
if (dx1>=0 && absdx1<=absdx2 && absdx1<=absdy1 && absdx1<=absdy2) {
// Move according to dx1
dx = dx1;
}
else if (dx2>=0 && absdx2<=absdx1 && absdx2<=absdy1 && absdx2<=absdy2) {
// Move according to dx2
dx = - dx2;
}
else if (dy1>=0 && absdy1<=absdx1 && absdy1<=absdx2 && absdy1<=absdy2) {
// Move according to dy1
dy = dy1;
}
else {
// Move according to dy2
dy = - dy2;
}
rectangleToMove.setFrame(
rectangleToMove.getMinX()+dx,
rectangleToMove.getMinY()+dy,
rectangleToMove.getWidth(),
rectangleToMove.getHeight());
return new Vector2f((float)dx, (float)dy);
}
/** Move the first specified rectangle to avoid collision
* with the reference.
*
* @param rectangleToMove is the rectangle to move.
* @param reference is the rectangle to avoid collision with.
* @param displacementDirection is the direction of the allowed displacement.
* @return the displacement vector.
*/
public static Vector2D avoidCollision(Rectangle2D rectangleToMove, Rectangle2D reference, Vector2D displacementDirection) {
if (displacementDirection==null || displacementDirection.lengthSquared()==0f)
return avoidCollision(rectangleToMove, reference);
double dx1 = reference.getMaxX() - rectangleToMove.getMinX();
double dx2 = reference.getMinX() - rectangleToMove.getMaxX();
double dy1 = reference.getMaxY() - rectangleToMove.getMinY();
double dy2 = reference.getMinY() - rectangleToMove.getMaxY();
double absdx1 = Math.abs(dx1);
double absdx2 = Math.abs(dx2);
double absdy1 = Math.abs(dy1);
double absdy2 = Math.abs(dy2);
double dx, dy;
if (displacementDirection.getX()<0) {
dx = -Math.min(absdx1, absdx2);
}
else {
dx = Math.min(absdx1, absdx2);
}
if (displacementDirection.getY()<0) {
dy = -Math.min(absdy1, absdy2);
}
else {
dy = Math.min(absdy1, absdy2);
}
rectangleToMove.setFrame(
rectangleToMove.getMinX()+dx,
rectangleToMove.getMinY()+dy,
rectangleToMove.getWidth(),
rectangleToMove.getHeight());
displacementDirection.set((float)dx, (float)dy);
return displacementDirection;
}
/** Replies the specified image filtered with the given transparency amount.
* <p>
* If this function replies <code>null</code>, you might invokes
* {@link #getTransparencyFilteredImage(Image, float)}
* with a not-<code>null</code> component parameter to obtain an
* image.
*
* @param image is the image to filter.
* @param alpha indicates how the alpha-component of the image is changed.
* The value is in <code>[-1;1]</code>.
* A value of <code>-1</code> means that the alpha-component is set to none.
* A value of <code>1</code> means that the alpha-component is completely
* set. A value of <code>0</code> means that the alpha-component
* remains the same.
* @return the filtered image or <code>null</code>.
*/
public static Image getTransparencyFilteredImage(Image image, float alpha) {
if (image==null) return null;
return TransparencyFilter.createFilteredImage(
image,
alpha);
}
/** Scale the given image.
*
* @param image is the image to scale.
* @param width is the new width.
* @param height is the new height.
* @param observer is invoked when the image becomes ready to use.
* @return the scaled image; or <code>null</code> if the image is not ready or
* not computable.
*/
public static Image computeScaledImage(ImageIcon image, int width, int height, ImageObserver observer) {
if (image==null) return null;
Image imageToScale = extractIconImage(null, image);
if (imageToScale==null) return null;
int currentWidth = imageToScale.getWidth(observer);
if (currentWidth<0) return null;
int currentHeight = imageToScale.getHeight(observer);
if (currentHeight<0) return null;
Dimension2D d = computeBothScaledSize(
currentWidth, currentHeight, width, height);
if (d==null) return imageToScale; // Scaling not necessary
return imageToScale.getScaledInstance(
(int)d.getWidth(),(int)d.getHeight(),Image.SCALE_SMOOTH);
}
/** Extract an image from the specified icon.
*
* @param component
* @param icon
* @return the image.
*/
public static Image extractIconImage(Component component, Icon icon) {
Image source;
if (icon instanceof ImageIcon) {
source = ((ImageIcon)icon).getImage();
}
else if (component!=null) {
source = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D)source.getGraphics();
icon.paintIcon(component, g, 0, 0);
g.dispose();
}
else {
source = null;
}
return source;
}
/** Scale the given image.
*
* @param image is the image to scale.
* @param width is the new width.
* @param height is the new height.
* @param observer is notified when the image cannot be loaded.
* @return the scaled image; or <code>null</code> if the image cannot be loaded.
*/
public static Image computeScaledImage(Image image, int width, int height, ImageObserver observer) {
if (image==null) return null;
int currentWidth = image.getWidth(observer);
if (currentWidth<0) return null;
int currentHeight = image.getHeight(observer);
if (currentHeight<0) return null;
Dimension2D d = computeBothScaledSize(
currentWidth, currentHeight,
width, height);
if (d==null) return image; // scaling is not necessary
return image.getScaledInstance(
(int)d.getWidth(),(int)d.getHeight(),Image.SCALE_SMOOTH);
}
private static Dimension2D computeBothScaledSize(int currentWidth, int currentHeight, int desiredWidth, int desiredHeight) {
int targetWidth = desiredWidth;
int targetHeight = desiredHeight;
if (targetWidth==-1) targetWidth = currentWidth;
if (targetHeight==-1) targetHeight = currentHeight;
if (targetWidth==currentWidth && targetHeight==currentHeight)
return null;
float wscale = (float)targetWidth / (float)currentWidth;
float hscale = (float)targetHeight / (float)currentHeight;
if (hscale<wscale) {
int nwidth = (int)(currentWidth * hscale);
if (nwidth>targetWidth) targetHeight = -1;
else targetWidth = -1;
}
else {
int nheight = (int)(currentHeight * wscale);
if (nheight>targetHeight) targetWidth = -1;
else targetHeight = -1;
}
return new DoubleDimension(targetWidth,targetHeight);
}
/** This class permits to filter the alpha component of an icon.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public static class TransparencyFilter extends RGBImageFilter {
private final float alpha;
/**
* Creates a alpha-filtered image
*
* @param i is the image to filter
* @param alpha indicates how the alpha-component of the icon is changed.
* The value is in <code>[-1;1]</code>.
* A value of <code>-1</code> means that the alpha-component is set to none.
* A value of <code>1</code> means that the alpha-component is completely
* set. A value of <code>0</code> means that the alpha-component
* remains the same.
* @return the result of the filtering.
*/
public static Image createFilteredImage(Image i, float alpha) {
TransparencyFilter filter = new TransparencyFilter(alpha);
ImageProducer prod = new FilteredImageSource(i.getSource(), filter);
Image filteredImage = Toolkit.getDefaultToolkit().createImage(prod);
return filteredImage;
}
/**
* Constructs an TransparencyFilter object that filters a color image to a
* alphaed image.
*
* @param alpha indicates how the alpha-component of the icon is changed.
* The value is in <code>[-1;1]</code>.
* A value of <code>-1</code> means that the alpha-component is set to none.
* A value of <code>1</code> means that the alpha-component is completely
* set. A value of <code>0</code> means that the alpha-component
* remains the same.
*/
public TransparencyFilter(float alpha) {
float f = alpha;
if (f<-1f) f = -1f;
else if (f>1f) f = 1f;
assert(f>=-1f && f<=1f);
this.alpha = -f;
// canFilterIndexColorModel indicates whether or not it is acceptable
// to apply the color filtering of the filterRGB method to the color
// table entries of an IndexColorModel object in lieu of pixel by pixel
// filtering.
this.canFilterIndexColorModel = true;
}
/**
* Filter the specified color.
*/
@Override
public int filterRGB(int x, int y, int rgb) {
int color_a = (rgb >> 24) & 0xFF;
int color_rgb = rgb & 0x00FFFFFF;
if (this.alpha<0f) {
color_a += color_a * this.alpha;
}
else {
color_a += (256-color_a) * this.alpha;
}
if (color_a<0) color_a = 0;
else if (color_a>255) color_a = 255;
return ((color_a << 24) & 0xFF000000) | color_rgb;
}
} // class AlphaFilter
}