WebPImageReaderTest.java

package com.twelvemonkeys.imageio.plugins.webp;

import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.*;
import java.awt.image.*;
import java.io.IOException;
import java.util.List;

import static java.util.Arrays.asList;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

/**
 * WebPImageReaderTest
 */
public class WebPImageReaderTest extends ImageReaderAbstractTest<WebPImageReader> {

    @Override
    protected List<TestData> getTestData() {
        return asList(
                // Original Google WebP sample files
                new TestData(getClassLoaderResource("/webp/1.webp"), new Dimension(550, 368)),
                new TestData(getClassLoaderResource("/webp/5.webp"), new Dimension(1024, 752)),
                // Various samples from javavp8codec project
                new TestData(getClassLoaderResource("/webp/bug3.webp"), new Dimension(95, 95)),
                new TestData(getClassLoaderResource("/webp/segment01.webp"), new Dimension(160, 160)),
                new TestData(getClassLoaderResource("/webp/segment02.webp"), new Dimension(160, 160)),
                new TestData(getClassLoaderResource("/webp/segment03.webp"), new Dimension(160, 160)),
                new TestData(getClassLoaderResource("/webp/small_1x1.webp"), new Dimension(1, 1)),
                new TestData(getClassLoaderResource("/webp/small_1x13.webp"), new Dimension(1, 13)),
                new TestData(getClassLoaderResource("/webp/small_13x1.webp"), new Dimension(13, 1)),
                new TestData(getClassLoaderResource("/webp/small_31x13.webp"), new Dimension(31, 13)),
                new TestData(getClassLoaderResource("/webp/test.webp"), new Dimension(128, 128)),
                new TestData(getClassLoaderResource("/webp/very_short.webp"), new Dimension(63, 66)),
                // Lossless
                new TestData(getClassLoaderResource("/webp/1_webp_ll.webp"), new Dimension(400, 301)),
                new TestData(getClassLoaderResource("/webp/2_webp_ll.webp"), new Dimension(386, 395)),
                new TestData(getClassLoaderResource("/webp/2_webp_ll_alt.webp"), new Dimension(386, 395)),
                new TestData(getClassLoaderResource("/webp/3_webp_ll.webp"), new Dimension(800, 600)),
                new TestData(getClassLoaderResource("/webp/4_webp_ll.webp"), new Dimension(421, 163)),
                new TestData(getClassLoaderResource("/webp/5_webp_ll.webp"), new Dimension(300, 300)),
                // Extended format: Alpha + VP8
                new TestData(getClassLoaderResource("/webp/1_webp_a.webp"), new Dimension(400, 301)),
                new TestData(getClassLoaderResource("/webp/2_webp_a.webp"), new Dimension(386, 395)),
                new TestData(getClassLoaderResource("/webp/3_webp_a.webp"), new Dimension(800, 600)),
                new TestData(getClassLoaderResource("/webp/4_webp_a.webp"), new Dimension(421, 163)),
                new TestData(getClassLoaderResource("/webp/5_webp_a.webp"), new Dimension(300, 300)),
                // Extended format: Anim
                new TestData(getClassLoaderResource("/webp/animated-webp-supported.webp"), new Dimension(400, 400),
                        new Dimension(400, 400), new Dimension(400, 400), new Dimension(400, 394),
                        new Dimension(371, 394), new Dimension(394, 382), new Dimension(400, 388),
                        new Dimension(394, 383), new Dimension(394, 394), new Dimension(372, 394),
                        new Dimension(400, 400), new Dimension(320, 382)),
                // Alpha transparency and Alpha filtering
                new TestData(getClassLoaderResource("/webp/alpha_filter.webp"), new Dimension(1600, 1600)),
                // Lossy with grayscale ICC profile
                new TestData(getClassLoaderResource("/webp/incompatible-icc-gray.webp"), new Dimension(766, 1100))
        );
    }

    @Override
    protected ImageReaderSpi createProvider() {
        return new WebPImageReaderSpi();
    }

    @Override
    protected List<String> getFormatNames() {
        return asList("webp", "WEBP");
    }

    @Override
    protected List<String> getSuffixes() {
        return asList("wbp", "webp");
    }

    @Override
    protected List<String> getMIMETypes() {
        return asList("image/webp", "image/x-webp");
    }

    @Test
    public void testReadAndApplyICCProfile() throws IOException {
        WebPImageReader reader = createReader();

        try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/webp/photo-iccp-adobergb.webp"))) {
            reader.setInput(stream);

            // We'll read a small portion of the image into a destination type that use sRGB
            ImageReadParam param = new ImageReadParam();
            param.setDestinationType(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
            param.setSourceRegion(new Rectangle(20, 20));

            BufferedImage image = reader.read(0, param);
            assertRGBEquals("RGB values differ, incorrect ICC profile or conversion?", 0xFFEA9600, image.getRGB(10, 10), 8);
        }
        finally {
            reader.dispose();
        }
    }

    @Test
    public void testRec601ColorConversion() throws IOException {
        WebPImageReader reader = createReader();

        try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/webp/blue_tile.webp"))) {
            reader.setInput(stream);

            BufferedImage image = reader.read(0, null);
            assertRGBEquals("RGB values differ, incorrect Y'CbCr -> RGB conversion", 0xFF72AED5, image.getRGB(80, 80), 1);
        }
        finally {
            reader.dispose();
        }
    }

    @Test
    public void testReadFromUnknownStreamLength() throws IOException {
        // See #672, image was not decoded and returned all black, when the stream length was unknown (-1).
        WebPImageReader reader = createReader();

        try (ImageInputStream stream = new MemoryCacheImageInputStream(getClassLoaderResource("/webp/photo-iccp-adobergb.webp").openStream()) {
            @Override public long length() {
                return -1;
            }
        }) {
            reader.setInput(stream);

            // We'll read a small portion of the image into a destination type that use sRGB
            ImageReadParam param = new ImageReadParam();
            param.setDestinationType(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
            param.setSourceRegion(new Rectangle(10, 10, 20, 20));

            BufferedImage image = reader.read(0, param);
            assertRGBEquals("RGB values differ, image all black?", 0xFFEC9800, image.getRGB(5, 5), 8);
        }
        finally {
            reader.dispose();
        }
    }

    @Test
    public void testReadAlphaTransparent() throws IOException {
        WebPImageReader reader = createReader();

        try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/webp/1_webp_a.webp"))) {
            reader.setInput(stream);
            BufferedImage image = reader.read(0);

            assertEquals(Transparency.TRANSLUCENT, image.getTransparency());

            assertRGBEquals("Expected transparent corner (0, 0)", 0x00000000, image.getRGB(0, 0) & 0xFF000000, 8);
            assertRGBEquals("Expected opaque center (200, 150)", 0xff9a4e01, image.getRGB(200, 150), 8);
            assertRGBEquals("Expected transparent corner (399, 0)", 0x00000000, image.getRGB(399, 0) & 0xFF000000, 8);
            assertRGBEquals("Expected transparent corner (0, 300)", 0x00000000, image.getRGB(0, 300) & 0xFF000000, 8);
            assertRGBEquals("Expected transparent corner (399, 300)", 0x00000000, image.getRGB(399, 300) & 0xFF000000, 8);
        }
        finally {
            reader.dispose();
        }
    }

    @Test
    public void testAlphaSubsampling() throws IOException {
        WebPImageReader reader = createReader();

        try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/webp/alpha_filter.webp"))) {
            reader.setInput(stream);

            // Read the image using a subsampling factor of 2 
            ImageReadParam param = new ImageReadParam();
            param.setSourceSubsampling(2, 2, 0, 0);

            BufferedImage image = reader.read(0, param);

            assertRGBEquals("Expected transparent at (100, 265)", 0x00000000, image.getRGB(100, 265) & 0xFF000000, 8);
            assertRGBEquals("Expected transparent at (512, 320)", 0x00000000, image.getRGB(512, 320) & 0xFF000000, 8);
            assertRGBEquals("Expected opaque at (666, 444)", 0xFF000000, image.getRGB(666, 444) & 0xFF000000, 8);
            assertRGBEquals("Expected opaque corner (799, 799)", 0xFF000000, image.getRGB(699, 699) & 0xFF000000, 8);
        }
        finally {
            reader.dispose();
        }
    }
}