coobird / thumbnailator

Thumbnailator - a thumbnail generation library for Java
MIT License
5.14k stars 784 forks source link

Images are larger in JDK 11 #156

Closed bagavathi-er closed 2 years ago

bagavathi-er commented 4 years ago

Expected behavior

We upgraded our Project to JDK 11 and the thumbnail is getting created of size greater than the original image. Could you please let me know, is there any plan for JDK 11 support?

Actual behavior

Please describe the actual behavior you are experiencing, including stack trace and other information which would help diagnose the issue.

Steps to reproduce the behavior

Please enter step-by-step instructions for reproducing the actual behavior. Including code can be helpful in diagnosing issue, but please keep the code to a minimal that will reproduce the behavior.

Environment

Please provide vendor and version information for the Operating System, JDK, and Thumbnailator. Please feel free to add any other information which may be pertinent.

coobird commented 4 years ago

We upgraded our Project to JDK 11 and the thumbnail is getting created of size greater than the original image.

Can you elaborate on the size being greater? Is it the resulting file size that's greater? Or is it the dimensions of the image? What file format are you using?

There isn't enough information to understand the situation you're having. It would really help investigating further if you would provide information in the template with expected behavior, actual behavior and steps to reproduce the issue. (Along with the environment information.)

Art2Cat commented 3 years ago

I have the same issue. All below cases base on Windows 10 and Thumbnailator latest version.

Origin source: size: 1.4MB format: png dimessions: 1500x3356

mesterjagels commented 3 years ago

@coobird Any update on this? I have a similar issue even when scaling images down to smaller sizes

Origin source: size: 8kb format: png dimensions: 320x224

Output: size: 12kb format: png dimensions: 300x200

when using following setup

Thumbnails.of(byteArrayInputStream).size(300, 200).keepAspectRatio(false).antialiasing(Antialiasing.ON).outputQuality(.1f).toOutputStream(byteArrayOutputStream);

Changing the outputQuality only slightly impacts the file size

coobird commented 3 years ago

@mesterjagels, what you're experiencing is unlikely to be related with this issue. It's quite possible that scaling down images can lead to larger file sizes, especially for cases where the image dimensions isn't changed much, and lossless compression is being used.

This is because resizing images can affect the compressibility of the image. This is especially true with anti-aliasing enabled, leading to larger variations in colors which are harder to compress for lossless formats like PNG which work well when repeated sequence of similar pixels are encountered, such as large regions of the same color.

coobird commented 3 years ago

@Art2Cat and @bagavathi-er, this issue may be due changes in the Java's PNG writer in Java 9.

After a bit of research, it appears that prior to Java 8, the compression level was always set to high, but from Java 9, the default compression level for a better balance of speed and resulting image size.

php-coder commented 3 years ago

@coobird In other words, this issue is only related to PNG format and doesn't affect JPEG.

coobird commented 3 years ago

@php-coder For this particular issue, only PNG is being mentioned, and there's evidence that the PNG writer after Java 9 has different defaults which is leading to lower compression than Java 8 and earlier.

Art2Cat commented 2 years ago

I compared PNGImagerwriter source code between jdk 8 and jdk 11. In jdk 8 the default deflater compression level is 9 (it's hard code) , while in the jdk 11 the value is 4. In the jdk 11 deflater level changes when the compressionMode set to ImageWriteParam.MODE_EXPLICIT. here is the relative code snippet:

JDK 11 PNGImageWriter line 360

    /** Default compression level = 4 ie medium compression */
    private static final int DEFAULT_COMPRESSION_LEVEL = 4;

JDK 11 PNGImageWriter line 1193


// reset compression level to default:
int deflaterLevel = DEFAULT_COMPRESSION_LEVEL;
    if (param != null) {
        switch(param.getCompressionMode()) {
        case ImageWriteParam.MODE_DISABLED:
            deflaterLevel = Deflater.NO_COMPRESSION;
            break;
        case ImageWriteParam.MODE_EXPLICIT:
            float quality = param.getCompressionQuality();
            if (quality >= 0f && quality <= 1f) {
                deflaterLevel = 9 - Math.round(9f * quality);
            }
            break;
        default:
        }

        // Use Adam7 interlacing if set in write param
        switch (param.getProgressiveMode()) {
        case ImageWriteParam.MODE_DEFAULT:
            metadata.IHDR_interlaceMethod = 1;
            break;
        case ImageWriteParam.MODE_DISABLED:
            metadata.IHDR_interlaceMethod = 0;
            break;
            // MODE_COPY_FROM_METADATA should already be taken care of
            // MODE_EXPLICIT is not allowed
        default:
        }
    }
> JDK 11 PNGImageWriter line 173
```java
    IDATOutputStream(ImageOutputStream stream, int chunkLength,
                            int deflaterLevel) throws IOException
    {
        this.stream = stream;
        this.chunkLength = chunkLength;
        this.def = new Deflater(deflaterLevel);

        startChunk();
    }

JDK 8 PNGImageWriter line 162

Deflater def = new Deflater(Deflater.BEST_COMPRESSION);

JDK 8 Deflater line 109

/**
* Compression level for best compression.
*/
public static final int BEST_COMPRESSION = 9;
coobird commented 2 years ago

This issue has been corrected in 0.4.15 by including a workaround to mimic Java 8 behavior when the default PNG writer is detected.