/**
* Copyright 2014 Comcast Cable Communications Management, LLC
*
* This file is part of CATS.
*
* CATS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CATS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CATS. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Image Compare ProviderImpl v0.2
*/
package com.comcast.cats.provider.impl;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.imageio.ImageIO;
import javax.xml.bind.JAXBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.comcast.cats.Settop;
import com.comcast.cats.image.BufferedImageLoader;
import com.comcast.cats.image.CoreImageCompare;
import com.comcast.cats.image.CoreRegionCompare;
import com.comcast.cats.image.ImageCompareRegionInfo;
import com.comcast.cats.image.ImageCompareResult;
import com.comcast.cats.image.RegionInfo;
import com.comcast.cats.provider.ImageCompareProvider;
import com.comcast.cats.provider.VideoProvider;
import com.comcast.cats.provider.exceptions.ImageCompareException;
/**
* Provides Implementation of ImageCompareProvider.
*
* @author sajayjk
*
*/
public class ImageCompareProviderImpl extends RegionLocatorProviderImpl implements ImageCompareProvider
{
/**
*
*/
private static final long serialVersionUID = 1L;
private VideoProvider videoProvider = null;
private int driftX = 0;
private int driftY = 0;
private BufferedImageLoader imageLoader;
private int redTolerance = ImageCompareRegionInfo.DEFAULT_RED_TOLERANCE;
private int greenTolerance = ImageCompareRegionInfo.DEFAULT_GREEN_TOLERANCE;
private int blueTolerance = ImageCompareRegionInfo.DEFAULT_BLUE_TOLERANCE;
private float matchPercent = ImageCompareRegionInfo.DEFAULT_MATCH_PERCENT;
private int xTolerance = RegionInfo.DEFAULT_X_TOLERANCE;
private int yTolerance = RegionInfo.DEFAULT_Y_TOLERANCE;
// Global counter used to avoid name collisions if
// multiple comparisons fail within a very short time.
// Unsure properly synchronized access!
private static int counter = 0;
private static final String imageSaveFormat = "%1$tY%1$tm%1$td-%1$tH%1$tM%1$tS-%2$04d";
private static final String actualFileExtn = "-actual.jpg";
private static final String expectedFileExtn = "-expected.jpg";
private String imageSaveLocation;
private static final Logger logger = LoggerFactory.getLogger( ImageCompareProviderImpl.class );
private RegionLocatorProviderImpl regionLocatorProvider;
public ImageCompareProviderImpl( Class< ? > resourceClass, VideoProvider videoProvider, int driftX, int driftY,
Properties theProps )
{
logger.debug( "Instatiating ImageCompareProviderImpl" );
this.videoProvider = videoProvider;
this.regionLocatorProvider = new RegionLocatorProviderImpl( resourceClass );
this.driftX = driftX;
this.driftY = driftY;
imageLoader = new BufferedImageLoader( resourceClass, "setup" );
if ( theProps != null )
{
String matchPct = theProps.getProperty( "matchPercent",
String.valueOf( ImageCompareRegionInfo.DEFAULT_MATCH_PERCENT ) );
String redt = theProps.getProperty( "redtolerance",
String.valueOf( ImageCompareRegionInfo.DEFAULT_RED_TOLERANCE ) );
String greent = theProps.getProperty( "greentolerance",
String.valueOf( ImageCompareRegionInfo.DEFAULT_GREEN_TOLERANCE ) );
String bluet = theProps.getProperty( "bluetolerance",
String.valueOf( ImageCompareRegionInfo.DEFAULT_BLUE_TOLERANCE ) );
imageSaveLocation = theProps.getProperty( "imageSaveLocation", null ).trim();
setMatchPercent( Float.parseFloat( matchPct ) );
setGreenTolerance( Integer.parseInt( greent ) );
setRedTolerance( Integer.parseInt( redt ) );
setBlueTolerance( Integer.parseInt( bluet ) );
}
ImageIO.setUseCache( false );
}
/**
* to get DriftX
*/
public int getDriftX()
{
return driftX;
}
/**
* to set DriftX
* @param driftX
*/
public void setDriftX( int driftX )
{
this.driftX = driftX;
}
/**
* to get DriftY
*/
public int getDriftY()
{
return driftY;
}
/**
* to set DriftY
* @param driftY
*/
public void setDriftY( int driftY )
{
this.driftY = driftY;
}
/**
* To get the image save location of failed comparisons.
* @return directoryPath
*/
public String getImageSaveLocation()
{
return imageSaveLocation;
}
/**
* To set the the directory location for saving the images of failed comparison.
* @param directoryPath String
*/
public void setImageSaveLocation( String directoryPath)
{
this.imageSaveLocation = directoryPath;
}
/**
* Checks if the image region is on the current screen. This function uses
* the match percent, RGB tolerances, and x & y tolerances from the
* imgXMLPath file.
*
* @param imgXMLPath
* The image xml file.
* @param regionName
* The region to wait for.
* @return true if the region is on screen. False otherwise.
*/
@Deprecated
public boolean isRegionOnScreenNow( String imgXMLPath, String regionName )
{
boolean retVal = false;
RegionInfo regionInfo = null;
if ( imgXMLPath != null && !imgXMLPath.isEmpty() && regionName != null && !regionName.isEmpty() )
{
try
{
regionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, regionName );
logger.debug( "RegionInfo " + regionInfo );
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
return false;
}
if ( null != regionInfo )
{
ImageCompareRegionInfo icRegionInfo = convertToImageCompareRegionInfo( regionInfo );
try
{
retVal = isRegionOnScreenNow( icRegionInfo );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
}
return retVal;
}
/**
* Checks if the image region is on the current screen. This function uses
* the match percent, RGB tolerances, and x & y tolerances from the
* regionInfo.
*
* @param icRegionInfo
* The region metadata as ImageCompareRegionInfo.
*
* @return true if the region is on screen. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean isRegionOnScreenNow( ImageCompareRegionInfo icRegionInfo ) throws ImageCompareException
{
boolean retVal = false;
if ( null != icRegionInfo )
{
logger.debug( "Image Filepath " + icRegionInfo.getFilepath() );
BufferedImage region = imageLoader.loadImage( icRegionInfo.getFilepath() );
if ( null == region )
{
logger.error( "Could not load image: " + imageLoader.getPath() );
throw new IllegalStateException( "Could not load image: " + imageLoader.getPath() );
}
retVal = isRegionOnScreenNow( icRegionInfo, region );
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Checks if all the images regions are on the current screen. This function
* uses the match percent, RGB tolerances, and x & y tolerances from the
* imgXMLPath file. Maintained for Backward Compatibility
*
* @param imgXMLPath
* The image xml file.
* @return true if all the images regions are on screen. False otherwise.
*/
@Deprecated
public boolean areAllRegionsOnScreenNow( String imgXMLPath )
{
boolean retVal = false;
List< RegionInfo > regionInfoList = null;
if ( imgXMLPath != null && !imgXMLPath.isEmpty() )
{
try
{
regionInfoList = regionLocatorProvider.getRegionInfo( imgXMLPath );
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
}
if ( null != regionInfoList )
{
List< ImageCompareRegionInfo > icRegionInfo = null;
icRegionInfo = convertToImageCompareRegionInfo( regionInfoList );
try
{
retVal = areAllRegionsOnScreenNow( icRegionInfo );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
}
return retVal;
}
/**
* Checks if all the images regions are on the current screen. This function
* uses the match percent, RGB tolerances, and x & y tolerances from the
* regionList.
*
* @param regionList
* List of region metadata's
* @return true if all the images regions are on screen. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean areAllRegionsOnScreenNow( List< ImageCompareRegionInfo > regionList ) throws ImageCompareException
{
boolean retVal = false;
if ( null != regionList )
{
try
{
retVal = doRegionCompare( regionList, null );
logger.info( "Result of comparision " + retVal );
}
catch ( IllegalStateException e )
{
logger.error( e.getMessage(), e );
retVal = false;
}
catch ( ImageCompareException e )
{
throw new ImageCompareException( e.getMessage() );
}
}
return retVal;
}
/**
* Checks if the full image matches the current screen. This function uses
* this class's match percent and RGB tolerances. Maintained for Backward
* Compatibility
*
* @param imgFile
* The image file.
* @return true if the image is on the current screen. False otherwise.
*/
@Deprecated
public boolean isSameImageOnScreenNow( String imgFile )
{
boolean retVal = false;
if ( null != imgFile && !imgFile.isEmpty() )
{
try
{
retVal = isImageOnScreenNow( imageLoader.loadImage( imgFile ) );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Checks if the full image matches the current screen. This function uses
* this class's match percent and RGB tolerances.
*
* @param refImage
* The image as BufferedImage.
* @return true if the image is on the current screen. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean isImageOnScreenNow( BufferedImage refImage ) throws ImageCompareException
{
boolean retVal = false;
if ( null != refImage )
{
BufferedImage currentScreen = getCurrentImage( refImage );
if ( null != currentScreen )
{
retVal = doFullScreenCompare( currentScreen, refImage );
logger.info( "Result of comparision " + retVal );
}
}
return retVal;
}
/**
* Waits for the full image to match the current screen within the given
* timeout. This function uses this class's match percent and RGB
* tolerances. Maintained for Backward Compatibility
*
* @param imgFile
* The image file.
* @param timeout
* The timeout in milliseconds.
* @return true if the image is on the screen within the timeout. False otherwise.
*/
@Deprecated
public boolean waitForSameImage( String imgFile, long timeout )
{
boolean retVal = false;
if ( null != imgFile && !imgFile.isEmpty() )
{
try
{
retVal = waitForImageOnScreen( imageLoader.loadImage( imgFile ), timeout );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Waits for the full image to match the current screen within the given
* timeout. This function uses this class's match percent and RGB
* tolerances.
*
* @param refImage
* The image as BufferedImage.
* @param timeout
* The timeout in milliseconds.
* @return true if the image is on the screen within the timeout. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean waitForImageOnScreen( BufferedImage refImage, long timeout ) throws ImageCompareException
{
boolean retVal = false;
if ( null != refImage )
{
retVal = runCallableTask( new CallableFullImageCompare( refImage ), timeout );
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Waits for the image to have all its regions on screen. This function uses
* the match percent, RGB tolerances, and x & y tolerances from the
* imgXMLPath file. Maintained for Backward Compatibility
*
* @param imgXMLPath
* The image xml file.
* @param timeout
* the timeout.
* @return true if all regions are on the screen within the timeout.
* False otherwise.
*/
@Deprecated
public boolean waitForAllRegions( String imgXMLPath, long timeout )
{
boolean retVal = false;
List< RegionInfo > regionInfoList = null;
if ( null != imgXMLPath && !imgXMLPath.isEmpty() )
{
try
{
regionInfoList = regionLocatorProvider.getRegionInfo( imgXMLPath );
logger.debug( "RegionInfoList " + regionInfoList );
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
return false;
}
List< ImageCompareRegionInfo > icRegionInfo = null;
if ( null != regionInfoList )
{
icRegionInfo = convertToImageCompareRegionInfo( regionInfoList );
}
try
{
retVal = waitForAllRegions( icRegionInfo, timeout );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Waits for the image to have all its regions on screen. This function uses
* the match percent, RGB tolerances, and x & y tolerances from the
* regionList.
*
* @param regionList
* List of region metadata's
* @param timeout
* the timeout.
* @return true if all regions are on the screen within the timeout.False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean waitForAllRegions( List< ImageCompareRegionInfo > regionList, long timeout )
throws ImageCompareException
{
boolean retVal = false;
if ( null != regionList )
{
CallableMultipleRegionCompare compare = null;
try
{
compare = new CallableMultipleRegionCompare( regionList );
}
catch ( Exception e )
{
logger.error( e.getMessage(), e );
return false;
}
retVal = runCallableTask( compare, timeout );
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Waits for the image to have the specified region on screen. This function
* uses the match percent, RGB tolerances, and x & y tolerances from the
* imgXMLPath file. Maintained for Backward Compatibility
*
* @param imgXMLPath
* The image xml file.
* @param regionName
* The name of the region to wait for.
* @param timeout
* the timeout.
* @return true if the region is on the screen within the timeout. False otherwise.
*/
@Deprecated
public boolean waitForRegion( String imgXMLPath, String regionName, long timeout )
{
boolean retVal = false;
if ( null != imgXMLPath && !imgXMLPath.isEmpty() && null != regionName && !regionName.isEmpty() )
{
RegionInfo regionInfo = null;
try
{
regionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, regionName );
logger.debug( "RegionInfo " + regionInfo );
if ( null != regionInfo )
{
ImageCompareRegionInfo icRegionInfo = convertToImageCompareRegionInfo( regionInfo );
retVal = waitForRegion( icRegionInfo, timeout );
logger.info( "Result of comparision " + retVal );
}
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
retVal = false;
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
}
return retVal;
}
/**
* Waits for the image to have the specified region on screen. This function
* uses the match percent, RGB tolerances, and x & y tolerances from the
* regionInfo.
*
* @param regionInfo
* The region metadata as ImageCompareRegionInfo
* @param timeout
* the timeout.
* @return true if the region is on the screen within the timeout. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean waitForRegion( ImageCompareRegionInfo regionInfo, long timeout ) throws ImageCompareException
{
boolean retVal = false;
if ( null != regionInfo )
{
regionInfo = ( ImageCompareRegionInfo ) addDrift( regionInfo );
logger.debug( "RegionInfo " + regionInfo );
retVal = runCallableTask( new CallableRegionCompare( regionInfo ), timeout );
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* To get the parent object.
* @return Object
*/
@Override
public Object getParent()
{
return null;
}
private ImageCompareRegionInfo convertToImageCompareRegionInfo( RegionInfo regionInfo )
{
ImageCompareRegionInfo info = null;
/**
* If region Info instance of ImageCompareRegionInfo, then there is not
* need to set default values of RGB tolerance and matchPercent
*/
if ( !( regionInfo instanceof ImageCompareRegionInfo ) )
{
logger.debug( "Converting to ImageCompareRegionInfo" );
info = new ImageCompareRegionInfo();
info.setBlueTolerance( ( int ) blueTolerance );
info.setRedTolerance( ( int ) redTolerance );
info.setGreenTolerance( ( int ) greenTolerance );
info.setMatchPct( matchPercent );
info.setFilepath( regionInfo.getFilepath() );
info.setHeight( regionInfo.getHeight() );
info.setWidth( regionInfo.getWidth() );
info.setName( regionInfo.getName() );
info.setX( regionInfo.getX() );
info.setY( regionInfo.getY() );
info.setXTolerance( regionInfo.getXTolerance() );
info.setYTolerance( regionInfo.getYTolerance() );
}
else
{
info = ( ImageCompareRegionInfo ) regionInfo;
}
return info;
}
private List< ImageCompareRegionInfo > convertToImageCompareRegionInfo( List< RegionInfo > regionInfoList )
{
logger.debug( "Converting to ImageCompareRegionInfo" );
List< ImageCompareRegionInfo > retVal = new ArrayList< ImageCompareRegionInfo >();
for ( RegionInfo regionInfo : regionInfoList )
{
retVal.add( convertToImageCompareRegionInfo( regionInfo ) );
}
return retVal;
}
/**
* Adds the drift values to RegionInfo object.
*
* @param info
* RegionInfo object for which the drift values needs to be
* updated.
* @return RegionInfo Updated RegionInfo object with image drift values.
*
*/
private RegionInfo addDrift( RegionInfo info )
{
logger.debug( "Adding Drift" );
int xValue = info.getX() + driftX;
int yValue = info.getY() + driftY;
info.setX( xValue );
info.setY( yValue );
return info;
}
private boolean doRegionCompare( List< ImageCompareRegionInfo > regionList, BufferedImage refImage )
throws ImageCompareException
{
logger.debug( "doRegionCompare()" );
if ( null == regionList )
{
logger.debug( "regionList == null" );
return false;
}
boolean returnCode = false;
// taking any region (in this case the first region) to get the filepath
// of the image to be loaded.
if ( refImage == null )
{ // try loading reference image from the filepath
// mentioned in xml.
ImageCompareRegionInfo firstRegion = regionList.get( 0 );
if ( null != firstRegion )
{
logger.info( "Image filepath : " + firstRegion.getFilepath() );
refImage = imageLoader.loadImage( firstRegion.getFilepath() );
if ( null == refImage )
{
logger.error( "Could not load image: " + imageLoader.getPath() );
throw new IllegalStateException( "Could not load image: " + imageLoader.getPath() );
}
}
}
try
{
BufferedImage currentScreen = getCurrentImage( refImage );
if ( null == currentScreen )
{
logger.debug( "currentScreen == null" );
return false;
}
if ( CoreRegionCompare.doRegionCompare( regionList, refImage, currentScreen ) )
{
returnCode = true;
}
else
{
saveImages( currentScreen, refImage );
}
}
catch ( Exception e )
{
throw new ImageCompareException( e.getMessage() );
}
return returnCode;
}
private void saveImages( BufferedImage actual, BufferedImage expected )
{
saveImages( actual, expected, null, null );
}
private void saveImages( BufferedImage actual, BufferedImage expected, RegionInfo targetInfo, ImageCompareRegionInfo refInfo )
{
logger.debug( "saveImages()" );
if ( null == imageSaveLocation || imageSaveLocation.isEmpty() )
{
logger.warn( "[IMAGECOMPARE] imagecompare directory not set" );
return;
}
File outputDir = new File( imageSaveLocation );
// Create a file name from the current date/time and the failure
// counter.
// Output format is YYYYmmdd-HHMMSS-0000.
int failureCount = getFailureCount();
String filename = String.format( imageSaveFormat, Calendar.getInstance(), Integer.valueOf( failureCount ) );
if ( null != refInfo )
{
filename += "-" + refInfo.getName();
}
logger.debug( "filename " + filename );
File actualFile = new File( outputDir, filename + actualFileExtn );
File expectedFile = new File( outputDir, filename + expectedFileExtn );
if ( outputDir.isDirectory() || outputDir.mkdirs() )
{
if ( saveImage( actualFile, actual, ( null != targetInfo )? targetInfo : refInfo ) )
{
logger.info( "[IMAGECOMPARE] Saved actual image: " + actualFile.getName() );
}
else
{
logger.error( "[IMAGECOMPARE] Failed to save actual image." );
}
if ( saveImage( expectedFile, expected, refInfo ) )
{
logger.info( "[IMAGECOMPARE] Saved expected image: " + expectedFile.getName() );
}
else
{
logger.error( "[IMAGECOMPARE] Failed to save expected image." );
}
}
else
{
logger.error( "[IMAGECOMPARE] Failed to create imagecompare directory." );
}
}
private boolean saveImage( File outputFile, BufferedImage image, RegionInfo info )
{
boolean savedFile = false;
try
{
if ( null != info )
{
// draw compare region on image
Graphics g = image.getGraphics();
g.setXORMode( Color.RED );
if ( g instanceof Graphics2D )
{
Stroke stroke = new BasicStroke( 1.5f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL );
( ( Graphics2D ) g ).setStroke( stroke );
}
g.drawRect( info.getX(), info.getY(), info.getWidth(), info.getHeight() );
}
// XXX: Saving the image as a (lossy) JPEG may make debugging more
// difficult
// as the lossiness may affect future comparison.
savedFile = ImageIO.write( image, "JPEG", outputFile );
}
catch ( IOException ioe )
{
logger.error( ioe.getMessage(), ioe );
}
return savedFile;
}
/**
* Increments the global counter and returns the new value. This method must
* be synchronized.
*
* @return The next number in the sequence.
*/
private synchronized int getFailureCount()
{
return ++counter;
}
/**
* Runs a full image compare between two specified image files.
*
* @param actual
* The actual image.
* @param expected
* The expected image.
* @return true if the images are equal.
* @throw IllegalStateException if the image cannot be loaded.
*/
private boolean doFullScreenCompare( BufferedImage actual, BufferedImage expected ) throws ImageCompareException
{
boolean returnCode = false;
if ( null != actual && null != expected )
{
try
{
returnCode = CoreImageCompare.compareImages( actual, expected, matchPercent, redTolerance,
greenTolerance, blueTolerance );
logger.info( "Result of comparision " + returnCode );
}
catch ( Exception e )
{
logger.error( e.getMessage() );
throw new ImageCompareException( e.getMessage() );
}
}
if ( false == returnCode )
{
saveImages( actual, expected );
}
return returnCode;
}
/**
* Sets the match percent required for a full image compare to pass. This
* value must be between 0 and 100.
*
* @param matchPercent
* The match percent.
*/
public final void setMatchPercent( float matchPercent )
{
if ( matchPercent < ImageCompareRegionInfo.MIN_MATCH_PERCENT
|| matchPercent > ImageCompareRegionInfo.MAX_MATCH_PERCENT )
{
throw new IllegalArgumentException( "matchPercent must be >= " + ImageCompareRegionInfo.MIN_MATCH_PERCENT
+ " and <= " + ImageCompareRegionInfo.MAX_MATCH_PERCENT );
}
this.matchPercent = matchPercent;
}
/**
* Gets the match percent required for a full image compare to pass.
*
* @return The match percent.
*/
public final float getMatchPercent()
{
return matchPercent;
}
/**
* Set the red tolerance used for full image comparison. Value must >=
* MIN_TOLERANCE and <= MAX_TOLERANCE.
*
* @param redTolerance
* the redTolerance to set.
*/
public final void setRedTolerance( int redTolerance )
{
checkTolerance( redTolerance );
this.redTolerance = redTolerance;
}
/**
* Get the red tolerance used for full image comparison.
*
* @return the redTolerance
*/
public final long getRedTolerance()
{
return redTolerance;
}
/**
* Set the green tolerance used for full image comparison. Value must >=
* MIN_TOLERANCE and <= MAX_TOLERANCE.
*
* @param greenTolerance
* the greenTolerance to set
*/
public final void setGreenTolerance( int greenTolerance )
{
checkTolerance( greenTolerance );
this.greenTolerance = greenTolerance;
}
/**
* Get the green tolerance used for image comparison.
*
* @return the greenTolerance
*/
public final long getGreenTolerance()
{
return greenTolerance;
}
/**
* Set the blue tolerance used for full image comparison. Value must >=
* MIN_TOLERANCE and <= MAX_TOLERANCE.
*
* @param blueTolerance
* the blueTolerance to set
*/
public final void setBlueTolerance( int blueTolerance )
{
checkTolerance( blueTolerance );
this.blueTolerance = blueTolerance;
}
/**
* Get the blue tolerance used for full image comparison.
*
* @return the blueTolerance
*/
public final long getBlueTolerance()
{
return blueTolerance;
}
private void checkTolerance( long value )
{
if ( value < ImageCompareRegionInfo.MIN_RGB_TOLERANCE || value > ImageCompareRegionInfo.MAX_RGB_TOLERANCE )
{
throw new IllegalArgumentException( "tolerance must be >= " + ImageCompareRegionInfo.MIN_RGB_TOLERANCE
+ " and <= " + ImageCompareRegionInfo.MAX_RGB_TOLERANCE );
}
}
private ImageCompareResult runCallableSearchRegionTask( CallableSearchForRegionOnTargetRegion compare, long timeout )
throws ImageCompareException
{
ImageCompareResult retVal = new ImageCompareResult();
if ( timeout > 0 )
{
ExecutorService service = Executors.newSingleThreadExecutor();
Future< ImageCompareResult > taskFuture = service.submit( compare );
try
{
retVal = taskFuture.get( timeout, TimeUnit.MILLISECONDS );
}
catch ( CancellationException e )
{
logger.error( "Task canceled for finding out region: " + compare.refRegionInfo.getName() +
" on search region: " + compare.targetRegionInfo.getName() );
throw new ImageCompareException( e.getMessage() );
}
catch ( ExecutionException ee )
{
logger.error( "ExecutionException while finding out region: " + compare.refRegionInfo.getName() +
" on search region: " + compare.targetRegionInfo.getName(), ee );
throw new ImageCompareException( ee.getMessage() );
}
catch ( InterruptedException ie )
{
logger.error( "InterruptedException while finding out region: " + compare.refRegionInfo.getName() +
" on search region: " + compare.targetRegionInfo.getName(), ie );
throw new ImageCompareException( ie.getMessage() );
}
catch ( TimeoutException te )
{
logger.error( "Task timed out for finding out region: " + compare.refRegionInfo.getName() +
" on search region: " + compare.targetRegionInfo.getName(), te );
taskFuture.cancel( true );
}
finally
{
// Just to be safe, make sure nothing else is running.
service.shutdownNow();
try
{
// wait for it to complete.
service.awaitTermination( timeout, TimeUnit.MILLISECONDS );
}
catch ( InterruptedException ie )
{
logger.error( "InterruptedException while finding out region: " + compare.refRegionInfo.getName() +
" on search region: " + compare.targetRegionInfo.getName(), ie );
}
}
}
return retVal;
}
private boolean runCallableTask( Callable< Boolean > compare, long timeout ) throws ImageCompareException
{
boolean retVal = false;
if ( timeout > 0 )
{
ExecutorService service = Executors.newSingleThreadExecutor();
Future< Boolean > taskFuture = service.submit( compare );
try
{
retVal = taskFuture.get( timeout, TimeUnit.MILLISECONDS );
}
catch ( CancellationException e )
{
logger.error( "Task canceled" );
throw new ImageCompareException( e.getMessage() );
}
catch ( ExecutionException ee )
{
logger.error( "ExecutionException: " + ee.getMessage(), ee );
throw new ImageCompareException( ee.getMessage() );
}
catch ( InterruptedException ie )
{
logger.error( "InterruptedException: " + ie.getMessage(), ie );
throw new ImageCompareException( ie.getMessage() );
}
catch ( TimeoutException te )
{
logger.error( "Task timed out" );
taskFuture.cancel( true );
}
finally
{
// Just to be safe, make sure nothing else is running.
service.shutdownNow();
try
{
// wait for it to complete.
service.awaitTermination( timeout, TimeUnit.MILLISECONDS );
}
catch ( InterruptedException ie )
{
logger.error( ie.getMessage(), ie );
}
}
}
return retVal;
}
/**
* A Callable class that performs the actual full screen image compare on a
* separate thread. This class helps to compare images against a timeout
* with live video streaming (simulated of course!!)
*
* @author sajayjk
*/
protected class CallableFullImageCompare implements Callable< Boolean >
{
private BufferedImage refImage;
CallableFullImageCompare( BufferedImage refImage )
{
logger.debug( "CallableFullImageCompare Constructor" );
this.refImage = refImage;
if ( null != refImage )
{
this.refImage = refImage;
}
else
{
throw new IllegalStateException( "Image load failed.." );
}
}
/**
* compare the images, or throws an exception if unable to do so.
*
* @return true if able to compare the images.
* @throws Exception if unable to compare the images.
*/
@Override
public Boolean call() throws Exception
{
Thread.currentThread().setName( "CallableFullImageCompare" );
boolean retVal = false;
BufferedImage currentScreen = null;
while ( !Thread.currentThread().isInterrupted() )
{
currentScreen = getCurrentImage( refImage );
logger.debug( "CallableFullImageCompare.call() currentScreen " + currentScreen );
if ( null != currentScreen
&& CoreImageCompare.compareImages( currentScreen, refImage, matchPercent, redTolerance,
greenTolerance, blueTolerance ) )
{
retVal = true;
break;
}
try
{
Thread.sleep( 100 );
}
catch ( InterruptedException ie )
{
logger.error( ie.getMessage() );
break;
}
}
if ( !retVal && currentScreen != null )
{
saveImages( currentScreen, getClone( refImage ) );
}
return retVal;
}
}
/**
* A Callable class that performs the actual region compares on a separate
* thread. This class helps to compare multiple regions against a timeout
* with live video streaming (simulated of course!!)
*
* @author sajayjk
*/
protected class CallableMultipleRegionCompare implements Callable< Boolean >
{
List< ImageCompareRegionInfo > regionList;
BufferedImage refImage = null;
CallableMultipleRegionCompare( List< ImageCompareRegionInfo > regionList )
{
logger.debug( "CallableFullImageCompare Constructor" );
if ( null == regionList || regionList.isEmpty() )
{
throw new IllegalArgumentException( "regionList is either null or empty" );
}
ImageCompareRegionInfo sampleRegion = ( ImageCompareRegionInfo ) regionList.get( 0 );
logger.debug( "Image filepath :" + sampleRegion.getFilepath() );
refImage = imageLoader.loadImage( sampleRegion.getFilepath() );
if ( null == refImage )
{
throw new IllegalStateException( "Could not load image: " + imageLoader.getPath() );
}
this.regionList = regionList;
}
CallableMultipleRegionCompare( List< ImageCompareRegionInfo > regionList, BufferedImage refImage )
{
logger.debug( "CallableFullImageCompare Constructor" );
if ( null == regionList || regionList.isEmpty() || null == refImage )
{
throw new IllegalArgumentException( "regionList is either null or empty" );
}
this.regionList = regionList;
this.refImage = refImage;
}
/**
* compare the region, or throws an exception if unable to do so.
*
* @return true if able to compare the region.
* @throws Exception if unable to compare the region.
*/
@Override
public Boolean call() throws Exception
{
boolean retVal = false;
BufferedImage currentScreen = null;
while ( !Thread.currentThread().isInterrupted() )
{
currentScreen = getCurrentImage( refImage );
logger.debug( "CallableMultipleRegionCompare.call() currentScreen " + currentScreen );
if ( null != currentScreen && CoreRegionCompare.doRegionCompare( regionList, refImage, currentScreen ) )
{
retVal = true;
break;
}
try
{
Thread.sleep( 100 );
}
catch ( InterruptedException ie )
{
logger.error( ie.getMessage() );
break;
}
}
if ( !retVal && currentScreen != null )
{
for ( ImageCompareRegionInfo regionInfo : regionList )
{
saveImages( currentScreen, refImage, null, regionInfo );
}
}
return retVal;
}
}
protected class CallableSearchForRegionOnTargetRegion implements Callable< ImageCompareResult >
{
private ImageCompareRegionInfo refRegionInfo;
private RegionInfo targetRegionInfo;
BufferedImage refImage;
public CallableSearchForRegionOnTargetRegion( ImageCompareRegionInfo refRegionInfo, RegionInfo targetRegionInfo )
{
this.refRegionInfo = refRegionInfo;
refImage = imageLoader.loadImage( refRegionInfo.getFilepath() );
if ( null == refImage )
{
throw new IllegalStateException( "Could not load image: " + imageLoader.getPath() );
}
this.targetRegionInfo = targetRegionInfo;
}
public CallableSearchForRegionOnTargetRegion( ImageCompareRegionInfo refRegionInfo,
RegionInfo targetRegionInfo, BufferedImage refImage )
{
if ( null == refImage )
{
throw new IllegalStateException( "No refImage Available" );
}
this.refRegionInfo = refRegionInfo;
this.targetRegionInfo = targetRegionInfo;
this.refImage = refImage;
}
/**
* compare the region on target, or throws an exception if unable to do so.
*
* @return true if able to compare the region on target.
* @throws Exception if unable to compare the region on target.
*/
@Override
public ImageCompareResult call() throws Exception
{
ImageCompareResult icResult = null;
BufferedImage currentScreen = null;
while ( !Thread.currentThread().isInterrupted() )
{
currentScreen = getCurrentImage( refImage );
logger.debug( "CallableSearchForRegionOnTargetRegion.call() currentScreen " + currentScreen );
if ( null != currentScreen )
{
icResult = CoreRegionCompare.findRegionOnTargetRegion( refRegionInfo, targetRegionInfo, refImage,
currentScreen );
if( icResult.getResult() )
{
break;
}
}
try
{
Thread.sleep( 100 );
}
catch ( InterruptedException ie )
{
logger.error( ie.getMessage() );
break;
}
}
if ( !icResult.getResult() && null != currentScreen )
{
saveImages( currentScreen, getClone( refImage), targetRegionInfo, refRegionInfo );
}
return icResult;
}
}
private BufferedImage getClone( BufferedImage image )
{
ColorModel colorModel = image.getColorModel();
WritableRaster raster = image.copyData( null );
return new BufferedImage( colorModel, raster, colorModel.isAlphaPremultiplied(), null );
}
/**
* A Callable class that performs the actual region compare on a separate
* thread. This class helps to compare a region against a timeout with live
* video streaming (simulated of course!!)
*
* @author sajayjk
*/
protected class CallableRegionCompare implements Callable< Boolean >
{
private ImageCompareRegionInfo info;
BufferedImage refImage;
CallableRegionCompare( ImageCompareRegionInfo info )
{
this.info = info;
refImage = imageLoader.loadImage( info.getFilepath() );
if ( null == refImage )
{
throw new IllegalStateException( "Could not load image: " + imageLoader.getPath() );
}
}
CallableRegionCompare( ImageCompareRegionInfo info, BufferedImage refImage )
{
if ( null == refImage )
{
throw new IllegalStateException( "No refImage Available" );
}
this.info = info;
this.refImage = refImage;
}
/**
* compare the region, or throws an exception if unable to do so.
*
* @return true if able to compare the region.
* @throws Exception if unable to compare the region.
*/
@Override
public Boolean call() throws Exception
{
boolean retVal = false;
BufferedImage currentScreen = null;
while ( !Thread.currentThread().isInterrupted() )
{
currentScreen = getCurrentImage( refImage );
logger.debug( "CallableMultipleRegionCompare.call() currentScreen " + currentScreen );
if ( null != currentScreen && CoreRegionCompare.doRegionCompare( info, refImage, currentScreen ) )
{
retVal = true;
break;
}
try
{
Thread.sleep( 100 );
}
catch ( InterruptedException ie )
{
logger.error( ie.getMessage() );
break;
}
}
if ( !retVal && null != currentScreen )
{
saveImages( currentScreen, getClone( refImage ), null, info );
}
return retVal;
}
}
/**
* Checks if all the images regions are on the current screen. This function
* uses the match percent, RGB tolerances, and x & y tolerances from the
* regionList.
*
* @param regionList
* List of region metadata's
* @param refImage
* The refImage to which the regions map to.
* @return true if all the images regions are on screen. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean areAllRegionsOnScreenNow( List< ImageCompareRegionInfo > regionList, BufferedImage refImage )
throws ImageCompareException
{
boolean retVal = false;
if ( null != regionList && null != refImage )
{
try
{
retVal = doRegionCompare( regionList, refImage );
logger.info( "Result of comparision " + retVal );
}
catch ( IllegalStateException e )
{
logger.error( e.getMessage(), e );
retVal = false;
}
}
return retVal;
}
/**
* Checks if the image region is on the current screen. This function uses
* the match percent, RGB tolerances, and x & y tolerances from the
* regionInfo.
*
* @param icRegionInfo
* The region metadata.
* @param refImage
* The refImage to which the regions map to.
* @return true if the region is on screen. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean isRegionOnScreenNow( ImageCompareRegionInfo icRegionInfo, BufferedImage refImage )
throws ImageCompareException
{
boolean retVal = false;
if ( null != icRegionInfo && null != refImage )
{
BufferedImage currentScreen = getCurrentImage( refImage );
logger.debug( "Current Screen " + currentScreen );
if ( null != currentScreen )
{
icRegionInfo = ( ImageCompareRegionInfo ) addDrift( icRegionInfo );
try
{
retVal = CoreRegionCompare.doRegionCompare( icRegionInfo, refImage, currentScreen );
logger.info( "Result of comparision " + retVal );
}
catch ( Exception e )
{
logger.debug( e.getMessage() );
throw new ImageCompareException( e.getMessage() );
}
}
}
return retVal;
}
/**
* Waits for the image to have all its regions on screen. This function uses
* the match percent, RGB tolerances, and x & y tolerances from the
* regionList.
*
* @param regionList
* List of region metadata's
* @param timeout
* the timeout.
* @param refImage as {@link BufferedImage BufferedImage}
* The refImage to which the regions map to.
* @return true if all regions are on the screen within the timeout.False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean waitForAllRegions( List< ImageCompareRegionInfo > regionList, BufferedImage refImage, long timeout )
throws ImageCompareException
{
boolean retVal = false;
if ( null != regionList )
{
CallableMultipleRegionCompare compare = null;
try
{
compare = new CallableMultipleRegionCompare( regionList, refImage );
}
catch ( Exception e )
{
logger.error( e.getMessage(), e );
return false;
}
retVal = runCallableTask( compare, timeout );
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Waits for the image to have the specified region on screen. This function
* uses the match percent, RGB tolerances, and x & y tolerances from the
* regionInfo.
*
* @param regionInfo
* The region metadata.
* @param timeout
* the timeout.
* @param refImage
* The refImage to which the regions map to.
* @return true if the region is on the screen within the timeout. False otherwise.
* @throws ImageCompareException
*/
@Override
public boolean waitForRegion( ImageCompareRegionInfo regionInfo, BufferedImage refImage, long timeout )
throws ImageCompareException
{
boolean retVal = false;
if ( null != regionInfo )
{
regionInfo = ( ImageCompareRegionInfo ) addDrift( regionInfo );
retVal = runCallableTask( new CallableRegionCompare( regionInfo, refImage ), timeout );
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Checks if the full image matches the current screen. This function uses
* this class's match percent and RGB tolerances.
*
* @param imgFile
* The image file path.
* @return true if the image is on the current screen, else false.
*/
@Override
public boolean waitForFullImage( String imgFile )
{
boolean retVal = false;
if ( null != imgFile && !imgFile.isEmpty() )
{
try
{
retVal = isImageOnScreenNow( imageLoader.loadImage( imgFile ) );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Waits for the full image to match the current screen within the given
* timeout. This function uses this class's match percent and RGB
* tolerances.
*
* @param imgFile
* The image file path.
* @param timeout
* The timeout in milliseconds.
* @return True if the image is on the screen within the timeout else false.
*/
@Override
public boolean waitForFullImage( String imgFile, long timeout )
{
boolean retVal = false;
if ( null != imgFile && !imgFile.isEmpty() )
{
try
{
retVal = waitForImageOnScreen( imageLoader.loadImage( imgFile ), timeout );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Checks if all the images regions are on the current screen. This function
* uses the match percent, RGB tolerances, and x & y tolerances from the
* imgXMLPath file.
*
* @param imgXMLPath
* The image xml file path.
* @return True if all the images regions are on current screen, else false.
*/
@Override
public boolean waitForImageRegion( String imgXMLPath )
{
boolean retVal = false;
List< RegionInfo > regionInfoList = null;
if ( imgXMLPath != null && !imgXMLPath.isEmpty() )
{
try
{
regionInfoList = regionLocatorProvider.getRegionInfo( imgXMLPath );
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
}
if ( null != regionInfoList )
{
List< ImageCompareRegionInfo > icRegionInfo = null;
icRegionInfo = convertToImageCompareRegionInfo( regionInfoList );
try
{
retVal = areAllRegionsOnScreenNow( icRegionInfo );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
}
return retVal;
}
/**
* Checks if the image region is on the current screen. This function uses
* the match percent, RGB tolerances, and x & y tolerances from the
* imgXMLPath file.
*
* @param imgXMLPath
* The Image xml file path.
* @param regionName
* The name of the region to be compared.
* @return True if the region is on the current screen, else false.
*/
@Override
public boolean waitForImageRegion( String imgXMLPath, String regionName )
{
boolean retVal = false;
RegionInfo regionInfo = null;
if ( imgXMLPath != null && !imgXMLPath.isEmpty() && regionName != null && !regionName.isEmpty() )
{
try
{
regionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, regionName );
logger.debug( "RegionInfo " + regionInfo );
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
return false;
}
if ( null != regionInfo )
{
ImageCompareRegionInfo icRegionInfo = convertToImageCompareRegionInfo( regionInfo );
try
{
retVal = isRegionOnScreenNow( icRegionInfo );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
}
return retVal;
}
/**
* Waits for all the regions to be on screen. This function uses the match
* percent, RGB tolerances, and x & y tolerances from the imgXMLPath file.
*
* @param imgXMLPath
* The image xml file path.
* @param timeout
* The timeout in milliseconds.
* @return True if all regions are on the screen within the timeout, else
* false.
*/
@Override
public boolean waitForImageRegion( String imgXMLPath, long timeout )
{
boolean retVal = false;
List< RegionInfo > regionInfoList = null;
if ( null != imgXMLPath && !imgXMLPath.isEmpty() )
{
try
{
regionInfoList = regionLocatorProvider.getRegionInfo( imgXMLPath );
logger.debug( "RegionInfoList " + regionInfoList );
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
return false;
}
List< ImageCompareRegionInfo > icRegionInfo = null;
if ( null != regionInfoList )
{
icRegionInfo = convertToImageCompareRegionInfo( regionInfoList );
}
try
{
retVal = waitForAllRegions( icRegionInfo, timeout );
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
logger.info( "Result of comparision " + retVal );
}
return retVal;
}
/**
* Waits for the specified region to be on screen for the specified timeout.
* This function uses the match percent, RGB tolerances, and x & y
* tolerances from the imgXMLPath file.
*
* @param imgXMLPath
* The Image xml file path.
* @param regionName
* The name of the region to be compared.
* @param timeout
* The timeout in milliseconds.
* @return True if the region is on the screen within the timeout, else
* false.
*/
@Override
public boolean waitForImageRegion( String imgXMLPath, String regionName, long timeout )
{
boolean retVal = false;
if ( null != imgXMLPath && !imgXMLPath.isEmpty() && null != regionName && !regionName.isEmpty() )
{
RegionInfo regionInfo = null;
try
{
regionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, regionName );
logger.debug( "RegionInfo " + regionInfo );
if ( null != regionInfo )
{
ImageCompareRegionInfo icRegionInfo = convertToImageCompareRegionInfo( regionInfo );
retVal = waitForRegion( icRegionInfo, timeout );
logger.info( "Result of comparision " + retVal );
}
}
catch ( IOException e )
{
logger.error( e.getMessage(), e );
retVal = false;
}
catch ( ImageCompareException e )
{
logger.debug( e.getMessage() );
}
}
return retVal;
}
/**
* to get current image
*
* @param referenceImage {@linkplain BufferedImage}
* @return BufferedImage
*/
protected BufferedImage getCurrentImage( BufferedImage referenceImage )
{
return videoProvider.getVideoImage( new Dimension( referenceImage.getWidth(), referenceImage.getHeight() ) );
}
@Override
public String saveImageRegion( String imageFilePath, String regionName, int width, int height, int x, int y,
int xtolerance, int yTolerance, int redTolerance, int greenTolerance, int blueTolerance,
float matchPercentage ) throws IOException, JAXBException
{
if ( null == imageFilePath || imageFilePath.isEmpty() || null == regionName || regionName.isEmpty() )
{
throw new IllegalArgumentException( "ImageFilePath and ReginName cannot be null or empty" );
}
String imgFilePath = validateImageFile( imageFilePath );
ImageCompareRegionInfo icRegionInfo = new ImageCompareRegionInfo();
icRegionInfo.setFilepath( new File( imgFilePath ).getName());
icRegionInfo.setBlueTolerance( blueTolerance );
icRegionInfo.setGreenTolerance( greenTolerance );
icRegionInfo.setHeight( height );
icRegionInfo.setMatchPct( matchPercentage );
icRegionInfo.setName( regionName );
icRegionInfo.setRedTolerance( redTolerance );
icRegionInfo.setWidth( width );
icRegionInfo.setX( x );
icRegionInfo.setXTolerance( xtolerance );
icRegionInfo.setY( y );
icRegionInfo.setYTolerance( yTolerance );
return saveImageRegion( icRegionInfo, imgFilePath );
}
/**
* Saves the specified region to the corresponding xml file of the image.
* Xml file will be created if it does not exist.
*
* @param imageFilePath
* Image file path
* @param regionName
* Region name
* @param width
* Width of the region
* @param height
* Height of the region
* @param x
* x position
* @param y
* y position
* @param matchPercentage
* match percentage
* @return xml file path
* @throws IOException
* @throws JAXBException
*/
@Override
public String saveImageRegion( String imageFilePath, String regionName, int width, int height, int x, int y,
float matchPercentage ) throws IOException, JAXBException
{
return saveImageRegion( imageFilePath, regionName, width, height, x, y, xTolerance, yTolerance, redTolerance,
greenTolerance, blueTolerance, matchPercentage );
}
/**
* Saves the specified region to the corresponding xml file of the image.
* Xml file will be created if it does not exist.
*
* @param imageFilePath
* Image file path
* @param regionName
* Region name
* @param width
* Width of the region
* @param height
* Height of the region
* @param x
* x position
* @param y
* y position
* @return xml file path
* @throws IOException
* @throws JAXBException
*/
@Override
public String saveImageRegion( String imageFilePath, String regionName, int width, int height, int x, int y )
throws IOException, JAXBException
{
return saveImageRegion( imageFilePath, regionName, width, height, x, y, xTolerance, yTolerance, redTolerance,
greenTolerance, blueTolerance, matchPercent );
}
private String getCleanMac()
{
String cleanMac = "";
if ( null != videoProvider.getParent() )
{
try
{
Settop settop = ( Settop ) videoProvider.getParent();
String hostMacAddress = settop.getHostMacAddress();
cleanMac = hostMacAddress.trim().replace( ":", "" ).toUpperCase();
}
catch ( ClassCastException e )
{
logger.error( "VideoProvider doesn't have a parent settop." );
}
}
return cleanMac;
}
private boolean isValidImageFile( File file )
{
boolean result = false;
if ( file.exists() && file.isFile() )
{
try
{
BufferedImage img = ImageIO.read( file );
if ( img != null )
{
result = true;
}
}
catch ( IOException e )
{
logger.error( e.getMessage() );
}
}
return result;
}
/**
* Finds out whether the reference region is anywhere in the target region
* of current screen.
*
* @param refRegionInfo
* The region to be searched {@link ImageCompareRegionInfo}
* @param targetRegionInfo
* The search area {@link RegionInfo}
* @return {@link ImageCompareResult} The comparison result
* @throws ImageCompareException
*/
@Override
public ImageCompareResult isRegionOnTargetRegion( ImageCompareRegionInfo refRegionInfo, RegionInfo targetRegionInfo )
throws ImageCompareException
{
ImageCompareResult icResult = null;
if ( null != refRegionInfo && null != targetRegionInfo )
{
logger.debug( "Image Filepath " + refRegionInfo.getFilepath() );
BufferedImage refImage = imageLoader.loadImage( refRegionInfo.getFilepath() );
if ( null == refImage )
{
throw new IllegalStateException( "Could not load image: " + imageLoader.getPath() );
}
icResult = isRegionOnTargetRegion( refRegionInfo, targetRegionInfo, refImage );
logger.info( "Result of comparision " + icResult.getResult() );
}
return icResult;
}
/**
* Finds out whether the reference region is anywhere in the target region
* of current screen.
*
* @param refRegionInfo
* The region to be searched {@link ImageCompareRegionInfo}
* @param targetRegionInfo
* The search area {@link RegionInfo}
* @param refImage
* The reference image {@link BufferedImage}
* @return {@link ImageCompareResult} The comparison result
* @throws ImageCompareException
*/
@Override
public ImageCompareResult isRegionOnTargetRegion( ImageCompareRegionInfo refRegionInfo,
RegionInfo targetRegionInfo, BufferedImage refImage ) throws ImageCompareException
{
ImageCompareResult icResult = null;
if ( null != refRegionInfo && null != targetRegionInfo && null != refImage )
{
BufferedImage currentScreen = getCurrentImage( refImage );
if ( null != currentScreen )
{
try
{
icResult = CoreRegionCompare.findRegionOnTargetRegion( refRegionInfo, targetRegionInfo, refImage,
currentScreen );
logger.info( "Result of comparision " + icResult.getResult() );
}
catch ( Exception e )
{
logger.error( "Error while finding out region: " + refRegionInfo.getName() +
" on search region: " + targetRegionInfo.getName(), e );
throw new ImageCompareException( e.getMessage() );
}
}
}
return icResult;
}
/**
* Waits for the reference region to appear anywhere in the target region
* until the time out happens.
*
* @param refRegionInfo
* The region to be searched {@link ImageCompareRegionInfo}
* @param targetRegionInfo
* The search area {@link RegionInfo}
* @param timeout
* The timeout in milliseconds.
* @return {@link ImageCompareResult} The comparison result
* @throws ImageCompareException
*/
@Override
public ImageCompareResult waitForRegionOnTargetRegion( ImageCompareRegionInfo refRegionInfo,
RegionInfo targetRegionInfo, long timeout ) throws ImageCompareException
{
ImageCompareResult icResult = null;
if ( null != refRegionInfo && null != targetRegionInfo )
{
icResult = runCallableSearchRegionTask( new CallableSearchForRegionOnTargetRegion( refRegionInfo,
targetRegionInfo ), timeout );
logger.info( "Result of comparision " + icResult.getResult() );
}
return icResult;
}
/**
* Waits for the reference region to appear anywhere in the target region
* until the time out happens.
*
* @param refRegionInfo
* The region to be searched {@link ImageCompareRegionInfo}
* @param targetRegionInfo
* The search area {@link RegionInfo}
* @param refImage
* The reference image {@link BufferedImage}
* @param timeout
* The timeout in milliseconds.
* @return {@link ImageCompareResult} The comparison result
* @throws ImageCompareException
*/
@Override
public ImageCompareResult waitForRegionOnTargetRegion( ImageCompareRegionInfo refRegionInfo,
RegionInfo targetRegionInfo, BufferedImage refImage, long timeout ) throws ImageCompareException
{
ImageCompareResult icResult = null;
if ( null != refRegionInfo && null != targetRegionInfo && null != refImage )
{
icResult = runCallableSearchRegionTask( new CallableSearchForRegionOnTargetRegion( refRegionInfo,
targetRegionInfo, refImage ), timeout );
logger.info( "Result of comparision " + icResult );
}
return icResult;
}
@Override
public ImageCompareResult waitForRegionOnTargetRegion( String imgXMLPath, String refRegionName,
String targetRegionName )
{
RegionInfo refRegionInfo = null;
RegionInfo targetRegionInfo = null;
ImageCompareResult icResult = null;
if ( imgXMLPath != null && !imgXMLPath.isEmpty() && refRegionName != null && !refRegionName.isEmpty()
&& targetRegionName != null && !targetRegionName.isEmpty() )
{
try
{
refRegionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, refRegionName );
logger.debug( "Reference RegionInfo " + refRegionInfo );
targetRegionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, targetRegionName );
logger.debug( "Target RegionInfo " + targetRegionInfo );
if ( null != refRegionInfo && null != targetRegionInfo )
{
ImageCompareRegionInfo icRegionInfo = convertToImageCompareRegionInfo( refRegionInfo );
icResult = isRegionOnTargetRegion( icRegionInfo, targetRegionInfo );
logger.info( "Result of comparision " + icResult.getResult() );
}
}
catch ( IOException e )
{
logger.error( "IOException while finding out region: " + refRegionName +
" on search region: " + targetRegionName, e );
}
catch ( ImageCompareException e )
{
logger.error( "ImageCompareException while finding out region: " + refRegionName +
" on search region: " + targetRegionName, e );
}
}
return icResult;
}
@Override
public ImageCompareResult waitForRegionOnTargetRegion( String imgXMLPath, String refRegionName,
String targetRegionName, long timeout )
{
RegionInfo refRegionInfo = null;
RegionInfo targetRegionInfo = null;
ImageCompareResult icResult = null;
if ( imgXMLPath != null && !imgXMLPath.isEmpty() && refRegionName != null && !refRegionName.isEmpty()
&& targetRegionName != null && !targetRegionName.isEmpty() )
{
try
{
refRegionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, refRegionName );
logger.debug( "Reference RegionInfo " + refRegionInfo );
targetRegionInfo = regionLocatorProvider.getRegionInfo( imgXMLPath, targetRegionName );
logger.debug( "Target RegionInfo " + targetRegionInfo );
if ( null != refRegionInfo && null != targetRegionInfo )
{
ImageCompareRegionInfo icRegionInfo = convertToImageCompareRegionInfo( refRegionInfo );
icResult = waitForRegionOnTargetRegion( icRegionInfo, targetRegionInfo, timeout );
logger.info( "Result of comparision " + icResult.getResult() );
}
}
catch ( IOException e )
{
logger.error( "IOException while finding out region: " + refRegionName +
" on search region: " + targetRegionName + " with timeout: " + timeout, e );
}
catch ( ImageCompareException e )
{
logger.error( "ImageCompareException while finding out region: " + refRegionName +
" on search region: " + targetRegionName + " with timeout: " + timeout, e );
}
}
return icResult;
}
@Override
public String saveSearchRegion( String imageFilePath, String regionName, int width, int height, int x, int y )
throws IOException, JAXBException
{
if ( null == imageFilePath || imageFilePath.isEmpty() || null == regionName || regionName.isEmpty() )
{
throw new IllegalArgumentException( "ImageFilePath and ReginName cannot be null or empty" );
}
String imgPath = validateImageFile( imageFilePath );
RegionInfo regionInfo = new RegionInfo();
regionInfo.setName( regionName );
regionInfo.setX( x );
regionInfo.setY( y );
regionInfo.setWidth( width );
regionInfo.setHeight( height );
return saveImageRegion( regionInfo, imgPath );
}
private String validateImageFile(String imageFilePath)
{
String imgPath = imageFilePath;
File imgFile = new File( imgPath );
if ( !imgFile.isAbsolute() )
{
String catsHome = System.getProperty( "cats.home" );
if ( null != catsHome )
{
imgPath = catsHome + System.getProperty( "file.separator" ) + getCleanMac()
+ System.getProperty( "file.separator" ) + imageFilePath;
}
imgFile = new File( imgPath );
}
if ( !isValidImageFile( imgFile ) )
{
throw new IllegalArgumentException( "Invalid image file! " + imageFilePath );
}
return imgPath;
}
}