I know how to fill a rectangle in Swing with a solid color:
Graphics2D g2d = bi.createGraphics();
g2d.setColor(Color.RED);
g2d.fillRect(0,0,100,100);
I know how to fill it with an image:
BufferedImage bi;
Graphics2D g2d = bi.createGraphics();
g2d.setPaint (new Color(r, g, b));
g2d.fillRect (0, 0, bi.getWidth(), bi.getHeight());
But how to fill rectangle of size 950x950 with some tiled pattern of size 100x100?
(pattern image should be used 100 times)
You're on the right track with setPaint. However, instead of setting it to a color, you want to set it to a TexturePaint object.
From the Java tutorial:
The pattern for a TexturePaint class is defined by a BufferedImage class. To create a TexturePaint object, you specify the image that contains the pattern and a rectangle that is used to replicate and anchor the pattern. The following image represents this feature:
If you have a BufferedImage for the texture, create a TexturePaint like so:
TexturePaint tp = new TexturePaint(myImage, new Rectangle(0, 0, 16, 16));
where the given rectangle represents the area of the source image you want to tile.
The constructor JavaDoc is here.
Then, run
g2d.setPaint(tp);
and you're good to go.
As #wchargin said, you can use TexturePaint. Here is an example:
public class TexturePanel extends JPanel {
private TexturePaint paint;
public TexturePanel(BufferedImage bi) {
super();
this.paint = new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(paint);
g2.fill(new Rectangle(0, 0, getWidth(), getHeight()));
}
}
Related
I try to prepare a BufferedImage with semi-transparency so that whereever this image is painted on top of the background will partially shine through. For this purpose I use among other things Graphics.drawLine() with a stroke > 1 and colors that have an alpha component < 255, i.e. are semi-transparent. Lines I draw will often overlap or intersect to make sure there will be no gaps. When such intersections occur, the colors of the lines will stack, i.e. the color that was there before and the semi-transparent color I am painting with will result in a new color. Which is logical.
That is not what I want, however. If possible, I would like to paint with my semi-transparent color as if it was completely opaque and the alpha component of the color just a fourth color component. I would like to copy the color I am painting with - including the alpha component - exactly to the image, replacing everything that was there before.
Is there a way to do this?
The following code produces a simple semi-transparent red X, that will let the background partially shine through wherever it is painted on top of. It will have, however, a darker part where both lines intersect. I would like to be able to paint the X without this darker part. Is that possible (while still using Graphics.drawLine())?
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
public class Test {
public static void main(String[] args) {
// create a new BufferedImage with alpha channel
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
// set a semi-transparent color and a stroke wider than one pixel
g.setColor(new Color(255, 0, 0, 128 /* alpha; semi-transparent */));
g.setStroke(new BasicStroke(2));
// draw a cross; where the lines cross the color will (logically) be darker
g.drawLine(0, 0, 100, 100);
g.drawLine(0, 100, 100, 0);
// use the image somehow, in this case write it to file
writeImage(img, "test.png");
}
private static void writeImage(BufferedImage img, String fileName) {
try {
ImageIO.write(img, "png", new File(fileName));
}
catch (IOException e) {
System.out.println(e);
}
}
}
I could, of course, solve this manually by implementing my own line drawing method and manipulate the pixels directly. (Not sure if that would create new problems with clipping which I also need.) However, that would increase the effort substantially which I would like to avoid.
A further constraint: I am restricted to Java 7.
Create a second image.
Draw on that second image with opaque colors.
Copy that second image onto your original image using translucent alpha.
import java.io.*;
import javax.imageio.*;
public class TranslucentX {
public static void main(String[] args) {
BufferedImage x = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = x.createGraphics();
g.setColor(new Color(255, 0, 0));
g.setStroke(new BasicStroke(2));
// draw a cross; where the lines cross the color will (logically) be darker
g.drawLine(0, 0, 100, 100);
g.drawLine(0, 100, 100, 0);
g.dispose();
// create a new BufferedImage with alpha channel
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
g = img.createGraphics();
// Draw opaque image using translucency.
g.setComposite(
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g.drawImage(x, 0, 0, null);
g.dispose();
// use the image somehow, in this case write it to file
writeImage(img, "test.png");
}
private static void writeImage(BufferedImage img, String fileName) {
try {
ImageIO.write(img, "png", new File(fileName));
}
catch (IOException e) {
System.out.println(e);
}
}
}
I've created a Java program that generates snowflakes and I'd like to save the image created as a .png file once the program finishes drawing.
I've searched on Internet, but I've found only programs using BufferedImage, while I use a BufferStrategy, so I don't know exactly where to start.
The draw method in my program uses a BufferStrategy to create the Graphics component.
For example, to draw a simple line the method is:
bs = display.getCanvas().getBufferStrategy();
if (bs == null) {
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
g.clearRect(0, 0, width, height);
g.setColor(Color.BLACK);
g.drawLine(0, 0, 50, 50);
What I would like is to get an exact copy of what has been drawn on the screen by the program to be saved as a .png image.
Hope you can help me.
Why not take a screenshot and then past it onto MS paint or some other(and better) image editing software like Photoshop or fire alpaca? That should solve your problem.
The common denominator between BufferedStrategy and BufferedImage is Graphics, so you want to write a paint routine so that you can simply pass a reference of Graphics to it
public void render(Graphics g) {
g.clearRect(0, 0, width, height);
g.setColor(Color.BLACK);
g.drawLine(0, 0, 50, 50);
}
Then you can pass what ever context you want.
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_RGB);
Graphics2D g2d = img.createGraphics();
render(g2d);
g2d.dispose();
Then you can use ImageIO.write to write the image to disk. See Writing/Saving an Image for more details
Actually I have these methods to get the width/height size of a string, both requires the command inside a paint component. But I want to get this values inside a class constructor. This is possible?
public int getStringWidth(Graphics g){
g2d = (Graphics2D) g;
metrics = g2d.getFontMetrics(this.font);
return metrics.stringWidth(this.string);
}
public int getStringHeight(Graphics g){
g2d = (Graphics2D) g;
metrics = g2d.getFontMetrics(this.font);
int height = (int)font.createGlyphVector(metrics.getFontRenderContext(), this.string).getVisualBounds().getHeight();
return height;
}
font.createGlyphVector(metrics.getFontRenderContext(), this.string).getVisualBounds().getHeight() it's the best command that I got to precisely calculate the string height size and it needs Graphics g too.
You can try to create a BufferedImage with the right size and use createGraphics() on it to get an actual Graphics object that is drawable.
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.createGraphics();
I would like to draw scaled objects containing raster as well as vector data. Currently, I am drawing into a scaled Graphics2D object
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
//Get transformation, apply scale and shifts
at = g2d.getTransform();
at.translate(x, y);
at.scale(zoom, zoom);
//Transform Graphics2D object
g2d.setTransform(at);
//Draw buffered image into the transformed object
g2d.drawImage(img, 0, 0, this);
//Draw line into transformed Graphics2D object
Line2D.Double line = new Line2D.Double();
line.x1 = (int)p1.getX();
line.y1 = (int)p1.getY();
line.x2 = (int)p2.getX();
line.y2 = (int)p2.getY();
g2d.draw(line);
g2d.dispose();
}
Unfortunately, this approach has some disadvantages (affects the Stroke width, worse quality of zoomed images).
Instead of that I would like to draw scaled objects. For the vector data, the approach is simple:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
//Update affine transformation
at = AffineTransform.getTranslateInstance(x, y);
at = AffineTransform.getScaleInstance(zoom, zoom);
//Transform line and draw
Line2D.Double line = new Line2D.Double();
line.x1 = (int)p1.getX();
line.y1 = (int)p1.getY();
line.x2 = (int)p2.getX();
line.y2 = (int)p2.getY();
g2d.draw(at.createTransformedShape((Shape)line));
//Transform buffered image and draw ???
g2d.draw(at.createTransformedShape((Shape)img)); //Error
g2d.dispose();
}
However, how to apply the transformation to the buffered image without rescaling the Graphics2D object? This approach does not work
g2d.draw(at.createTransformedShape((Shape)img));
because raster image cannot be understood as a vector shape...
Thanks for your help.
A possible solution may represent an affine transformation of the raster:
//Update affine transformation
at = AffineTransform.getTranslateInstance(x, y);
at = AffineTransform.getScaleInstance(zoom, zoom);
at.translate(x, y);
at.scale(zoom, zoom);
//Create affine transformation
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
//Apply transformation
BufferedImage img2 = op.filter(img, null);
//Draw image
g2d.drawImage(img2, 0, 0, this);
Unfortunately, this approach is inappropriate for the zoom operations; it is more computationally expensive. For the raster data (JPG, 5000 x 7000 pix) a simple bilinear interpolation
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
takes about 1.5 sec... Conversely, using a scaling Graphics2D object is significantly faster (< 0.1 s), the image can be shifted and zoomed in the real-time.
In my opinion, rescaling objects together with the raster data is slower than rescaling Graphics2D object....
I want to make a background for a game I have created.
I'm having some issues with fillRect.
Does getHeight and getWidth have to be in a certain order or should getX/Y/Height/Width be used at all?
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//Graphical loop start
Rectangle rect = new Rectangle(0,0,1440,900) ;
g.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
//Graphical loop end
}
Firstly, you are using the Graphics object instead of Graphics2D. You need to set the paint colour first then use the fill method.
g2d.setPaint(Color.BLUE);
g2d.fill(new Rectangle2D.Double(0, 0, screenWidth, screenHeight));
Rectangle2D constructor arguments are: x-coordinate of top-left, y-coordinate of top-left, width, height
It is good practice to use the Graphics2D.fill() method which will accept any object that implements the Shape interface. This makes it easier to change a shape to a different one should you decide to do so.