0

I am manipulating code of a image renderer that is making output image from Color[] array and my code simply update it with additional stuff right before saving, that is when the original image is actually prepared (all pixels positions prepared to be filled with RGBs in that Color[] array ready for final saving).

Reason why I am doing this is to have ability to insert text describing my render without need of another external graphics program that would do that (I want to have it all in one-go! action without need of another external app).

For that cause - as I have no reach/access for the original prepared BufferedImage (but I have access to actual Color[] that it is created from) I had to make my own class method that:

  1. convert that original Color[] to my own temporary BufferedImage
  2. update that temp. BufferedImage with my stuff via Graphics2D (adding some text to image)
  3. convert my result (temp. BufferedImage with Graphics2D) back to Color[]
  4. send that final Color[] back to the original image rendering method that would actually make it to be the final image that is rendered out and saved as png

Now everything works just fine as I expected except one really annoying thing that I cannot get rid off: my updated image looks very bleached-like/pale (almost no depth or shadows presented) compared to the original un-watermarked version...

To me it simply looks like after the image2color[] conversion (using @stacker's solution from here Converting Image to Color array) something goes wrong/is not right so the colors become pale and I do not have any clue why.

Here is the main part of my code that is in question:

            BufferedImage sourceImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

            // Color[] to BufferedImage
            for (int k = 0; k < multiArrayList.size(); k++) {

                // PREPARE...
                int x = (int) multiArrayList.get(k)[0];
                int y = (int) multiArrayList.get(k)[1];
                int w = (int) multiArrayList.get(k)[2];
                int h = (int) multiArrayList.get(k)[3];
                Color[] data = (Color[]) multiArrayList.get(k)[4];
                int border = BORDERS[k % BORDERS.length];

                for (int by = 0; by < h; by++) {
                    for (int bx = 0; bx < w; bx++) {
                        if (bx == 0 || bx == w - 1) {
                            if (5 * by < h || 5 * (h - by - 1) < h) {
                                sourceImage.setRGB(x + bx, y + by, border);
                            }
                        } else if (by == 0 || by == h - 1) {
                            if (5 * bx < w || 5 * (w - bx - 1) < w) {
                                sourceImage.setRGB(x + bx, y + by, border);
                            }
                        }
                    }
                }
                // UPDATE...
                for (int j = 0, index = 0; j < h; j++) {
                    for (int i = 0; i < w; i++, index++) {
                        sourceImage.setRGB(x + i, y + j, data[index].copy().toNonLinear().toRGB());
                    }
                }
            }

            Graphics2D g2d = (Graphics2D) sourceImage.getGraphics();

            // paints the textual watermark
            drawString(g2d, text, centerX, centerY, sourceImage.getWidth());

            // when saved to png at this point ALL IS JUST FINE
            ImageIO.write(sourceImage, "png", new File(imageSavePath));
            g2d.dispose();

            // BufferedImage to Color array
            int[] dt = ((DataBufferInt) sourceImage.getRaster().getDataBuffer()).getData();

            bucketFull = new Color[dt.length];
            for (int i = 0; i < dt.length; i++) {
                bucketFull[i] = new Color(dt[i]);
            }

            // update and repaint output image - THIS OUTPUT IS ALREADY BLEACHED/PALE
            d.ip(0, 0, width, height, renderThreads.length + 1);
            d.iu(0, 0, width, height, bucketFull);

            // reset objects
            g2d = null;
            sourceImage = null;
            bucketFull = null;
            multiArrayList = new ArrayList<>();

I have tested (by saving it to another .png file right after the Graphics2D addition) that before it gets that 2nd conversion it looks absolutely OK 1:1 to the original image incl. my text on that image.

But as I said when it is send for render it becomes bleached/pale that is a problem I am trying to solve.

BTW I first thought it might be that Graphics2D addition so I did try it without it but the result was the same, that is bleached/pale version.

Although my process and code is completely different the output image is basically suffering exactly the same way as in this topic (still not solved) BufferedImage color saturation

Here are my 2 examples - 1st ORIGINAL, 2nd UPDATED (bleached/pale)

enter image description here enter image description here

Community
  • 1
  • 1
errerr
  • 63
  • 8
  • 1
    What is the `Color` class you use? It does not seem to be `java.awt.Color`... And more specific, what does `data[index].copy().toNonLinear().toRGB()` do? It seems you convert the color values before passing them to the `BufferedImage`. But you don't do the reverse anywhere. I suspect this is the problem. – Harald K Jun 26 '16 at 11:29
  • @haraldK it uses float 0-1 instead of int 0-255, data[index].copy().toNonLinear().toRGB() is taken from original code not quite sure what exactly it does but I think it corrects/manipulates gamma...anyway: I tested it now without the ".copy().toNonLinear()" part and **WOW: NOW IT RENDERS OK** - thank you so much! Would you like to post this as stand alone suggestion so I could sign it as the right answer? ;-) – errerr Jun 26 '16 at 13:10
  • Good! I've added an answer. :-) – Harald K Jun 26 '16 at 17:37

1 Answers1

0

As suspected, the problem is that you convert the color values from linear RGB to gamma-corrected/sRGB values when setting the RGB values to the BufferedImage, but the reverse transformation (back to linear RGB) is not done when you put the values back into the Color array.

Either change the line (inside the double for loop):

sourceImage.setRGB(x + i, y + j, data[index].copy().toNonLinear().toRGB());

to

sourceImage.setRGB(x + i, y + j, data[index].toRGB());

(you don't need the copy() any more, as you no longer mutate the values, using toNonLinear()).

This avoids the conversion altogether.


... or you could probably also change the line setting the values back, from:

bucketFull[i] = new Color(dt[i]);

to

bucketFull[i] = new Color(dt[i]).toLinear();

Arguably, this is more "correct" (as AWT treats the values as being in the sRGB color space, regardless), but I believe the first version is faster, and the difference in color is negligible. So I'd probably try the first suggested fix first, and use that unless you experience colors that are off.

Harald K
  • 26,314
  • 7
  • 65
  • 111