1 | package org.greenstone.applet.GsdlCollageApplet;
|
---|
2 |
|
---|
3 | import java.awt.*;
|
---|
4 | import java.awt.geom.*;
|
---|
5 | import java.io.*;
|
---|
6 | import java.net.*;
|
---|
7 | import java.awt.image.*;
|
---|
8 |
|
---|
9 |
|
---|
10 | /**
|
---|
11 | * @author Katrina Edgar
|
---|
12 | * @author David Bainbridge
|
---|
13 | *
|
---|
14 | * Data Structure to store an image once it is displayed in the applet.
|
---|
15 | * This structure remembers the images graphical representation, source url,
|
---|
16 | * name, width, height, position on the screen and whether or not it has
|
---|
17 | * previously been displayed. It provides methods to set the dimensions and
|
---|
18 | * position of the image. It also provides access methods to the translated
|
---|
19 | * x and y co-ordinates of the image */
|
---|
20 | public class CollageImage {
|
---|
21 |
|
---|
22 | /** Decrements the alpha value between adjacent rows on the edge of an image<br>
|
---|
23 | * Used to fade the edges faster than the rest of the image <br>
|
---|
24 | * Only used when java2 parameter is true and advanced image processing
|
---|
25 | * techniques may be applied */
|
---|
26 | static final int FADEVALUE = 3;
|
---|
27 |
|
---|
28 | /** Defines the shades of white used to colour the backgrounds of white images <br>
|
---|
29 | * These colours are used because when several white images appear in the collage
|
---|
30 | * simultaneously it is difficult to distinguish their edges<br>
|
---|
31 | * Only used when java2 parameter is true and advanced image processing
|
---|
32 | * techniques may be applied */
|
---|
33 | int colorMask [] = {0xF8FFFF, 0xFFF8FF, 0xFFFFF8, 0xF8F8FF, 0xFFF8F8, 0xF8FFF8, 0xFAFAFA};
|
---|
34 |
|
---|
35 |
|
---|
36 | /** Defines the number of shades of white used to colour the backgrounds of white images <br>
|
---|
37 | * These colours are used because when several white images appear in the collage
|
---|
38 | * simultaneously it is difficult to distinguish their edges<br>
|
---|
39 | * Only used when java2 parameter is true and advanced image processing
|
---|
40 | * techniques may be applied */
|
---|
41 | static final int NO_OF_COLORS = 7;
|
---|
42 |
|
---|
43 | /** Will fire a removal operation when 4 images in the collage overlap a single area */
|
---|
44 | static final int NO_IMAGES_OF_OVERLAP = 4;
|
---|
45 |
|
---|
46 |
|
---|
47 | /** Source url of the image */
|
---|
48 | String from_url_ = null;
|
---|
49 | /** Name of the image */
|
---|
50 | String name_ = null;
|
---|
51 | /** Width of the image */
|
---|
52 | int image_x_dim_ = 0;
|
---|
53 | /** Height of the image */
|
---|
54 | int image_y_dim_ = 0;
|
---|
55 | /** Indicates whether or not the image has been drawn on the applet previously */
|
---|
56 | boolean fresh = true;
|
---|
57 |
|
---|
58 | URL url_ = null;
|
---|
59 |
|
---|
60 | /** Reflects the translation of the image from the origin to its position on the screen */
|
---|
61 | AffineTransform af_ = null;
|
---|
62 | /** Left x co-ordinate */
|
---|
63 | protected int xl_ = 0;
|
---|
64 | /** Top y co-ordinate */
|
---|
65 | protected int yt_ = 0;
|
---|
66 | /** Right x co-ordinate */
|
---|
67 | protected int xr_ = 0;
|
---|
68 | /** Bottom y co-ordinate */
|
---|
69 | protected int yb_ = 0;
|
---|
70 |
|
---|
71 | /** Refers to applet */
|
---|
72 | GsdlCollageApplet app_ = null;
|
---|
73 |
|
---|
74 | boolean isJava2_;
|
---|
75 |
|
---|
76 | Image image_;
|
---|
77 |
|
---|
78 | /** Constructs an CollageImage from the three specified parameters
|
---|
79 | *
|
---|
80 | * @param image The graphical representation of the image
|
---|
81 | * @param from_url The source url for the image
|
---|
82 | * @param name The file name of the image */
|
---|
83 | public CollageImage(GsdlCollageApplet app, boolean isJava2, DownloadImages.ImageUrlTriple iutriple)
|
---|
84 | {
|
---|
85 |
|
---|
86 | image_ = iutriple.image();
|
---|
87 | from_url_ = iutriple.urlString();
|
---|
88 | name_ = iutriple.name();
|
---|
89 | url_ = iutriple.url();
|
---|
90 |
|
---|
91 | isJava2_=isJava2;
|
---|
92 | app_=app;
|
---|
93 |
|
---|
94 | process();
|
---|
95 | }
|
---|
96 |
|
---|
97 | /** Sets the translation for the image from the origin
|
---|
98 | * And regenerate the image as the scaled version
|
---|
99 | *
|
---|
100 | * @param af The AffineTransform translation that has been calculated */
|
---|
101 | public void setAffineTransform(MyAffineTransform af, boolean is_java2_)
|
---|
102 | {
|
---|
103 | image_x_dim_ = (int) (image_x_dim_ * af.scaleX);
|
---|
104 | image_y_dim_ = (int) (image_y_dim_ * af.scaleY);
|
---|
105 |
|
---|
106 | image_ = image_.getScaledInstance(image_x_dim_, image_y_dim_, Image.SCALE_DEFAULT);
|
---|
107 |
|
---|
108 | if (is_java2_) {
|
---|
109 | af_ = new AffineTransform();
|
---|
110 | af_.translate(af.translateX, af.translateY);
|
---|
111 | af_.scale(1.0, 1.0);
|
---|
112 |
|
---|
113 | calculate_rect();
|
---|
114 | }
|
---|
115 | else {
|
---|
116 | double trans_x = af.translateX;
|
---|
117 | double trans_y = af.translateY;
|
---|
118 |
|
---|
119 | xl_ = Math.round((float)trans_x);
|
---|
120 | xr_ = Math.round((float)(trans_x + image_x_dim_)) -1;
|
---|
121 | yt_ = Math.round((float)trans_y);
|
---|
122 | yb_ = Math.round((float)(trans_y + image_y_dim_)) -1;
|
---|
123 | }
|
---|
124 |
|
---|
125 | }
|
---|
126 |
|
---|
127 | public void expand () {
|
---|
128 |
|
---|
129 | // still expands a little too fast... how to fix this?
|
---|
130 | magnify(new Rectangle((int) (xl_ + (image_x_dim_ * 0.000001)),
|
---|
131 | (int) (yt_ + (image_y_dim_ * 0.000001)),
|
---|
132 | (int) (image_x_dim_ * 0.999998),
|
---|
133 | (int) (image_y_dim_ * 0.999998)));
|
---|
134 |
|
---|
135 | }
|
---|
136 |
|
---|
137 | public void magnify(Rectangle border) {
|
---|
138 |
|
---|
139 | double magX = image_x_dim_/(double)border.width;
|
---|
140 | double magY = image_y_dim_/(double)border.height;
|
---|
141 | int x = border.x + border.width/2;
|
---|
142 | int y = border.y + border.height/2;
|
---|
143 | paintImage(x,y,magX, magY);
|
---|
144 | }
|
---|
145 |
|
---|
146 | public void paintImage(int magCenterX, int magCenterY, double magX, double magY){
|
---|
147 |
|
---|
148 | try {
|
---|
149 | //Point2D mgp = null;
|
---|
150 | //mgp = af_.inverseTransform((new Point(magCenterX, magCenterY)),(Point)mgp);
|
---|
151 | //double x = (mgp.getX()*magX)-mgp.getX();
|
---|
152 | //double y = (mgp.getY()*magY)-mgp.getY();
|
---|
153 | //scale(-x,-y, magX, magY);
|
---|
154 | }catch (Exception e) {System.out.println(e); }
|
---|
155 | }
|
---|
156 |
|
---|
157 | public void scale(double magOffsetX, double magOffsetY, double magX, double magY){
|
---|
158 |
|
---|
159 | af_.translate(magOffsetX,magOffsetY);
|
---|
160 | af_.scale(magX, magY);
|
---|
161 | }
|
---|
162 |
|
---|
163 | /** Calculates the new image co-ordinates after translation has occurred */
|
---|
164 | protected void calculate_rect()
|
---|
165 | {
|
---|
166 | double trans_x = af_.getTranslateX();
|
---|
167 | double trans_y = af_.getTranslateY();
|
---|
168 |
|
---|
169 | xl_ = Math.round((float)trans_x);
|
---|
170 | xr_ = Math.round((float)(trans_x + image_x_dim_)) -1;
|
---|
171 | yt_ = Math.round((float)trans_y);
|
---|
172 | yb_ = Math.round((float)(trans_y + image_y_dim_)) -1;
|
---|
173 | }
|
---|
174 |
|
---|
175 | /** Determines whether a given co-ordinate is inside this image
|
---|
176 | *
|
---|
177 | * @param x The x co-ordinate
|
---|
178 | * @param y The y co-ordinate */
|
---|
179 | public boolean inside(int x, int y)
|
---|
180 | {
|
---|
181 | return ((x>=xl_) && (x<=xr_)) && ((y>=yt_) && (y<=yb_));
|
---|
182 | }
|
---|
183 |
|
---|
184 | /** Gets the width of the translated image */
|
---|
185 | public double getX() {
|
---|
186 | return af_.getTranslateX();
|
---|
187 | }
|
---|
188 |
|
---|
189 | /** Gets the height of the translated image */
|
---|
190 | public double getY() {
|
---|
191 | return af_.getTranslateY();
|
---|
192 | }
|
---|
193 |
|
---|
194 |
|
---|
195 | /** Fades and colours the image on a pixel-by-pixel basis <br>
|
---|
196 | * First it grabs the pixels of the entire image and stores them in a 2D array.
|
---|
197 | * Then a bound is calculated to indicate the point from which edge
|
---|
198 | * fading should occur. The position of the bound will change in proportion
|
---|
199 | * to the age of the image so that older images have a larger faded edge.
|
---|
200 | * The area within the bounds, that forms the center of the image, is processed
|
---|
201 | * first and faded by a standard alpha value. Then each edge of the image
|
---|
202 | * is processed separately, creating a gradient fade from the true edge to the
|
---|
203 | * position of the bound. <br>
|
---|
204 | * The faded pixel array is then turned into a new image a returned.
|
---|
205 | *
|
---|
206 | * @param img the image that requires processing
|
---|
207 | * @param x the x co-ordinate of the image
|
---|
208 | * @param y the y co-ordinate of the image
|
---|
209 | * @param w the width of the image
|
---|
210 | * @param h the height of the image
|
---|
211 | * @param p the position of the image in the applet (indicates age)
|
---|
212 | * @param fresh whether the image is being processed for the first time */
|
---|
213 | public int[] handlepixels(int p) {
|
---|
214 |
|
---|
215 | int h = image_y_dim_;
|
---|
216 | int w = image_x_dim_;
|
---|
217 |
|
---|
218 | // declare an array to hold the pixels
|
---|
219 | int[] pixels = new int[w * h];
|
---|
220 | // get the pixels of the image into the pixels array
|
---|
221 | PixelGrabber pg = new PixelGrabber(image_, 0, 0, w, h, pixels, 0, w);
|
---|
222 |
|
---|
223 | try {
|
---|
224 | pg.grabPixels();
|
---|
225 | } catch (InterruptedException e) {
|
---|
226 | System.err.println("interrupted waiting for pixels!");
|
---|
227 | }
|
---|
228 |
|
---|
229 |
|
---|
230 | // check for any failures
|
---|
231 | if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
|
---|
232 | System.err.println("image fetch aborted or errored");
|
---|
233 |
|
---|
234 | }
|
---|
235 |
|
---|
236 |
|
---|
237 | // calculates the bound from which fading should begin
|
---|
238 | double bound = p * 0.01;
|
---|
239 | if (w > h)
|
---|
240 | bound *= w;
|
---|
241 | else
|
---|
242 | bound *= h;
|
---|
243 |
|
---|
244 | int upperboundheight = h - (int) bound;
|
---|
245 | int upperboundwidth = w - (int) bound;
|
---|
246 | int lowerbound = (int) bound;
|
---|
247 |
|
---|
248 | // loop through every pixel in the picture and handle it
|
---|
249 | for (int j = lowerbound; j < upperboundheight; j++) {
|
---|
250 | for (int i = lowerbound; i < upperboundwidth; i++) {
|
---|
251 | // width and height: x+i y+j
|
---|
252 | pixels[j * w + i] = handlesinglepixel(pixels[j * w + i], p, fresh, false, 255);
|
---|
253 | }
|
---|
254 | }
|
---|
255 |
|
---|
256 | int fade = 0;
|
---|
257 | int fader = 0;
|
---|
258 | int corealpha = (pixels[(h/2) * w + (w/2)] >> 24) & 0xff;
|
---|
259 |
|
---|
260 | // top edge
|
---|
261 | for (int n = lowerbound; n >= 0; n--) {
|
---|
262 | fader = corealpha - fade;
|
---|
263 | if (fader < 0)
|
---|
264 | fader = 0;
|
---|
265 |
|
---|
266 | for (int m = 0; m < w; m++){
|
---|
267 | int index = n* w + m;
|
---|
268 | if (index <pixels.length){
|
---|
269 | pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
|
---|
270 |
|
---|
271 | }
|
---|
272 | }
|
---|
273 |
|
---|
274 | fade+= FADEVALUE;
|
---|
275 | }
|
---|
276 |
|
---|
277 | // bottom edge
|
---|
278 | fade = 0;
|
---|
279 | for (int n = upperboundheight; n < h; n++) {
|
---|
280 | fader = corealpha - fade;
|
---|
281 | if (fader < 0)
|
---|
282 | fader = 0;
|
---|
283 | for (int m = 0; m < w; m++){
|
---|
284 | int index = n * w + m;
|
---|
285 | if (index <pixels.length){
|
---|
286 | pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
|
---|
287 |
|
---|
288 | }
|
---|
289 | }
|
---|
290 |
|
---|
291 | fade += FADEVALUE;
|
---|
292 | }
|
---|
293 |
|
---|
294 | // left edge
|
---|
295 | fade = 0;
|
---|
296 | for (int n = lowerbound; n >= 0; n--) {
|
---|
297 | fader = corealpha - fade;
|
---|
298 | if (fader < 0)
|
---|
299 | fader = 0;
|
---|
300 | for (int m = 0; m < h; m++) {
|
---|
301 | if ( m < lowerbound && n > m);
|
---|
302 | else if ( m > upperboundheight && n > (h - m));
|
---|
303 | else {
|
---|
304 | int index = m * w + n;
|
---|
305 | if (index <pixels.length){
|
---|
306 | pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
|
---|
307 |
|
---|
308 | }
|
---|
309 | }
|
---|
310 |
|
---|
311 | }
|
---|
312 | fade += FADEVALUE;
|
---|
313 | }
|
---|
314 |
|
---|
315 | // right edge
|
---|
316 | fade = 0;
|
---|
317 | for (int n = upperboundwidth; n < w; n++) {
|
---|
318 | fader = corealpha - fade;
|
---|
319 | if (fader < 0)
|
---|
320 | fader = 0;
|
---|
321 | for (int m = 0; m < h; m++) {
|
---|
322 | if ( m < lowerbound && (w - n) > m);
|
---|
323 | else if ( m > upperboundheight && (w - n) > (h - m));
|
---|
324 | else {
|
---|
325 | int index = m * w + n;
|
---|
326 | if (index <pixels.length){
|
---|
327 | pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
|
---|
328 |
|
---|
329 | }
|
---|
330 | }
|
---|
331 |
|
---|
332 | }
|
---|
333 |
|
---|
334 | fade += FADEVALUE;
|
---|
335 | }
|
---|
336 |
|
---|
337 | // set the pixels of the whole picture to the pixels array
|
---|
338 | pg.setPixels(0, 0, w, h, pg.getColorModel(), pixels, 0, w);
|
---|
339 |
|
---|
340 | image_ = app_.createImage(new MemoryImageSource(w, h, pixels, 0, w));
|
---|
341 |
|
---|
342 | return pixels;
|
---|
343 | }
|
---|
344 |
|
---|
345 |
|
---|
346 | /** Adjusts the colour and alpha value of an individual pixel <br>
|
---|
347 | * If the image is being drawn for the first time, the RGB values are
|
---|
348 | * extracted. If the pixel is close to white (RGB > 250) then an offwhite
|
---|
349 | * colour is applied to this pixel.
|
---|
350 | * This is done because when several white images appear in the collage
|
---|
351 | * simultaneously it is difficult to distinguish their edges. <br>
|
---|
352 | * This function also fades the alpha value of the pixel as the image ages.
|
---|
353 | * The alpha value is more heavily decremented as pixels get closer to the
|
---|
354 | * edge of the image
|
---|
355 | *
|
---|
356 | * @param pixel the pixel to manipulate
|
---|
357 | * @param p the position of the image in collage (representative of age)
|
---|
358 | * @param fresh indicates whether or not the image is being drawn for the first time
|
---|
359 | * @param edge indicates whether or not this pixel is near the edge of the image
|
---|
360 | * @param fade the amount by which to fade this pixel
|
---|
361 | * @return the adjusted pixel as an int */
|
---|
362 | public int handlesinglepixel(int pixel, int p, boolean fresh, boolean edge, int fade) {
|
---|
363 | int newpixel = 0;
|
---|
364 |
|
---|
365 | //changes the colour of the picture, only when first drawn
|
---|
366 | //and only if the pixel is close to white
|
---|
367 | if (fresh) {
|
---|
368 |
|
---|
369 | int red = (pixel >> 16) & 0xff;
|
---|
370 | int green = (pixel >> 8) & 0xff;
|
---|
371 | int blue = (pixel ) & 0xff;
|
---|
372 |
|
---|
373 | if (red >= 250 && green >= 250 && blue >= 250) {
|
---|
374 |
|
---|
375 | int c = colorMask[((int) (p%NO_OF_COLORS))];
|
---|
376 |
|
---|
377 | red = (c >> 16) & 0xff;
|
---|
378 | green = (c >> 8) & 0xff;
|
---|
379 | blue = (c) & 0xff;
|
---|
380 | }
|
---|
381 |
|
---|
382 | newpixel |= (red << 16) & 0x00ff0000;
|
---|
383 | newpixel |= (green << 8) & 0x0000ff00;
|
---|
384 | newpixel |= blue & 0x000000ff;
|
---|
385 | }
|
---|
386 |
|
---|
387 | else {
|
---|
388 | newpixel |= pixel & 0x00ffffff;
|
---|
389 | }
|
---|
390 |
|
---|
391 | int alpha = (pixel >> 24) & 0xff;
|
---|
392 |
|
---|
393 | if (edge) {
|
---|
394 | // fade the edges more...
|
---|
395 | alpha = fade;
|
---|
396 | }
|
---|
397 | else if (alpha > 10 && !fresh) {
|
---|
398 | alpha -= 10;
|
---|
399 | }
|
---|
400 |
|
---|
401 | newpixel |= (alpha << 24) & 0xff000000;
|
---|
402 |
|
---|
403 | return (newpixel);
|
---|
404 | }
|
---|
405 |
|
---|
406 |
|
---|
407 |
|
---|
408 |
|
---|
409 | /** Resets the alpha channel of an image so that it appears solid
|
---|
410 | *
|
---|
411 | * @param img the image to restore
|
---|
412 | * @param x the x co-ordinate of the image
|
---|
413 | * @param y the y co-ordinate of the image
|
---|
414 | * @param w the width of the image
|
---|
415 | * @param h the height of the image */
|
---|
416 | public void restoreAlpha() {
|
---|
417 | int h = image_y_dim_;
|
---|
418 | int w = image_x_dim_;
|
---|
419 |
|
---|
420 | // declare an array to hold the pixels
|
---|
421 | int[] pixels = new int[w * h];
|
---|
422 | // get the pixels of the image into the pixels array
|
---|
423 | PixelGrabber pg = new PixelGrabber(image_, 0, 0, w, h, pixels, 0, w);
|
---|
424 | try {
|
---|
425 | pg.grabPixels();
|
---|
426 |
|
---|
427 |
|
---|
428 | } catch (InterruptedException e) {
|
---|
429 | System.err.println("interrupted waiting for pixels!");
|
---|
430 | }
|
---|
431 |
|
---|
432 |
|
---|
433 | // check for any failures
|
---|
434 | if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
|
---|
435 | System.err.println("image fetch aborted or errored");
|
---|
436 | }
|
---|
437 |
|
---|
438 |
|
---|
439 |
|
---|
440 | // loop through every pixel in the picture and handle it
|
---|
441 | for (int j = 0; j < h; j++) {
|
---|
442 | for (int i = 0; i < w; i++) {
|
---|
443 | pixels[j * w + i] |= (255 << 24) & 0xff000000;
|
---|
444 | }
|
---|
445 | }
|
---|
446 |
|
---|
447 | // set the pixels of the whole picture to the pixels array
|
---|
448 | pg.setPixels(0, 0, w, h, pg.getColorModel(), pixels, 0, w);
|
---|
449 |
|
---|
450 | image_ = app_.createImage(new MemoryImageSource(w, h, pixels, 0, w));
|
---|
451 |
|
---|
452 | }
|
---|
453 |
|
---|
454 | /** Checks whether an image has faded to the point where it must be removed
|
---|
455 | * from the collage.
|
---|
456 | *
|
---|
457 | * @param img the image to restore
|
---|
458 | * @param x the x co-ordinate of the image
|
---|
459 | * @param y the y co-ordinate of the image
|
---|
460 | * @param w the width of the image
|
---|
461 | * @param h the height of the image */
|
---|
462 | public boolean checkFaded (int[] pixels) {
|
---|
463 | int h = image_y_dim_;
|
---|
464 | int w = image_x_dim_;
|
---|
465 |
|
---|
466 | // get the alpha value of the middle pixel of the image
|
---|
467 | int corealpha = (pixels[(h/2) * w + (w/2)] >> 24) & 0xff;
|
---|
468 |
|
---|
469 | if (corealpha < 50)
|
---|
470 | return true;
|
---|
471 |
|
---|
472 | return false;
|
---|
473 |
|
---|
474 | }
|
---|
475 |
|
---|
476 | public boolean isValid(){
|
---|
477 | image_x_dim_ = image_.getWidth(app_);
|
---|
478 | image_y_dim_ = image_.getHeight(app_);
|
---|
479 |
|
---|
480 | return (image_x_dim_) >0 && ( image_x_dim_ >0);
|
---|
481 |
|
---|
482 | }
|
---|
483 |
|
---|
484 |
|
---|
485 | public void process(){
|
---|
486 |
|
---|
487 | // images x and y dimensions
|
---|
488 | image_x_dim_ = image_.getWidth(app_);
|
---|
489 | image_y_dim_ = image_.getHeight(app_);
|
---|
490 | if (( image_x_dim_ >0) && ( image_x_dim_ >0))
|
---|
491 | {
|
---|
492 | // places and sizes the image
|
---|
493 | MyAffineTransform af = new MyAffineTransform(image_x_dim_,image_y_dim_);
|
---|
494 |
|
---|
495 | // sets location & size of collage image
|
---|
496 | setAffineTransform(af, isJava2_);
|
---|
497 | fresh = false;
|
---|
498 | if (isJava2_ ) {
|
---|
499 | restoreAlpha();
|
---|
500 | }
|
---|
501 |
|
---|
502 | }
|
---|
503 |
|
---|
504 |
|
---|
505 | }
|
---|
506 |
|
---|
507 | }
|
---|
508 |
|
---|