RGBETest.java

package com.twelvemonkeys.imageio.plugins.hdr;

import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

public class RGBETest {

    // A scanline whose leading 4 bytes mark it as "not run length encoded" must be read flat and
    // must not fall through into the RLE path, which decodes a second time past the output array.
    @Test
    public void testNonRLEScanlineDoesNotOverflow() throws Exception {
        int width = 8;
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();

        // Leading quad: first byte != 2 selects the non-RLE branch, last two bytes match the width
        bytes.write(new byte[] {1, 0, 0, (byte) width});
        // Flat pixel data for the remaining (width - 1) pixels
        for (int i = 0; i < (width - 1) * 4; i++) {
            bytes.write(0x11);
        }
        // Well-formed RLE channel data that the buggy fall-through would decode on top of the row
        for (int c = 0; c < 4; c++) {
            bytes.write(width);
            bytes.write(0x22);
            for (int i = 0; i < width - 1; i++) {
                bytes.write(0x33);
            }
        }

        byte[] row = new byte[width * 4];
        DataInput input = new DataInputStream(new ByteArrayInputStream(bytes.toByteArray()));

        assertDoesNotThrow(() -> RGBE.readPixelsRawRLE(input, row, 0, width, 1));

        byte[] expected = new byte[width * 4];
        expected[0] = 1;
        expected[1] = 0;
        expected[2] = 0;
        expected[3] = (byte) width;
        for (int i = 4; i < expected.length; i++) {
            expected[i] = 0x11;
        }
        assertArrayEquals(expected, row);
    }

    // Widths below the RLE minimum are read flat and must likewise not continue into the RLE path.
    @Test
    public void testSubMinimumWidthDoesNotOverflow() throws Exception {
        int width = 4;
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        for (int i = 0; i < width * 4; i++) {
            bytes.write(0x44);
        }

        byte[] row = new byte[width * 4];
        DataInput input = new DataInputStream(new ByteArrayInputStream(bytes.toByteArray()));

        assertDoesNotThrow(() -> RGBE.readPixelsRawRLE(input, row, 0, width, 1));

        byte[] expected = new byte[width * 4];
        for (int i = 0; i < expected.length; i++) {
            expected[i] = 0x44;
        }
        assertArrayEquals(expected, row);
    }
}