| /* |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.batik.test.svg; |
| |
| import java.awt.Color; |
| import java.awt.Graphics2D; |
| import java.awt.image.BufferedImage; |
| import java.awt.image.ColorModel; |
| import java.awt.image.RenderedImage; |
| import java.awt.image.WritableRaster; |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| |
| import org.apache.batik.ext.awt.image.GraphicsUtil; |
| import org.apache.batik.ext.awt.image.renderable.Filter; |
| import org.apache.batik.ext.awt.image.spi.ImageTagRegistry; |
| import org.apache.batik.ext.awt.image.spi.ImageWriter; |
| import org.apache.batik.ext.awt.image.spi.ImageWriterRegistry; |
| import org.apache.batik.test.AbstractTest; |
| import org.apache.batik.test.TestReport; |
| import org.apache.batik.util.ParsedURL; |
| |
| /** |
| * This test does a pixel comparison of two images and passes if the |
| * two images are identical. It fails otherwise, producing a report |
| * describing why the two images are different. |
| * |
| * @author <a href="mailto:vhardy@apache.org">Vincent Hardy</a> |
| * @version $Id$ |
| */ |
| public class ImageCompareTest extends AbstractTest { |
| public static final String ERROR_COULD_NOT_OPEN_IMAGE |
| = "ImageCompareTest.error.could.not.open.image"; |
| |
| public static final String ERROR_COULD_NOT_LOAD_IMAGE |
| = "ImageCompareTest.error.could.not.load.image"; |
| |
| public static final String ERROR_DIFFERENCES |
| = "ImageCompareTest.error.differences"; |
| |
| public static final String ERROR_WHILE_COMPARING_FILES |
| = "ImageCompareTest.error.while.comparing.files"; |
| |
| public static final String ENTRY_KEY_FIRST_IMAGE |
| = "ImageCompareTest.entry.key.first.image"; |
| |
| public static final String ENTRY_KEY_SECOND_IMAGE |
| = "ImageCompareTest.entry.key.second.image"; |
| |
| public static final String ENTRY_KEY_COMPARISON |
| = "ImageCompareTest.entry.key.comparison"; |
| |
| public static final String ENTRY_KEY_DIFFERENCE |
| = "ImageCompareTest.entry.key.difference"; |
| |
| public static final String ENTRY_KEY_IMAGE_URL |
| = "ImageCompareTest.entry.key.image.url"; |
| |
| public static final String IMAGE_TYPE_DIFFERENCE |
| = "_diff"; |
| |
| public static final String IMAGE_TYPE_COMPARISON |
| = "_cmp"; |
| |
| /** |
| * Prefix for the temporary files created by Tests |
| * of this class |
| */ |
| public static final String TEMP_FILE_PREFIX |
| = "ImageCompareTest"; |
| |
| /** |
| * Suffix for the temporary files created by |
| * Tests of this class |
| */ |
| public static final String TEMP_FILE_SUFFIX |
| = ""; |
| |
| /** |
| * URL for the first image to be compared. |
| */ |
| protected String urlAStr; |
| protected URL urlA; |
| |
| /** |
| * URL for the second image to be compared |
| */ |
| protected String urlBStr; |
| protected URL urlB; |
| |
| /** |
| * Resolves the input string as follows. |
| * + First, the string is interpreted as a file description. |
| * If the file exists, then the file name is turned into |
| * a URL. |
| * + Otherwise, the string is supposed to be a URL. If it |
| * is an invalid URL, an IllegalArgumentException is thrown. |
| */ |
| protected URL resolveURL(String url){ |
| // Is url a file? |
| File f = (new File(url)).getAbsoluteFile(); |
| if(f.exists()){ |
| try{ |
| return f.toURI().toURL(); |
| }catch(MalformedURLException e){ |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| // url is not a file. It must be a regular URL... |
| try{ |
| return new URL(url); |
| }catch(MalformedURLException e){ |
| throw new IllegalArgumentException(url); |
| } |
| } |
| |
| /** |
| * This test makes a binary comparison of the two images |
| * (and not a pixel comparison). If the images are different, |
| * the test generates a report containing the two images and |
| * a delta images to help the user visualize the difference. |
| * |
| * @param urlA first image |
| * @param urlB second image |
| */ |
| public ImageCompareTest(String urlA, |
| String urlB){ |
| urlAStr = urlA; |
| urlBStr = urlB; |
| } |
| |
| protected void initURLs(){ |
| if(urlA == null){ |
| throw new IllegalArgumentException(); |
| } |
| |
| if(urlB == null){ |
| throw new IllegalArgumentException(); |
| } |
| |
| this.urlA = resolveURL(urlAStr); |
| this.urlB = resolveURL(urlBStr); |
| } |
| |
| public TestReport rumImpl() throws Exception { |
| initURLs(); |
| |
| InputStream streamA = null; |
| |
| try{ |
| streamA = new BufferedInputStream(urlA.openStream()); |
| }catch(IOException e){ |
| return reportException(ERROR_COULD_NOT_OPEN_IMAGE, e); |
| } |
| |
| InputStream streamB = null; |
| |
| try{ |
| streamB = new BufferedInputStream(urlB.openStream()); |
| }catch(IOException e){ |
| return reportException(ERROR_COULD_NOT_OPEN_IMAGE, e); |
| } |
| |
| boolean accurate = false; |
| |
| try{ |
| accurate = compare(streamA, streamB); |
| }catch(IOException e){ |
| TestReport report = reportException(ERROR_WHILE_COMPARING_FILES, e); |
| report.addDescriptionEntry(ENTRY_KEY_FIRST_IMAGE, |
| urlA.toString()); |
| report.addDescriptionEntry(ENTRY_KEY_SECOND_IMAGE, |
| urlB.toString()); |
| return report; |
| } |
| |
| if(accurate){ |
| return reportSuccess(); |
| } |
| |
| // We are in error (images are different: produce an image |
| // with the two images side by side as well as a diff image) |
| BufferedImage imageA = getImage(urlA); |
| if(imageA == null){ |
| TestReport report = reportError(ERROR_COULD_NOT_LOAD_IMAGE); |
| report.addDescriptionEntry(ENTRY_KEY_IMAGE_URL, |
| urlA.toString()); |
| return report; |
| } |
| |
| BufferedImage imageB = getImage(urlB); |
| if(imageB == null){ |
| TestReport report = reportError(ERROR_COULD_NOT_LOAD_IMAGE); |
| report.addDescriptionEntry(ENTRY_KEY_IMAGE_URL, |
| urlB.toString()); |
| return report; |
| } |
| |
| BufferedImage diff = buildDiffImage(imageA, imageB); |
| BufferedImage cmp = buildCompareImage(imageA, imageB); |
| |
| File tmpDiff = imageToFile(diff, IMAGE_TYPE_DIFFERENCE); |
| File tmpCmp = imageToFile(cmp, IMAGE_TYPE_COMPARISON); |
| |
| TestReport report = reportError(ERROR_DIFFERENCES); |
| report.addDescriptionEntry(ENTRY_KEY_COMPARISON, tmpCmp); |
| report.addDescriptionEntry(ENTRY_KEY_DIFFERENCE, tmpDiff); |
| |
| return report; |
| } |
| |
| protected BufferedImage buildCompareImage(BufferedImage ref, |
| BufferedImage gen){ |
| BufferedImage cmp = new BufferedImage(ref.getWidth()*2, |
| ref.getHeight(), |
| BufferedImage.TYPE_INT_ARGB); |
| |
| Graphics2D g = cmp.createGraphics(); |
| g.setPaint(Color.white); |
| g.fillRect(0, 0, cmp.getWidth(), cmp.getHeight()); |
| g.drawImage(ref, 0, 0, null); |
| g.translate(ref.getWidth(), 0); |
| g.drawImage(gen, 0, 0, null); |
| g.dispose(); |
| |
| return cmp; |
| } |
| |
| /** |
| * Creates a temporary File into which the input image is |
| * saved. |
| */ |
| protected File imageToFile(BufferedImage img, |
| String imageType) |
| throws IOException { |
| |
| File imageFile = makeRandomFileName(imageType); |
| imageFile.deleteOnExit(); |
| |
| ImageWriter writer = ImageWriterRegistry.getInstance() |
| .getWriterFor("image/png"); |
| OutputStream out = new FileOutputStream(imageFile); |
| try { |
| writer.writeImage(img, out); |
| } finally { |
| out.close(); |
| } |
| |
| return imageFile; |
| |
| } |
| |
| /** |
| * Creates a temporary File into which the input image is |
| * saved. |
| */ |
| protected File makeRandomFileName(String imageType) |
| throws IOException { |
| |
| return File.createTempFile(TEMP_FILE_PREFIX, |
| TEMP_FILE_SUFFIX + imageType, |
| null); |
| } |
| |
| /** |
| * Builds a new BufferedImage that is the difference between the two input images |
| */ |
| public static BufferedImage buildDiffImage(BufferedImage ref, |
| BufferedImage gen) { |
| BufferedImage diff = new BufferedImage(ref.getWidth(), |
| ref.getHeight(), |
| BufferedImage.TYPE_INT_ARGB); |
| WritableRaster refWR = ref.getRaster(); |
| WritableRaster genWR = gen.getRaster(); |
| WritableRaster dstWR = diff.getRaster(); |
| |
| boolean refPre = ref.isAlphaPremultiplied(); |
| if (!refPre) { |
| ColorModel cm = ref.getColorModel(); |
| cm = GraphicsUtil.coerceData(refWR, cm, true); |
| ref = new BufferedImage(cm, refWR, true, null); |
| } |
| boolean genPre = gen.isAlphaPremultiplied(); |
| if (!genPre) { |
| ColorModel cm = gen.getColorModel(); |
| cm = GraphicsUtil.coerceData(genWR, cm, true); |
| gen = new BufferedImage(cm, genWR, true, null); |
| } |
| |
| |
| int w=ref.getWidth(); |
| int h=ref.getHeight(); |
| |
| int y, i,val; |
| int [] refPix = null; |
| int [] genPix = null; |
| for (y=0; y<h; y++) { |
| refPix = refWR.getPixels (0, y, w, 1, refPix); |
| genPix = genWR.getPixels (0, y, w, 1, genPix); |
| for (i=0; i<refPix.length; i++) { |
| // val = ((genPix[i]-refPix[i])*5)+128; |
| val = ((refPix[i]-genPix[i])*10)+128; |
| if ((val & 0xFFFFFF00) != 0) |
| if ((val & 0x80000000) != 0) val = 0; |
| else val = 255; |
| genPix[i] = val; |
| } |
| dstWR.setPixels(0, y, w, 1, genPix); |
| } |
| |
| if (!genPre) { |
| ColorModel cm = gen.getColorModel(); |
| cm = GraphicsUtil.coerceData(genWR, cm, false); |
| } |
| |
| if (!refPre) { |
| ColorModel cm = ref.getColorModel(); |
| cm = GraphicsUtil.coerceData(refWR, cm, false); |
| } |
| |
| return diff; |
| } |
| |
| |
| /** |
| * Compare the two input streams |
| */ |
| public static boolean compare(InputStream refStream, |
| InputStream newStream) |
| throws IOException{ |
| int b, nb; |
| do { |
| b = refStream.read(); |
| nb = newStream.read(); |
| } while (b != -1 && nb != -1 && b == nb); |
| refStream.close(); |
| newStream.close(); |
| return (b == nb); |
| } |
| |
| /** |
| * Loads an image from a URL |
| */ |
| protected BufferedImage getImage(URL url) { |
| ImageTagRegistry reg = ImageTagRegistry.getRegistry(); |
| Filter filt = reg.readURL(new ParsedURL(url)); |
| if(filt == null){ |
| return null; |
| } |
| |
| RenderedImage red = filt.createDefaultRendering(); |
| if(red == null){ |
| return null; |
| } |
| |
| BufferedImage img = new BufferedImage(red.getWidth(), |
| red.getHeight(), |
| BufferedImage.TYPE_INT_ARGB); |
| red.copyData(img.getRaster()); |
| |
| return img; |
| } |
| |
| |
| } |
| |