001 /*
002 * Created on Jun 7, 2007
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 @2007-2011 the original author or authors.
014 */
015 package org.fest.assertions;
016
017 import static org.fest.assertions.ErrorMessages.*;
018 import static org.fest.assertions.Formatting.format;
019 import static org.fest.assertions.Threshold.threshold;
020 import static org.fest.util.Objects.areEqual;
021
022 import java.awt.Dimension;
023 import java.awt.image.BufferedImage;
024 import java.io.File;
025 import java.io.IOException;
026
027 /**
028 * Assertions for <code>{@link BufferedImage}</code>s.
029 * <p>
030 * To create a new instance of this class invoke <code>{@link Assertions#assertThat(BufferedImage)}</code>.
031 * </p>
032 *
033 * @author Yvonne Wang
034 * @author Alex Ruiz
035 * @author Ansgar Konermann
036 */
037 public class ImageAssert extends GenericAssert<ImageAssert, BufferedImage> {
038
039 private static final Threshold ZERO_THRESHOLD = threshold(0);
040
041 private static ImageReader imageReader = new ImageReader();
042
043 /**
044 * Reads the image in the specified path.
045 * @param imageFilePath the path of the image to read.
046 * @return the read image.
047 * @throws NullPointerException if the given path is {@code null}.
048 * @throws IllegalArgumentException if the given path does not belong to a file.
049 * @throws IOException if any I/O error occurred while reading the image.
050 */
051 public static BufferedImage read(String imageFilePath) throws IOException {
052 if (imageFilePath == null) throw new NullPointerException("The path of the image to read should not be null");
053 File imageFile = new File(imageFilePath);
054 if (!imageFile.isFile())
055 throw new IllegalArgumentException(format("The path <%s> does not belong to a file", imageFilePath));
056 return imageReader.read(imageFile);
057 }
058
059 /**
060 * Creates a new </code>{@link ImageAssert}</code>.
061 * @param actual the target to verify.
062 */
063 protected ImageAssert(BufferedImage actual) {
064 super(ImageAssert.class, actual);
065 }
066
067 /**
068 * Verifies that the actual image is equal to the given one. Two images are equal if they have the same size and the
069 * pixels at the same coordinates have the same color.
070 * @param expected the given image to compare the actual image to.
071 * @return this assertion object.
072 * @throws AssertionError if the actual image is not equal to the given one.
073 */
074 @Override public ImageAssert isEqualTo(BufferedImage expected) {
075 return isEqualTo(expected, ZERO_THRESHOLD);
076 }
077
078 /**
079 * Verifies that the actual image is equal to the given one. Two images are equal if:
080 * <ol>
081 * <li>they have the same size</li>
082 * <li>the difference between the RGB values of the color of each pixel is less than or equal to the given
083 * threshold</li>
084 * </ol>
085 * @param expected the given image to compare the actual image to.
086 * @param threshold the threshold to use to decide if the color of two pixels are similar: two pixels that are
087 * identical to the human eye may still have slightly different color values. For example, by using a threshold of 1
088 * we can indicate that a blue value of 60 is similar to a blue value of 61.
089 * @return this assertion object.
090 * @throws AssertionError if the actual image is not equal to the given one.
091 * @since 1.1
092 */
093 public ImageAssert isEqualTo(BufferedImage expected, Threshold threshold) {
094 if (areEqual(actual, expected)) return this;
095 failIfNull(expected);
096 failIfNotEqual(sizeOf(actual), sizeOf(expected));
097 failIfNotEqualColor(expected, threshold);
098 return this;
099 }
100
101 private void failIfNull(BufferedImage expected) {
102 if (expected != null) return;
103 failIfCustomMessageIsSet();
104 fail(unexpectedNotEqual(actual, null));
105 }
106
107 private void failIfNotEqual(Dimension a, Dimension e) {
108 if (areEqual(a, e)) return;
109 failIfCustomMessageIsSet();
110 fail(format("image size: expected:<%s> but was:<%s>", e, a));
111 }
112
113 private void failIfNotEqualColor(BufferedImage expected, Threshold threshold) {
114 int w = actual.getWidth();
115 int h = actual.getHeight();
116 for (int x = 0; x < w; x++)
117 for (int y = 0; y < h; y++)
118 failIfNotEqual(new RGBColor(actual.getRGB(x, y)), new RGBColor(expected.getRGB(x, y)), threshold, x, y);
119 }
120
121 private void failIfNotEqual(RGBColor a, RGBColor e, Threshold threshold, int x, int y) {
122 if (a.isEqualTo(e, threshold.value())) return;
123 failIfCustomMessageIsSet();
124 fail(String.format("expected:<%s> but was:<%s> at pixel [%d,%d]", a, e, x, y));
125 }
126
127 /**
128 * Verifies that the actual image is not equal to the given one. Two images are equal if they have the same size and
129 * the pixels at the same coordinates have the same color.
130 * @param image the given image to compare the actual image to.
131 * @return this assertion object.
132 * @throws AssertionError if the actual image is equal to the given one.
133 */
134 @Override public ImageAssert isNotEqualTo(BufferedImage image) {
135 if (areEqual(actual, image)) {
136 failIfCustomMessageIsSet();
137 throw failure(unexpectedEqual(actual, image));
138 }
139 if (image == null) return this;
140 if (areEqual(sizeOf(actual), sizeOf(image)) && hasEqualColor(image)) {
141 failIfCustomMessageIsSet();
142 throw failure("images are equal");
143 }
144 return this;
145 }
146
147 private static Dimension sizeOf(BufferedImage image) {
148 return new Dimension(image.getWidth(), image.getHeight());
149 }
150
151 private boolean hasEqualColor(BufferedImage expected) {
152 int w = actual.getWidth();
153 int h = actual.getHeight();
154 for (int x = 0; x < w; x++)
155 for (int y = 0; y < h; y++)
156 if (actual.getRGB(x, y) != expected.getRGB(x, y)) return false;
157 return true;
158 }
159
160 /**
161 * Verifies that the size of the actual image is equal to the given one.
162 * @param expected the expected size of the actual image.
163 * @return this assertion object.
164 * @throws AssertionError if the actual image is {@code null}.
165 * @throws NullPointerException if the given size is {@code null}.
166 * @throws AssertionError if the size of the actual image is not equal to the given one.
167 */
168 public ImageAssert hasSize(Dimension expected) {
169 isNotNull();
170 if (expected == null)
171 throw new NullPointerException(formattedErrorMessage("The size to compare to should not be null"));
172 Dimension actualDimension = new Dimension(actual.getWidth(), actual.getHeight());
173 Fail.failIfNotEqual(customErrorMessage(), rawDescription(), actualDimension, expected);
174 return this;
175 }
176
177 static void imageReader(ImageReader newImageReader) {
178 imageReader = newImageReader;
179 }
180 }