Subversion Repositories general

Rev

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

package ak.photoalbum.images;

import java.util.Map;
import java.util.HashMap;
import java.util.Comparator;
import java.util.Arrays;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;
import javax.imageio.ImageIO;
import org.apache.log4j.Logger;
import marcoschmidt.image.ImageInfo;
import ak.photoalbum.util.TimestampRecipient;
import ak.photoalbum.util.FileUtils;
import ak.photoalbum.util.FileNameComparator;

public class Thumbnailer
{
  protected static final String DEFAULT_FORMAT        = "jpg";
  protected static final String SMALL_SUFFIX          = ".small";
  protected static final String MEDIUM_SUFFIX         = ".medium";
  protected static final String DIR_SUFFIX            = ".dir";
  protected static final String DIR_PART_SUFFIX       = ".dir_part";
  protected static final int    DEFAULT_SMALL_WIDTH   = 120;
  protected static final int    DEFAULT_SMALL_HEIGHT  = 120;
  protected static final int    DEFAULT_MEDIUM_WIDTH  = 800;
  protected static final int    DEFAULT_MEDIUM_HEIGHT = 800;

  protected static final int    CACHED_NOT_FOUND      = 0;
  protected static final int    CACHED_NOT_MODIFIED   = 1;
  protected static final int    CACHED_WRITTEN        = 2;

  private static final Logger logger = Logger.getLogger(Thumbnailer.class);

  protected ImageResizer resizer;
  protected int          smallWidth            = DEFAULT_SMALL_WIDTH;
  protected int          smallHeight           = DEFAULT_SMALL_HEIGHT;
  protected int          mediumWidth           = DEFAULT_MEDIUM_WIDTH;
  protected int          mediumHeight          = DEFAULT_MEDIUM_HEIGHT;
  protected File         cacheDir;
  protected String       format                = DEFAULT_FORMAT;
  protected Map          smallCache            = new HashMap();
  protected Map          mediumCache           = new HashMap();
  protected Map          dirCache              = new HashMap();
  protected File         imagesRoot;
  protected FileFilter   imagesFilter          = null;
  protected Comparator   fileNameComparator    = new FileNameComparator(true);
  protected Comparator   fileNameComparatorRev = new FileNameComparator(false);
  protected boolean      nativeMode            = true; // FIXME config

  protected File                dirTemplate;
  protected ThumbnailPosition[] dirThumbnailPositions;
  protected int[]               dirTemplateSize;
  protected long                dirTemplateTimestamp;

  public Thumbnailer()
  {
  }

  public String getMime()
  {
    return FileUtils.getMime(format);
  }

  public int[] getDirSize(File origin)
    throws IOException
  {
    if(dirTemplateSize == null
      || dirTemplateTimestamp != dirTemplate.lastModified())
    {
      dirTemplateSize      = getOriginSize(dirTemplate);
      dirTemplateTimestamp = dirTemplate.lastModified();
    }

    return dirTemplateSize;
  }

  public int[] getSmallSize(File origin)
    throws IOException
  {
    CachedFile cached = getCached(smallCache, origin);

    if(cached == null) {
      int[] originSize = getOriginSize(origin);

      return calcSizes(originSize[0], originSize[1], smallWidth, smallHeight);
    }
    else {
      int[] originSize = new int[2];

      originSize[0] = cached.getWidth();
      originSize[1] = cached.getHeight();

      return originSize;
    }
  }

  public int[] getMediumSize(File origin)
    throws IOException
  {
    CachedFile cached = getCached(mediumCache, origin);

    if(cached == null) {
      int[] originSize = getOriginSize(origin);

      return calcSizes(originSize[0], originSize[1], mediumWidth, mediumHeight);
    }
    else {
      int[] originSize = new int[2];

      originSize[0] = cached.getWidth();
      originSize[1] = cached.getHeight();

      return originSize;
    }
  }

  protected int[] getOriginSize(File origin)
    throws IOException
  {
    if(logger.isDebugEnabled())
      logger.debug("get size of " + origin.getCanonicalPath());

    ImageInfo       ii  = new ImageInfo();
    FileInputStream in  = null;
    int[]           res = new int[2];

    try {
      in = new FileInputStream(origin);
      ii.setInput(in);

      if(ii.check()) {
        res[0] = ii.getWidth();
        res[1] = ii.getHeight();
      }
      else{
        logger.warn("not supported format of " + origin.getCanonicalPath());
        res[0] = 0;
        res[1] = 0;
      }
    }
    finally {
      if(in != null) in.close();
    }

    return res;
  }

  public void rebuildCache()
    throws IOException
  {
    logger.info("rebuild cache");

    deleteCache();
    buildCache();
  }

  public void deleteCache()
    throws IOException
  {
    logger.info("delete cache");

    deleteCache(cacheDir);
  }

  public void buildCache()
    throws IOException
  {
    logger.info("build cache");

    buildCache(imagesRoot);
  }

  public void reloadCache()
    throws IOException
  {
    logger.info("reload cache");

    smallCache.clear();
    mediumCache.clear();
    dirCache.clear();
    loadCaches(cacheDir);
  }

  protected void deleteCache(File dir)
    throws IOException
  {
    File[] children = dir.listFiles();

    if(children == null) return; // the dir does not exists

    Arrays.sort(children, fileNameComparator);

    for(int i = 0; i < children.length; i++) {
      if(children[i].isDirectory())
        deleteCache(children[i]);
      else
        children[i].delete();
    }
    dir.delete();
  }

  protected void buildCache(File dir)
    throws IOException
  {
    File[] children;

    if(imagesFilter == null)
      children = dir.listFiles();
    else
      children = dir.listFiles(imagesFilter);

    if(children == null) return; // the dir does not exists

    Arrays.sort(children, fileNameComparator);

    for(int i = 0; i < children.length; i++) {
      if(children[i].isDirectory()) {
        writeDir(children[i], -1, null, null);
        buildCache(children[i]);
      }
      else {
        writeSmall(children[i], -1, null, null);
        writeMedium(children[i], -1, null, null);
      }
    }
  }

  protected CachedFile getCached(Map cache, File imageFile)
    throws IOException
  {
    String     key    = imageFile.getCanonicalPath();
    CachedFile cached = (CachedFile)cache.get(key);

    if(cached == null || !cached.getFile().exists()) {
      cache.remove(key);
      logger.debug("not found in cache");
      return null;
    }

    if(cached.getOriginTimestamp() != imageFile.lastModified()) {
      cached.getFile().delete();
      cache.remove(key);
      logger.debug("timestamps dont match");
      return null;
    }

    return cached;
  }

  protected int writeCached(Map cache, File imageFile, long ifModifiedSince,
      OutputStream out, TimestampRecipient timestampRecipient)
    throws IOException
  {
    CachedFile cached = getCached(cache, imageFile);

    if(cached == null) return CACHED_NOT_FOUND;

    if(ifModifiedSince >= 0 && ifModifiedSince <= cached.getTimestamp())
      return CACHED_NOT_MODIFIED; // cached image found and not modified
                                  //  since given timestamp - do nothing

    if(logger.isDebugEnabled())
      logger.debug("write cached " + imageFile.getCanonicalPath());

    if(timestampRecipient != null) {
      timestampRecipient.setTimestamp(cached.getTimestamp());
    }

    if(out != null) {
      FileInputStream in = null;

      try {
        in  = new FileInputStream(cached.getFile());
        FileUtils.copyStreams(in, out);
      }
      finally {
        if(in != null) in.close();
      }
    }

    return CACHED_WRITTEN;
  }

  protected File getCacheFile(File dir, File imageFile, String suffix)
  {
    return new File(dir, imageFile.getName() + suffix + "." + format);
  }

  // @return    last modified timestamp pf the cached thumbnail
  protected long cacheThumbnail(Map cache, File imageFile,
      BufferedImage thumbnail, String suffix, int width, int height)
    throws IOException
  {
    logger.debug("cache thumbnail " + suffix + " "
      + imageFile.getCanonicalPath());

    File dir       = getCacheFileDir(imageFile);
    File cacheFile = getCacheFile(dir, imageFile, suffix);

    dir.mkdirs();
    ImageIO.write(thumbnail, format, cacheFile);

    cache.put(imageFile.getCanonicalPath(),
      new CachedFile(cacheFile, imageFile,
      cacheFile.lastModified(), imageFile.lastModified(), width, height));

    return cacheFile.lastModified();
  }

  public void startup()
    throws IOException
  {
    logger.info("startup");
    loadCaches(cacheDir);
    logger.info("started");
  }

        protected void loadFileCache(File file)
    throws IOException
        {
                /* FIXME use this optimization?
    String dirEnd    = DIR_SUFFIX    + "." + format;
    String smallEnd  = SMALL_SUFFIX  + "." + format;
    String mediumEnd = MEDIUM_SUFFIX + "." + format;
    */

    File origin = null;
    Map  cache  = null;

    if(file.getName().endsWith(SMALL_SUFFIX + "." + format)) {
      origin = getOriginFile(file, SMALL_SUFFIX);
      cache  = smallCache;

      if(logger.isDebugEnabled())
        logger.debug("load cached small " + file.getCanonicalPath()
          + " for " + origin.getCanonicalPath());
    }
    else if(file.getName().endsWith(MEDIUM_SUFFIX + "." + format)) {
      origin = getOriginFile(file, MEDIUM_SUFFIX);
      cache  = mediumCache;

      if(logger.isDebugEnabled())
        logger.debug("load cached medium " + file.getCanonicalPath()
          + " for " + origin.getCanonicalPath());
    }
    else if(file.getName().endsWith(DIR_SUFFIX + "." + format)) {
      origin = getOriginFile(file, DIR_SUFFIX);
      cache  = dirCache;

      if(logger.isDebugEnabled())
        logger.debug("load cached dir " + file.getCanonicalPath()
          + " for " + origin.getCanonicalPath());
    }
    else if(file.getName().endsWith(DIR_PART_SUFFIX + "." + format)) {
      if(logger.isDebugEnabled())
        logger.debug("skip cached dir part " + file.getCanonicalPath());
    }
    else {
      if(logger.isInfoEnabled())
        logger.warn(
          "unknown type of cached " + file.getCanonicalPath());
    }

    if(origin == null || cache == null) {
      file.delete();

      if(logger.isDebugEnabled())
        logger.debug("deleted (unknown origin)");
    }
    else {
      loadCacheInfo(file, origin, cache);
    }
        }

        protected void loadCacheInfo(File file, File origin, Map cache)
    throws IOException
        {
    long  originTimestamp = origin.lastModified();
    long  cachedTimestamp = file.lastModified();
    int[] sizes           = getOriginSize(file);

    if(origin.exists() && cachedTimestamp >= originTimestamp) {
      cache.put(origin.getCanonicalPath(),
        new CachedFile(file, origin, cachedTimestamp,
          originTimestamp, sizes[0], sizes[1]));

      logger.debug("added");
    }
    else {
      file.delete();

      if(logger.isDebugEnabled())
        logger.debug("deleted: " + origin.exists()
          + " " + cachedTimestamp + " " + originTimestamp);
    }
        }

  protected void loadCaches(File dir)
    throws IOException
  {
    if(logger.isDebugEnabled())
      logger.debug("load caches in " + dir.getCanonicalPath());

    File[] children = dir.listFiles();

    if(children == null) return; // the dir does not exists

    for(int i = 0; i < children.length; i++) {
      if(children[i].isDirectory())
        loadCaches(children[i]);
      else
        loadFileCache(children[i]);
    }
  }

  protected File getOriginFile(File cached, String suffix)
    throws IOException
  {
    String fileEnd    = suffix + "." + format;
    String fileName   = cached.getName();
    String cachedPath = cached.getParentFile().getCanonicalPath();
    String cacheRoot  = cacheDir.getCanonicalPath();

    fileName = fileName.substring(0, fileName.length() - fileEnd.length());

    if(!cacheRoot.equals(cachedPath))
      if(!cacheRoot.endsWith(File.separator)) cacheRoot += File.separator;

    return new File(imagesRoot, cachedPath.substring(cacheRoot.length())
      + File.separator + fileName);
  }

  protected File getCacheFileDir(File imageFile)
    throws IOException
  {
    String imagePath = imageFile.getParentFile().getCanonicalPath();
    String rootPath  = imagesRoot.getCanonicalPath();

    if(imagePath.equals(rootPath)) return cacheDir;
    if(!rootPath.endsWith(File.separator)) rootPath += File.separator;

    if(!imagePath.startsWith(rootPath))
      throw new RuntimeException("Image " + imageFile.getCanonicalPath()
        + " is not under images root " + imagesRoot.getCanonicalPath());

    return new File(cacheDir, imagePath.substring(rootPath.length()));
  }

  protected boolean writeThumbnail(Map cache, String suffix, int width, int height, 
      File imageFile, long ifModifiedSince, OutputStream out, TimestampRecipient timestampRecipient)
    throws IOException
  {
    int cachedResult = writeCached(cache, imageFile, ifModifiedSince, out, timestampRecipient);
    if(cachedResult != CACHED_NOT_FOUND) return (cachedResult == CACHED_WRITTEN);

                if(nativeMode) {
                        File dir       = getCacheFileDir(imageFile);
                        File cacheFile = getCacheFile(dir, imageFile, suffix);

        createThumbnailNative(dir, cacheFile, imageFile, width, height);
                        loadCacheInfo(cacheFile, imageFile, cache);
      writeCached(cache, imageFile, -1, out, timestampRecipient);
                }
                else {
        BufferedImage thumbnail = createThumbnail(imageFile, width, height);

            if(thumbnail != null) {
                // a thumbnail returned - save it into the cache dir
              int sizes[] = calcSizes(
                thumbnail.getWidth(null), thumbnail.getHeight(null), width, height);
              long lastModified = cacheThumbnail(
                cache, imageFile, thumbnail, suffix, sizes[0], sizes[1]);

        if(timestampRecipient != null) {
          timestampRecipient.setTimestamp(lastModified);
        }

              if(out != null) {
          ImageIO.write(thumbnail, format, out);
        }
            }
                }

    return true; // image written
  }

  public boolean writeSmall(File imageFile, long ifModifiedSince, 
      OutputStream out, TimestampRecipient timestampRecipient)
    throws IOException
  {
    if(logger.isInfoEnabled())
      logger.info("write small " + imageFile.getCanonicalPath());

    return writeThumbnail(smallCache, SMALL_SUFFIX, smallWidth, smallHeight,
      imageFile, ifModifiedSince, out, timestampRecipient);
  }

  public boolean writeMedium(File imageFile, long ifModifiedSince,
      OutputStream out, TimestampRecipient timestampRecipient)
    throws IOException
  {
    if(logger.isInfoEnabled())
      logger.info("write medium " + imageFile.getCanonicalPath());

    return writeThumbnail(mediumCache, MEDIUM_SUFFIX, mediumWidth, mediumHeight,
      imageFile, ifModifiedSince, out, timestampRecipient);
  }

  public boolean writeDir(File dir, long ifModifiedSince,
      OutputStream out, TimestampRecipient timestampRecipient)
    throws IOException
  {
    if(logger.isInfoEnabled())
      logger.info("write dir " + dir.getCanonicalPath());

    int cachedResult = writeCached(dirCache, dir, ifModifiedSince, out, timestampRecipient);
    if(cachedResult != CACHED_NOT_FOUND) return (cachedResult == CACHED_WRITTEN);

    BufferedImage thumbnail = createDirThumbnail(dir);

    if(thumbnail != null) {
      if(dirTemplateSize == null
        || dirTemplateTimestamp != dirTemplate.lastModified())
      {
        dirTemplateSize      = getOriginSize(dirTemplate);
        dirTemplateTimestamp = dirTemplate.lastModified();
      }

      long lastModified = cacheThumbnail(dirCache, dir, thumbnail, DIR_SUFFIX,
        dirTemplateSize[0], dirTemplateSize[1]);

      if(timestampRecipient != null) {
        timestampRecipient.setTimestamp(lastModified);
      }

      if(out != null) {
        ImageIO.write(thumbnail, format, out);
      }
    }

    return true; // image written
  }

  protected BufferedImage createThumbnail(File imageFile,
      int width, int height)
    throws IOException
  {
    // FIXME make several instances parallel if we have sevelar processors
    synchronized(this) {
      if(logger.isDebugEnabled())
        logger.debug("create thumbnail " + imageFile.getCanonicalPath());

      Image image = loadImage(imageFile.getCanonicalPath());
        // = ImageIO.read(imageFile);
      int[] sizes;

      if(image == null) {   // not supported format
        logger.warn("unsupported format for origin or operation interrupted");

        return null;
      }
      else {
        sizes = calcSizes(image.getWidth(null), image.getHeight(null),
          width, height);
        logger.debug("resize to " + sizes[0] + "x" + sizes[1]);

        return resizer.resize(image, sizes[0], sizes[1]);
      }
    }
  }

  protected BufferedImage createThumbnailNative(File dir, File cacheFile,
                File imageFile, int width, int height)
    throws IOException
  {
    // FIXME make several instances parallel if we have sevelar processors
    synchronized(this) {
      if(logger.isDebugEnabled())
        logger.debug("create thumbnail2 " + imageFile.getCanonicalPath() + " to "
          +     cacheFile.getCanonicalPath());

      dir.mkdirs();

      // FIXME: 1) make util path (and params?) configurable
      Process process = Runtime.getRuntime().exec(new String[] {
        "/usr/local/bin/convert",
        "-size",      width + "x" + height,
        "-thumbnail", width + "x" + height,
        imageFile.getCanonicalPath(),
        cacheFile.getCanonicalPath()
      });

      // FIXME make it finner
      BufferedReader in = new BufferedReader(new InputStreamReader(process.getErrorStream()));
      String line;
      while((line = in.readLine()) != null) {
        logger.info("EXEC: " + line);
      }

      try {
        int res = process.waitFor();

        if(logger.isDebugEnabled())
          logger.debug("process exited with result " + res);
      }
      catch(InterruptedException ex) {
        logger.debug("process interrupted");
      }

      return null;
    }
  }

  protected BufferedImage createDirThumbnail(File dir)
    throws IOException
  {
    // FIXME make several instances parallel if we have sevelar processors
    synchronized(this) {
      long timeStart = System.currentTimeMillis();

      if(logger.isDebugEnabled())
        logger.debug("create dir thumbnail " + dir.getCanonicalPath());

      Image         template = loadImage(dirTemplate.getCanonicalPath());
      BufferedImage dirThumbnail;
      Graphics      graphics;
      int           count;
      File[]        firstFiles;

      if(template == null) {   // not supported format
        logger.warn("unsupported format for template or operation interrupted");

        return null;
      }

      dirThumbnail = createBufferedImage(template);

      graphics     = dirThumbnail.getGraphics();
      count        = dirThumbnailPositions.length;
      firstFiles   = new File[count];
      count        = getFirstFiles(dir, count, firstFiles, 0);

      for(int i = 0; i < count; i++) {
        Image         image;
        BufferedImage thumbnail = null;
        int[]         sizes;

        if(nativeMode) {
          File cacheFileDir = getCacheFileDir(firstFiles[i]);
          File cacheFile    = getCacheFile(cacheFileDir, firstFiles[i], DIR_PART_SUFFIX);
          createThumbnailNative(cacheFileDir, cacheFile, firstFiles[i],
            dirThumbnailPositions[i].getWidth(), dirThumbnailPositions[i].getHeight());
          image     = loadImage(cacheFile.getCanonicalPath());
          thumbnail = createBufferedImage(image);
        }
        else {
          image = loadImage(firstFiles[i].getCanonicalPath());
        }

        if(image == null) {   // not supported format
          logger.warn("unsupported format for origin or operation interrupted");

          return null;
        }
        else {
          sizes = calcSizes(image.getWidth(null), image.getHeight(null),
            dirThumbnailPositions[i].getWidth(),
            dirThumbnailPositions[i].getHeight());
        }

        if(!nativeMode) {
          thumbnail = resizer.resize(image, sizes[0], sizes[1]);
        }

        graphics.drawImage(thumbnail,
          getXPosition(dirThumbnailPositions[i].getX(),
            dirThumbnailPositions[i].getWidth(), sizes[0],
            dirThumbnailPositions[i].getHorAlign()),
          getYPosition(dirThumbnailPositions[i].getY(),
            dirThumbnailPositions[i].getHeight(), sizes[1],
            dirThumbnailPositions[i].getVertAlign()),
          null);
      }

      if(logger.isDebugEnabled()) {
        logger.debug("dir thumbnail created in "
          + (System.currentTimeMillis() - timeStart) + " ms");
      }

      return dirThumbnail;
    }
  }

  protected Image loadImage(String fileName)
  {
        long timeStart = System.currentTimeMillis();

    // FIXME: probably toolbox reads an image not by every request but
    //        caches it
    Toolkit toolkit = Toolkit.getDefaultToolkit();
    Image   image   = toolkit.getImage(fileName);

    toolkit.prepareImage(image, -1, -1, null);

    while(true) {
      int status = toolkit.checkImage(image, -1, -1, null);

      if((status & ImageObserver.ALLBITS) != 0) break;
      if((status & ImageObserver.ERROR)   != 0) return null;

      try {
        Thread.sleep(100);
      }
      catch(Exception ex) {
        return null;
      }
    }

    if(logger.isDebugEnabled()) {
      logger.debug("image " + fileName + " loaded in "
        + (System.currentTimeMillis() - timeStart) + " ms");
    }

    return image;
  }

  protected int[] calcSizes(int width, int height, int maxWidth, int maxHeight)
  {
    int[]  result = new int[2];
    double xRate;
    double yRate;

    if(width == 0 || height == 0) {
      result[0] = 0;
      result[1] = 0;
      return result;
    }

    xRate  = (double)maxWidth  / (double)width;
    yRate  = (double)maxHeight / (double)height;
    if(xRate >= 1.0 || yRate >= 1.0) {
      result[0] = width;
      result[1] = height;
    }
    else if(xRate > yRate) {
      result[0] = maxHeight * width / height;
      result[1] = maxHeight;
    }
    else {
      result[0] = maxWidth;
      result[1] = maxWidth * height / width;
    }

    return result;
  }

  protected int getXPosition(int left, int maxWidth, int width, int align)
  {
    if(align == ThumbnailPosition.ALIGN_HOR_LEFT)
      return left;
    else if(align == ThumbnailPosition.ALIGN_HOR_RIGHT)
      return left + (maxWidth - width);
    else if(align == ThumbnailPosition.ALIGN_HOR_CENTER)
      return left + (maxWidth - width) / 2;
    else
      throw new RuntimeException("Unknown align type: " + align);
  }

  protected int getYPosition(int top, int maxHeight, int height, int align)
  {
    if(align == ThumbnailPosition.ALIGN_VERT_TOP)
      return top;
    else if(align == ThumbnailPosition.ALIGN_VERT_BOTTOM)
      return top + (maxHeight - height);
    else if(align == ThumbnailPosition.ALIGN_VERT_CENTER)
      return top + (maxHeight - height) / 2;
    else
      throw new RuntimeException("Unknown align type: " + align);
  }

  protected int getFirstFiles(File dir, int count, File[] files, int pos)
  {
    File[] children;

    if(imagesFilter == null)
      children = dir.listFiles();
    else
      children = dir.listFiles(imagesFilter);

    if(children == null) return 0; // the dir does not exists

    Arrays.sort(children, fileNameComparatorRev);

    for(int i = 0; i < children.length && pos < count; i++) {
      if(children[i].isDirectory()) {
        //pos = getFirstFiles(children[i], count, files, pos);
      }
      else {
        files[pos++] = children[i];
      }
    }

    return pos;
  }

  protected BufferedImage createBufferedImage(Image image)
  {
    if(image == null) return null;

    int width  = image.getWidth(null);
    int height = image.getHeight(null);

    if(width < 1 || height < 1) return null;

    // get pixels
    int[]        pixels = new int[width * height];
    PixelGrabber pg     = new PixelGrabber(image,
      0, 0, width, height, pixels, 0, width);

    try  {
      pg.grabPixels();
    }
    catch(InterruptedException e) {
      return null;
    }

    if((pg.getStatus() & ImageObserver.ABORT) != 0) return null;

    // create buffered image
    BufferedImage buffered = new BufferedImage(width, height,
      BufferedImage.TYPE_INT_RGB);

    for(int y = 0; y < height; y++) {
      for(int x = 0; x < width; x++)
        buffered.setRGB(x, y, pixels[y * width + x]);
    }

    return buffered;
  }

  public ImageResizer getResizer()
  {
    return resizer;
  }

  public void setResizer(ImageResizer resizer)
  {
    this.resizer = resizer;
  }

  public String getFormat()
  {
    return format;
  }

  public void setFormat(String format)
  {
    this.format = format;
  }

  public File getCacheDir()
  {
    return cacheDir;
  }

  public void setCacheDir(File dir)
  {
    this.cacheDir = dir;
  }

  public File getImagesRoot()
  {
    return imagesRoot;
  }

  public void setImagesRoot(File dir)
  {
    this.imagesRoot = dir;
  }

  public int getSmallWidth()
  {
    return smallWidth;
  }

  public void setSmallWidth(int width)
  {
    this.smallWidth = width;
  }

  public int getSmallHeight()
  {
    return smallHeight;
  }

  public void setSmallHeight(int height)
  {
    this.smallHeight = height;
  }

  public int getMediumWidth()
  {
    return mediumWidth;
  }

  public void setMediumWidth(int width)
  {
    this.mediumWidth = width;
  }

  public int getMediumHeight()
  {
    return mediumHeight;
  }

  public void setMediumHeight(int height)
  {
    this.mediumHeight = height;
  }

  public FileFilter getImagesFilter()
  {
    return imagesFilter;
  }

  public void setImagesFilter(FileFilter filter)
  {
    this.imagesFilter = filter;
  }

  public File getDirTemplate()
  {
    return dirTemplate;
  }

  public void setDirTemplate(File dirTemplate)
  {
    this.dirTemplate = dirTemplate;
  }

  public ThumbnailPosition[] getDirThumbnailPositions()
  {
    return dirThumbnailPositions;
  }

  public void setDirThumbnailPositions(
    ThumbnailPosition[] dirThumbnailPositions)
  {
    this.dirThumbnailPositions = dirThumbnailPositions;
  }
}