1 package com.hammurapi.jcapture; 2 3 import java.awt.Color; 4 import java.awt.Point; 5 import java.awt.Rectangle; 6 import java.awt.image.BufferedImage; 7 import java.io.IOException; 8 import java.nio.channels.FileChannel; 9 10 public class Region extends Rectangle { 11 12 private static final int TRANSPARENT_COLOR = new Color(0,0,0,0).getRGB(); 13 14 private BufferedImage master; 15 private BufferedImage prev; 16 private int grabRange; 17 private MappedImage image; 18 private boolean transparency; 19 Region(BufferedImage master, String format, FileChannel channel, BufferedImage prev, boolean transparency, int x, int y, int grabRange)20 public Region(BufferedImage master, String format, FileChannel channel, BufferedImage prev, boolean transparency, int x, int y, int grabRange) { 21 this.master = master; 22 this.format = format; 23 this.channel = channel; 24 this.prev = prev; 25 this.transparency = transparency; 26 this.grabRange = grabRange; 27 28 setBounds(x-grabRange, y-grabRange, grabRange*2+1, grabRange*2+1); 29 } 30 31 /** 32 * Special case when region covers the whole image. 33 * @param master 34 * @param x 35 * @param y 36 * @param grabRange 37 * @throws IOException 38 */ Region(MappedImage master)39 public Region(MappedImage master) throws IOException { 40 this.image = master; 41 this.grabRange = 0; 42 imageLocation = new Point(0,0); 43 coversEverything = true; 44 45 setBounds(0,0,master.getWidth(),master.getHeight()); 46 BufferedImage img = master.getImage(); 47 for (int sx=0, sw=master.getWidth(); sx<sw; ++sx) { 48 for (int sy=0, sh=master.getHeight(); sy<sh; ++sy) { 49 imageHash^=img.getRGB(sx, sy); 50 Long.rotateRight(imageHash, 1); 51 } 52 } 53 } 54 55 private Point imageLocation; 56 57 private String format; 58 59 private FileChannel channel; 60 grabImage()61 public void grabImage() throws IOException { 62 if (image==null) { 63 imageLocation = new Point(Math.max(0, x), Math.max(0, y)); 64 65 int imageWidth = width; 66 int widthDelta = imageWidth+imageLocation.x - master.getWidth(); 67 if (widthDelta>0) { 68 imageWidth-=widthDelta; 69 } 70 71 int imageHeight = height; 72 int heightDelta = imageHeight+imageLocation.y - master.getHeight(); 73 if (heightDelta>0) { 74 imageHeight-=heightDelta; 75 } 76 77 BufferedImage bImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); 78 for (int x=0; x<imageWidth; ++x) { 79 for (int y=0; y<imageHeight; ++y) { 80 int xt = x + imageLocation.x; 81 int yt = y + imageLocation.y; 82 int newPixel = master.getRGB(xt, yt); 83 int oldPixel = prev.getRGB(xt, yt); 84 int pixelRGB = newPixel==oldPixel && transparency ? TRANSPARENT_COLOR : newPixel; 85 bImage.setRGB(x, y, pixelRGB); 86 imageHash^=pixelRGB; 87 Long.rotateRight(imageHash, 1); 88 } 89 } 90 91 image = new MappedImage(bImage, format, channel); 92 93 //For debugging 94 // Graphics2D ssg = image.createGraphics(); 95 // ssg.setColor(java.awt.Color.GRAY); 96 // ssg.drawRect(0, 0, image.getWidth()-1, image.getHeight()-1); 97 98 // Make eligible for garbage collection. 99 master = null; 100 prev = null; 101 } 102 } 103 getImage()104 public MappedImage getImage() { 105 return image; 106 } 107 getImageLocation()108 public Point getImageLocation() { 109 return imageLocation; 110 } 111 112 /** 113 * Add point, extend region if added (if it is within grab range). Returns true if point was added 114 */ merge(int x, int y)115 boolean merge(int x, int y) { 116 if (image!=null) { 117 throw new IllegalStateException("Image already grabbed"); 118 } 119 if (contains(x, y)) { 120 int newMinX = Math.min(x-grabRange, this.x); 121 int newMinY = Math.min(y-grabRange, this.y); 122 int newMaxX = Math.max(x+grabRange, this.x+this.width); 123 int newMaxY = Math.max(y+grabRange, this.y+this.height); 124 setBounds(newMinX, newMinY, newMaxX-newMinX, newMaxY-newMinY); 125 return true; 126 } 127 return false; 128 } 129 130 /** 131 * 132 * @param region 133 * @return True if region is within grab range and was merged. 134 */ merge(Region region)135 boolean merge(Region region) { 136 if (region==this) { 137 throw new IllegalArgumentException("Self-merge"); 138 } 139 if (image!=null) { 140 throw new IllegalStateException("Image already grabbed"); 141 } 142 if (intersects(region)) { 143 int newMinX = Math.min(region.x, this.x); 144 int newMinY = Math.min(region.y, this.y); 145 int newMaxX = Math.max(region.x+region.width, this.x+this.width); 146 int newMaxY = Math.max(region.y+region.height, this.y+this.height); 147 setBounds(newMinX, newMinY, newMaxX-newMinX, newMaxY-newMinY); 148 return true; 149 } 150 return false; 151 } 152 153 private Region masterImageRegion; 154 155 /** 156 * @return Region with the identical image. 157 */ getMasterImageRegion()158 public Region getMasterImageRegion() { 159 return masterImageRegion; 160 } 161 162 /** 163 * Sets master region with identical image. 164 * @param masterImageRegion 165 */ setMasterImageRegion(Region masterImageRegion)166 public void setMasterImageRegion(Region masterImageRegion) { 167 this.masterImageRegion = masterImageRegion; 168 while (this.masterImageRegion.getMasterImageRegion()!=null) { 169 this.masterImageRegion = this.masterImageRegion.getMasterImageRegion(); 170 } 171 } 172 imageEquals(Region other)173 public boolean imageEquals(Region other) throws IOException { 174 if (image==null) { 175 throw new IllegalStateException("Image not grabbed"); 176 } 177 178 MappedImage otherImage = other.getImage(); 179 if (otherImage==null 180 || imageHash!=other.imageHash 181 || image.getHeight()!=otherImage.getHeight() 182 || image.getWidth()!=otherImage.getWidth()) { 183 return false; 184 } 185 186 BufferedImage bImage = image.getImage(); 187 BufferedImage oImage = otherImage.getImage(); 188 for (int sx=0, sw=bImage.getWidth(); sx<sw; ++sx) { 189 for (int sy=0, sh=bImage.getHeight(); sy<sh; ++sy) { 190 if (bImage.getRGB(sx, sy)!=oImage.getRGB(sx, sy)) { 191 return false; 192 } 193 } 194 } 195 return true; 196 } 197 198 private long imageHash; 199 200 /** 201 * Analyzes if this region image is a duplicate of the argument region image and if so sets it as its master. 202 * @param sr 203 * @return true if duplicate. 204 * @throws IOException 205 */ dedup(Region sr)206 public boolean dedup(Region sr) throws IOException { 207 if (imageEquals(sr)) { 208 setMasterImageRegion(sr); 209 image=null; 210 return true; 211 } 212 return false; 213 } 214 coversEverything()215 public boolean coversEverything() { 216 return coversEverything; 217 } 218 219 private boolean coversEverything; 220 221 } 222