package net.hearthstats.game.imageanalysis;
import net.hearthstats.util.Coordinate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* <p>A base class for pixel analysers that caches the coordinates of pixels for the current screen size to avoid
* recalculating pixel coordinates for every iteration.</p>
* <p>All classes that extend CoordinateCacheBase share the same cache for efficiency, even if there are multiple
* instances of those subclasses.</p>
*
* @author gtch
*/
public class CoordinateCacheBase {
private final static int MAXIMUM_CACHE_ITEMS = 1000;
private final static Logger debugLog = LoggerFactory.getLogger(CoordinateCacheBase.class);
private final static Map<UniquePixelIdentifier, Coordinate> coordinateCache = new HashMap<>();
protected Coordinate getCachedCoordinate(UniquePixelIdentifier upi) {
Coordinate coordinate = coordinateCache.get(upi);
if (coordinate == null) {
coordinate = calculatePixelCoordinate(upi);
if (coordinateCache.size() > MAXIMUM_CACHE_ITEMS) {
coordinateCache.clear();
}
coordinateCache.put(upi, coordinate);
}
return coordinate;
}
protected Coordinate calculatePixelCoordinate(UniquePixelIdentifier upi) {
if (debugLog.isDebugEnabled()) {
debugLog.debug("Calculating pixel position {},{} for width {} height {}",
new Object[] { upi.x, upi.y, upi.width, upi.height });
}
Coordinate result;
if (upi.width == PixelLocation.REFERENCE_SIZE.x && upi.height == PixelLocation.REFERENCE_SIZE.y) {
// The screen size is exactly what our reference pixels are based on, so we can use their coordinates directly
result = new Coordinate(upi.x, upi.y);
debugLog.debug("Stored position as {}", result);
} else {
// The screen size is different to our reference pixels, so coordinates need to be adjusted
float ratio = (float) upi.height / (float) PixelLocation.REFERENCE_SIZE.y;
float screenRatio = (float) upi.width / (float) upi.height;
int xOffset;
if (screenRatio > 1.4) {
xOffset = (int) (((float) upi.width - (ratio * PixelLocation.REFERENCE_SIZE.x)) / 2);
} else {
xOffset = 0;
}
int x = (int) (upi.x * ratio) + xOffset;
int y = (int) (upi.y * ratio);
result = new Coordinate(x, y);
debugLog.debug("Calculated position as {}", result);
}
return result;
}
public static class UniquePixelIdentifier {
final int x;
final int y;
final int width;
final int height;
UniquePixelIdentifier(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UniquePixelIdentifier that = (UniquePixelIdentifier) o;
if (height != that.height) return false;
if (width != that.width) return false;
if (x != that.x) return false;
if (y != that.y) return false;
return true;
}
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
result = 31 * result + width;
result = 31 * result + height;
return result;
}
}
}