Package net.mindengine.galen.validation.specs

Source Code of net.mindengine.galen.validation.specs.SpecValidationImage$ImageCheck

/*******************************************************************************
* Copyright 2014 Ivan Shubin http://mindengine.net
*
* Licensed 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 net.mindengine.galen.validation.specs;

import net.mindengine.galen.page.PageElement;
import net.mindengine.galen.page.Rect;
import net.mindengine.galen.specs.SpecImage;
import net.mindengine.galen.utils.GalenUtils;
import net.mindengine.galen.validation.*;
import net.mindengine.rainbow4j.ComparisonOptions;
import net.mindengine.rainbow4j.ImageCompareResult;
import net.mindengine.rainbow4j.Rainbow4J;

import javax.xml.bind.ValidationException;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedList;

public class SpecValidationImage extends SpecValidation<SpecImage> {

    private static class ImageCheck {

        private final String imagePath;
        private final double difference;
        private final ImageCompareResult result;
        private final String errorMessage;

        public ImageCheck(String imagePath, double difference, ImageCompareResult result, String errorMessage) {
            this.imagePath = imagePath;
            this.difference = difference;
            this.result = result;
            this.errorMessage = errorMessage;
        }
    }

    @Override
    public void check(PageValidation pageValidation, String objectName, SpecImage spec) throws ValidationErrorException {
        PageElement pageElement = pageValidation.findPageElement(objectName);
        checkAvailability(pageElement, objectName);

        final BufferedImage pageImage = pageValidation.getPage().getScreenshotImage();

        // TODO fix. should only take screenshot once per the same page if there are multiple image comparison checks

        int tolerance = spec.getTolerance() != null ? Math.abs(spec.getTolerance()) : 25;

        ComparisonOptions options = new ComparisonOptions();
        options.setStretchToFit(spec.isStretch());
        options.setOriginalFilters(spec.getOriginalFilters());
        options.setSampleFilters(spec.getSampleFilters());
        options.setMapFilters(spec.getMapFilters());
        options.setTolerance(tolerance);


        Rect elementArea = pageElement.getArea();

        ImageCheck minCheck = new ImageCheck(spec.getImagePaths().get(0), elementArea.getHeight() * elementArea.getWidth() * 2, null, null);

        Iterator<String> it = spec.getImagePaths().iterator();

        if (!it.hasNext()) {
            throw new ValidationErrorException("There are now images defined to compare with")
                    .withErrorArea(new ErrorArea(pageElement.getArea(), objectName));
        }

        try {
            while (minCheck.difference > 0 && it.hasNext()) {
                String imagePath = it.next();

                ImageCheck imageCheck = checkImages(spec, pageImage, options, elementArea, imagePath);
                if (imageCheck.difference <= minCheck.difference) {
                    minCheck = imageCheck;
                }
            }
        }
        catch (ValidationErrorException ex) {
            ex.withErrorArea(new ErrorArea(pageElement.getArea(), objectName));
            throw ex;
        }
        catch (Exception ex) {
            throw new ValidationErrorException(ex).withErrorArea(new ErrorArea(pageElement.getArea(), objectName));
        }

        if (minCheck.difference > 0) {
            throw new ValidationErrorException(minCheck.errorMessage)
                    .withErrorArea(new ErrorArea(pageElement.getArea(), objectName))
                    .withImageComparison(new ImageComparison(spec.getSelectedArea(), minCheck.imagePath, minCheck.result.getComparisonMap()));
        }
    }

    private ImageCheck checkImages(SpecImage spec, BufferedImage pageImage, ComparisonOptions options, Rect elementArea, String imagePath) throws ValidationErrorException {
        BufferedImage sampleImage;
        try {
            InputStream stream = GalenUtils.findFileOrResourceAsStream(imagePath);
            sampleImage = Rainbow4J.loadImage(stream);
        } catch (Throwable e) {
            throw new ValidationErrorException("Couldn't load image: " + spec.getImagePaths().get(0));
        }

        Rectangle sampleArea = spec.getSelectedArea() != null ? toRectangle(spec.getSelectedArea()) : new Rectangle(0, 0, sampleImage.getWidth(), sampleImage.getHeight());


        if (elementArea.getLeft() >= pageImage.getWidth() || elementArea.getTop() >= pageImage.getHeight()) {
            throw new RuntimeException(String.format("The page element is located outside of the screenshot. (Element {x: %d, y: %d, w: %d, h: %d}, Screenshot {w: %d, h: %d})",
                    elementArea.getLeft(), elementArea.getTop(), elementArea.getWidth(), elementArea.getHeight(),
                    pageImage.getWidth(), pageImage.getHeight()));
        }

        if (spec.isCropIfOutside()) {
            elementArea = cropElementAreaIfOutside(elementArea, pageImage.getWidth(), pageImage.getHeight());
        }

        ImageCompareResult result = Rainbow4J.compare(pageImage, sampleImage, toRectangle(elementArea), sampleArea, options);

        double difference = 0.0;
        String errorMessage = null;
        if (spec.getMaxPercentage() != null) {
            difference = result.getPercentage() - spec.getMaxPercentage();
            if (difference > 0) {
                errorMessage = createErrorMessageForPercentage(msgErrorPrefix(spec.getImagePaths().get(0)), spec.getMaxPercentage(), result.getPercentage());
            }
        } else {
            if (spec.getMaxPixels() == null) {
                spec.setMaxPixels(0);
            }

            difference = result.getTotalPixels() - spec.getMaxPixels();
            if (difference > 0) {
                errorMessage = createErrorMessageForPixels(msgErrorPrefix(spec.getImagePaths().get(0)), spec.getMaxPixels(), result.getTotalPixels());
            }
        }

        return new ImageCheck(imagePath, difference, result, errorMessage);
    }

    private Rect cropElementAreaIfOutside(Rect elementArea, int width, int height) {
        int x2 = elementArea.getLeft() + elementArea.getWidth();
        int y2 = elementArea.getTop() + elementArea.getHeight();

        int originalWidth = elementArea.getWidth();
        int originalHeight = elementArea.getHeight();

        if (originalWidth > 0 && originalHeight > 0) {
            int newWidth = originalWidth;
            int newHeight = originalHeight;

            if (x2 >= width) {
                newWidth -= x2 - width + 1;
            }
            if (y2 >= height) {
                newHeight -= y2 - height + 1;
            }


            if ((double)(newWidth * newHeight) / (double)(originalWidth * originalHeight) < 0.5) {
                throw new RuntimeException(String.format("The cropped area is less than a half of element area (Element {x: %d, y: %d, w: %d, h: %d}, Screenshot {w: %d, h: %d})",
                        elementArea.getLeft(), elementArea.getTop(), newWidth, newHeight,
                        width, height));
            }

            return new Rect(elementArea.getLeft(), elementArea.getTop(), newWidth, newHeight);
        }
        return elementArea;
    }

    private String msgErrorPrefix(String imagePath) {
        return String.format("Element does not look like \"%s\". ", imagePath);
    }

    private String createErrorMessageForPixels(String msgPrefix, Integer maxPixels, long totalPixels) throws ValidationErrorException {
        return String.format("%sThere are %d mismatching pixels but max allowed is %d", msgPrefix, totalPixels, maxPixels);
    }

    private String createErrorMessageForPercentage(String msgPrefix, Double maxPercentage, double percentage) throws ValidationErrorException {
        return String.format("%sThere are %s%% mismatching pixels but max allowed is %s%%", msgPrefix, percentage, maxPercentage);
    }


    private Rectangle toRectangle(Rect area) {
        return new Rectangle(area.getLeft(), area.getTop(), area.getWidth(), area.getHeight());
    }
}
TOP

Related Classes of net.mindengine.galen.validation.specs.SpecValidationImage$ImageCheck

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.