Subversion Repositories general

Rev

Rev 1254 | Rev 1272 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
936 dev 1
package ak.photoalbum.images;
2
 
3
import java.util.Map;
4
import java.util.HashMap;
5
import java.util.Comparator;
6
import java.util.Arrays;
7
import java.io.File;
8
import java.io.FileFilter;
9
import java.io.IOException;
10
import java.io.FileInputStream;
11
import java.io.FileOutputStream;
12
import java.io.OutputStream;
1257 dev 13
import java.io.BufferedReader;
14
import java.io.InputStreamReader;
936 dev 15
import java.awt.Image;
16
import java.awt.Toolkit;
17
import java.awt.Graphics;
18
import java.awt.image.BufferedImage;
19
import java.awt.image.ImageObserver;
20
import java.awt.image.PixelGrabber;
21
import javax.imageio.ImageIO;
22
import org.apache.log4j.Logger;
23
import marcoschmidt.image.ImageInfo;
24
import ak.photoalbum.util.FileUtils;
25
import ak.photoalbum.util.FileNameComparator;
26
 
27
public class Thumbnailer
28
{
29
  protected static final String DEFAULT_FORMAT        = "jpg";
30
  protected static final String SMALL_SUFFIX          = ".small";
31
  protected static final String MEDIUM_SUFFIX         = ".medium";
32
  protected static final String DIR_SUFFIX            = ".dir";
1074 dev 33
  protected static final String DIR_PART_SUFFIX       = ".dir_part";
936 dev 34
  protected static final int    DEFAULT_SMALL_WIDTH   = 120;
35
  protected static final int    DEFAULT_SMALL_HEIGHT  = 120;
36
  protected static final int    DEFAULT_MEDIUM_WIDTH  = 800;
37
  protected static final int    DEFAULT_MEDIUM_HEIGHT = 800;
38
 
39
  protected Logger       logger;
40
  protected ImageResizer resizer;
41
  protected int          smallWidth            = DEFAULT_SMALL_WIDTH;
42
  protected int          smallHeight           = DEFAULT_SMALL_HEIGHT;
43
  protected int          mediumWidth           = DEFAULT_MEDIUM_WIDTH;
44
  protected int          mediumHeight          = DEFAULT_MEDIUM_HEIGHT;
45
  protected File         cacheDir;
46
  protected String       format                = DEFAULT_FORMAT;
47
  protected Map          smallCache            = new HashMap();
48
  protected Map          mediumCache           = new HashMap();
49
  protected Map          dirCache              = new HashMap();
50
  protected File         imagesRoot;
51
  protected FileFilter   imagesFilter          = null;
52
  protected Comparator   fileNameComparator    = new FileNameComparator(true);
53
  protected Comparator   fileNameComparatorRev = new FileNameComparator(false);
1074 dev 54
  protected boolean      nativeMode            = true; // FIXME config
936 dev 55
 
56
  protected File                dirTemplate;
57
  protected ThumbnailPosition[] dirThumbnailPositions;
58
  protected int[]               dirTemplateSize;
59
  protected long                dirTemplateTimestamp;
60
 
61
  public Thumbnailer()
62
  {
63
    this.logger = Logger.getLogger(this.getClass());
64
  }
65
 
66
  public String getMime()
67
  {
68
    return FileUtils.getMime(format);
69
  }
70
 
71
  public int[] getDirSize(File origin)
72
    throws IOException
73
  {
74
    if(dirTemplateSize == null
75
      || dirTemplateTimestamp != dirTemplate.lastModified())
76
    {
77
      dirTemplateSize      = getOriginSize(dirTemplate);
78
      dirTemplateTimestamp = dirTemplate.lastModified();
79
    }
80
 
81
    return dirTemplateSize;
82
  }
83
 
84
  public int[] getSmallSize(File origin)
85
    throws IOException
86
  {
87
    CachedFile cached = getCached(smallCache, origin);
88
 
89
    if(cached == null) {
90
      int[] originSize = getOriginSize(origin);
91
 
92
      return calcSizes(originSize[0], originSize[1], smallWidth, smallHeight);
93
    }
94
    else {
95
      int[] originSize = new int[2];
96
 
97
      originSize[0] = cached.getWidth();
98
      originSize[1] = cached.getHeight();
99
 
100
      return originSize;
101
    }
102
  }
103
 
104
  public int[] getMediumSize(File origin)
105
    throws IOException
106
  {
107
    CachedFile cached = getCached(mediumCache, origin);
108
 
109
    if(cached == null) {
110
      int[] originSize = getOriginSize(origin);
111
 
112
      return calcSizes(originSize[0], originSize[1], mediumWidth, mediumHeight);
113
    }
114
    else {
115
      int[] originSize = new int[2];
116
 
117
      originSize[0] = cached.getWidth();
118
      originSize[1] = cached.getHeight();
119
 
120
      return originSize;
121
    }
122
  }
123
 
124
  protected int[] getOriginSize(File origin)
125
    throws IOException
126
  {
127
    if(logger.isDebugEnabled())
128
      logger.debug("get size of " + origin.getCanonicalPath());
129
 
130
    ImageInfo       ii  = new ImageInfo();
131
    FileInputStream in  = null;
132
    int[]           res = new int[2];
133
 
134
    try {
135
      in = new FileInputStream(origin);
136
      ii.setInput(in);
137
 
138
      if(!ii.check()) {
139
        logger.warn("not supported format of " + origin.getCanonicalPath());
140
        res[0] = 0;
141
        res[1] = 0;
142
      }
143
      else{
144
        res[0] = ii.getWidth();
145
        res[1] = ii.getHeight();
146
      }
147
    }
148
    finally {
149
      if(in != null) in.close();
150
    }
151
 
152
    return res;
153
  }
154
 
155
  public void rebuildCache()
156
    throws IOException
157
  {
158
    logger.info("rebuild cache");
159
 
160
    deleteCache();
161
    buildCache();
162
  }
163
 
164
  public void deleteCache()
165
    throws IOException
166
  {
167
    logger.info("delete cache");
168
 
169
    deleteCache(cacheDir);
170
  }
171
 
172
  public void buildCache()
173
    throws IOException
174
  {
175
    logger.info("build cache");
176
 
177
    buildCache(imagesRoot);
178
  }
179
 
1257 dev 180
  public void reloadCache()
181
    throws IOException
182
  {
183
    logger.info("reload cache");
184
 
185
    smallCache.clear();
186
    mediumCache.clear();
187
    dirCache.clear();
188
    loadCaches(cacheDir);
189
  }
190
 
936 dev 191
  protected void deleteCache(File dir)
192
    throws IOException
193
  {
194
    File[] children = dir.listFiles();
195
 
196
    if(children == null) return; // the dir does not exists
197
 
198
    Arrays.sort(children, fileNameComparator);
199
 
200
    for(int i = 0; i < children.length; i++) {
201
      if(children[i].isDirectory())
202
        deleteCache(children[i]);
203
      else
204
        children[i].delete();
205
    }
206
    dir.delete();
207
  }
208
 
209
  protected void buildCache(File dir)
210
    throws IOException
211
  {
212
    File[] children;
213
 
214
    if(imagesFilter == null)
215
      children = dir.listFiles();
216
    else
217
      children = dir.listFiles(imagesFilter);
218
 
219
    if(children == null) return; // the dir does not exists
220
 
221
    Arrays.sort(children, fileNameComparator);
222
 
223
    for(int i = 0; i < children.length; i++) {
224
      if(children[i].isDirectory()) {
225
        writeDir(children[i], null);
226
        buildCache(children[i]);
227
      }
228
      else {
229
        writeSmall(children[i], null);
230
        writeMedium(children[i], null);
231
      }
232
    }
233
  }
234
 
235
  protected CachedFile getCached(Map cache, File imageFile)
236
    throws IOException
237
  {
238
    CachedFile cached = (CachedFile)cache.get(imageFile.getCanonicalPath());
239
 
240
    if(cached == null || !cached.getFile().exists()) {
241
      logger.debug("not found in cache");
242
      return null;
243
    }
244
 
245
    if(cached.getOriginTimestamp() != imageFile.lastModified()) {
246
      cached.getFile().delete();
247
      cache.remove(imageFile.getCanonicalPath());
248
      logger.debug("timestamps dont match");
249
      return null;
250
    }
251
 
252
    return cached;
253
  }
254
 
255
  protected boolean writeCached(Map cache, File imageFile, OutputStream out)
256
    throws IOException
257
  {
258
    CachedFile cached = getCached(cache, imageFile);
259
 
260
    if(cached == null) return false;
261
 
262
    if(logger.isDebugEnabled())
263
      logger.debug("write cached " + imageFile.getCanonicalPath());
264
 
265
    if(out != null) {
266
      FileInputStream in = null;
267
 
268
      try {
269
        in  = new FileInputStream(cached.getFile());
270
        FileUtils.copyStreams(in, out);
271
      }
272
      finally {
273
        if(in != null) in.close();
274
      }
275
    }
276
 
277
    return true;
278
  }
279
 
1074 dev 280
  protected File getCacheFile(File dir, File imageFile, String suffix)
281
  {
282
    return new File(dir, imageFile.getName() + suffix + "." + format);
283
  }
284
 
936 dev 285
  protected void cacheThumbnail(Map cache, File imageFile,
286
      BufferedImage thumbnail, String suffix, int width, int height)
287
    throws IOException
288
  {
289
    logger.debug("cache thumbnail " + suffix + " "
290
      + imageFile.getCanonicalPath());
291
 
292
    File dir       = getCacheFileDir(imageFile);
1074 dev 293
    File cacheFile = getCacheFile(dir, imageFile, suffix);
936 dev 294
 
295
    dir.mkdirs();
296
    ImageIO.write(thumbnail, format, cacheFile);
297
 
298
    cache.put(imageFile.getCanonicalPath(),
299
      new CachedFile(cacheFile, imageFile,
300
      cacheFile.lastModified(), imageFile.lastModified(), width, height));
301
  }
302
 
303
  public void startup()
304
    throws IOException
305
  {
306
    logger.info("startup");
307
    loadCaches(cacheDir);
308
    logger.info("started");
309
  }
310
 
1074 dev 311
	protected void loadFileCache(File file)
936 dev 312
    throws IOException
1074 dev 313
	{
314
		/* FIXME use this optimization?
936 dev 315
    String dirEnd    = DIR_SUFFIX    + "." + format;
316
    String smallEnd  = SMALL_SUFFIX  + "." + format;
317
    String mediumEnd = MEDIUM_SUFFIX + "." + format;
1074 dev 318
    */
936 dev 319
 
1251 dev 320
    File origin = null;
321
    Map  cache  = null;
936 dev 322
 
1074 dev 323
    if(file.getName().endsWith(SMALL_SUFFIX + "." + format)) {
324
      origin = getOriginFile(file, SMALL_SUFFIX);
325
      cache  = smallCache;
936 dev 326
 
1074 dev 327
      if(logger.isDebugEnabled())
328
        logger.debug("load cached small " + file.getCanonicalPath()
329
          + " for " + origin.getCanonicalPath());
330
    }
331
    else if(file.getName().endsWith(MEDIUM_SUFFIX + "." + format)) {
332
      origin = getOriginFile(file, MEDIUM_SUFFIX);
333
      cache  = mediumCache;
936 dev 334
 
1074 dev 335
      if(logger.isDebugEnabled())
336
        logger.debug("load cached medium " + file.getCanonicalPath()
337
          + " for " + origin.getCanonicalPath());
338
    }
339
    else if(file.getName().endsWith(DIR_SUFFIX + "." + format)) {
340
      origin = getOriginFile(file, DIR_SUFFIX);
341
      cache  = dirCache;
936 dev 342
 
1074 dev 343
      if(logger.isDebugEnabled())
344
        logger.debug("load cached dir " + file.getCanonicalPath()
345
          + " for " + origin.getCanonicalPath());
346
    }
1251 dev 347
    else if(file.getName().endsWith(DIR_PART_SUFFIX + "." + format)) {
348
      if(logger.isDebugEnabled())
349
        logger.debug("skip cached dir part " + file.getCanonicalPath());
350
    }
1074 dev 351
    else {
352
      if(logger.isInfoEnabled())
353
        logger.warn(
354
          "unknown type of cached " + file.getCanonicalPath());
355
    }
936 dev 356
 
1257 dev 357
    if(origin != null && cache != null) {
1251 dev 358
      loadCacheInfo(file, origin, cache);
1257 dev 359
    }
360
    else {
361
      file.delete();
362
 
363
      if(logger.isDebugEnabled())
364
        logger.debug("deleted (unknown origin)");
365
    }
1074 dev 366
	}
936 dev 367
 
1074 dev 368
	protected void loadCacheInfo(File file, File origin, Map cache)
369
    throws IOException
370
	{
371
    long  originTimestamp = origin.lastModified();
372
    long  cachedTimestamp = file.lastModified();
373
    int[] sizes           = getOriginSize(file);
936 dev 374
 
1074 dev 375
    if(origin.exists() && cachedTimestamp >= originTimestamp) {
376
      cache.put(origin.getCanonicalPath(),
377
        new CachedFile(file, origin, cachedTimestamp,
378
          originTimestamp, sizes[0], sizes[1]));
936 dev 379
 
1074 dev 380
      logger.debug("added");
381
    }
382
    else {
383
      file.delete();
936 dev 384
 
1074 dev 385
      if(logger.isDebugEnabled())
386
        logger.debug("deleted: " + origin.exists()
387
          + " " + cachedTimestamp + " " + originTimestamp);
388
    }
389
	}
936 dev 390
 
1074 dev 391
  protected void loadCaches(File dir)
392
    throws IOException
393
  {
394
    if(logger.isDebugEnabled())
395
      logger.debug("load caches in " + dir.getCanonicalPath());
396
 
397
    File[] children = dir.listFiles();
398
 
399
    if(children == null) return; // the dir does not exists
400
 
401
    for(int i = 0; i < children.length; i++) {
402
      if(children[i].isDirectory())
403
        loadCaches(children[i]);
404
      else
405
      	loadFileCache(children[i]);
936 dev 406
    }
407
  }
408
 
409
  protected File getOriginFile(File cached, String suffix)
410
    throws IOException
411
  {
412
    String fileEnd    = suffix + "." + format;
413
    String fileName   = cached.getName();
414
    String cachedPath = cached.getParentFile().getCanonicalPath();
415
    String cacheRoot  = cacheDir.getCanonicalPath();
416
 
417
    fileName = fileName.substring(0, fileName.length() - fileEnd.length());
418
 
419
    if(!cacheRoot.equals(cachedPath))
420
      if(!cacheRoot.endsWith(File.separator)) cacheRoot += File.separator;
421
 
422
    return new File(imagesRoot, cachedPath.substring(cacheRoot.length())
423
      + File.separator + fileName);
424
  }
425
 
426
  protected File getCacheFileDir(File imageFile)
427
    throws IOException
428
  {
429
    String imagePath = imageFile.getParentFile().getCanonicalPath();
430
    String rootPath  = imagesRoot.getCanonicalPath();
431
 
432
    if(imagePath.equals(rootPath)) return cacheDir;
433
    if(!rootPath.endsWith(File.separator)) rootPath += File.separator;
434
 
435
    if(!imagePath.startsWith(rootPath))
436
      throw new RuntimeException("Image " + imageFile.getCanonicalPath()
437
        + " is not under images root " + imagesRoot.getCanonicalPath());
438
 
439
    return new File(cacheDir, imagePath.substring(rootPath.length()));
440
  }
441
 
442
  public void writeSmall(File imageFile, OutputStream out)
443
    throws IOException
444
  {
445
    if(logger.isInfoEnabled())
446
      logger.info("write small " + imageFile.getCanonicalPath());
447
 
448
    if(writeCached(smallCache, imageFile, out)) return;
449
 
1074 dev 450
		if(nativeMode) {
451
			File dir       = getCacheFileDir(imageFile);
452
			File cacheFile = getCacheFile(dir, imageFile, SMALL_SUFFIX);
936 dev 453
 
1074 dev 454
    	createThumbnailNative(dir, cacheFile, imageFile, smallWidth, smallHeight);
455
			loadCacheInfo(cacheFile, imageFile, smallCache);
456
      writeCached(smallCache, imageFile, out);
457
		}
458
		else {
459
    	BufferedImage small = createThumbnail(imageFile, smallWidth, smallHeight);
936 dev 460
 
1074 dev 461
	    if(small != null) {
462
	    	// a thumbnail returned - save it into the cache dir
463
	      int sizes[] = calcSizes(
464
	        small.getWidth(null), small.getHeight(null), smallWidth, smallHeight);
465
	      cacheThumbnail(
466
	        smallCache, imageFile, small, SMALL_SUFFIX, sizes[0], sizes[1]);
467
 
468
	      if(out != null) ImageIO.write(small, format, out);
469
	    }
470
		}
936 dev 471
  }
472
 
473
  public void writeMedium(File imageFile, OutputStream out)
474
    throws IOException
475
  {
476
    if(logger.isInfoEnabled())
477
      logger.info("write medium " + imageFile.getCanonicalPath());
478
 
479
    if(writeCached(mediumCache, imageFile, out)) return;
480
 
1074 dev 481
		if(nativeMode) {
482
			File dir       = getCacheFileDir(imageFile);
483
			File cacheFile = getCacheFile(dir, imageFile, MEDIUM_SUFFIX);
936 dev 484
 
1074 dev 485
    	createThumbnailNative(dir, cacheFile, imageFile, mediumWidth, mediumHeight);
486
			loadCacheInfo(cacheFile, imageFile, mediumCache);
487
      writeCached(mediumCache, imageFile, out);
488
		}
489
		else {
490
    	BufferedImage medium = createThumbnail(imageFile, mediumWidth, mediumHeight);
936 dev 491
 
1074 dev 492
	    if(medium != null) {
493
	    	// a image returned - save it into the cache dir
494
	      int sizes[] = calcSizes(medium.getWidth(null), medium.getHeight(null),
495
	        mediumWidth, mediumHeight);
496
	      cacheThumbnail(
497
	        mediumCache, imageFile, medium, MEDIUM_SUFFIX, sizes[0], sizes[1]);
498
 
499
	      if(out != null) ImageIO.write(medium, format, out);
500
	    }
501
	  }
936 dev 502
  }
503
 
504
  public void writeDir(File dir, OutputStream out)
505
    throws IOException
506
  {
507
    if(logger.isInfoEnabled())
508
      logger.info("write dir " + dir.getCanonicalPath());
509
 
510
    if(writeCached(dirCache, dir, out)) return;
511
 
512
    BufferedImage thumbnail = createDirThumbnail(dir);
513
 
514
    if(thumbnail != null) {
515
      if(dirTemplateSize == null
516
        || dirTemplateTimestamp != dirTemplate.lastModified())
517
      {
518
        dirTemplateSize      = getOriginSize(dirTemplate);
519
        dirTemplateTimestamp = dirTemplate.lastModified();
520
      }
521
 
522
      cacheThumbnail(dirCache, dir, thumbnail, DIR_SUFFIX,
523
        dirTemplateSize[0], dirTemplateSize[1]);
524
 
525
      if(out != null) ImageIO.write(thumbnail, format, out);
526
    }
527
  }
528
 
529
  synchronized protected BufferedImage createThumbnail(File imageFile,
530
      int width, int height)
531
    throws IOException
532
  {
533
    if(logger.isDebugEnabled())
534
      logger.debug("create thumbnail " + imageFile.getCanonicalPath());
535
 
536
    Image image = loadImage(imageFile.getCanonicalPath());
537
      // = ImageIO.read(imageFile);
538
    int[] sizes;
539
 
540
    if(image == null) {   // not supported format
541
      logger.warn("unsupported format for origin or operation interrupted");
542
 
543
      return null;
544
    }
545
    else {
546
      sizes = calcSizes(image.getWidth(null), image.getHeight(null),
547
        width, height);
548
      logger.debug("resize to " + sizes[0] + "x" + sizes[1]);
549
 
550
      return resizer.resize(image, sizes[0], sizes[1]);
551
    }
552
  }
553
 
1074 dev 554
  synchronized protected BufferedImage createThumbnailNative(File dir, File cacheFile,
555
  		File imageFile, int width, int height)
556
    throws IOException
557
  {
558
    if(logger.isDebugEnabled())
559
      logger.debug("create thumbnail2 " + imageFile.getCanonicalPath() + " to "
560
      	+	cacheFile.getCanonicalPath());
561
 
562
    dir.mkdirs();
563
 
564
		// FIMXE: 1) make util path (and params?) configurable
565
		Process process = Runtime.getRuntime().exec(new String[] {
566
			"/usr/local/bin/convert",
567
			"-size",      width + "x" + height,
568
			"-thumbnail", width + "x" + height,
569
      imageFile.getCanonicalPath(),
570
    	cacheFile.getCanonicalPath()
571
		});
572
 
573
    // FIXME make it finner
574
    BufferedReader in = new BufferedReader(new InputStreamReader(process.getErrorStream()));
575
    String line;
576
    while((line = in.readLine()) != null) {
577
    	System.out.println("EXEC: " + line);
578
    }
579
 
580
		try {
581
	    int res = process.waitFor();
582
 
583
	    if(logger.isDebugEnabled())
584
	      logger.debug("process exited with result " + res);
585
		}
586
		catch(InterruptedException ex) {
587
      logger.debug("process interrupted");
588
		}
589
 
590
    return null;
591
  }
592
 
936 dev 593
  synchronized protected BufferedImage createDirThumbnail(File dir)
594
    throws IOException
595
  {
1074 dev 596
	long timeStart = System.currentTimeMillis();
597
 
936 dev 598
    if(logger.isDebugEnabled())
599
      logger.debug("create dir thumbnail " + dir.getCanonicalPath());
600
 
601
    Image         template = loadImage(dirTemplate.getCanonicalPath());
602
    BufferedImage dirThumbnail;
603
    Graphics      graphics;
604
    int           count;
605
    File[]        firstFiles;
606
 
607
    if(template == null) {   // not supported format
608
      logger.warn("unsupported format for template or operation interrupted");
609
 
610
      return null;
611
    }
612
 
613
    dirThumbnail = createBufferedImage(template);
614
 
615
    graphics     = dirThumbnail.getGraphics();
616
    count        = dirThumbnailPositions.length;
617
    firstFiles   = new File[count];
618
    count        = getFirstFiles(dir, count, firstFiles, 0);
619
 
620
    for(int i = 0; i < count; i++) {
1074 dev 621
      Image         image;
622
      BufferedImage thumbnail = null;
623
      int[]         sizes;
936 dev 624
 
1074 dev 625
			if(nativeMode) {
626
				File cacheFileDir = getCacheFileDir(firstFiles[i]);
627
				File cacheFile    = getCacheFile(cacheFileDir, firstFiles[i], DIR_PART_SUFFIX);
628
	    	createThumbnailNative(cacheFileDir, cacheFile, firstFiles[i],
629
	    		dirThumbnailPositions[i].getWidth(), dirThumbnailPositions[i].getHeight());
630
	      image     = loadImage(cacheFile.getCanonicalPath());
631
	      thumbnail = createBufferedImage(image);
632
			}
633
			else {
634
	      image = loadImage(firstFiles[i].getCanonicalPath());
635
      }
636
 
936 dev 637
      if(image == null) {   // not supported format
638
        logger.warn("unsupported format for origin or operation interrupted");
639
 
640
        return null;
641
      }
642
      else {
643
        sizes = calcSizes(image.getWidth(null), image.getHeight(null),
644
          dirThumbnailPositions[i].getWidth(),
645
          dirThumbnailPositions[i].getHeight());
1074 dev 646
      }
936 dev 647
 
1074 dev 648
			if(!nativeMode) {
936 dev 649
        thumbnail = resizer.resize(image, sizes[0], sizes[1]);
650
      }
1074 dev 651
 
652
      graphics.drawImage(thumbnail,
653
        getXPosition(dirThumbnailPositions[i].getX(),
654
          dirThumbnailPositions[i].getWidth(), sizes[0],
655
          dirThumbnailPositions[i].getHorAlign()),
656
        getYPosition(dirThumbnailPositions[i].getY(),
657
          dirThumbnailPositions[i].getHeight(), sizes[1],
658
          dirThumbnailPositions[i].getVertAlign()),
659
        null);
936 dev 660
    }
661
 
1074 dev 662
    if(logger.isDebugEnabled()) {
663
      logger.debug("dir thumbnail created in "
664
      	+ (System.currentTimeMillis() - timeStart) + " ms");
665
    }
666
 
936 dev 667
    return dirThumbnail;
668
  }
669
 
670
  protected Image loadImage(String fileName)
671
  {
1074 dev 672
	long timeStart = System.currentTimeMillis();
673
 
936 dev 674
    // FIXME: probably toolbox reads an image not by every request but
675
    //        caches it
676
    Toolkit toolkit = Toolkit.getDefaultToolkit();
677
    Image   image   = toolkit.getImage(fileName);
678
 
679
    toolkit.prepareImage(image, -1, -1, null);
680
 
681
    while(true) {
682
      int status = toolkit.checkImage(image, -1, -1, null);
683
 
684
      if((status & ImageObserver.ALLBITS) != 0) break;
685
      if((status & ImageObserver.ERROR)   != 0) return null;
686
 
687
      try {
688
        Thread.sleep(100);
689
      }
690
      catch(Exception ex) {
691
        return null;
692
      }
693
    }
694
 
1074 dev 695
    if(logger.isDebugEnabled()) {
696
      logger.debug("image " + fileName + " loaded in "
697
      	+ (System.currentTimeMillis() - timeStart) + " ms");
698
    }
699
 
936 dev 700
    return image;
701
  }
702
 
703
  protected int[] calcSizes(int width, int height, int maxWidth, int maxHeight)
704
  {
705
    int[]  result = new int[2];
706
    double xRate;
707
    double yRate;
708
 
709
    if(width == 0 || height == 0) {
710
      result[0] = 0;
711
      result[1] = 0;
712
      return result;
713
    }
714
 
715
    xRate  = (double)maxWidth  / (double)width;
716
    yRate  = (double)maxHeight / (double)height;
717
    if(xRate >= 1.0 || yRate >= 1.0) {
718
      result[0] = width;
719
      result[1] = height;
720
    }
721
    else if(xRate > yRate) {
722
      result[0] = maxHeight * width / height;
723
      result[1] = maxHeight;
724
    }
725
    else {
726
      result[0] = maxWidth;
727
      result[1] = maxWidth * height / width;
728
    }
729
 
730
    return result;
731
  }
732
 
733
  protected int getXPosition(int left, int maxWidth, int width, int align)
734
  {
735
    if(align == ThumbnailPosition.ALIGN_HOR_LEFT)
736
      return left;
737
    else if(align == ThumbnailPosition.ALIGN_HOR_RIGHT)
738
      return left + (maxWidth - width);
739
    else if(align == ThumbnailPosition.ALIGN_HOR_CENTER)
740
      return left + (maxWidth - width) / 2;
741
    else
742
      throw new RuntimeException("Unknown align type: " + align);
743
  }
744
 
745
  protected int getYPosition(int top, int maxHeight, int height, int align)
746
  {
747
    if(align == ThumbnailPosition.ALIGN_VERT_TOP)
748
      return top;
749
    else if(align == ThumbnailPosition.ALIGN_VERT_BOTTOM)
750
      return top + (maxHeight - height);
751
    else if(align == ThumbnailPosition.ALIGN_VERT_CENTER)
752
      return top + (maxHeight - height) / 2;
753
    else
754
      throw new RuntimeException("Unknown align type: " + align);
755
  }
756
 
757
  protected int getFirstFiles(File dir, int count, File[] files, int pos)
758
  {
759
    File[] children;
760
 
761
    if(imagesFilter == null)
762
      children = dir.listFiles();
763
    else
764
      children = dir.listFiles(imagesFilter);
765
 
766
    if(children == null) return 0; // the dir does not exists
767
 
768
    Arrays.sort(children, fileNameComparatorRev);
769
 
1254 dev 770
    for(int i = 0; i < children.length && pos < count; i++) {
936 dev 771
      if(children[i].isDirectory())
772
        ; //pos = getFirstFiles(children[i], count, files, pos);
773
      else {
774
        files[pos++] = children[i];
775
      }
776
    }
777
 
778
    return pos;
779
  }
780
 
781
  protected BufferedImage createBufferedImage(Image image)
782
  {
783
    if(image == null) return null;
784
 
785
    int width  = image.getWidth(null);
786
    int height = image.getHeight(null);
787
 
788
    if(width < 1 || height < 1) return null;
789
 
790
    // get pixels
791
    int[]        pixels = new int[width * height];
792
    PixelGrabber pg     = new PixelGrabber(image,
793
      0, 0, width, height, pixels, 0, width);
794
 
795
    try  {
796
      pg.grabPixels();
797
    }
798
    catch(InterruptedException e) {
799
      return null;
800
    }
801
 
802
    if((pg.getStatus() & ImageObserver.ABORT) != 0) return null;
803
 
804
    // create buffered image
805
    BufferedImage buffered = new BufferedImage(width, height,
806
      BufferedImage.TYPE_INT_RGB);
807
 
808
    for(int y = 0; y < height; y++) {
809
      for(int x = 0; x < width; x++)
810
        buffered.setRGB(x, y, pixels[y * width + x]);
811
    }
812
 
813
    return buffered;
814
  }
815
 
816
  public ImageResizer getResizer()
817
  {
818
    return resizer;
819
  }
820
 
821
  public void setResizer(ImageResizer resizer)
822
  {
823
    this.resizer = resizer;
824
  }
825
 
826
  public String getFormat()
827
  {
828
    return format;
829
  }
830
 
831
  public void setFormat(String format)
832
  {
833
    this.format = format;
834
  }
835
 
836
  public File getCacheDir()
837
  {
838
    return cacheDir;
839
  }
840
 
841
  public void setCacheDir(File dir)
842
  {
843
    this.cacheDir = dir;
844
  }
845
 
846
  public File getImagesRoot()
847
  {
848
    return imagesRoot;
849
  }
850
 
851
  public void setImagesRoot(File dir)
852
  {
853
    this.imagesRoot = dir;
854
  }
855
 
856
  public int getSmallWidth()
857
  {
858
    return smallWidth;
859
  }
860
 
861
  public void setSmallWidth(int width)
862
  {
863
    this.smallWidth = width;
864
  }
865
 
866
  public int getSmallHeight()
867
  {
868
    return smallHeight;
869
  }
870
 
871
  public void setSmallHeight(int height)
872
  {
873
    this.smallHeight = height;
874
  }
875
 
876
  public int getMediumWidth()
877
  {
878
    return mediumWidth;
879
  }
880
 
881
  public void setMediumWidth(int width)
882
  {
883
    this.mediumWidth = width;
884
  }
885
 
886
  public int getMediumHeight()
887
  {
888
    return mediumHeight;
889
  }
890
 
891
  public void setMediumHeight(int height)
892
  {
893
    this.mediumHeight = height;
894
  }
895
 
896
  public FileFilter getImagesFilter()
897
  {
898
    return imagesFilter;
899
  }
900
 
901
  public void setImagesFilter(FileFilter filter)
902
  {
903
    this.imagesFilter = filter;
904
  }
905
 
906
  public File getDirTemplate()
907
  {
908
    return dirTemplate;
909
  }
910
 
911
  public void setDirTemplate(File dirTemplate)
912
  {
913
    this.dirTemplate = dirTemplate;
914
  }
915
 
916
  public ThumbnailPosition[] getDirThumbnailPositions()
917
  {
918
    return dirThumbnailPositions;
919
  }
920
 
921
  public void setDirThumbnailPositions(
922
    ThumbnailPosition[] dirThumbnailPositions)
923
  {
924
    this.dirThumbnailPositions = dirThumbnailPositions;
925
  }
926
}