How can I overlay images over one another in Java? - java

So I have been posting all over and have yet to get a solid answer:
I have created an image resizing class, with a crop method. The cropping works great. The issue that I am having is the background color that I specify in the drawImage function of Graphics is not working correctly. It defaults to black as the background regardless of what I supply (in this case Color.WHITE).
Also, the overlaying image or top most image (comes from a file) is being inverted (I think it is) or otherwise discolored. Just so you can conceptualize this a little bit better, I am taking a jpeg and overlaying it on top of a new BufferedImage, the new buffered image's background is not being set. Here is the code below that I am working with:
public void Crop(int Height, int Width, int SourceX, int SourceY) throws Exception {
//output height and width
int OutputWidth = this.OutputImage.getWidth();
int OutputHeight = this.OutputImage.getHeight();
//create output streams
ByteArrayOutputStream MyByteArrayOutputStream = new ByteArrayOutputStream();
MemoryCacheImageOutputStream MyMemoryCacheImageOutputStream = new MemoryCacheImageOutputStream(MyByteArrayOutputStream);
//Create a new BufferedImage
BufferedImage NewImage = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
Graphics MyGraphics = NewImage.createGraphics();
MyGraphics.drawImage(this.OutputImage, -SourceX, -SourceY, OutputWidth, OutputHeight, Color.WHITE, null);
// Get Writer and set compression
Iterator MyIterator = ImageIO.getImageWritersByFormatName("png");
if (MyIterator.hasNext()) {
//get image writer
ImageWriter MyImageWriter = (ImageWriter)MyIterator.next();
//get params
ImageWriteParam MyImageWriteParam = MyImageWriter.getDefaultWriteParam();
//set outputstream
MyImageWriter.setOutput(MyMemoryCacheImageOutputStream);
//create new ioimage
IIOImage MyIIOImage = new IIOImage(NewImage, null, null);
//write new image
MyImageWriter.write(null, MyIIOImage, MyImageWriteParam);
}
//convert output stream back to inputstream
ByteArrayInputStream MyByteArrayInputStream = new ByteArrayInputStream(MyByteArrayOutputStream.toByteArray());
MemoryCacheImageInputStream MyMemoryCacheImageInputStream = new MemoryCacheImageInputStream(MyByteArrayInputStream);
//resassign as a buffered image
this.OutputImage = ImageIO.read(MyMemoryCacheImageInputStream);
}

Can you isolate whether it's the Graphics methods or the ImageIO methods that are mangling your image? It looks like you could test this by short-circuiting the entire ImageIO process and simply assigning
this.OutputImage = NewImage;
For that matter, I assume there's something gained by the ImageIO operations? As the sample is written, it appears to be (ideally) a no-op.
Also, you don't dispose your Graphics2D before you begin the ImageIO process. It often doesn't make a difference, but you don't want to assume that.

On the overlay color distortion problem, make sure your graphics context is in paint mode and not xor mode. (Graphics.setPaintMode()). Otherwise the color bits are XOR'd together.

Related

How to extract a subimage using PixelReader and JavaFX?

I have a .png image, and I want to extract one part of that image using the PixelReader class, and rebuild it as an image :
Image image = new Image("file:ressources/spritesheets/Zelda_Overworld.png");
byte[] buffer = new byte[1024];
PixelReader pr = image.getPixelReader();
pr.getPixels(0, 0, 16, 16, PixelFormat.getByteBgraInstance(), buffer, 0, 64);
Image tile = new Image(new ByteArrayInputStream(buffer));
I can display image and buffer seems to contain values, but I can't display tile, tile.getPixelReader() returns null, tile.getWidth() and tile.getHeight() return 0.0.
Do you know what I am doing wrong?
Paul
Let WritableImage do this for you:
Image image = new Image("file:ressources/spritesheets/Zelda_Overworld.png");
Image tile = new WritableImage(image.getPixelReader(), x, y, width, height);
Depending on the use of tile doing this may not be necessary at all. ImageView has a viewport property that allows you to choose the part of the image to display and GraphicsContext provides an overloaded version of the drawImage method to draw a part of the image to the Canvas.

How to save indexed color PNG in java

How to save image in java as java.awt.image.IndexColorModel PNG? I'm loading indexed color png with ImageIO, manipulate it with Catalino library which unfortunately converts the color space to java.awt.image.DirectColorModel.
Now I want to save the result in the exactly same format as the original image. I tried the following snippet of code.
private static void testIndexedColor() throws IOException {
FastBitmap input = new FastBitmap("test.png");
BufferedImage bi = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
bi.getGraphics().drawImage(input.toBufferedImage(), 0, 0, null);
ImageIO.write(bi, "PNG", new File("test_result.png"));
}
But in the result weird light gray pixel artifacts appeared in the white background, and PPI decreased . How to correctly convert back to indexed color mode without quality loss and distortion?
Assuming I'm correct about the Catalano framework, you should be able to re-write your methods as this:
private static void testIndexedColor() throws IOException {
BufferedImage bi = ImageIO.read(new File("test.png"));
FastBitmap input = new FastBitmap(bi);
Graphics2D g = bi.createGraphics();
try {
g.drawImage(input.toBufferedImage(), 0, 0, null);
}
finally {
g.dispose(); // Good practice ;-)
}
ImageIO.write(bi, "PNG", new File("test_result.png"));
}
At least you should get away with the fixed palette and the artifacts.
However, this will likely still modify the PPI (but this won't affect the pixels). And even in some cases the image might be written back as a non-palette PNG.
Update: It seems the PNGImageWriter (through the PNGMetadata) actually re-writes an IndexColorModel containing a perfect grayscale, to a grayscale PNG by default. This is normally a good idea, as you reduce file size by not writing the PLTE chunk. You should be able to get around this, by passing the metadata from the original, along with the image pixel data, to instruct the writer to keep the IndexColorModel (ie. write PLTE chunk):
private static void testIndexedColor() throws IOException {
File in = new File("test.png");
File out new File("test_result.png");
try (ImageInputStream input = ImageIO.createImageInputStream(in);
ImageOutputStream output = ImageIO.createImageOutputStream(out)) {
ImageReader reader = ImageIO.getImageReaders(input).next(); // Will fail if no reader
reader.setInput(input);
ImageWriter writer = ImageIO.getImageWriter(reader); // Will obtain a writer that understands the metadata from the reader
writer.setOutput(output); // Will fail if no writer
// Now, the important part, we'll read the pixel AND metadata all in one go
IIOImage image = reader.readAll(0, null); // PNGs only have a single image, so index 0 is safe
// You can now access and modify the image data using:
BufferedImage bi = (BufferedImage) image.getRenderedImage();
FastBitmap fb = new FastBitmap(bi);
// ...do stuff...
Graphics2D g = bi.createGraphics();
try {
g.drawImage(fb.toBufferedImage(), 0, 0, null);
}
finally {
g.dispose();
}
// Write pixel and metadata back
writer.write(null, image, writer.getDefaultWriteParam());
}
}
This should (as a bonus) also keep your PPI as-is.
PS: For production code, you also want to dispose() of the reader and writer above, but I left it out to keep focus and avoid further discussion on try/finally. ;-)

How to make a TIFF transparent in Java using JAI?

I'm trying to write a TIFF from a BufferedImage using Java Advanced Imaging (JAI), and am unsure of how to make it transparent. The following method works for making PNGs and GIFs transparent:
private static BufferedImage makeTransparent(BufferedImage image, int x, int y) {
ColorModel cm = image.getColorModel();
if (!(cm instanceof IndexColorModel)) {
return image;
}
IndexColorModel icm = (IndexColorModel) cm;
WritableRaster raster = image.getRaster();
int pixel = raster.getSample(x, y, 0);
// pixel is offset in ICM's palette
int size = icm.getMapSize();
byte[] reds = new byte[size];
byte[] greens = new byte[size];
byte[] blues = new byte[size];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
IndexColorModel icm2 = new IndexColorModel(8, size, reds, greens, blues, pixel);
return new BufferedImage(icm2, raster, image.isAlphaPremultiplied(), null);
}
But when writing a TIFF, the background is always white. Below is my code used for writing TIFF:
BufferedImage destination = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
Graphics imageGraphics = destination.getGraphics();
imageGraphics.drawImage(sourceImage, 0, 0, backgroundColor, null);
if (isTransparent) {
destination = makeTransparent(destination, 0, 0);
}
destination.createGraphics().drawImage(sourceImage, 0, 0, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
TIFFImageWriter writer = new TIFFImageWriter(new TIFFImageWriterSpi());
writer.setOutput(ios);
writer.write(destination);
I also do some metadata manipulation later as I'm actually dealing with GeoTIFF. But still the images are white at this point. While debugging, I can view the BufferedImage and it is transparent, but when i write the image, the file has a white background. Do I need to do something specific with TiffImageWriteParam? Thanks for any help you can provide.
TIFF has no option of storing transparency info (alpha channel) in the palette (as in your IndexedColorModel). The palette only supports RGB triplets. Thus, the fact that you set a color index to transparent is lost when you write the image to a TIFF.
If you need a transparent TIFF, your options are:
Use normal RGBA instead of indexed color (RGB, 4 samples/pixel, unassociated alpha). Just use BufferedImage.TYPE_INT_ARGB or TYPE_4BYTE_ABGR, probably. This will make the output file bigger, but is easy to implement, and should be more compatible. Supported by almost all TIFF software.
Save a separate transparency mask (a 1 bit image with photometric interpretation set to 4) with the palette image. Not sure if it supported by much software, some may display the mask as a separate b/w image. Not sure how this can be achieved in JAI/ImageIO, might require writing a sequence and setting some extra metadata.
Store a custom field containing the transparent index. Will not be supported by anything but your own software, but the file will still be compatible and displayed with the white (solid) background in other software. You should be able to set this using the TIFF metadata.

How can I create an image with Java?

I would like to create a gif image of a filled red circle on a green background. What is the easiest way to do it in Java?
If you already have an image file or image URL, then you can use Toolkit to get an Image:
Image img = Toolkit.getDefaultToolkit().createImage(filename);
If you need to construct a new image raster and paint into the image, then you can use BufferedImage. You can paint onto a BufferedImage, by calling its createGraphics function and painting on the graphics object. To save the BufferedImage into a GIF, you can use the ImageIO class to write out the image.
The best way is to generate a BufferedImage:
BufferedImage img = new BufferedImage(int width, int height, int imageType)
// you can find the Types variables in the api
Then, generated the Graphics2D of this image, this object allows you to set a background and to draw shapes:
Graphics2D g = img.createGraphics();
g.setBackground(Color color) ; //Find how to built this object look at the java api
g.draw(Shape s);
g.dispose(); //don't forget it!!!
To built the image:
File file = new File(dir, name);
try{
ImageIO.write(img, "gif", file);
}catch(IOException e){
e.printStackTrace();
}
Create a BufferedImage and then write it to a file with ImageIO.write(image, "gif", fileName).

In java, how do you write a java.awt.image.BufferedImage to an 8-bit png file?

I am trying to write out a png file from a java.awt.image.BufferedImage. Everything works fine but the resulting png is a 32-bit file.
Is there a way to make the png file be 8-bit? The image is grayscale, but I do need transparency as this is an overlay image. I am using java 6, and I would prefer to return an OutputStream so that I can have the calling class deal with writing out the file to disk/db.
Here is the relevant portion of the code:
public static ByteArrayOutputStream createImage(InputStream originalStream)
throws IOException {
ByteArrayOutputStream oStream = null;
java.awt.Image newImg = javax.imageio.ImageIO.read(originalStream);
int imgWidth = newImg.getWidth(null);
int imgHeight = newImg.getHeight(null);
java.awt.image.BufferedImage bim = new java.awt.image.BufferedImage(imgWidth,
imgHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);
Color bckgrndColor = new Color(0x80, 0x80, 0x80);
Graphics2D gf = (Graphics2D)bim.getGraphics();
// set transparency for fill image
gf.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
gf.setColor(bckgrndColor);
gf.fillRect(0, 0, imgWidth, imgHeight);
oStream = new ByteArrayOutputStream();
javax.imageio.ImageIO.write(bim, "png", oStream);
oStream.close();
return oStream;
}
The build in imageio png writer will write 32bit png files on all the platforms I have used it on, no matter what the source image is. You should also be aware that many people have complained that the resulting compression is much lower than what is possible with the png format. There are several independent png libraries available that allow you to specify the exact format, but I don't actually have any experience with any of them.
I found the answer as to how to convert RGBA to Indexed here: http://www.eichberger.de/2007/07/transparent-gifs-in-java.html
However, the resulting 8-bit png file only has 100% or 0% transparency. You could probably tweak the IndexColorModel arrays, but we have decided to make the generated file (what was an overlay mask) into an underlay jpg and use what was the static base as the transparent overlay.
It is an interesting question... It is late, I will experiment tomorrow. I will first try and use a BufferedImage.TYPE_BYTE_INDEXED (perhaps after drawing) to see if Java is smart enough to generate an 8bit PNG.
Or perhaps some image library can allow that.
[EDIT] Some years later... Actually, I made the code at the time, but forgot to update this thread... I used the code pointed at by Kat, with a little refinement on the handling of transparency, and saving in PNG format instead of Gif format. It works in making a 8-bit PNG file with all-or-nothing transparency.
You can find a working test file at http://bazaar.launchpad.net/~philho/+junk/Java/view/head:/Tests/src/org/philhosoft/tests/image/AddTransparency.java
using my ImageUtil class.
Since the code isn't that big, for posterity sake, I post it here, without the JavaDoc to save some lines.
public class ImageUtil
{
public static int ALPHA_BIT_MASK = 0xFF000000;
public static BufferedImage imageToBufferedImage(Image image, int width, int height)
{
return imageToBufferedImage(image, width, height, BufferedImage.TYPE_INT_ARGB);
}
public static BufferedImage imageToBufferedImage(Image image, int width, int height, int type)
{
BufferedImage dest = new BufferedImage(width, height, type);
Graphics2D g2 = dest.createGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
return dest;
}
public static BufferedImage convertRGBAToIndexed(BufferedImage srcImage)
{
// Create a non-transparent palletized image
Image flattenedImage = transformTransparencyToMagenta(srcImage);
BufferedImage flatImage = imageToBufferedImage(flattenedImage,
srcImage.getWidth(), srcImage.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
BufferedImage destImage = makeColorTransparent(flatImage, 0, 0);
return destImage;
}
private static Image transformTransparencyToMagenta(BufferedImage image)
{
ImageFilter filter = new RGBImageFilter()
{
#Override
public final int filterRGB(int x, int y, int rgb)
{
int pixelValue = 0;
int opacity = (rgb & ALPHA_BIT_MASK) >>> 24;
if (opacity < 128)
{
// Quite transparent: replace color with transparent magenta
// (traditional color for binary transparency)
pixelValue = 0x00FF00FF;
}
else
{
// Quite opaque: get pure color
pixelValue = (rgb & 0xFFFFFF) | ALPHA_BIT_MASK;
}
return pixelValue;
}
};
ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
public static BufferedImage makeColorTransparent(BufferedImage image, int x, int y)
{
ColorModel cm = image.getColorModel();
if (!(cm instanceof IndexColorModel))
return image; // No transparency added as we don't have an indexed image
IndexColorModel originalICM = (IndexColorModel) cm;
WritableRaster raster = image.getRaster();
int colorIndex = raster.getSample(x, y, 0); // colorIndex is an offset in the palette of the ICM'
// Number of indexed colors
int size = originalICM.getMapSize();
byte[] reds = new byte[size];
byte[] greens = new byte[size];
byte[] blues = new byte[size];
originalICM.getReds(reds);
originalICM.getGreens(greens);
originalICM.getBlues(blues);
IndexColorModel newICM = new IndexColorModel(8, size, reds, greens, blues, colorIndex);
return new BufferedImage(newICM, raster, image.isAlphaPremultiplied(), null);
}
}
Thanks for responding, I was going to try an TYPE_BYTE_INDEXED with an IndexColorModel and may still but if ImageIO writes out 32-bit regardless it appears that I may be wasting my time there.
The image I am trying to write out can be very large (up to 8000x4000) but is just a simple mask for the image underneath, so will only have a ~30% transparent gray and a 100% transparent cutout. I would use a GIF but IE6 seems to have trouble with displaying one that large.
It only gets generated once and in an internal set-up type screen, so performance isn't an issue either, but it does have to be done within the java code and not by an offline utility.
The libraries that you specified might be used to transform it while writing... I am going to go check that out.
If anyone has a better way, please let me know!!
Thanks!

Categories

Resources