I'm struggling to understand how to merge 4 pictures together in java, I want to copy each image to the merged image with the overlapping 20 pixels blended in a 50% merge. To give the merged image a 20 pixel boundary that is a blend of the appropriate portion of each image.
So a 4 image box with the images blended into each other by 20 pixels. Not sure how I should use the width and height of the images as it is very confusing.
Something like this. How to do it?
I got all of my info from: AlphaComposite, Compositing Graphics, Concatenating Images.
The following program is improved. It uses two methods: joinHorizontal and joinVertical to join the images. Inside the methods, the following happens
the second image is copied, but only the part that overlaps
the copied image is set at half alpha (transparency)
on the canvas of the 'return image', the first image is painted, followed by the second without the overlapping part
the copied image is painted onto the canvas.
the image is returned
Why do I only set one image at half alpha and not both?
Picture a clear, glass window:
Paint random points red so that half of the window is covered with red. Now, treat the window with the red dots as your new canvas.
Paint random points blue so that the new "canvas" is half covered with blue. The window won't be completely covered; you will still be able to see through it.
But let's imagine that we first painted the window red, and then painted half of it blue. Now, it will be half blue and half red, but not transparent at all.
public class ImageMerger {
/**
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedImage img1 = //some code here
BufferedImage img2 = //some code here
BufferedImage img3 = //some code here
BufferedImage img4 = //some code here
int mergeWidth = 20; // pixels to merge.
BufferedImage merge = ImageMerger.joinVertical(
ImageMerger.joinHorizontal(img1, img2, mergeWidth),
ImageMerger.joinHorizontal(img3, img4, mergeWidth),mergeWidth);
//do whatever you want with merge. gets here in about 75 milliseconds
}
public static BufferedImage joinHorizontal(BufferedImage i1, BufferedImage i2, int mergeWidth){
if (i1.getHeight() != i2.getHeight()) throw new IllegalArgumentException("Images i1 and i2 are not the same height");
BufferedImage imgClone = new BufferedImage(mergeWidth, i2.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D cloneG = imgClone.createGraphics();
cloneG.drawImage(i2, 0, 0, null);
cloneG.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 0.5f));
cloneG.drawImage(i2, 0, 0, null);
BufferedImage result = new BufferedImage(i1.getWidth() + i2.getWidth()
- mergeWidth, i1.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = result.createGraphics();
g.drawImage(i1, 0, 0, null);
g.drawImage(i2.getSubimage(mergeWidth, 0, i2.getWidth() - mergeWidth,
i2.getHeight()), i1.getWidth(), 0, null);
g.drawImage(imgClone, i1.getWidth() - mergeWidth, 0, null);
return result;
}
public static BufferedImage joinVertical(BufferedImage i1, BufferedImage i2, int mergeWidth){
if (i1.getWidth() != i2.getWidth()) throw new IllegalArgumentException("Images i1 and i2 are not the same width");
BufferedImage imgClone = new BufferedImage(i2.getWidth(), mergeWidth, BufferedImage.TYPE_INT_ARGB);
Graphics2D cloneG = imgClone.createGraphics();
cloneG.drawImage(i2, 0, 0, null);
cloneG.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 0.5f));
cloneG.drawImage(i2, 0, 0, null);
BufferedImage result = new BufferedImage(i1.getWidth(),
i1.getHeight() + i2.getHeight() - mergeWidth, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = result.createGraphics();
g.drawImage(i1, 0, 0, null);
g.drawImage(i2.getSubimage(0, mergeWidth, i2.getWidth(),
i2.getHeight() - mergeWidth), 0, i1.getHeight(), null);
g.drawImage(imgClone, 0, i1.getHeight() - mergeWidth, null);
return result;
}
}
Related
While trying to scale the image drawn on a canvas, I came across Graphics2D.drawImage(originalImage) [Graphics2D object obtained from BufferedImage object].
I used it to draw the image into the graphics created from BufferedImage, this new image could be now be drawn onto a Graphics object of the Panel/Frame to get a zoomed image.
I populated the original image using BufferedImage.setRGB.
So, what is it actually doing? Is it selectively omitting the pixels from the original image?
Similar to this code.
int newImageWidth = imageWidth * zoomLevel;
int newImageHeight = imageHeight * zoomLevel;
BufferedImage resizedImage = new BufferedImage(newImageWidth , newImageHeight, imageType);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(originalImage, 0, 0, newImageWidth , newImageHeight , null);
g.dispose();
Original Answer for the above code
I want to copy a part of BufferedImage, but the copying form is not a simple square but rather a square rotated on some angle. As an output I want to get the BufferedImage with width and height equals to the copying square size and contents from the initial image, where copying square intersects the initial image.
What is the simplest way to do it?
A simple approach would be to create a new image with the desired size, transform a Graphics2D of this image according to the selected square region, and then simply paint the original image into this graphics:
private static BufferedImage extractRegion(
BufferedImage source, Point2D center, Point2D size, double angleRad)
{
int w = (int)size.getX();
int h = (int)size.getY();
BufferedImage result =
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = result.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.translate(size.getX() / 2, size.getY() / 2);
g.rotate(angleRad);
g.translate(-center.getX(), -center.getY());
g.drawImage(source, 0, 0, null);
g.dispose();
return result;
}
Recently I have been attempting to scale pixel arrays (int[]) in Java. I used .setRGB() to add all my pixel data into the BufferedImage. BufferedImage then offers a function called .getScaledInstance(). This should work great for my purposes, but I ran into a problem. .getScaledInstance() returns a Image, not a BufferedImage. With an Image object, I cannot use .getRGB() to add all the pixel data (in int[] form) from the scaled Image back into an array. Is there a way to get raw pixel data from an Image file? Am I missing something? I looked at other questions and did a bit of googling, and they only seemed to be wanting to get picture data in a different form of array (int[][]) or in bytes. Any help would be appreciated, thanks. Also, Sprite is a class I made that is being used. Here is my code:
public Sprite scaleSprite(Sprite s, int newWidth, int newHeight){
BufferedImage image = new BufferedImage(s.getWidth(), s.getHeight(), BufferedImage.TYPE_INT_RGB);
for(int y = 0; y < s.getHeight(); y++){
for(int x = 0; x < s.getWidth(); x++){
image.setRGB(x, y, s.getPixel(x, y));
}
}
Image newImage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_AREA_AVERAGING);
Sprite newS = new Sprite(newWidth, newHeight);
int[] pixels = new int[newWidth * newHeight];
newImage.getRGB(0, 0, newWidth, newHeight, pixels, 0, newWidth); //This is where I am running into problems. newImage is an Image and I cannot retrieve the raw pixel data from it.
newS.setPixels(pixels);
return newS;
}
You can draw the resulting Image onto a BufferedImage like this:
Image newImage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_AREA_AVERAGING);
BufferedImage buffImg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2 = (Graphics2D) buffImg.getGraphics();
g2.drawImage(newImage, 0, 0, 10, 10, null);
g2.dispose();
Or you can scale the image directly by drawing it on another BufferedImage:
BufferedImage scaled = new BufferedImage(newWidth, newWidth, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2 = (Graphics2D) scaled.getGraphics();
g2.drawImage(originalImage, 0, 0, newWidth, newWidth, 0, 0, originalImage.getWidth(), originalImage.getHeight(), null);
g2.dispose();
The second approach will work correctly if the two BufferedImages have the same aspect ratio.
To be clear, getScaledInstance() is a method of Image, not BufferedImage. You don't generally want to revert to working directly with the Image superclass once you're working with BufferedImage; Image is really not easy to work with.
Please see if this will help: How to scale a BufferedImage
Or from Scaling a BufferedImage, where they yield the following example:
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
public class Main {
public static void main(String[] argv) throws Exception {
BufferedImage bufferedImage = new BufferedImage(200, 200,
BufferedImage.TYPE_BYTE_INDEXED);
AffineTransform tx = new AffineTransform();
tx.scale(1, 2);
AffineTransformOp op = new AffineTransformOp(tx,
AffineTransformOp.TYPE_BILINEAR);
bufferedImage = op.filter(bufferedImage, null);
}
This will give you the ability to scale entirely at the level of BufferedImage. From there you can apply whatever sprite specific or array data algorithm you wish.
I had a BufferedImage an image of size (100mb) pixels 6720x9239 and needed many small images with pixels 60x60
firstly i used this code i found on the net
BufferedImage bi= ImageIO.read(new File(filename));
......
//in paint()
Image b=createImage(new FilteredImageSource(bi.getSource(),
new CropImageFilter(x, y , 60, 60)));
needed to wait around 1 to 5 secs for each small image very slow because i my app needed like 50 images which would mean ppl would have to w8 from 50 to 5*50 sec for panel to reload, so i chan that to
BufferedImage bi= ImageIO.read(new File(filename));
......
//in paint()
BufferedImage a = imageMap.getSubimage(x, y , 60, 60);
Image b = createImage(a.getSource());
feel really happy now had to let the world know this
Oh my god you solved my problem which had stumped me for like 5 days. I had just finished typing out the question and was about to submit it. APPARENTLY (now that I know using Images works) when you use a bufferedImage in g2d.drawImage(Image, at, this) the drawing is MUCH slower than if you use an Image. Something like 50x slower. By converting the BufferedImages to Images (I didn't know you could do that, less that would solve the problem) like this:
Inside the loadBullets function:
BufferedImage a;
Image b;
a = spriteSheetPositive.getSubimage(
//going to use the same image 252 times until I get the motherload of data string converted to the format:
//sprites[shotId]=spriteSheetPositive.getSubimage(x, y, width, height);
520, //x 520,1,536,16 (small lime star) id=100
1, //y
16, //width
15 //height
);
b= createImage(a.getSource());
sprites[shotID]=b;
I can now use images from a spritesheet as projectile sprites with as many as 1,000 on screen at time with no lag! Hooray!
My original question:
This is code within the paint function:
for (int i = 0; i < Projectiles.size(); i++) {
Shot02 m = (Shot02) Projectiles.get(i);
//m.getImage();
// g2d.drawImage(m.getImage(), m.getIntX(), m.getIntY(), this);
AffineTransform at = new AffineTransform();
// 4. translate it to the center of the component
at.translate(m.getDrawX(), m.getDrawY());
// 3. do the actual rotation
at.rotate(m.getAngle()); //rotation is Clockwise
g2d.drawImage(m.getImage(), at, this);
}
I am working on a platformer perspective shooting game. I switched from using a simple imageicon image to a bufferedImage created as a subImage from a sprite sheet. However, as a result of that the program lags with as little as 20 projectiles on screen, whereas previously I could have up to around 1000.
private void loadBullets() {//is done only once, when the window starts
// Get Image
ImageIcon icon = new ImageIcon(this.getClass().getResource("AbsoluteWin\\CustomShotsPositive.png"));
Image image = icon.getImage();
// Create empty BufferedImage, sized to Image
BufferedImage spriteSheetPositive =
new BufferedImage(
image.getWidth(null),
image.getHeight(null),
Transparency.BITMASK);
// Draw Image into BufferedImage
Graphics g = spriteSheetPositive.createGraphics();
g.drawImage(image, 0, 0, null);
int shotID = 1;
System.out.println(shotID);
while (shotID <= length) {//fills the array with the bullets from the sprite sheet spriteSheetPositive
sprites[shotID] = spriteSheetPositive.getSubimage(
//going to use the same image 252 times until I get the coordinates for all the other sub-images
//sprites[shotId]=spriteSheetPositive.getSubimage(x, y, width, height);
520, //x 520,1,536,16 (small lime star) id=100
1, //y
16, //width
15 //height
);
shotID += 1;
}
System.out.println(shotID);
}
Shot02 Class:
ImageIcon ii =
new ImageIcon(this.getClass().getResource("missile.png"));
image = ii.getImage();
//image=Destination.sprites[100];//is the source
This is the code in the Shot02 Class that controls the what image the bullets use. Again, if I uncomment the second option and use the BufferedImages, the program slows down like crazy.
I am creating a tile game where I need to flip images. With the code I have it produces this error:
Exception in thread "main" java.lang.IllegalArgumentException: Width (-1) and height (-1) cannot be <= 0
from this line:
Image newImage = gc.createCompatibleImage(
image.getWidth(null),
image.getHeight(null),
Transparency.BITMASK);
here is the coding i have:
public Image getMirrorImage(Image image) {
return getScaledImage(image, -1, 1);
}
private Image getScaledImage(Image image, float x, float y) {
// set up the transform
AffineTransform transform = new AffineTransform();
transform.scale(x, y);
transform.translate(
(x-1) * image.getWidth(null) / 2,
(y-1) * image.getHeight(null) / 2);
// create a transparent (not translucent) image
Image newImage = gc.createCompatibleImage(
image.getWidth(null),
image.getHeight(null),
Transparency.BITMASK);
// draw the transformed image
Graphics2D g = (Graphics2D)newImage.getGraphics();
g.drawImage(image, transform, null);
g.dispose();
return newImage;
}
Can anyone explain to me what i might need to do different or how i can make it work? Many thanks
It's possible for image.getWidth(null) and image.getHeight(null) to return -1 if they're not known yet. Image processing can be done concurrently and it might still be going when you try to call those methods (even locally it'll do this IIRC). Here's a snippet of code I've used before to wait for an image to be loaded before trying to access its width and height:
private void waitForImage(Image image) {
Container container = new Container();
MediaTracker tracker = new MediaTracker(container);
tracker.addImage(image, 0);
try {
tracker.waitForID(0, 1000);
} catch (Exception e) {
}
}
I just tried a local test and the wait times were generally 1ms for some small images. It's not really any additional time, it just forces your code to wait for the last stage to complete to get the width and height.
The easiest way to flip the image is by negative scaling it. Example:
g2.drawImage(image, x, y, -width, height, null);
That will flip it vertically. This will flip it horizontally:
g2.drawImage(image, x, y, width, -height, null);