This question already has an answer here:
How to draw a BufferedImage with a color tint
(1 answer)
Closed 7 years ago.
How would I tint the icon that is passed through here to be a different color? Say I wanted to take a white image and make it a bit darker. I have looked into BufferedImages and such but I can't seem to find anything that will fit into the setup that I am using. I should also note that I am drawing the images onto a JLabel if that makes a difference.
Here is the source that I am using so that you can get an idea as to what I am working with.
public class Icon extends ImageIcon{
private int scale = 1;
private boolean mirror = false;
public Icon(URL url) throws IOException{
super(ImageIO.read(url));
}
public void setScale(int scale){
this.scale = scale;
}
#Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D)g.create();
int height = 0, width = this.getIconWidth(), x1 = 1;
if(mirror || scale != 1){
height = -this.getIconHeight();
}
if(mirror){
x1 = -1;
}else{
width = 0;
}
g2.translate(width * scale, height);
g2.scale(x1 * scale, 1 * scale);
super.paintIcon(c, g2, x, y);
}
public boolean isMirror() {
return mirror;
}
public void setMirror(boolean mirror) {
this.mirror = mirror;
}
}
you need to create a new BufferedImage to make the transform into:
public BufferedImage colorImage(BufferedImage loadImg, int red, int green, int blue) {
BufferedImage img = new BufferedImage(loadImg.getWidth(), loadImg.getHeight(),
BufferedImage.TRANSLUCENT);
Graphics2D graphics = img.createGraphics();
Color newColor = new Color(red, green, blue, 0 /* alpha needs to be zero */);
graphics.setXORMode(newColor);
graphics.drawImage(loadImg, null, 0, 0);
graphics.dispose();
return img;
}
Related
I cannot seem to figure out how to draw a transparent and rotated image. I need to be able to draw an image that is transparent and rotated to a certain degree.
I tried this code:
// draws an image that is rotated to a certain degree
public static void drawRotatedImage(BufferedImage image_, int x, int y, int degrees, float scale) {
// graphics used for the utilities of drawing the image (processing)
Graphics2D utilGraphics;
// make rectangular image
int radius = (int) Math.sqrt(image_.getWidth() * image_.getWidth() + image_.getHeight() * image_.getHeight());
BufferedImage image1 = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
utilGraphics = image1.createGraphics();
// centers image
utilGraphics.drawImage(image_, image1.getWidth() / 2 - image_.getWidth() / 2, image1.getHeight() / 2 - image_.getHeight() / 2, null);
// scale image
int nw = (int) (image1.getWidth() * scale);
int nh = (int) (image1.getHeight() * scale);
BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
utilGraphics.drawImage(image1, 0, 0, nw, nh, null);
// Rotation information
double rotationRequired = Math.toRadians (degrees);
double locationX = image.getWidth() / 2;
double locationY = image.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
ImageProducer filteredImgProd = new FilteredImageSource(op.filter(image, null).getSource(), filter);
Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);
// Drawing the rotated image at the required drawing locations
g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null);
}
The filter variable is defined as:
private static final ImageFilter filter = new RGBImageFilter() {
int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;
public final int filterRGB(int x, int y, int rgb) {
if ((rgb | 0x0000ffcc) == transparentColor) {
return 0x0000ffcc & rgb;
} else {
return rgb;
}
}
};
This ...
BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
centeredGraphics.drawImage(image1, 0, 0, nw, nh, null);
You're creating a new BufferedImage (image), but you never actually paint anything to it, instead, you paint image1 to it's own Graphics context.
Now, if you wanted a transparent image, you should have used...
BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
instead of...
BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
And I never used g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null); as it just doesn't make sense to me (transparentImg is already an Image 🤷♂️)
Now, having said all that, I would "suggest" you take each step individually, start by scaling the original image using something like Java: maintaining aspect ratio of JPanel background image and the rotate the image using something like Rotate a buffered image in Java (which will generate a image large enough to contain the rotated image)
Also, if you "create" a Graphics context, you should also dispose of it when you no longer need it, otherwise you could end up with a memory leak.
"Fixed" code...
Just to be clear, I would still recommend sing ARGB instead of RGB for centeredImage as your filter workflow never seemed to work for, but I started with a transparent image anyway
public Image rotateAndScaleImage(BufferedImage originalImage, int degrees, float scale) {
// make rectangular image
int radius = (int) Math.sqrt(originalImage.getWidth() * originalImage.getWidth() + originalImage.getHeight() * originalImage.getHeight());
BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = centeredImage.createGraphics();
// centers image
int xPos = (centeredImage.getWidth() - originalImage.getWidth()) / 2;
int yPos = (centeredImage.getHeight() - originalImage.getHeight()) / 2;
graphics.drawImage(originalImage, xPos, yPos, null);
graphics.dispose();
// scale image
int nw = (int) (centeredImage.getWidth() * scale);
int nh = (int) (centeredImage.getHeight() * scale);
BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
graphics = image.createGraphics();
// No scaling is done ???
graphics.drawImage(centeredImage, 0, 0, nw, nh, null);
// Rotation information
double rotationRequired = Math.toRadians(degrees);
double locationX = centeredImage.getWidth() / 2;
double locationY = centeredImage.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
ImageProducer filteredImgProd = new FilteredImageSource(op.filter(centeredImage, null).getSource(), filter);
Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);
return transparentImg;
}
private static final ImageFilter filter = new RGBImageFilter() {
int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;
public final int filterRGB(int x, int y, int rgb) {
if ((rgb | 0x0000ffcc) == transparentColor) {
return 0x0000ffcc & rgb;
} else {
return rgb;
}
}
};
Oh, and I'm returning an Image because I painted directly to a component for testing
I'm trying scale an image so it will always fit my JPanel. Unfortunately using this method I don't always receive an Image I wanted to receive. Mostly it is zoomed and I would rather have the whole image but scaled.
Thats the class that creates the image. 600 is the PanelWidth and 400 is the PanelHeight.
Any ideas what goes wrong?
public class Image extends Component{
private BufferedImage img;
protected int width;
protected int height;
private String path;
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
double scale = getScale(600,400,img.getWidth(),img.getHeight());
double xPos = (600 - scale * img.getWidth())/2;
double yPos = (400 - scale *img.getHeight())/2;
AffineTransform at = AffineTransform.getTranslateInstance(xPos, yPos);
at.scale(scale, scale);
g2.drawRenderedImage(img, at);
System.out.println(scale);
}
public Image(String path){
try{
img = ImageIO.read(new File(path));
} catch (IOException e) { }
this.width=img.getWidth();
this.height=img.getHeight();
this.path = path;
}
public double getScale(int panelWidth, int panelHeight, int imageWidth, int imageHeight){
double scale = 1;
double xScale, yScale;
if(imageWidth > panelWidth || imageHeight > panelHeight){
xScale = (double)imageWidth/panelWidth;
yScale = (double)imageHeight/panelHeight;
scale = Math.max(xScale, yScale);
}else if(imageWidth < panelWidth && imageHeight < panelHeight){
xScale = (double)panelWidth/imageWidth;
yScale = (double)panelHeight/imageHeight;
scale = Math.max(xScale, yScale);
}else{
scale = 1;
}
return scale;
}
A JPanel is a Swing component which implies you are using Swing.
For custom painting you should extend JPanel or JComponent. Most people use JPanel because it will clear the background of the component for you.
Custom painting of a Swing component is done by overriding paintComponent(...)
so it will always fit my JPanel
Define "fit"?
Assuming you are trying to scale the image to retain its original proportions you could to something like:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
double imageWidth = image.getWidth(null);
double imageHeight = image.getHeight(null);
double factor = Math.min(getWidth() / imageWidth, getHeight() / imageHeight);
int width = (int)(image.getWidth(null) * factor);
int height = (int)(image.getHeight(null) * factor);
g.drawImage(image, 0, 0, width, height, this);
}
If you are just trying to fit the image on the panel then you do:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
You don't need to use float if you do your operations in the right order. Assuming imageWidth, imageHeight, panelWidth are all int:
// Calculate the width of the scaled image; if the image is wider than the
// panel, use the panel width, otherwise use the image width (i.e. don't upscale)
int scaledWidth = Math.min(imageWidth, panelWidth);
// Given the scaled width, calculate the scaled height
// Force it to be at least 1 pixel, since if you have an image that's wider than
// the panel and only 1 pixel tall, this will scale to zero height, which you
// don't want
int scaledHeight = Math.max(1, imageHeight * scaledWidth / imageWidth);
The above assumes you want to fit the width and will be providing a scrolling mechanism if the image height exceeds the panel height. If you want to fit height instead (and horizontal scroll for overflow) just make the necessary changes in variables.
I have been trying to export my swing 2d diagram to a png file. I tried the following code :
BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
Graphics2D referenceGraphics = bufferedImage.createGraphics();
referenceGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
paintComponent(referenceGraphics);
File imageFile = new File(fileName);
if (imageFile.exists() || imageFile.createNewFile()) {
ImageIO.write(bufferedImage, "png", imageFile);
}
However the png file is been created successfully, but the quality is too low. I need to create at least 300ppi image file. How to achieve that?
Simply increasing the size of the component won't make the resolution of the image any better. It will just be larger. What you need to do is create the BufferedImage larger that the component (i.e. 3 times for 3 times the current resolution) and the scale the Graphics object of the BufferedImage. The resulting code would look something like this:
public static void main(String[] args) throws IOException {
Component comp = ...
BufferedImage img = scaledImageFromComponent(comp, 3);
File imageFile = new File(fileName);
if (imageFile.exists() || imageFile.createNewFile()) {
ImageIO.write(img, "png", imageFile);
}
}
public static BufferedImage scaledImageFromComponent(final Component c, final double scale) {
c.setSize(c.getPreferredSize());
c.doLayout();
Rectangle r = new Rectangle(0, 0, c.getWidth(), c.getHeight());
return scaledImageFromComponent(c, r, scale, scale, false);
}
public static BufferedImage scaledImageFromComponent(final Component c, final Rectangle bounds,
final double scalex, final double scaley,
final boolean print) {
BufferedImage image = createCompatibleTransparentImage((int) (scalex * bounds.width),
(int) (scaley * bounds.height));
final Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.scale(scalex, scaley);
g2d.translate(-bounds.x, -bounds.y);
if (print) {
c.print(g2d);
} else {
c.paint(g2d);
}
g2d.dispose();
return image;
}
public static BufferedImage createCompatibleTransparentImage(final int width,
final int height) {
return isHeadless() ? new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
: getGraphicsConfiguration().createCompatibleImage(width, height,
Transparency.BITMASK);
}
private static boolean isHeadless() {
return GraphicsEnvironment.isHeadless();
}
private static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
If your diagram contains text you may want to switch Transparency.BITMASK with Transparency.OPAQUE for better anti-aliasing support (on Windows).
Hello) Help solve the problem:
We need to create a green square image and display it.
I could draw a square, but I need to create it using java.
Please help me to do this)
That's what I tried to do:
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Game extends Canvas {
private static final long serialVersionUID = 1L;
private static final int WIDTH = 400;
private static final int HEIGHT = 400;
#Override
public void paint(Graphics g) {
super.paint(g);
int w = 10;
int h = 10;
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage image = new BufferedImage(w, h, type);
int color = 257; // RGBA value, each component in a byte
for (int x = 1; x < w; x++) {
for (int y = 1; y < h; y++) {
image.setRGB(x, y, color);
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(WIDTH, HEIGHT);
frame.add(new Game());
frame.setVisible(true);
}
}
But nothing is displayed (
Let me remind the goal - to create a picture in the form of green squares, help to make it)
The simplest approach would be to simply use the graphics API...
#Override
public void paint(Graphics g) {
super.paint(g);
int w = 10;
int h = 10;
g.setColor(Color.GREEN);
g.fillRect(0, 0, width, height);
}
But something tells me this isn't what you want, but it does form the basics for what you need to achieve your result.
Start by making image a instance field...
private BufferedImage image;
Then you need to create the image...
int type = BufferedImage.TYPE_INT_ARGB;
image = new BufferedImage(w, h, type);
Graphics2D g2d = image.createGraphics();
g2d.setColor(Color.GREEN);
g2d.fillRect(0, 0, w, h);
g2d.dispoe();
Then in you paint method, you need to draw the image...
g.drawImage(image, x, y, this);
Take a look at the 2D Graphics trail for mor details
You must use Graphics.drawRect() and Graphics.fillRect():
http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawRect%28int,%20int,%20int,%20int%29
http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#fillRect%28int,%20int,%20int,%20int%29
if you want to create an Image with rectangle containing it, first create the image, draw on it using it's graphics. I am writing code snippets for you:
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.setColor(Color.blue);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
This will produce an image with Blue rectangle.
I'm wondering how a person could change the alpha transparency of a color, if given the hex color code. For example if given
Color.red.getRGB()
how could I change it's alpha to 0x80?
To put this in context, I'm working on a static method to tint a BufferedImage, by creating a graphics device from the given image, and rendering a half transparent mask with that, disposing of the graphics, and returning the image. It works, but you have to define the alpha yourself in the given hex color code. I want to give a Color object, and double between 0 and 1.0 to determine the intensity of the tinting. Here's my code so far:
public static Image tintImage(Image loadImg, int color) {
Image gImage = loadImg;
Graphics2D g = gImage.image.createGraphics();
Image image = new Image(new BufferedImage(loadImg.width, loadImg.height, BufferedImage.TYPE_INT_ARGB));
for(int x = 0; x < loadImg.width; x++) {
for(int y = 0; y < loadImg.height; y++) {
if(loadImg.image.getRGB(x, y) >> 24 != 0x00) {
image.image.setRGB(x, y, color);
}
}
}
g.drawImage(image.image, 0, 0, null);
g.dispose();
return gImage;
}
You can construct a new Color from the old one with the lower alpha.
Color cNew = new Color(cOld.getRed(), cOld.getGreen(), cOld.getBlue(), 0x80);
Using the Color(int r, int g, int b, int a) constructor.