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 }