1 /*
   2  * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.jemmy.image.pixel;
  24 
  25 import java.util.Arrays;
  26 import org.jemmy.Dimension;
  27 import org.jemmy.env.Environment;
  28 import org.jemmy.image.Image;
  29 import org.jemmy.image.ImageComparator;
  30 import org.jemmy.image.pixel.Raster.Component;
  31 
  32 /**
  33  *
  34  * @author shura
  35  */
  36 public abstract class PixelImageComparator implements ImageComparator {
  37 
  38     static {
  39         Environment.getEnvironment().setPropertyIfNotSet(RasterComparator.class, 
  40                 new PixelEqualityRasterComparator(0));
  41 //                new MaxDistanceComparator((double)1/0x8f));
  42     }
  43 
  44     private RasterComparator comparator = null;
  45     private Environment env = null;
  46     
  47     /**
  48      *
  49      * @param comparator
  50      */
  51     public PixelImageComparator(RasterComparator comparator) {
  52         this.comparator = comparator;
  53     }
  54 
  55     public PixelImageComparator(Environment env) {
  56         this.env = env;
  57     }
  58 
  59     public synchronized RasterComparator getRasterComparator() {
  60         if(comparator == null) {
  61             return env.getProperty(RasterComparator.class);
  62         } else {
  63             return comparator;
  64         }
  65     }
  66 
  67     /**
  68      *
  69      * @param one
  70      * @param two
  71      * @return
  72      */
  73     public static Dimension computeDiffSize(Raster one, Raster two) {
  74         if (one.getSize().equals(two.getSize())) {
  75             return one.getSize();
  76         } else {
  77             return null;
  78         }
  79     }
  80 
  81     public Image compare(Image image1, Image image2) {
  82         Raster pi1 = toRaster(image1);
  83         Raster pi2 = toRaster(image2);
  84         if (!getRasterComparator().compare(pi1, pi2)) {
  85             return toImage(computeDifference(pi1, pi2));
  86         } else {
  87             return null;
  88         }
  89     }
  90 
  91     /**
  92      *
  93      * @param image1
  94      * @param image2
  95      * @return
  96      */
  97     public WriteableRaster computeDifference(Raster image1, Raster image2) {
  98         Dimension size = computeDiffSize(image1, image2);
  99         if (size == null) {
 100             size = new Dimension(Math.max(image1.getSize().width, image2.getSize().width),
 101                     Math.max(image1.getSize().height, image2.getSize().height));
 102         }
 103         WriteableRaster res = createDiffRaster(image1, image2);
 104         double[] colors1 = new double[image1.getSupported().length];
 105         double[] colors2 = new double[image2.getSupported().length];
 106         double[] colorsRes = new double[res.getSupported().length];
 107         for (int x = 0; x < size.width; x++) {
 108             for (int y = 0; y < size.height; y++) {
 109                 if (x < image1.getSize().width && y < image1.getSize().height) {
 110                     image1.getColors(x, y, colors1);
 111                 } else {
 112                     Arrays.fill(colors1, 0);
 113                 }
 114                 if (x < image2.getSize().width && y < image2.getSize().height) {
 115                     image2.getColors(x, y, colors2);
 116                 } else {
 117                     Arrays.fill(colors2, 1);
 118                 }
 119                 calcDiffColor(image1.getSupported(), colors1, image2.getSupported(), colors2, 
 120                         res.getSupported(), colorsRes);
 121                 res.setColors(x, y, colorsRes);
 122             }
 123         }
 124         return res;
 125     }
 126 
 127     private static final Component[] diffComponents = {
 128         Component.RED, Component.BLUE, Component.GREEN
 129     };
 130     /**
 131      *
 132      * @param comps1 
 133      * @param colors1 
 134      * @param comps2 
 135      * @param colors2 
 136      * @param compsRes 
 137      * @param colorsRes  
 138      */
 139     protected void calcDiffColor(Raster.Component[] comps1, double[] colors1,
 140             Raster.Component[] comps2, double[] colors2, Raster.Component[] compsRes, double[] colorsRes) {
 141         double square1, square2;
 142         double dist = 0;
 143         
 144         for (Component c : diffComponents) {
 145             square1 = getComponentValue(comps1, colors1, c);
 146             square2 = getComponentValue(comps2, colors2, c);
 147             dist += (square2 - square1) * (square2 - square1);
 148         }
 149         for (Component c : diffComponents) {
 150             colorsRes[arrayIndexOf(compsRes, c)] = Math.sqrt(dist) / Math.sqrt(3);
 151         }
 152         colorsRes[arrayIndexOf(compsRes, Component.ALPHA)] = 1;
 153     }
 154 
 155     public String getID() {
 156         return getRasterComparator().getID();
 157     }
 158 
 159     /**
 160      *
 161      * @param image
 162      * @return
 163      */
 164     protected abstract Image toImage(Raster image);
 165 
 166     /**
 167      *
 168      * @param image
 169      * @return
 170      */
 171     protected abstract Raster toRaster(Image image);
 172 
 173     /**
 174      *
 175      * @param r1 
 176      * @param r2 
 177      * @return
 178      */
 179     protected abstract WriteableRaster createDiffRaster(Raster r1, Raster r2);
 180 
 181     /**
 182      * 
 183      * @param comps
 184      * @param comp
 185      * @return
 186      */
 187     public static int arrayIndexOf(Raster.Component[] comps, Raster.Component comp) {
 188         for (int i = 0; i < comps.length; i++) {
 189             if (comp == comps[i]) {
 190                 return i;
 191             }
 192         }
 193         throw new IllegalArgumentException("Unknown component " + comp);
 194     }
 195     
 196     /**
 197      * Returns color component value using its alpha information
 198      * 
 199      * @param components available color components
 200      * @param colors color components values
 201      * @param comp required color component
 202      * 
 203      * @return value of the required color component.
 204      * If pixel is not opaque, then it is blended with white
 205      * opaque background
 206      */
 207     protected double getComponentValue(Component[] components, double[] colors, Component comp) {
 208         
 209         double result = colors[arrayIndexOf(components, comp)];
 210         
 211         if(result < 0.0 || result > 1.0) throw new IllegalStateException("Component value = " + result);
 212         
 213         //Find alpha index if exists
 214         int idxAlpha = -1;
 215         try {
 216             idxAlpha = arrayIndexOf(components, Component.ALPHA);
 217         } catch (IllegalArgumentException ex) {
 218         }
 219         
 220         //If alpha component is available
 221         if (idxAlpha != -1) {
 222             double alpha = colors[idxAlpha];
 223             
 224             if(alpha < 0.0 || alpha > 1.0) throw new IllegalStateException("Alpha value = " + alpha);
 225 
 226             //If not opaque
 227             if (alpha < 1.0) {
 228                 //Blend with opaque white
 229                 result = Math.min(1.0, alpha * result + 1 - alpha);
 230             }
 231         }
 232         
 233         return result;
 234     }
 235 }