GalleryDBTools.java

package sk.iway.iwcm.common;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.doc.DebugTimer;
import sk.iway.iwcm.gallery.GalleryDB;
import sk.iway.iwcm.gallery.ImageInfo;
import sk.iway.iwcm.io.IwcmFile;
import sk.iway.iwcm.io.IwcmInputStream;
import sk.iway.iwcm.io.IwcmOutputStream;

public class GalleryDBTools {
    //hash mapa pre ukladanie rozmerov obrazkov do cache
    private static Map<String, int[]> imagesDims;

    private GalleryDBTools() {

    }

    /**
     * Vrati velkost zadaneho obrazku
     * @param imageFile - IwcmFile obrazku
     * @return int[0] - sirka, int[1] - vyska, ak nenejade rozmery vracia nulove hodnoty
     */
    public static int[] getImageSize(IwcmFile imageFile)
    {
        int[] dims = {0,0};

        if(imageFile == null)
            return dims;

        String imagesDimsKey = imageFile.getAbsolutePath()+"-"+imageFile.lastModified();

        if(imagesDims == null) imagesDims = new ConcurrentHashMap<String, int[]>();
        dims = imagesDims.get(imagesDimsKey); //skusim ziskat rozmery z cache

        if(dims == null)
        {
            dims = new int[2];

            ImageInfo ii = new ImageInfo();
            try
            {
                IwcmInputStream is = new IwcmInputStream(imageFile);
                ii.setInput(is);
                if (ii.check())
                {
                    dims[0] = ii.getWidth();
                    dims[1] = ii.getHeight();
                }
                is.close();
            }
            catch (Exception ex)
            {
                sk.iway.iwcm.Logger.error(ex);
            }

            imagesDims.put(imagesDimsKey, dims); //ukladam rozmery do cache
        }

        return dims;
    }

    /**
     * Najprv vyreze z obrazka dany rozmer a vyrezu zmeni velkost
     */
    public static int cropAndResize(IwcmFile from, int cwidth, int cheight, int cleft, int ctop, int finalWidth, int finalHeight, String fillColor, boolean exactFinalSize, IwcmFile to, int imageQuality, int ip)
    {
        // comand line :   convert '*.jpg' -crop 120x120+10+5 -resize 105x20  thumbnail%03d.png

        if (!to.getParentFile().exists())
        {
            to.getParentFile().mkdirs();
        }

        //zakladna ochrana pred code injection
        if (fillColor!=null && fillColor.length()!=6) fillColor = null;


        /** Overim, ci na serveri existuje ImageMagic **/
        String imageMagickDir = GalleryDB.getImageMagicDir();
        // mame ho aj dostupny
        boolean convertExists = false;
        String runtimeFile = GalleryDB.getRuntimeFile();

        if (Tools.isNotEmpty(imageMagickDir))
        {
            File f = new File(imageMagickDir + File.separatorChar + runtimeFile);
            if (f.exists() && f.canRead())
            {
                convertExists = true;
            }
        }

        if (Tools.isNotEmpty(imageMagickDir) && convertExists)
        {

            return cropAndResizeImageMagick(from, cwidth, cheight, cleft, ctop, finalWidth, finalHeight, fillColor, exactFinalSize, to, imageQuality);
        }
        else
        {
            return cropAndResizeJava(from, cwidth, cheight, cleft, ctop, finalWidth, finalHeight, fillColor, exactFinalSize, to, imageQuality, ip);
        }
    }

    private static int cropAndResizeImageMagick(IwcmFile from, int cwidth, int cheight, int cleft, int ctop, int finalWidth, int finalHeight, String fillColor, boolean exactFinalSize, IwcmFile to, int imageQuality)
    {
        finalWidth = limitMaxSize(finalWidth);
        finalHeight = limitMaxSize(finalHeight);

        List<String> args = new ArrayList<String>();

        args.add(""); //toto sa neskor nahradi za convert prikaz v executeImageMagickCommand
        args.add(from.getAbsolutePath());
        args.add("-crop");
        args.add(cwidth+"x"+cheight+"+"+cleft+"+"+ctop);
        args.add("-resize");
        if (exactFinalSize) args.add(finalWidth+"x"+finalHeight+"!");
        else  args.add(finalWidth+"x"+finalHeight);

        if (imageQuality > 0 && to.getName().toLowerCase().endsWith(".jpg"))
        {
            args.add("-quality");
            args.add(String.valueOf(imageQuality));
        }

        if (from.getAbsolutePath().endsWith(".gif")) args.add("+repage");

        if (fillColor == null)
        {
            args.add("-strip");
            args.add(to.getAbsolutePath());
        }
        else
        {
            args.add("-gravity");
            args.add("center");
            args.add("-background");
            args.add("#"+fillColor);
            args.add("-extent");
            args.add(finalWidth+"x"+finalHeight);
            args.add("-strip");
            args.add(to.getAbsolutePath());
        }

        return GalleryDB.executeImageMagickCommand(args.toArray(new String[0]));
    }

    private static int cropAndResizeJava(IwcmFile from, int cwidth, int cheight, int cleft, int ctop, int finalWidth, int finalHeight, String fillColor, boolean exactFinalSize, IwcmFile to, int imageQuality, int ip)
    {
        try
        {
            IwcmInputStream iwStream = new IwcmInputStream(from.getPath(), false);
            BufferedImage originalBufferedImage = ImageIO.read(iwStream);
            iwStream.close();
            // Logger.println(GalleryDB.class,"w="+w+" h="+h);
            int scaleType = Image.SCALE_AREA_AVERAGING;
            if (originalBufferedImage.getWidth() > 1000)
            {
                Logger.debug(GalleryDB.class, "smooth resize");
                scaleType = Image.SCALE_SMOOTH;
            }

            if (imageQuality >= 90) scaleType = Image.SCALE_SMOOTH;
            else if (imageQuality >= 85) scaleType = Image.SCALE_AREA_AVERAGING;
            else if (imageQuality >= 80) scaleType = Image.SCALE_REPLICATE;
            else if (imageQuality > 0) scaleType = Image.SCALE_FAST;

            if (originalBufferedImage.getWidth() > 2000)
            {
                // Logger.println(GalleryDB.class,"smooth fast");
                // scaleType = Image.SCALE_FAST;
            }
            int resizeWidth = finalWidth;
            int resizeHeight = finalHeight;
            int offsetW = 0;
            int offsetH = 0;

            /*** Ak 3 alebo 4, potrebujem dopocitat velkost obrazku - ostatne sa doplni bielou farbou (ip=3) alebo farbou fillColor ***/
            if(ip == 3 || ip == 4)
            {
                if(finalWidth > finalHeight)
                {
                    double pomer = (double)cwidth / (double) cheight;
                    resizeWidth = (int)Math.round(finalHeight * pomer);
                    if(ip == 4) offsetW = (finalWidth - resizeWidth)/2;	//vycentrovanie obrazka
                }
                else
                {
                    double pomer = (double)cwidth / (double) cheight;
                    resizeHeight = (int)Math.round(finalWidth / pomer);
                    if(ip == 4) offsetH = (finalHeight - resizeHeight)/2;	//vycentrovanie obrazka
                }
            }

            DebugTimer timer = new DebugTimer("ImageResize");
            timer.diff("starting: " + scaleType + " cleft="+cleft+" ctop="+ctop+" cwidth="+cwidth+" cheight="+cheight);
            Image smallImage = null;

            /*** Orezanie ***/
            smallImage = originalBufferedImage.getSubimage(cleft, ctop, cwidth, cheight);
            BufferedImage bi = new BufferedImage(cwidth, cheight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = bi.createGraphics();
            g.drawImage(smallImage, 0, 0, null);
            timer.diff("croped");

            /*** Zmena velkosti obrazku ***/
            smallImage = bi.getScaledInstance(resizeWidth, resizeHeight, scaleType);
            timer.diff("scaled");
            BufferedImage bufSmall = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_INT_RGB);
            timer.diff("buf created");
            // bufSmall.getGraphics().drawImage(smallImage, 0, 0, null);
            Graphics2D graphics = bufSmall.createGraphics();
            if(Tools.isNotEmpty(fillColor))	//farba vyplne
            {
                graphics.setPaint ( Color.decode("#"+fillColor) );
                graphics.fillRect ( 0, 0, finalWidth, finalHeight );
            }
            else
            {
                graphics.setPaint ( Color.white );
                graphics.fillRect ( 0, 0, finalWidth, finalHeight );
            }
            graphics.drawImage(smallImage, offsetW, offsetH, null);
            timer.diff("image drawed");
            bufSmall.getGraphics().dispose();
            timer.diff("disposed");

            ImageWriteParam iwparam = null;
            // ImageIO.write(bufSmall, format,iwparam, fSmallImg);
            // Jimi.putImage("image/" + type, image, realPathSmall);
            ImageWriter writer = null;

            String format = "jpg";
            if (from.getAbsolutePath().endsWith(".png"))
            {
                format = "png";
            }
            else
            {
                iwparam = new JPEGImageWriteParam(null);
                iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                iwparam.setCompressionQuality(0.85F);
            }

            Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(format);
            if (iter.hasNext())
            {
                writer = iter.next();
            }
            if (writer != null) {
                // Prepare output file
                IwcmOutputStream out = new IwcmOutputStream(to.getPath());
                ImageOutputStream ios = ImageIO.createImageOutputStream(out);
                writer.setOutput(ios);
                timer.diff("write start");
                // Write the image
                writer.write(null, new IIOImage(bufSmall, null, null), iwparam);
                timer.diff("writed to file");
                // Cleanup
                ios.flush();
                writer.dispose();
                ios.close();
                out.close();
            }
            return (0);
        }
        catch (javax.imageio.IIOException ex)
        {
            sk.iway.iwcm.Logger.error(ex);
            return (4);
        }
        catch (Exception ex)
        {
            sk.iway.iwcm.Logger.error(ex);
            return (2);
        }
    }

    /**
     * Limituje maximalnu velkost obrazku pre resize na hodnotu 5000 aby nedoslo k zahlteniu disku / CPU
     * @param size
     * @return
     */
    public static int limitMaxSize(int size)
    {
        if (size > 5000) return 5000;
        return size;
    }

    /**
     * Pouzi GalleryToolsForCore.getImagePathPrefix
     * @param prefix
     * @param fullPath
     * @return
     * @deprecated
     */
    @Deprecated
    public static String getImagePathPrefix(String prefix, String fullPath) {
        return GalleryToolsForCore.getImagePathPrefix(prefix,fullPath);
    }

    /**
     * Pouzi Pouzi GalleryToolsForCore.getImagePathSmall
     * @param fullPath
     * @return
     * @deprecated
     */
    @Deprecated
    public static String getImagePathSmall(String fullPath) {
        return GalleryToolsForCore.getImagePathSmall(fullPath);
    }

    /**
     * Pouzi Pouzi GalleryToolsForCore.getImagePathNormal
     * @param fullPath
     * @return
     * @deprecated
     */
    @Deprecated
    public static String getImagePathNormal(String fullPath) {
        return GalleryToolsForCore.getImagePathNormal(fullPath);
    }

    /**
     * Pouzi Pouzi GalleryToolsForCore.getImagePathOriginal
     * @param fullPath
     * @return
     * @deprecated
     */
    @Deprecated
    public static String getImagePathOriginal(String fullPath) {
        return GalleryToolsForCore.getImagePathOriginal(fullPath);
    }
}