SpriteReferenceOccurrence.java

/*
 * SmartSprites Project
 *
 * Copyright (C) 2007-2009, Stanisław Osiński.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of  source code must  retain the above  copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following  disclaimer in  the documentation  and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of the SmartSprites Project nor the names of its contributors
 *   may  be used  to endorse  or  promote  products derived   from  this  software
 *   without specific prior written permission.
 *
 * - We kindly request that you include in the end-user documentation provided with
 *   the redistribution and/or in the software itself an acknowledgement equivalent
 *   to  the  following: "This product includes software developed by the SmartSprites
 *   Project."
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  AND
 * ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED  TO, THE IMPLIED
 * WARRANTIES  OF  MERCHANTABILITY  AND  FITNESS  FOR  A  PARTICULAR  PURPOSE   ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE  FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL,  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  DAMAGES
 * (INCLUDING, BUT  NOT LIMITED  TO, PROCUREMENT  OF SUBSTITUTE  GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  ON
 * ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT  LIABILITY,  OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY  OUT OF THE USE  OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.carrot2.labs.smartsprites;

import java.awt.image.BufferedImage;

import org.carrot2.labs.smartsprites.SpriteImageDirective.SpriteImageLayout;
import org.carrot2.labs.smartsprites.SpriteLayoutProperties.SpriteAlignment;
import org.carrot2.util.BufferedImageUtils;

/**
 * Represents an occurrence of a {@link SpriteReferenceDirective} in a specific CSS file.
 */
public class SpriteReferenceOccurrence extends SpriteDirectiveOccurrence {

    /** The directive. */
    public final SpriteReferenceDirective spriteReferenceDirective;

    /** CSS file relative path to the individual image to be added to a sprite. */
    public final String imagePath;

    /** Indicates whether the original css property has been marked as important. */
    public final boolean important;

    /**
     * Instantiates a new sprite reference occurrence.
     *
     * @param spriteReferenceDirective
     *            the sprite reference directive
     * @param imageFile
     *            the image file
     * @param cssFile
     *            the css file
     * @param line
     *            the line
     * @param important
     *            the important
     */
    public SpriteReferenceOccurrence(SpriteReferenceDirective spriteReferenceDirective, String imageFile,
            String cssFile, int line, boolean important) {
        super(cssFile, line);
        this.spriteReferenceDirective = spriteReferenceDirective;
        this.imagePath = imageFile;
        this.important = important;
    }

    /**
     * Computes the minimum width the individual image will need when rendering.
     *
     * @param image
     *            the image
     * @param layout
     *            the layout
     *
     * @return the required width
     */
    public int getRequiredWidth(BufferedImage image, SpriteImageLayout layout) {
        if (SpriteAlignment.REPEAT.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)
                && SpriteImageLayout.VERTICAL.equals(layout)) {
            // Ignoring left/right margins on repeated
            // images in vertically stacked sprites
            return image.getWidth();
        }
        return image.getWidth() + spriteReferenceDirective.spriteLayoutProperties.marginLeft
                + spriteReferenceDirective.spriteLayoutProperties.marginRight;
    }

    /**
     * Computes the minimum height the individual image will need when rendering.
     *
     * @param image
     *            the image
     * @param layout
     *            the layout
     *
     * @return the required height
     */
    public int getRequiredHeight(BufferedImage image, SpriteImageLayout layout) {
        if (SpriteAlignment.REPEAT.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)
                && SpriteImageLayout.HORIZONTAL.equals(layout)) {
            // Ignoring top/bottom margins on repeated
            // images in horizontally lined sprites
            return image.getHeight();
        }
        return image.getHeight() + spriteReferenceDirective.spriteLayoutProperties.marginTop
                + spriteReferenceDirective.spriteLayoutProperties.marginBottom;
    }

    /**
     * Renders the individual image, including margins and repeats if any.
     *
     * @param image
     *            the individual image as read from the file
     * @param layout
     *            the layout the enclosing sprite
     * @param dimension
     *            height/width of a horizontal/vertical sprite
     *
     * @return the rendered individual image
     */
    public BufferedImage render(BufferedImage image, SpriteImageLayout layout, int dimension) {
        final BufferedImage rendered;
        if (SpriteImageLayout.VERTICAL.equals(layout)) {
            rendered = new BufferedImage(dimension, getRequiredHeight(image, layout), BufferedImage.TYPE_4BYTE_ABGR);

            if (SpriteAlignment.LEFT.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                BufferedImageUtils.drawImage(image, rendered,
                        spriteReferenceDirective.spriteLayoutProperties.marginLeft,
                        spriteReferenceDirective.spriteLayoutProperties.marginTop);
            } else if (SpriteAlignment.RIGHT.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                BufferedImageUtils.drawImage(image, rendered,
                        dimension - spriteReferenceDirective.spriteLayoutProperties.marginRight - image.getWidth(),
                        spriteReferenceDirective.spriteLayoutProperties.marginTop);
            } else if (SpriteAlignment.CENTER.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                BufferedImageUtils.drawImage(image, rendered, (rendered.getWidth() - image.getWidth()) / 2,
                        spriteReferenceDirective.spriteLayoutProperties.marginTop);
            } else {
                // Repeat, ignoring margin-left and margin-right
                for (int x = 0; x < dimension; x += image.getWidth()) {
                    BufferedImageUtils.drawImage(image, rendered, x,
                            spriteReferenceDirective.spriteLayoutProperties.marginTop);
                }
            }
        } else {
            rendered = new BufferedImage(getRequiredWidth(image, layout), dimension, BufferedImage.TYPE_4BYTE_ABGR);

            if (SpriteAlignment.TOP.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                BufferedImageUtils.drawImage(image, rendered,
                        spriteReferenceDirective.spriteLayoutProperties.marginLeft,
                        spriteReferenceDirective.spriteLayoutProperties.marginTop);
            } else if (SpriteAlignment.BOTTOM.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                BufferedImageUtils.drawImage(image, rendered,
                        spriteReferenceDirective.spriteLayoutProperties.marginLeft,
                        dimension - spriteReferenceDirective.spriteLayoutProperties.marginBottom - image.getHeight());
            } else if (SpriteAlignment.CENTER.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                BufferedImageUtils.drawImage(image, rendered,
                        spriteReferenceDirective.spriteLayoutProperties.marginLeft,
                        (rendered.getHeight() - image.getHeight()) / 2);
            } else {
                // Repeat, ignoring margin-top and margin-bottom
                for (int y = 0; y < dimension; y += image.getHeight()) {
                    BufferedImageUtils.drawImage(image, rendered,
                            spriteReferenceDirective.spriteLayoutProperties.marginLeft, y);
                }
            }
        }
        return rendered;
    }

    /**
     * Returns the {@link SpriteReferenceReplacement} corresponding to the occurrence, taking into account the layout
     * the the enclosing sprite and the offset at which the individual image was rendered.
     *
     * @param layout
     *            the layout
     * @param offset
     *            the offset
     *
     * @return the sprite reference replacement
     */
    public SpriteReferenceReplacement buildReplacement(SpriteImageLayout layout, int offset) {
        if (SpriteImageLayout.VERTICAL.equals(layout)) {
            String horizontalPosition;
            if (SpriteAlignment.RIGHT.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                horizontalPosition = "right";
            } else if (SpriteAlignment.CENTER.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
                horizontalPosition = "center";
            } else {
                horizontalPosition = "left";
            }

            return new SpriteReferenceReplacement(this, offset, horizontalPosition);
        }
        String verticalPosition;
        if (SpriteAlignment.BOTTOM.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
            verticalPosition = "bottom";
        } else if (SpriteAlignment.CENTER.equals(spriteReferenceDirective.spriteLayoutProperties.alignment)) {
            verticalPosition = "center";
        } else {
            verticalPosition = "top";
        }
        return new SpriteReferenceReplacement(this, verticalPosition, offset);
    }
}