[MGNLIMG-100] Loading image from JCR using AbstractLoader will result in adding the alpha value as a color to generated JPGs Created: 01/Nov/12  Updated: 09/Jul/13  Resolved: 05/Dec/12

Status: Closed
Project: Imaging
Component/s: image operations
Affects Version/s: 2.2.1
Fix Version/s: 2.2.2

Type: Bug Priority: Critical
Reporter: Christian Ringele Assignee: Roman Kovařík
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: Text File AbstractLoader.java.patch     PNG File Generator.png     JPEG File OrigImage-PlacedIntoDMS.jpg     JPEG File Output-using-BufferedImage.TYPE_INT_RGB.jpg     PNG File out1-works.png     JPEG File out1-worksNot.jpg    
Issue Links:
causality
is causing MGNLIMG-114 Tests should output test images in th... Closed
Template:
Patch included:
Yes
Acceptance criteria:
Empty
Task DoD:
[ ]* Doc/release notes changes? Comment present?
[ ]* Downstream builds green?
[ ]* Solution information and context easily available?
[ ]* Tests
[ ]* FixVersion filled and not yet released
[ ]  Architecture Decision Record (ADR)
Bug DoR:
[ ]* Steps to reproduce, expected, and actual results filled
[ ]* Affected version filled
Date of First Response:

 Description   

Description:
Calling an image generator which uses a FromContent (extends AbstractLoader) operation will result in wrong JPGs.
If storing PNGs it works fine. As PNGs have a alpha channel, the alpha value is correctly applied.
Storing a JPG, it seems as the ImageIO.write() method writes the alpha value as a color and not ignoring it as it should.

Reproduce:
1. Import the generator attached to this issue. Just containing one operation, the load operation.
2. Add a test image to DMS.
3. execute this code with correct paths:

final ImagingModuleConfig config = (ImagingModuleConfig) ModuleRegistry.Factory.getInstance().getModuleInstance("imaging");
final ImageGenerator generator = config.getGenerators().get("myOperationChainBResize");

Session dmsSession = MgnlContext.getJCRSession("dms");
Node origImage = dmsSession.getNode("/test/orig");

final Content content = info.magnolia.cms.util.ContentUtil.asContent(origImage);

final ParameterProvider<Content> parameterProvider = new ContentParameterProvider(new SimpleEqualityContentWrapper(content));
final BufferedImage bufferedImage = generator.generate(parameterProvider);

ImageIO.write(bufferedImage, "png", new File("/Users/cringele/Documents/temp_stuff/image-test/create/out1-works.png"));
ImageIO.write(bufferedImage, "jpg", new File("/Users/cringele/Documents/temp_stuff/image-test/create/out1-worksNot.jpg"));

You can also write the stream back into JCR, the result is the same. So for testing much easier into a file.

Source of the problem:
info.magnolia.imaging.operations.load.AbstractLoader.apply(BufferedImage, P)

if (source != null) {
    throw new ImagingException("This operation currently does not support overlaying images");
}
final BufferedImage loaded = loadSource(filterParams);
if (loaded == null) {
    throw new ImagingException("Could not load image for " + filterParams);
}

//This line is the source of the problem. Using BufferedImage.TYPE_INT_RGB would work because it wouldn't contain any alpha channel. Of course just for testing applicable.
final BufferedImage img = new BufferedImage(loaded.getWidth(), loaded.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
final Graphics2D g = img.createGraphics();

if (backgroundColor != null) {
    g.setColor(backgroundColor);
    g.fill(new Rectangle(0, 0, img.getWidth(), img.getHeight()));
}
g.drawImage(loaded, null, 0, 0);
// TODO would this make any difference ? g.drawRenderedImage(loaded, null);

g.dispose();
return img;

Proof of the problem source:
First possibility: Execute this code just passing back the loaded BufferedImage

if (source != null) {
    throw new ImagingException("This operation currently does not support overlaying images");
}
final BufferedImage loaded = loadSource(filterParams);
if (loaded == null) {
    throw new ImagingException("Could not load image for " + filterParams);
}
return loaded;

Second possibility: Pass back the created BufferedImage (img) withour loagin the orig image on top.
Like this you can compare alpha value in PNG to color in JPG.

if (source != null) {
    throw new ImagingException("This operation currently does not support overlaying images");
}
final BufferedImage loaded = loadSource(filterParams);
if (loaded == null) {
    throw new ImagingException("Could not load image for " + filterParams);
}
final BufferedImage img = new BufferedImage(loaded.getWidth(), loaded.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
final Graphics2D g = img.createGraphics();

if (backgroundColor != null) {
    g.setColor(backgroundColor);
    g.fill(new Rectangle(0, 0, img.getWidth(), img.getHeight()));
}
//Image not loaded on top of it
//g.drawImage(loaded, null, 0, 0);
// TODO would this make any difference ? g.drawRenderedImage(loaded, null);

g.dispose();
return img;

It seems to be a known problem:
http://stackoverflow.com/questions/8410996/java-bufferedimage-saves-with-unwanted-background-color
Trying out the solution they suggest did not work.

Was not able to supply a patch with a final solution. Added a pacth as a workaround.



 Comments   
Comment by Christian Ringele [ 01/Nov/12 ]

Look at the non working image with Gimp or Photoshop.

Comment by Tobias Mattsson [ 01/Nov/12 ]

Opening out1-worksNot.jpg shows it with psychedelic colors in chrome and firefox while its black in safari, preview and seashore.

Comment by Tobias Mattsson [ 01/Nov/12 ]

The output image is a CMYK jpeg while the input is an RGB jpeg. There's a special case variant of jpeg that uses the four channel CMYK to store what is effectively an RGBA jpeg, seems very hackish.

Comment by Tobias Mattsson [ 01/Nov/12 ]

Possible its not a cmyk image, just a four channel jpeg which is then assumed to be cmyk because of the four channels.

If you draw it onto an RGB bufferedImage and then save that; does that result in an RGB image that is correct?

Comment by Christian Ringele [ 01/Nov/12 ]

using BufferedImage.TYPE_INT_RGB to create the image.

Comment by Tobias Mattsson [ 01/Nov/12 ]

Output-using-BufferedImage.TYPE_INT_RGB.jpg is an RGB jpeg and looks correct in all the apps i have on my machine.

Comment by Tobias Mattsson [ 01/Nov/12 ]

Okay, found this in the JRE bug database, http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4836466, closed as not a bug.

This question on stackoverflow describes the same problem and shows images with the same problems as we're having.
http://stackoverflow.com/questions/6212897/can-i-use-libjpeg-to-read-jpegs-with-an-alpha-channel

The solution is to save a BufferedImage without alpha.

Comment by Christian Ringele [ 04/Nov/12 ]

The problem is, that on the moment of loading the original image, it is unknown as what format the target image will be saved.
If being saved as a png, but BufferedImage is created without an alpha channel, operations based on that channel are not possible.

Comment by Jan Haderka [ 05/Dec/12 ]

Any reason to not use tools for creating compatible images that already exist? Like e.g. org.jdesktop.swingx.graphics.GraphicsUtilities.createColorModelCompatibleImage(BufferedImage image)

should be in maven repo - http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22swingx-core%22

Comment by Roman Kovařík [ 05/Dec/12 ]

The utils form GraphicsUtilities (createCompatibleImage or createColormodelCompatibleImage) cause failing of some other tests. When source images are of type TYPE_CUSTOM, the target will be TYPE_CUSTOM too. That produces Unknows image type later...
The current code can handle this situation - it sets the default types, if the source type is of TYPE_CUSTOM:

int imageType = loaded.getType(); //set the output image type to the source image type
if (imageType == BufferedImage.TYPE_CUSTOM) { //if the source image type is not set...
   if ( loaded.getAlphaRaster() != null ) { //with alpha channel
              imageType = BufferedImage.TYPE_INT_ARGB_PRE;
   } else { //without alpha channel
              imageType = BufferedImage.TYPE_INT_RGB;
   }
}
Generated at Mon Feb 12 02:12:16 CET 2024 using Jira 9.4.2#940002-sha1:46d1a51de284217efdcb32434eab47a99af2938b.