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