Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ResampleOp: black cropped images after scaling TYPE_INT_RGB with VALUE_INTERPOLATION_BICUBIC #585

Closed
wigun opened this issue Jan 22, 2021 · 3 comments

Comments

@wigun
Copy link

wigun commented Jan 22, 2021

Sporadically TYPE_INT_RGB with VALUE_INTERPOLATION_BICUBIC scaled images are black on the right side of the scaled image.
It might be a bug only on windows.
I m using Win 10 / jdk-11.0.2

Just run this test:

package com.twelvemonkeys.image;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;

import javax.imageio.ImageIO;

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;

public class ResampleOpTest {

    @Test
    public void filter_TYPE_INT_RGB_VALUE_INTERPOLATION_BICUBIC() throws IOException {

        Color sourceColor = Color.RED;
        int sourceWidth = 100;
        int sourceHeight = 100;

        int targetWidth = 50;
        int targetHeight = 50;

        BufferedImage sourceBufferedImage = createSourceBufferedImage(sourceColor, BufferedImage.TYPE_INT_RGB, sourceWidth, sourceHeight);

        // on my windows machine, the error occurs within 30 cycles in in over 99% of this executed test
        // the bug apparently doesn't appear on mac
        for (int cycle = 0; cycle < 30; cycle++) {
            System.out.println("cycle: " + cycle);

            BufferedImage targetBufferedImage = createCompatibleBufferedImage(sourceBufferedImage, targetWidth, targetHeight);

            HashMap<RenderingHints.Key, Object> renderingHintMap = new HashMap<>();
            renderingHintMap.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            ResampleOp resampleOp = new ResampleOp(targetWidth, targetHeight, new RenderingHints(renderingHintMap));
            resampleOp.filter(sourceBufferedImage, targetBufferedImage);

            boolean correctColor = checkColor(targetBufferedImage, sourceColor);

            if (!correctColor) {
                ImageIO.write(sourceBufferedImage, "PNG", new File("sourceBufferedImage.png"));
                ImageIO.write(targetBufferedImage, "PNG", new File("targetBufferedImage.png"));
            }

            Assert.assertTrue(String.format("color error after %s cycles", cycle), correctColor);
        }
    }

    private boolean checkColor(BufferedImage bufferedImage, Color expectedColor) {
        DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
        for (int index = 0; index < dataBuffer.getSize(); index++) {
            if (expectedColor.getRGB() != dataBuffer.getElem(index)) {
                System.out.println(String.format("unexpected color %s (instead of %s) at index %s", dataBuffer.getElem(index), expectedColor.getRGB(), index));
                return false;
            }
        }
        return true;
    }

    private BufferedImage createSourceBufferedImage(Color sourceColor, int imageType, int sourceWidth, int sourceHeight) {
        BufferedImage bufferedImage = new BufferedImage(sourceWidth, sourceHeight, imageType);
        WritableRaster writableRaster = bufferedImage.getRaster();
        DataBufferInt dataBuffer = (DataBufferInt) writableRaster.getDataBuffer();
        int[] pixels = dataBuffer.getData();
        Arrays.fill(pixels, sourceColor.getRGB());
        return bufferedImage;
    }

    private BufferedImage createCompatibleBufferedImage(BufferedImage sourceBufferedImage, int width, int height) {
        WritableRaster raster = sourceBufferedImage.getColorModel().createCompatibleWritableRaster(width, height);
        return new BufferedImage(sourceBufferedImage.getColorModel(), raster, sourceBufferedImage.isAlphaPremultiplied(), null);
    }
}

@wigun
Copy link
Author

wigun commented Jan 22, 2021

Proposed fix:

Change the Method
private static BufferedImage fastResample(final BufferedImage input, final BufferedImage output, final int width, final int height, final int type)
so, that if (output != null)
you also DO NOT use null for internal scaling here:
temp = scale.filter(temp, null) (line 706, version 3.6.1)

but create a temporary BufferedImage of the same type as the output

if (output == null) {
    temp = scale.filter(temp, null);
} else {
    BufferedImage prescaled = new BufferedImage((int) Math.round(xScale), (int) Math.round(yScale), output.getType());
    temp = scale.filter(temp, prescaled);
}

And maybe in line 745 (Version 3.6.1) something similar (I don't know, how to hit this line).

@haraldk
Copy link
Owner

haraldk commented Jan 26, 2021

Hi Gunther,

I'll look into it! I'm a bit overworked these days, so it may take some time. Also I don't have access to a Windows machine at this time. I usually have access to one in the office, but as I've been working from home due to covid restrictions my options are a bit limited...

If you can create both a test case that fails with the old code and a fix, and create a PR for that, that will help a lot!

Unfortunately, I don't think you can create a an image with the same type in all cases, as type may be TYPE_CUSTOM... But it should be possible still!

--
Harald K

@haraldk
Copy link
Owner

haraldk commented Oct 25, 2022

@wigun is this still an issue?

I realize it's a bit late, but I haven't had access to a Windows machine for ages, so I haven't been able to reproduce your findings... If the problem still persists, perhaps you could set up a test that would reproduce it on GitHub Actions or similar virtual environment?

Otherwise, I'll close this issue for now. But of course, feel free to reopen if you have additional details or can provide a workaround.

@haraldk haraldk closed this as completed Oct 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants