I have a web application written in Java (Spring, Hibernate/JPA, Struts2) where users can upload images and store them in the file system. I would like to scale those images so that they are of a consistent size for display on the site. What libraries or built in functions will offer the best results? I will consider the following criteria in making my decision (in this order):
Free/Open Source (essential)
Easy to implement
Quality of results
Performance
Size of executable
I would really recommend giving imgscalr a look.
It is released under an Apache 2 license, hosted on GitHub, been deployed in a handful of web applications already, has a very simple, but pedantically documented API, has code that works around 2 major image bugs in the JDK for you transparently that you'll only ever notice if you suddenly start getting "black" images after a scale operation or horrible-looking results, gives you the best possible looking results available in Java, is available via Maven as well as a ZIP and is just a single class.
Basic use looks like this:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 320);
This is the simplest call where the library will make a best-guess at the quality, honor your image proportions, and fit the result within a 320x320 bounding box. NOTE, the bounding box is just the maximum W/H used, since your image proportions are honored, the resulting image would still honor that, say 320x200.
If you want to override the automatic mode and force it to give you the best-looking result and even apply a very mild anti-alias filter to the result so it looks even better (especially good for thumbnails), that call would look like:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY,
150, 100, Scalr.OP_ANTIALIAS);
These are all just examples, the API is broad and covers everything from super-simple use cases to very specialized. You can even pass in your own BufferedImageOps to be applied to the image (and the library automatically fixes the 6-year BufferedImageOp JDK bug for you!)
There is a lot more to scaling images in Java successfully than the library does for you, for example always keeping the image in one of the best supported RGB or ARGB image types while operating on it. Under the covers the Java2D image processing pipeline falls back to an inferior software pipeline if the image type used for any image operations is poorly supported.
If all that sounded like a lot of headaches, it sort of is... that's why I wrote the library and open-sourced it, so folks could just resize their images and move on with their lives without needing to worry about it.
Hope that helps.
Have a look at the Java Image I/O API to read/write the image. Then use AffineTransform to resize.
Also, here's a complete example using java.awt.Image.
Look into also to java-image-scaling library. It created better quality images that ImageIO.
The best tool for image editing is ImageMagick and it is open source.
There are two interfaces for the Java Language:
JMagick which uses JNI interface to ImageMagick
and
im4java what is a command line interface for ImageMagick
Found this to be faster:
public static BufferedImage getScaledInstance(final BufferedImage img, final int targetWidth, final int targetHeight,
final Object hint) {
final int type = BufferedImage.TYPE_INT_RGB;
int drawHeight = targetHeight;
int drawWidth = targetWidth;
final int imageWidth = img.getWidth();
final int imageHeight = img.getHeight();
if ((imageWidth <= targetWidth) && (imageHeight <= targetHeight)) {
logger.info("Image " + imageWidth + "/" + imageHeight + " within desired scale");
return img;
}
final double sar = ((double) imageWidth) / ((double) imageHeight);
if (sar != 0) {
final double tar = ((double) targetWidth) / ((double) targetHeight);
if ((Math.abs(tar - sar) > .001) && (tar != 0)) {
final boolean isSoureWider = sar > (targetWidth / targetHeight);
if (isSoureWider) {
drawHeight = (int) (targetWidth / sar);
}
else {
drawWidth = (int) (targetHeight * sar);
}
}
}
logger.info("Scaling image from " + imageWidth + "/" + imageHeight + " to " + drawWidth + "/" + drawHeight);
final BufferedImage result = new BufferedImage(drawWidth, drawHeight, type);
try {
final Graphics2D g2 = result.createGraphics();
try {
if (hint != null) {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
}
g2.drawImage(img, 0, 0, drawWidth, drawHeight, null);
}
finally {
g2.dispose();
}
return result;
}
finally {
result.flush();
}
}
I know this is a very old question, but I got my own solution for this using standard Java API
import java.awt.*;
import java.awt.event.*;
import javax.imageio.*
import java.awt.image.*;
BufferedImage im, bi, bi2;
Graphics2D gfx;
int imWidth, imHeight, dstWidth, dstHeight;
int DESIRED_WIDTH = 500, DESIRED_HEIGHT = 500;
im = ImageIO.read(new File(filePath));
imWidth = im.getWidth(null);
imHeight = im.getHeight(null);
dstWidth = DESIRED_WIDTH;
dstHeight = (dstWidth * imHeight) / imWidth;
bi = new BufferedImage(dstWidth, dstHeight, im.getType());
gfx = bi.createGraphics();
gfx.drawImage(im, 0, 0, dstWidth, dstHeight, 0, 0, imWidth, imHeight, null);
bi2 = new BufferedImage(DESIRED_WIDTH, DESIRED_HEIGHT, im.getType());
gfx = bi2.createGraphics();
gfx.drawImage(bi, 0, 0, DESIRED_WIDTH, DESIRED_HEIGHT, null);
ImageIO.write(bi2, "jpg", new File(filePath));
I am sure it can be improved and adapted.
I tried imgscalr comparing to standard Java 1.6 and I cannot say it is better.
What I've tried is
BufferedImage bufferedScaled = Scalr.resize(sourceImage, Method.QUALITY, 8000, height);
and
Image scaled = sourceImage.getScaledInstance(-1, height, Image.SCALE_SMOOTH);
BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
bufferedScaled.getGraphics().drawImage(scaled, 0, 0, null);
some 5 minute testing by my eye got impression that second thing (pure Java 1.6) produces better results.
Related
I'm using JVM 14.0.2 in VSCode IDE.
The purpose of the code is to change the original input image to grayscale image and save the new gray image to the desired location.
The code runs with no exceptions and i tried to print some progress lines(System.out.println("Saving completed...");), those lines printed throughout the program where i plugged in. However, when i go to the selected filepath to search for the saved GrayScale image, i do not see the new image in the directory.
I then tried the BlueJ IDE, and the gray image was saved. Can you check if it's VSCode developing environment issue or my code issue? or I need a different class/method to edit images in VSCode? Thanks for your help.Let me know if you need more details.
public class GrayImage {
public static void main(String args[]) throws IOException {
BufferedImage img = null;
// read image
try {
File f = new File("C:\\original.jpg");
img = ImageIO.read(f);
// get image width and height
int width = img.getWidth();
int height = img.getHeight();
BufferedImage grayimg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// convert to grayscale
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color color = new Color(img.getRGB(x, y));
int r = (int) color.getRed();
int g = (int) color.getBlue();
int b = (int) color.getGreen();
// calculate average
int avg = (r + g + b) / 3;
// replace RGB value with avg
Color newColor = new Color(avg, avg, avg, color.getAlpha());
grayimg.setRGB(x, y, newColor.getRGB());
}
}
// write image
System.out.println("Trying to write the new image...");
File newf = new File("H:\\gray.jpg");
ImageIO.write(grayimg, "jpg", newf);
System.out.println("Finished writing the new image...");
} catch (IOException e) {
System.out.println(e);
}
}// main() ends here
}
If I understand this problem correctly, the important lesson here is that ImageIO.write(...) returns a boolean, indicating whether it succeeded or not. You should handle situations where the value is false, even if there is no exception. For reference, see the API doc.
Something like:
if (!ImageIO.write(grayimg, "JPEG", newf)) {
System.err.println("Could not store image as JPEG: " + grayimg);
}
Now, for the reason your code does indeed work in one JRE and not in another, is probably related to the image being of type TYPE_INT_ARGB (ie. contains alpha channel). This used to work in Oracle JDK/JREs but support was removed:
Previously, the Oracle JDK used proprietary extensions to the widely used IJG JPEG library in providing optional color space support.
This was used to support PhotoYCC and images with an alpha component on both reading and writing. This optional support has been removed in Oracle JDK 11.
The fix is easy; as your source is a JPEG file, it probably does not contain an alpha component anyway, so you could change to a different type with no alpha. As you want a gray image, I believe the best match would be:
BufferedImage grayimg = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
But TYPE_INT_RGB or TYPE_3BYTE_BGR should work too, should you later run into the same problem with color images.
I need a way to find an image on the screen. I've searched for ways to do this on SO but some take extremely long. I need it to be fast and efficient, does not need to be accurate. Basically i'm planning to compare or search for a small pixelated image, say 11x10 pixels for example, on the screen.
I also need a way to know the x and y coordinates of the small image on the screen.
Although I've looked through many tools out there like JavaCV and OpenCV, I just wanted to see if there are any other ways to do this.
TL;DR
I need a fast way to search for a small (11x10 example.) image on the screen and know its x,y coordinates.
I think you many find this answer relevant! But it is for Windows & in c++. But i'm sure that you can convert it very easily to any language.
This question is very old, But im trying to acheive the exact same thing here. Ive found that combining these answers would do the trick:
Convert BufferedImage TYPE_INT_RGB to OpenCV Mat Object
OpenCV Template Matching example in Android
The reason you need to do a conversion is because when u grab a screenshot with awt.Robot class its in the INT_RGB format. The matching template example expects bytes and you cannot grab byte data from this type of image directly.
Heres my implementation of these two answers, but it is incomplete. The output is all screwed up and i think it may have something to do with the IntBuffer/ByteBuffers.
-Edit-
I've added a new helper method that converts a INT_RGB to a BYTE_BGR. I can now grab the coordinates of template on the image using matchLoc.This seems to work pretty well, I was able to use this with a robot that clicks the start menu for me based on the template.
private BufferedImage FindTemplate() {
System.out.println("\nRunning Template Matching");
int match_method = Imgproc.TM_SQDIFF;
BufferedImage screenShot = null;
try {
Robot rob = new Robot();
screenShot = rob.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
} catch (AWTException ex) {
Logger.getLogger(MainGUI.class.getName()).log(Level.SEVERE, null, ex);
}
if(screenShot == null) return;
Mat img = BufferedImageToMat(convertIntRGBTo3ByteBGR(screenShot));
String templateFile = "C:\\Temp\\template1.JPG";
Mat templ = Highgui.imread(templateFile);
// / Create the result matrix
int result_cols = img.cols() - templ.cols() + 1;
int result_rows = img.rows() - templ.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
// / Do the Matching and Normalize
Imgproc.matchTemplate(img, templ, result, match_method);
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
Highgui.imwrite("out2.png", result);
// / Localizing the best match with minMaxLoc
MinMaxLocResult mmr = Core.minMaxLoc(result);
Point matchLoc;
if (match_method == Imgproc.TM_SQDIFF
|| match_method == Imgproc.TM_SQDIFF_NORMED) {
matchLoc = mmr.minLoc;
} else {
matchLoc = mmr.maxLoc;
}
Graphics2D graphics = screenShot.createGraphics();
graphics.setColor(Color.red);
graphics.setStroke(new BasicStroke(3));
graphics.drawRect(matchLoc.x, matchLoc.y, templ.width(), templ.height());
graphics.dispose();
return screenShot;
}
private Mat BufferedImageToMat(BufferedImage img){
int[] data = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 4);
IntBuffer intBuffer = byteBuffer.asIntBuffer();
intBuffer.put(data);
Mat mat = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, byteBuffer.array());
return mat;
}`
private BufferedImage convertIntRGBTo3ByteBGR(BufferedImage img){
BufferedImage convertedImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D graphics = convertedImage.createGraphics();
graphics.drawImage(img, 0, 0, null);
graphics.dispose();
return convertedImage;
}
Results:
Template:
I'm working on a website that allows users to upload images and crop them.
I convert every image to .PNG for better quality. The problem I'm having is the picture size.
If I upload a 200 kb image, after cropping and making it PNG it has 600 kb. This is not a solution for me, because the images are stored in the database as BLOBs and the website loads slower.
I'm trying to find a way to compress the png, to have a smaller size, without reducing the quality.
I couldn't find any library or solution for this problem. I need something for Java like TinyPNG.
This is how I do it:
BufferedImage resizedImage = resizeImage(image,extension,width,height);
System.out.println("dimensiuni:" + resizedImage.getHeight()+ "x" + resizedImage.getWidth());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( resizedImage, "png", baos );
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
And this is the resizeImage function:
public BufferedImage resizeImage(BufferedImage image, String extension, int targetWidth, int targetHeight) {
int type = (image.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage)image;
int w, h;
w = image.getWidth();
h = image.getHeight();
do {
if (w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if ( h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_DEFAULT);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp.flush();
} while (w != targetWidth || h != targetHeight);
return ret;
}
Help me!!
On tinypng website they have explained in how doe it work question that how they compress the image see below:
How does it work?
Excellent question! When you upload a PNG (Portable Network Graphics)
file, similar colors in your image are combined. This technique is
called “quantization”. By reducing the number of colors, 24-bit PNG
files can be converted to much smaller 8-bit indexed color images. All
unnecessary metadata is stripped too. The result better PNG files with
100% support for transparency. Have your cake and eat it too!
Absolutely they are using some libraries for that,
You can use http://pngquant.org/ for compressing or reducing size of image,
In another SO question someone answered that tinypng also uses pngquant library, but i don't know how far the truth in it, but you can use this library in Java also,
In the pngquant website they have given the java library link, take a look at it, i have provided that same github repository link as provided in website:
https://github.com/ImageOptim/libimagequant/tree/master/org/pngquant
Basically the above library is written in c/c++ so it uses JNI(Java Native Interface) the interface which joins c/c++ with Java.
I am trying to implement a simple class that will allow a user to crop an image to be used for their profile picture. This is a java web application.
I have done some searching and found that java.awt has a BufferedImage class, and this appears (at first glance) to be perfect for what I need. However, it seems that there is a bug in this (or perhaps java, as I have seen suggested) that means that the cropping does not always work correctly.
Here is the code I am using to try to crop my image:
BufferedImage profileImage = getProfileImage(form, modelMap);
if (profileImage != null) {
BufferedImage croppedImage = profileImage
.getSubimage(form.getStartX(), form.getStartY(), form.getWidth(), form.getHeight());
System.err.println(form.getStartX());
System.err.println(form.getStartY());
File finalProfileImage = new File(form.getProfileImage());
try {
String imageType = getImageType(form.getProfileImage());
ImageIO.write(croppedImage, imageType, finalProfileImage);
}
catch (Exception e) {
logger.error("Unable to write cropped image", e);
}
}
return modelAndView;
}
protected BufferedImage getProfileImage(CropImageForm form, Map<String, Object> modelMap) {
String profileImageFileName = form.getProfileImage();
if (validImage(profileImageFileName) && imageExists(profileImageFileName)) {
BufferedImage image = null;
try {
image = getCroppableImage(form, ImageIO.read(new File(profileImageFileName)), modelMap);
}
catch (IOException e) {
logger.error("Unable to crop image, could not read profile image: [" + profileImageFileName + "]");
modelMap.put("errorMessage", "Unable to crop image. Please try again");
return null;
}
return image;
}
modelMap.put("errorMessage", "Unable to crop image. Please try again.");
return null;
}
private boolean imageExists(String profileImageFileName) {
return new File(profileImageFileName).exists();
}
private BufferedImage getCroppableImage(CropImageForm form, BufferedImage image, Map<String, Object> modelMap) {
int cropHeight = form.getHeight();
int cropWidth = form.getWidth();
if (cropHeight <= image.getHeight() && cropWidth <= image.getWidth()) {
return image;
}
modelMap.put("errorMessage", "Unable to crop image. Crop size larger than image.");
return null;
}
private boolean validImage(String profileImageFileName) {
String extension = getImageType(profileImageFileName);
return (extension.equals("jpg") || extension.equals("gif") || extension.equals("png"));
}
private String getImageType(String profileImageFileName) {
int indexOfSeparator = profileImageFileName.lastIndexOf(".");
return profileImageFileName.substring(indexOfSeparator + 1);
}
The form referred to in this code snippet is a simple POJO which contains integer values of the upper left corner to start cropping (startX and startY) and the width and height to make the new image.
What I end up with, however, is a cropped image that always starts at 0,0 rather than the startX and startY position. I have inspected the code to make sure the proper values are being passed in to the getSubimage method, and they appear to be.
Are there simple alternatives to using BufferedImage for cropping an image. I have taken a brief look at JAI. I would rather add a jar to my application than update the jdk installed on all of the production boxes, as well as any development/testing servers and local workstations.
My criteria for selecting an alternative are:
1) simple to use to crop an image as this is all I will be using it for
2) if not built into java or spring, the jar should be small and easily deployable in a web-app
Any suggestions?
Note: The comment above that there is an issue with bufferedImage or Java was something I saw in this posting: Guidance on the BufferedImage.getSubimage(int x, int y, int w, int h) method?
I have used getSubimage() numerous times before without any problems. Have you added a System.out.println(form.getStartX() + " " + form.getStartY()) before that call to make sure they're not both 0?
Also, are you at least getting an image that is form.getWidth() x form.getHeight()?
Do make sure you are not modifying/disposing profileImage in any way since the returned BufferedImage shares the same data array as the parent.
The best way is to just simply draw it across if you want a completely new and independent BufferedImage:
BufferedImage croppedImage = new BufferedImage(form.getWidth(),form.getHeight(),BufferedImage.TYPE_INT_ARGB);
Graphics g = croppedImage.getGraphics();
g.drawImage(profileImage,0,0,form.getWidth(),form.getHeight(),form.getStartX(),form.getStartY(),form.getWidth(),form.getHeight(),null);
g.dispose();
You can do it in this manner as well (code is not 100% tested as I adopted for example from an existing app i did):
import javax.imageio.*;
import java.awt.image.*;
import java.awt.geom.*;
...
BufferedImage img = ImageIO.read(imageStream);
...
/*
* w = image width, h = image height, l = crop left, t = crop top
*/
ColorModel dstCM = img.getColorModel();
BufferedImage dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(w, h), dstCM.isAlphaPremultiplied(), null);
Graphics2D g = dst.createGraphics();
g.drawRenderedImage(img, AffineTransform.getTranslateInstance(-l,-t));
g.dispose();
java.io.File outputfile = new java.io.File(sessionScope.get('absolutePath') + java.io.File.separator + sessionScope.get('lastUpload'));
ImageIO.write(dst, 'png', outputfile);
Thanks for all who replied. It turns out that the problem was not in the cropping code at all.
When I displayed the image to be cropped, I resized it to fit into my layout nicely, then used a javascript cropping tool to figure out the coordinates to crop.
Since I had resized my image, but didn't take the resizing into account when I was determining the cropping coordinates, I ended up with coordinates that appeared to coincide with the top left corner.
I have changed the display to no longer resize the image, and now cropping is working beautifully.
I'd like to convert gif images to jpeg using Java. It works great for most images, but I have a simple transparent gif image:
Input gif image http://img292.imageshack.us/img292/2103/indexedtestal7.gif
[In case the image is missing: it's a blue circle with transparent pixels around it]
When I convert this image using the following code:
File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
File f = new File("indexed_test.jpg");
ImageIO.write(image, "jpg", f);
This code works without throwing an Exception, but results an invalid jpeg image:
[In case the image is missing: IE cannot show the jpeg, Firefox shows the image with invalid colors.]
I'm using Java 1.5.
I also tried converting the sample gif to png with gimp and using the png as an input for the Java code. The result is the same.
Is it a bug in the JDK? How can I convert images correctly preferably without 3rd party libraries?
UPDATE:
Answers indicate that jpeg conversion cannot handle transparency correctly (I still think that this is a bug) and suggest a workaround for replacing transparent pixels with predefined color. Both of the suggested methods are quite complex, so I've implemented a simpler one (will post as an answer). I accept the first published answer with this workaround (by Markus). I don't know which implementation is the better. I go for the simplest one still I found a gif where it's not working.
For Java 6 (and 5 too, I think):
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
g = bufferedImage.createGraphics();
//Color.WHITE estes the background to white. You can use any other color
g.drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), Color.WHITE, null);
As already mentioned in the UPDATE of the question I've implemented a simpler way of replacing transparent pixels with predefined color:
public static BufferedImage fillTransparentPixels( BufferedImage image,
Color fillColor ) {
int w = image.getWidth();
int h = image.getHeight();
BufferedImage image2 = new BufferedImage(w, h,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = image2.createGraphics();
g.setColor(fillColor);
g.fillRect(0,0,w,h);
g.drawRenderedImage(image, null);
g.dispose();
return image2;
}
and I call this method before jpeg conversion in this way:
if( inputImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
inputImage = fillTransparentPixels(inputImage, Color.WHITE);
}
The problem (at least with png to jpg conversion) is that the color scheme isn't the same, because jpg doesn't support transparency.
What we've done successfully is something along these lines (this is pulled from various bits of code - so please forgive the crudeness of the formatting):
File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
int width = image.getWidth();
int height = image.getHeight();
BufferedImage jpgImage;
//you can probably do this without the headless check if you just use the first block
if (GraphicsEnvironment.isHeadless()) {
if (image.getType() == BufferedImage.TYPE_CUSTOM) {
//coerce it to TYPE_INT_ARGB and cross fingers -- PNGs give a TYPE_CUSTOM and that doesn't work with
//trying to create a new BufferedImage
jpgImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
} else {
jpgImage = new BufferedImage(width, height, image.getType());
}
} else {
jgpImage = GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration().
createCompatibleImage(width, height, image.getTransparency());
}
//copy the original to the new image
Graphics2D g2 = null;
try {
g2 = jpg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(image, 0, 0, width, height, null);
}
finally {
if (g2 != null) {
g2.dispose();
}
}
File f = new File("indexed_test.jpg");
ImageIO.write(jpgImage, "jpg", f);
This works for png to jpg and gif to jpg. And you will have a white background where the transparent bits were. You can change this by having g2 fill the image with another color before the drawImage call.
3 months late, but I am having a very similar problem (although not even loading a gif, but simply generating a transparent image - say, no background, a colored shape - where when saving to jpeg, all colors are messed up, not only the background)
Found this bit of code in this rather old thread of the java2d-interest list, thought I'd share, because after a quick test, it is much more performant than your solution:
final WritableRaster raster = img.getRaster();
final WritableRaster newRaster = raster.createWritableChild(0, 0, img.getWidth(), img.getHeight(), 0, 0, new int[]{0, 1, 2});
// create a ColorModel that represents the one of the ARGB except the alpha channel
final DirectColorModel cm = (DirectColorModel) img.getColorModel();
final DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());
// now create the new buffer that we'll use to write the image
return new BufferedImage(newCM, newRaster, false, null);
Unfortunately, I can't say I understand exactly what it does ;)
If you create a BufferedImage of type BufferedImage.TYPE_INT_ARGB and save to JPEG weird things will result. In my case the colors are scewed into orange. In other cases the produced image might be invalid and other readers will refuse loading it.
But if you create an image of type BufferedImage.TYPE_INT_RGB then saving it to JPEG works fine.
I think this is therefore a bug in Java JPEG image writer - it should write only what it can without transparency (like what .NET GDI+ does). Or in the worst case thrown an exception with a meaningful message e.g. "cannot write an image that has transparency".
JPEG has no support for transparency. So even when you get the circle color correctly you will still have a black or white background, depending on your encoder and/or renderer.
BufferedImage originalImage = ImageIO.read(getContent());
BufferedImage newImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
for (int x = 0; x < originalImage.getWidth(); x++) {
for (int y = 0; y < originalImage.getHeight(); y++) {
newImage.setRGB(x, y, originalImage.getRGB(x, y));
}
}
ImageIO.write(newImage, "jpg", f);
7/9/2020 Edit: added imageIO.write