001 /* 002 * Created on Oct 20, 2010 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 005 * the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 011 * specific language governing permissions and limitations under the License. 012 * 013 * Copyright @2010-2011 the original author or authors. 014 */ 015 package org.fest.assertions.internal; 016 017 import static org.fest.assertions.data.Offset.offset; 018 import static org.fest.assertions.data.RgbColor.color; 019 import static org.fest.assertions.error.ShouldBeEqualColors.shouldBeEqualColors; 020 import static org.fest.assertions.error.ShouldBeEqualImages.shouldBeEqualImages; 021 import static org.fest.assertions.error.ShouldHaveSize.shouldHaveSize; 022 import static org.fest.assertions.error.ShouldNotBeEqualImages.shouldNotBeEqualImages; 023 import static org.fest.assertions.internal.ColorComparisonResult.*; 024 import static org.fest.assertions.internal.CommonValidations.checkOffsetIsNotNull; 025 import static org.fest.util.Objects.areEqual; 026 027 import java.awt.Dimension; 028 import java.awt.image.BufferedImage; 029 030 import org.fest.assertions.core.AssertionInfo; 031 import org.fest.assertions.data.*; 032 import org.fest.assertions.error.ErrorMessageFactory; 033 import org.fest.util.VisibleForTesting; 034 035 /** 036 * Reusable assertions for <code>{@link BufferedImage}</code>s. 037 * 038 * @author Yvonne Wang 039 */ 040 public class Images { 041 042 private static final Images INSTANCE = new Images(); 043 private static final Offset<Integer> ZERO = offset(0); 044 045 /** 046 * Returns the singleton instance of this class. 047 * @return the singleton instance of this class. 048 */ 049 public static Images instance() { 050 return INSTANCE; 051 } 052 053 @VisibleForTesting Failures failures = Failures.instance(); 054 055 @VisibleForTesting Images() {} 056 057 /** 058 * Asserts that two images are equal. Two images are equal if: 059 * <ol> 060 * <li>they have equal size</li> 061 * <li>the the RGB values of the color at each pixel are equal</li> 062 * </ol> 063 * @param info contains information about the assertion. 064 * @param actual the actual image. 065 * @param expected the expected image. 066 * @throws AssertionError if the actual image is not equal to the expected one. 067 */ 068 public void assertEqual(AssertionInfo info, BufferedImage actual, BufferedImage expected) { 069 assertEqual(info, actual, expected, ZERO); 070 } 071 072 /** 073 * Asserts that two images are equal. Two images are equal if: 074 * <ol> 075 * <li>they have the same size</li> 076 * <li>the difference between the RGB values of the color at each pixel is less than or equal to the given 077 * offset</li> 078 * </ol> 079 * @param info contains information about the assertion. 080 * @param actual the actual image. 081 * @param expected the expected image. 082 * @param offset helps decide if the color of two pixels are similar: two pixels that are identical to the human eye 083 * may still have slightly different color values. For example, by using an offset of 1 we can indicate that a blue 084 * value of 60 is similar to a blue value of 61. 085 * @throws NullPointerException if the given offset is {@code null}. 086 * @throws AssertionError if the actual image is not equal to the expected one. 087 */ 088 public void assertEqual(AssertionInfo info, BufferedImage actual, BufferedImage expected, Offset<Integer> offset) { 089 checkOffsetIsNotNull(offset); 090 if (areEqual(actual, expected)) return; 091 if (actual == null || expected == null) throw imagesShouldBeEqual(info, offset); 092 // BufferedImage does not have an implementation of 'equals,' which means that "equality" is verified by identity. 093 // We need to verify that two images are equal ourselves. 094 if (!haveEqualSize(actual, expected)) throw imageShouldHaveSize(info, actual, sizeOf(actual), sizeOf(expected)); 095 ColorComparisonResult haveEqualColor = haveEqualColor(actual, expected, offset); 096 if (haveEqualColor == ARE_EQUAL) return; 097 throw failures.failure(info, imagesShouldHaveEqualColor(haveEqualColor, offset)); 098 } 099 100 private AssertionError imagesShouldBeEqual(AssertionInfo info, Offset<Integer> offset) { 101 return failures.failure(info, shouldBeEqualImages(offset)); 102 } 103 104 private ErrorMessageFactory imagesShouldHaveEqualColor(ColorComparisonResult r, Offset<Integer> offset) { 105 return shouldBeEqualColors(r.color2, r.color1, r.point, offset); 106 } 107 108 /** 109 * Asserts that two images are not equal. 110 * @param info contains information about the assertion. 111 * @param actual the given image. 112 * @param other the object to compare {@code actual} to. 113 * @throws AssertionError if {@code actual} is equal to {@code other}. 114 */ 115 public void assertNotEqual(AssertionInfo info, BufferedImage actual, BufferedImage other) { 116 if (areEqual(actual, other)) throw imagesShouldNotBeEqual(info); 117 if (actual == null || other == null) return; 118 if (!(haveEqualSize(actual, other))) return; 119 ColorComparisonResult haveEqualColor = haveEqualColor(actual, other, ZERO); 120 if (haveEqualColor != ARE_EQUAL) return; 121 throw imagesShouldNotBeEqual(info); 122 } 123 124 private AssertionError imagesShouldNotBeEqual(AssertionInfo info) { 125 return failures.failure(info, shouldNotBeEqualImages()); 126 } 127 128 private boolean haveEqualSize(BufferedImage i1, BufferedImage i2) { 129 return i1.getWidth() == i2.getWidth() && i1.getHeight() == i2.getHeight(); 130 } 131 132 private ColorComparisonResult haveEqualColor(BufferedImage i1, BufferedImage i2, Offset<Integer> offset) { 133 int w = i1.getWidth(); 134 int h = i1.getHeight(); 135 for (int x = 0; x < w; x++) { 136 for (int y = 0; y < h; y++) { 137 RgbColor c1 = color(i1.getRGB(x, y)); 138 RgbColor c2 = color(i2.getRGB(x, y)); 139 if (c1.isEqualTo(c2, offset)) continue; 140 return notEqual(c1, c2, x, y); 141 } 142 } 143 return ARE_EQUAL; 144 } 145 146 /** 147 * Asserts that the size of the given image is equal to the given size. 148 * @param info contains information about the assertion. 149 * @param actual the given image. 150 * @param size the expected size of {@code actual}. 151 * @throws NullPointerException if the given size is {@code null}. 152 * @throws AssertionError if the size of the given image is not equal to the given size. 153 */ 154 public void assertHasSize(AssertionInfo info, BufferedImage actual, Dimension size) { 155 if (size == null) throw new NullPointerException("The given size should not be null"); 156 Objects.instance().assertNotNull(info, actual); 157 Dimension sizeOfActual = sizeOf(actual); 158 if (areEqual(sizeOfActual, size)) return; 159 throw imageShouldHaveSize(info, actual, sizeOfActual, size); 160 } 161 162 private AssertionError imageShouldHaveSize(AssertionInfo info, BufferedImage image, Dimension actual, Dimension expected) { 163 return failures.failure(info, shouldHaveSize(image, actual, expected)); 164 } 165 166 @VisibleForTesting static Dimension sizeOf(BufferedImage image) { 167 return new Dimension(image.getWidth(), image.getHeight()); 168 } 169 }