/* * 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. */ /* $Id$ */ package org.apache.fop.visual; 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.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import org.apache.commons.io.IOUtils; 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.util.ParsedURL; /** * Helper class to visually compare two bitmap images. * <p> * This class was created by extracting reusable code from * org.apache.batik.test.util.ImageCompareText (Apache Batik) * into this separate class. * <p> * TODO Move as utility class to XML Graphics Commons when possible */ public final class BitmapComparator { private BitmapComparator() { } /** * Builds a new BufferedImage that is the difference between the two input images * @param ref the reference bitmap * @param gen the newly generated bitmap * @return the diff bitmap */ 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 [] refPix = null; int [] genPix = null; for (int y = 0; y < h; y++) { refPix = refWR.getPixels(0, y, w, 1, refPix); genPix = genWR.getPixels(0, y, w, 1, genPix); for (int i = 0; i < refPix.length; i++) { // val = ((genPix[i] - refPix[i]) * 5) + 128; int 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; } /** * Builds a combined image that places a number of images next to each other for * manual, visual comparison. * @param images the array of bitmaps * @return the combined image */ public static BufferedImage buildCompareImage(BufferedImage[] images) { BufferedImage cmp = new BufferedImage( images[0].getWidth() * images.length, images[0].getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g = cmp.createGraphics(); g.setPaint(Color.white); g.fillRect(0, 0, cmp.getWidth(), cmp.getHeight()); int lastWidth = 0; for (BufferedImage image : images) { if (lastWidth > 0) { g.translate(lastWidth, 0); } if (image != null) { g.drawImage(image, 0, 0, null); lastWidth = image.getWidth(); } else { lastWidth = 20; //Maybe add a special placeholder image instead later } } g.dispose(); return cmp; } /** * Builds a combined image that places two images next to each other for * manual, visual comparison. * @param ref the reference image * @param gen the actual image * @return the combined image */ public static BufferedImage buildCompareImage(BufferedImage ref, BufferedImage gen) { return buildCompareImage(new BufferedImage[] {ref, gen}); } /** * Loads an image from a URL * @param url the URL to the image * @return the bitmap as BufferedImage * TODO This method doesn't close the InputStream opened by the URL. */ public static 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; } /** * Loads an image from a URL * @param bitmapFile the bitmap file * @return the bitmap as BufferedImage */ public static BufferedImage getImage(File bitmapFile) { try { InputStream in = new java.io.FileInputStream(bitmapFile); try { in = new java.io.BufferedInputStream(in); ImageTagRegistry reg = ImageTagRegistry.getRegistry(); Filter filt = reg.readStream(in); 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; } finally { IOUtils.closeQuietly(in); } } catch (IOException e) { return null; } } }