diff --git a/src/net/sourceforge/plantuml/graphic/color/ColorHSB.java b/src/net/sourceforge/plantuml/graphic/color/ColorHSB.java new file mode 100644 index 000000000..24a6c9a1d --- /dev/null +++ b/src/net/sourceforge/plantuml/graphic/color/ColorHSB.java @@ -0,0 +1,43 @@ +package net.sourceforge.plantuml.graphic.color; + +import java.awt.Color; + +/** + * {@link Color} with hue, saturation and brightness. + */ +public class ColorHSB extends Color { + final float hue; + final float saturation; + final float brightness; + + public ColorHSB(int rgba) { + super(rgba, true); + final float[] hsb = Color.RGBtoHSB(getRed(), getGreen(), getBlue(), null); + hue = hsb[0]; + saturation = hsb[1]; + brightness = hsb[2]; + } + + public ColorHSB(Color other) { + this(other.getRGB()); + } + + public float getHue() { + return hue; + } + + public float getSaturation() { + return saturation; + } + + public float getBrightness() { + return brightness; + } + + @Override + public String toString() { + return String.format("%s[a=%02X r=%02X g=%02X b=%02X / h=%f s=%f b=%f]", + getClass().getSimpleName(), getAlpha(), getRed(), getGreen(), getBlue(), getHue(), getSaturation(), getBrightness() + ); + } +} diff --git a/test/net/sourceforge/plantuml/graphic/color/ColorHSBTest.java b/test/net/sourceforge/plantuml/graphic/color/ColorHSBTest.java new file mode 100644 index 000000000..b1d4e8140 --- /dev/null +++ b/test/net/sourceforge/plantuml/graphic/color/ColorHSBTest.java @@ -0,0 +1,24 @@ +package net.sourceforge.plantuml.graphic.color; + +import static java.lang.Long.parseLong; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class ColorHSBTest { + + @ParameterizedTest + @CsvSource({ + " AA000000 , ColorHSB[a=AA r=00 g=00 b=00 / h=0.000000 s=0.000000 b=0.000000] ", + " AAFF0000 , ColorHSB[a=AA r=FF g=00 b=00 / h=0.000000 s=1.000000 b=1.000000] ", + " AA00FF00 , ColorHSB[a=AA r=00 g=FF b=00 / h=0.333333 s=1.000000 b=1.000000]", + " AA0000FF , ColorHSB[a=AA r=00 g=00 b=FF / h=0.666667 s=1.000000 b=1.000000] ", + " FFFF8080 , ColorHSB[a=FF r=FF g=80 b=80 / h=0.000000 s=0.498039 b=1.000000] ", + " FF7F0000 , ColorHSB[a=FF r=7F g=00 b=00 / h=0.000000 s=1.000000 b=0.498039] ", + }) + void test_toString(String argb, String expectedToString) { + assertThat(new ColorHSB((int) parseLong(argb, 16))) + .hasToString(expectedToString); + } +} diff --git a/test/net/sourceforge/plantuml/test/ImageTestUtils.java b/test/net/sourceforge/plantuml/test/ImageTestUtils.java new file mode 100644 index 000000000..619afb270 --- /dev/null +++ b/test/net/sourceforge/plantuml/test/ImageTestUtils.java @@ -0,0 +1,69 @@ +package net.sourceforge.plantuml.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.awt.image.BufferedImage; +import java.util.Comparator; + +import org.opentest4j.AssertionFailedError; + +import net.sourceforge.plantuml.graphic.color.ColorHSB; + +// Beware there is also https://github.com/assertj/assertj-swing which has some image comparisons that might help us. +// It does not compare using HSB, so we have built that ourselves. +public class ImageTestUtils { + + public static void assertImagesEqual(BufferedImage expected, BufferedImage actual) { + assertImagesEqual(expected, actual, new Comparator() { + @Override + public int compare(ColorHSB expected, ColorHSB actual) { + return expected.getRGB() - actual.getRGB(); + } + }); + } + + /** + * Compares images using {@link ColorHSB}. + */ + public static void assertImagesEqual(BufferedImage expected, BufferedImage actual, Comparator comparator) { + assertImageSizeEqual(expected, actual); + + final int height = expected.getHeight(); + final int width = expected.getWidth(); + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + final ColorHSB expectedColor = new ColorHSB(expected.getRGB(x, y)); + final ColorHSB actualColor = new ColorHSB(actual.getRGB(x, y)); + if (comparator.compare(expectedColor, actualColor) != 0) { + String expectedString = expectedColor.toString(); + String actualString = String.format("%s at:<[%d, %d]>", actualColor, x, y); + throw new AssertionFailedError( + String.format("expected:%s but was:%s", expectedString, actualString), + expectedString, actualString + ); + } + } + } + } + + public static void assertImageSizeEqual(BufferedImage expected, BufferedImage actual) { + assertThat(expected).isNotNull(); + assertThat(actual).isNotNull(); + + final int expectedHeight = expected.getHeight(); + final int expectedWidth = expected.getWidth(); + + final int actualHeight = actual.getHeight(); + final int actualWidth = actual.getWidth(); + + if (expectedHeight != actualHeight || expectedWidth != actualWidth) { + String expectedString = String.format("[width=%d height=%d]", expectedWidth, expectedHeight); + String actualString = String.format("[width=%d height=%d]", actualWidth, actualHeight); + throw new AssertionFailedError( + String.format("expected:%s but was:%s", expectedString, actualString), + expectedString, actualString + ); + } + } +} diff --git a/test/net/sourceforge/plantuml/test/ImageTestUtilsTest.java b/test/net/sourceforge/plantuml/test/ImageTestUtilsTest.java new file mode 100644 index 000000000..e23a56a0b --- /dev/null +++ b/test/net/sourceforge/plantuml/test/ImageTestUtilsTest.java @@ -0,0 +1,96 @@ +package net.sourceforge.plantuml.test; + +import static net.sourceforge.plantuml.test.ImageTestUtils.assertImagesEqual; +import static net.sourceforge.plantuml.test.ImageTestUtils.assertImageSizeEqual; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import java.awt.image.BufferedImage; + +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +class ImageTestUtilsTest { + + @Test + void test_assertImagesEqual_same() { + final BufferedImage expected = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); + expected.setRGB(0, 0, 0xFFFF0000); + expected.setRGB(1, 1, 0xFF00FF00); + expected.setRGB(2, 2, 0xFF0000FF); + expected.setRGB(3, 3, 0xAA000000); + + final BufferedImage actual = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); + actual.setRGB(0, 0, 0xFFFF0000); + actual.setRGB(1, 1, 0xFF00FF00); + actual.setRGB(2, 2, 0xFF0000FF); + actual.setRGB(3, 3, 0xAA000000); + + try { + assertImagesEqual(expected, actual); + } catch (Throwable t) { + fail("assertImagesEqual() should not throw an exception here", t); + } + } + + @Test + void test_assertImagesEqual_different() { + final BufferedImage expected = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); + + final BufferedImage actual = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); + actual.setRGB(1, 2, 0xAAFF0000); + + try { + assertImagesEqual(expected, actual); + } catch (AssertionFailedError e) { + assertThat(e) + .hasMessage("expected:ColorHSB[a=00 r=00 g=00 b=00 / h=0.000000 s=0.000000 b=0.000000]" + + " but was:ColorHSB[a=AA r=FF g=00 b=00 / h=0.000000 s=1.000000 b=1.000000] at:<[1, 2]>"); + assertThat(e.isExpectedDefined()) + .isTrue(); + assertThat(e.isActualDefined()) + .isTrue(); + assertThat(e.getExpected().getStringRepresentation()) + .isEqualTo("ColorHSB[a=00 r=00 g=00 b=00 / h=0.000000 s=0.000000 b=0.000000]"); + assertThat(e.getActual().getStringRepresentation()) + .isEqualTo("ColorHSB[a=AA r=FF g=00 b=00 / h=0.000000 s=1.000000 b=1.000000] at:<[1, 2]>"); + return; + } + fail("AssertionFailedError expected"); + } + + @Test + void test_assertImageSizeEqual_same() { + try { + assertImageSizeEqual( + new BufferedImage(10, 20, BufferedImage.TYPE_BYTE_GRAY), + new BufferedImage(10, 20, BufferedImage.TYPE_INT_ARGB) + ); + } catch (Throwable t) { + fail("assertImagesSameSize() should not throw an exception here", t); + } + } + + @Test + void test_assertImageSizeEqual_different() { + try { + assertImageSizeEqual( + new BufferedImage(10, 20, BufferedImage.TYPE_BYTE_GRAY), + new BufferedImage(11, 22, BufferedImage.TYPE_INT_ARGB) + ); + } catch (AssertionFailedError e) { + assertThat(e) + .hasMessage("expected:[width=10 height=20] but was:[width=11 height=22]"); + assertThat(e.isExpectedDefined()) + .isTrue(); + assertThat(e.isActualDefined()) + .isTrue(); + assertThat(e.getExpected().getStringRepresentation()) + .isEqualTo("[width=10 height=20]"); + assertThat(e.getActual().getStringRepresentation()) + .isEqualTo("[width=11 height=22]"); + return; + } + fail("AssertionFailedError expected"); + } +}