001/*
002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 * $Id$
018 *
019 */
020package org.nuxeo.ecm.platform.pictures.tiles.magick.tiler;
021
022import java.util.HashMap;
023import java.util.Map;
024
025import org.nuxeo.common.utils.Path;
026import org.nuxeo.ecm.core.api.NuxeoException;
027import org.nuxeo.ecm.platform.commandline.executor.api.CommandException;
028import org.nuxeo.ecm.platform.commandline.executor.api.CommandNotAvailable;
029import org.nuxeo.ecm.platform.picture.api.ImageInfo;
030import org.nuxeo.ecm.platform.picture.magick.utils.ImageCropperAndResizer;
031import org.nuxeo.ecm.platform.pictures.tiles.api.PictureTiles;
032import org.nuxeo.ecm.platform.pictures.tiles.api.PictureTilesImpl;
033import org.nuxeo.ecm.platform.pictures.tiles.helpers.StringMaker;
034import org.nuxeo.ecm.platform.pictures.tiles.tilers.PictureTiler;
035
036/**
037 * ImageMagic based Tiler Uses several ImageMagick command lines to extract a tile form a picture file
038 *
039 * @author tiry
040 */
041public class MagickTiler implements PictureTiler {
042
043    /** @since 5.9.5. */
044    public static final String CMYK_MAP_COMPONENTS = "cmyk";
045
046    public boolean needsSync() {
047        return false;
048    }
049
050    public String getName() {
051        return "MagicTiler";
052    }
053
054    static int[] computeCropCoords(ImageInfo input, int maxTiles, int tileWidth, int tileHeight, int xCenter,
055            int yCenter) {
056        int startX = 0;
057        int startY = 0;
058        int cropWidth = 0;
059        int cropHeight = 0;
060        int ntx = 0;
061        int nty = 0;
062
063        if (input.getWidth() > input.getHeight()) {
064            cropWidth = input.getWidth() / maxTiles;
065            if ((input.getWidth() % maxTiles) > 0) {
066                cropWidth += 1;
067            }
068            Double exactCropHeight = (new Double(tileHeight)) / tileWidth * cropWidth;
069            if (exactCropHeight - exactCropHeight.intValue() > 0) {
070                cropHeight = exactCropHeight.intValue() + 1;
071            } else {
072                cropHeight = exactCropHeight.intValue();
073            }
074            ntx = maxTiles;
075            nty = input.getHeight() / cropHeight;
076            if ((input.getHeight() % cropHeight) > 0) {
077                nty += 1;
078            }
079        } else {
080            cropHeight = input.getHeight() / maxTiles;
081            if ((input.getHeight() % maxTiles) > 0) {
082                cropHeight += 1;
083            }
084            Double exactCropWidth = (new Double(tileWidth)) / tileHeight * cropHeight;
085            if (exactCropWidth - exactCropWidth.intValue() > 0) {
086                cropWidth = exactCropWidth.intValue() + 1;
087            } else {
088                cropWidth = exactCropWidth.intValue();
089            }
090            nty = maxTiles;
091            ntx = input.getWidth() / cropWidth;
092            if ((input.getWidth() % cropWidth) > 0) {
093                ntx += 1;
094            }
095        }
096
097        startX = xCenter * cropWidth;
098        startY = yCenter * cropHeight;
099
100        double widthRatio = new Double(tileWidth) / cropWidth;
101        double heightRatio = new Double(tileHeight) / cropHeight;
102
103        if (xCenter == ntx - 1) {
104            cropWidth = input.getWidth() - (xCenter) * cropWidth;
105            tileWidth = (int) Math.round(cropWidth * widthRatio);
106        }
107        if (yCenter == nty - 1) {
108            cropHeight = input.getHeight() - (yCenter) * cropHeight;
109            tileHeight = (int) Math.round(cropHeight * heightRatio);
110        }
111
112        int[] result = { startX, startY, cropWidth, cropHeight, ntx, nty, tileWidth, tileHeight };
113
114        return result;
115    }
116
117    public PictureTiles getTilesFromFile(ImageInfo input, String outputDirPath, int tileWidth, int tileHeight,
118            int maxTiles, int xCenter, int yCenter, long lastModificationTime, boolean fullGeneration)
119            {
120
121        int[] cropCoords = computeCropCoords(input, maxTiles, tileWidth, tileHeight, xCenter, yCenter);
122
123        String fileName = StringMaker.getTileFileName(xCenter, yCenter, lastModificationTime);
124        String outputFilePath = new Path(outputDirPath).append(fileName).toString();
125
126        try {
127            String mapComponents = null;
128            if (CMYK_MAP_COMPONENTS.equalsIgnoreCase(input.getColorSpace())) {
129                mapComponents = CMYK_MAP_COMPONENTS;
130            }
131
132            ImageCropperAndResizer.cropAndResize(input.getFilePath(), outputFilePath, cropCoords[2], cropCoords[3],
133                    cropCoords[0], cropCoords[1], cropCoords[6], cropCoords[7], mapComponents);
134        } catch (CommandNotAvailable | CommandException e) {
135            throw new NuxeoException(e);
136        }
137
138        Map<String, String> infoMap = new HashMap<String, String>();
139        infoMap.put(PictureTilesImpl.TILE_OUTPUT_DIR_KEY, outputDirPath);
140        infoMap.put(PictureTilesImpl.X_TILES_KEY, Integer.toString(cropCoords[4]));
141        infoMap.put(PictureTilesImpl.Y_TILES_KEY, Integer.toString(cropCoords[5]));
142
143        return new PictureTilesImpl(infoMap);
144    }
145
146}