Writing a BMP with transparency using ImageIO - java

Does anyone have a way of storing a BufferedImage with transparency as BMP in Java? Preferably using the ImageIO API.
For some reason I can't write a BMP in ARGB (BGRA) format, even though the BMP has supported alpha channel since, at least, Win95. I can, however, easily write the same image as a PNG. It also works fine storing an image without alpha, like TYPE_INT_RGB or TYPE_3BYTE_BGR.
Consider the following code snippet:
public static void main(String[] args) throws IOException {
if (!ImageIO.write(new BufferedImage(300, 200, BufferedImage.TYPE_INT_ARGB), "BMP", new File("foo.bmp"))) {
System.err.println("Couldn't write BMP!");
}
if (!ImageIO.write(new BufferedImage(300, 200, BufferedImage.TYPE_INT_ARGB), "PNG", new File("foo.png"))) {
System.err.println("Couldn't write PNG!");
}
}
Output:
Couldn't write BMP!

In BMPImageWriterSpi:
public boolean canEncodeImage(ImageTypeSpecifier type) {
...
if (!(numBands == 1 || numBands == 3))
return false;
I'm afraid Java doesn't support 32 bit BMPs. You'd have to write your own or find an existing library (ImageJ comes to mind although I don't know if it supports BMP). Wikipedia is very elaborate on the file format and it looks quite easy, even giving a complete field-by-field example of a 32-bit BMP.

Related

Compress JPEG image to specific quality level without using Bitmap#compress() method

I want to compress a Bitmap (JPEG image) to quality 64, the result with be stored in a byte array. Previously I can achieve this with Bitmap#compress() method:
public static byte[] compressJPEG(Bitmap bmp, int quality) {
if (quality <= 0)
return new byte[0];
byte[] array = new byte[0];
try {
ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, quality, bmpStream);
array = bmpStream.toByteArray();
bmpStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return array;
}
But the result byte array (with the same input image file) is different when I run this on different devices. (May be because it use nativeCompress() internally)
After checking, I find out the different is in Huffman Table(s), SOS & image data parts, see this:
Diff check between 2 image photos
So how can I compress JPEG image to specific quality level without using Bitmap#compress() method? Because I really want the result byte array to be similar in structure.
There is no concept of "quality level" in JPEG. It is a shorthand that some encoders use to select quantization tables. There are a number of variables in JPEG compression, including the type and breakdown of scans, samples, huffman table selection, and quantization table selection. If any of those are different in the encoder you use, you are going to get different output. It's the nature of the beast.

javafx read javafx.scene.image.Image to ImageIO.write(), from CMYK to RGB

What I want to do is read an image from FileChooser and write it to file. I had to store the image in a javafx.scene.image.Image so that I can display it and clip it inside a circle. I have a little problem with trying to write the image that I got from javafx.scene.image.Image to file. The conversion process is not fluid, converts from CMYK to RGB (therefore turning my picture to some pink thing.
Please, I have checked a lot of other sources, and no one has been able to give me a notable solution
FileChooser fileChooser = new FileChooser();
File selectedFile = fileChooser.showOpenDialog(parent);
// get Image from selectedFile
Image userImg = = new Image( selectedFile.toURI().toURL().toString() );
if ( userImg != null ) {
String format = "jpg";
String filename = "d:\\pictureName."+ format;
File file = new File(filename);
// convert Image to BufferedImage
BufferedImage bufferedImage = SwingFXUtils.fromFXImage( userImg, null);
try {
// this is where i want to convert the color mode
// from cmyk to rgb, before i write it to file
ImageIO.write( bufferedImage, format, file );
} catch (IOException e) {
System.out.println("Exception :: "+ e.getMessage() );
}
}
Why do you think that there is some CMYK to RGB conversion happening? I suspect the reason for your "pink thing" is something different. The easiest way to find out is to change your output format from jpeg to png and see whether it makes a difference.
I think you are again one of the many people who are hit by this bug https://bugs.openjdk.java.net/browse/JDK-8119048 which is not considered important enough to be fixed. If you read the comments in there you will find a work-arround. Basically the idea is to copy the image after the conversion into a new image without alpha channel.
I'd really like to know how many more people have to waste their time until this bug finally gets enough attention to be fixed.

Barcode4J on Android ( missing BufferedImage support )

I have been trying to use Barcode4J on Android but I can not seem to get BufferedImage class and I am not sure how I am suppose to replace this class with anything from Android.graphic.* which does not seem to have something similar. Also the Barcode4J will not accept anything other then BufferedImage object for obvious reasons.
What could I use instead or is there a Barcode generator Lib better suited for Android?
I have tried Barcode4Android which really made no sense since the Example they gave on GIT used BufferedImage from the java.awt.image.BufferedReader package also >.< . So I was back at step 1.
I actually just need the QR generating function.
My Questions.
1. Is there an Alternative to Barcode4J for Android.
2. OR is there a work around for my problem ?
Here is one of the Java tutorials I tried to use
public class HelloExample1 {
public static void main(String[] args) throws Exception{
//Create the barcode bean
Code39Bean bean = new Code39Bean();
final int dpi = 150;
//Configure the barcode generator
bean.setModuleWidth(UnitConv.in2mm(1.0f / dpi)); //makes the narrow bar, width exactly one pixel
bean.setWideFactor(3);
bean.doQuietZone(false);
//Open output file
File outputFile = new File("resources"+"/"+"images"+"/"+"out.png");
OutputStream out = new FileOutputStream(outputFile);
try {
//Set up the canvas provider for monochrome PNG output
BitmapCanvasProvider canvas = new BitmapCanvasProvider(
out, "image/x-png", dpi, BufferedImage.TYPE_BYTE_BINARY, false, 0);
//Generate the barcode
bean.generateBarcode(canvas, "Hello World");
//Signal end of generation
canvas.finish();
} finally {
out.close();
}
}
}
Try Zxing, its a code generator and reader, easy to use in Android. Hope it helps.

BufferedImage get resized with different colors

I am resizing many jpeg images using Apache Sanselan which also deals with CMYK colors.
I have a problem when trying to convert jpeg images that has an alpha channel... when doing it the result is an image with different colors, and i guess that java somehow handles these type of images as a different color format.
As i said, the RGB resizing works fine as well as CMYK. ARGB images turn out with different colors.
An example:
Any suggestions? Can i force somehow ignore the alpha channel and handle the image as an RGB image? or convert it to be an RGB image without losing the real colors?
The code that handles this image is:
ImageInputStream stream = ImageIO.createImageInputStream(file);
Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
while (iter.hasNext()) {
ImageReader reader = iter.next();
reader.setInput(stream);
BufferedImage image = null;
ICC_Profile profile = null;
try {
image = reader.read(0);
} catch (IIOException e) {
... (CMYK conversion if needed)
}
return image;
}
return null;
Thanks in advance
I found a good solution here (first solution worked great):
problem using ImageIO.write jpg file
Edit:
There is a new open source library which supports CMYK processing.
All you need to do is to add the dependency to your project and a new reader will be added to the list of readers (while the known JPEGImageReader can't deal with CMYK).
You will probably want to iterate over these readers and read the image using the first reader which doesn't throw exception.
This package is a release candidate, but i am using it and it solved a huge problem that we had hard time dealing with.
http://mvnrepository.com/artifact/com.twelvemonkeys.imageio/imageio-jpeg/3.0-rc5
You can do the iteration this way to get the BufferedImage, and after you got that, the rest is easy (you can use any existing image converting package to save it as another format):
try (ImageInputStream input = ImageIO.createImageInputStream(source)) {
// Find potential readers
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
// For each reader: try to read
while (readers != null && readers.hasNext()) {
ImageReader reader = readers.next();
try {
reader.setInput(input);
BufferedImage image = reader.read(0);
return image;
} catch (IIOException e) {
// Try next reader, ignore.
} catch (Exception e) {
// Unexpected exception. do not continue
throw e;
} finally {
// Close reader resources
reader.dispose();
}
}
// Couldn't resize with any of the readers
throw new IIOException("Unable to resize image");
}

ImageIO.read illegal argument exception - raster bands/colour space components?

Apologies for the somewhat vague title, I can't work out what the keywords are here. The setup's quite simple, I'm opening an image with
ImageIO.read(new File(filename));
This works for most files, however for one I get an IllegalArgumentException with the detail: "numbers of source Raster bands and source color space components do not match". This image was obtained via wget on a valid Flickr URL, and I've used other images obtained this way, so the method for obtaining images seems sound in principle. I'm not sure what's causing the exception.
A workaround would be more than acceptable - I'm not fussed with using ImageIO in particular, and the image looks fine visually. I just need to get it being read without Java freaking out!
Here's the image in question, in case it's of any use:
So I was having this same issue and found that the image was gray-scale and that the default ImageIO.read implementation was not figuring that out because the image metadata wasn't quite as expected. I wrote a work around that retries the load as 'BufferedImage.TYPE_BYTE_GRAY' if it fails the main load.
Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
Exception lastException = null;
while (iter.hasNext()) {
ImageReader reader = null;
try {
reader = (ImageReader)iter.next();
ImageReadParam param = reader.getDefaultReadParam();
reader.setInput(stream, true, true);
Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
while (imageTypes.hasNext()) {
ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
param.setDestinationType(imageTypeSpecifier);
break;
}
}
bufferedImage = reader.read(0, param);
if (null != bufferedImage) break;
} catch (Exception e) {
lastException = e;
} finally {
if (null != reader) reader.dispose();
}
}
// If you don't have an image at the end of all readers
if (null == bufferedImage) {
if (null != lastException) {
throw lastException;
}
}
The error message is informative and indicates that the number of raster bands, as mentioned in the ICC color profile, seems to be incorrect. I used ImageMagick to strip the ICC profile from the image. ImageIO subsequently has no problems reading the images (~1k bad images). Hope that helps.
It is possible to read this image using twelvemonkeys ImageIO, which is a more robust and forgiving replacement for the original ImageIO provided by the JRE.
See https://github.com/haraldk/TwelveMonkeys/
I found this solution in the PDF Box Jira https://issues.apache.org/jira/browse/PDFBOX-3637
In order to use twelvemonkeys, it is sufficient to add it as a maven dependency. It then registers itself before the default image processor.
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.3.2</version> <!-- Alternatively, build your own version -->
</dependency>

Categories