MonochromeColorModelTest.java

/*
 * Copyright (c) 2026, Harald Kuhr
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.twelvemonkeys.image;

import org.junit.jupiter.api.Test;
import java.awt.image.IndexColorModel;
import static org.junit.jupiter.api.Assertions.*;


/**
 * MonochromeColorModelTest
 */
class MonochromeColorModelTest {

    @Test
    void testGetInstance() {
        IndexColorModel instance1 = MonochromeColorModel.getInstance();
        IndexColorModel instance2 = MonochromeColorModel.getInstance();

        assertNotNull(instance1);
        assertSame(instance1, instance2);
        assertInstanceOf(MonochromeColorModel.class, instance1);
    }

    @Test
    void testProperties() {
        IndexColorModel cm = MonochromeColorModel.getInstance();
        assertEquals(1, cm.getPixelSize());
        assertEquals(2, cm.getMapSize());
        assertEquals(-1, cm.getTransparentPixel());

        int[] palette = new int[cm.getMapSize()];
        cm.getRGBs(palette);
        assertEquals(0xFF000000, palette[0]);
        assertEquals(0xFFFFFFFF, palette[1]);
    }

    @Test
    void testGetDataElements() {
        MonochromeColorModel cm = (MonochromeColorModel) MonochromeColorModel.getInstance();

        // Black
        assertPixelEquals(0, cm.getDataElements(0xFF000000, null));
        
        // White
        assertPixelEquals(1, cm.getDataElements(0xFFFFFFFF, null));

        // Dark gray (should be black)
        assertPixelEquals(0, cm.getDataElements(0xFF404040, null));

        // Light gray (0x81 = 129, should be white)
        assertPixelEquals(1, cm.getDataElements(0xFF818181, null));

        // Colors
        // Red (r=255, g=0, b=0) -> gray = (222 * 255 + 0 + 0) / 1000 = 56.61 -> 0
        assertPixelEquals(0, cm.getDataElements(0xFFFF0000, null));

        // Green (r=0, g=255, b=0) -> gray = (0 + 707 * 255 + 0) / 1000 = 180.285 -> 1
        assertPixelEquals(1, cm.getDataElements(0xFF00FF00, null));

        // Blue (r=0, g=0, b=255) -> gray = (0 + 0 + 71 * 255) / 1000 = 18.105 -> 0
        assertPixelEquals(0, cm.getDataElements(0xFF0000FF, null));

        // Mixed (r=128, g=128, b=128) -> gray = 128 -> 0
        assertPixelEquals(0, cm.getDataElements(0xFF808080, null));

        // Mixed (r=129, g=129, b=129) -> gray = 129 -> 1
        assertPixelEquals(1, cm.getDataElements(0xFF818181, null));

        // Test alpha (should be ignored)
        assertPixelEquals(1, cm.getDataElements(0x00FFFFFF, null));
        assertPixelEquals(0, cm.getDataElements(0x00000000, null));

        // Edge cases for gray threshold (0x80)
        // (222 * r + 707 * g + 71 * b) / 1000
        // To get exactly 128:
        // If r=g=b=128 -> 128 (already tested)
        // If we want slightly more than 128:
        // r=129, g=128, b=128 -> (222*129 + 707*128 + 71*128)/1000 = (28638 + 90496 + 9088)/1000 = 128.222 -> 128 -> 0
        assertPixelEquals(0, cm.getDataElements(0xFF818080, null));

        // r=130, g=128, b=128 -> (222*130 + 707*128 + 71*128)/1000 = (28860 + 90496 + 9088)/1000 = 128.444 -> 128 -> 0
        assertPixelEquals(0, cm.getDataElements(0xFF828080, null));

        // Let's find something that results in 129
        // r=255, g=102, b=0 -> (222*255 + 707*102 + 0)/1000 = (56610 + 72114)/1000 = 128.724 -> 128 -> 0
        assertPixelEquals(0, cm.getDataElements(0xFFFF6600, null));
        // r=255, g=103, b=0 -> (222*255 + 707*103 + 0)/1000 = (56610 + 72821)/1000 = 129.431 -> 129 -> 1
        assertPixelEquals(1, cm.getDataElements(0xFFFF6700, null));
    }

    @Test
    void testGetDataElementsReuse() {
        MonochromeColorModel cm = (MonochromeColorModel) MonochromeColorModel.getInstance();
        byte[] pixel = new byte[1];
        
        Object result = cm.getDataElements(0xFFFFFFFF, pixel);
        assertSame(pixel, result);
        assertEquals(1, pixel[0]);

        result = cm.getDataElements(0xFF000000, pixel);
        assertSame(pixel, result);
        assertEquals(0, pixel[0]);
    }

    @Test
    void testGetRGB() {
        IndexColorModel cm = MonochromeColorModel.getInstance();
        assertEquals(0xFF000000, cm.getRGB(0));
        assertEquals(0xFFFFFFFF, cm.getRGB(1));
    }

    @Test
    void testGetDataElementsInvalid() {
        MonochromeColorModel cm = (MonochromeColorModel) MonochromeColorModel.getInstance();
        assertThrows(ClassCastException.class, () -> cm.getDataElements(0xFFFFFFFF, new int[1]));
    }

    private void assertPixelEquals(int expected, Object pixel) {
        assertInstanceOf(byte[].class, pixel);
        byte[] p = (byte[]) pixel;
        assertEquals(1, p.length);
        assertEquals((byte) expected, p[0]);
    }
}