SpriteImageRenderer.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.Color;
import java.awt.image.BufferedImage;
import org.carrot2.labs.smartsprites.SmartSpritesParameters.PngDepth;
import org.carrot2.labs.smartsprites.SpriteImageDirective.SpriteImageFormat;
import org.carrot2.labs.smartsprites.message.Message.MessageLevel;
import org.carrot2.labs.smartsprites.message.Message.MessageType;
import org.carrot2.labs.smartsprites.message.MessageLog;
import org.carrot2.util.ColorQuantizer;
import org.carrot2.util.ColorQuantizer.ColorReductionInfo;
/**
* Applies color quantization to the merged sprite image if required.
*/
public class SpriteImageRenderer {
/** This builder's configuration. */
public final SmartSpritesParameters parameters;
/** This builder's message log. */
private final MessageLog messageLog;
/**
* Instantiates a new sprite image renderer.
*
* @param parameters
* the parameters
* @param messageLog
* the message log
*/
SpriteImageRenderer(SmartSpritesParameters parameters, MessageLog messageLog) {
this.parameters = parameters;
this.messageLog = messageLog;
}
/**
* If needed, quantizes the image.
*
* @param spriteImage
* the sprite image
*
* @return the buffered image[]
*/
BufferedImage[] render(SpriteImage spriteImage) {
final BufferedImage sprite = spriteImage.sprite;
final SpriteImageDirective spriteImageDirective = spriteImage.spriteImageOccurrence.spriteImageDirective;
final boolean isPng = spriteImageDirective.format == SpriteImageFormat.PNG;
final boolean isJpg = spriteImageDirective.format == SpriteImageFormat.JPG;
final boolean isPngAuto = isPng && parameters.getSpritePngDepth() == PngDepth.AUTO;
final boolean isPngDirect = isPng && parameters.getSpritePngDepth() == PngDepth.DIRECT;
final ColorReductionInfo colorReductionInfo = ColorQuantizer.getColorReductionInfo(sprite);
final boolean canReduceWithoutQualityLoss = colorReductionInfo.canReduceWithoutQualityLoss();
final BufferedImage[] result = new BufferedImage[2];
if (isPngDirect || (isPngAuto && !canReduceWithoutQualityLoss) || isJpg) {
result[0] = sprite;
if (spriteImageDirective.matteColor != null) {
// Can't or no need to handle indexed color
messageLog.warning(MessageType.IGNORING_MATTE_COLOR_NO_SUPPORT, spriteImageDirective.spriteId);
}
return result;
}
if (canReduceWithoutQualityLoss) {
// Can perform reduction to indexed color without data loss
if (spriteImageDirective.matteColor != null) {
messageLog.warning(MessageType.IGNORING_MATTE_COLOR_NO_PARTIAL_TRANSPARENCY,
spriteImageDirective.spriteId);
}
result[0] = ColorQuantizer.reduce(sprite);
} else {
result[0] = quantize(sprite, spriteImage, colorReductionInfo, MessageLevel.WARN);
}
return result;
}
/**
* Performs quantization, logs the appropriate messages if needed.
*
* @param sprite
* the sprite
* @param spriteImage
* the sprite image
* @param colorReductionInfo
* the color reduction info
* @param logLevel
* the log level
*
* @return the buffered image
*/
private BufferedImage quantize(BufferedImage sprite, SpriteImage spriteImage,
final ColorReductionInfo colorReductionInfo, MessageLevel logLevel) {
final SpriteImageDirective spriteImageDirective = spriteImage.spriteImageOccurrence.spriteImageDirective;
// Need to quantize
if (colorReductionInfo.hasPartialTransparency) {
messageLog.log(logLevel, MessageType.ALPHA_CHANNEL_LOSS_IN_INDEXED_COLOR, spriteImageDirective.spriteId);
} else {
messageLog.log(logLevel, MessageType.TOO_MANY_COLORS_FOR_INDEXED_COLOR, spriteImageDirective.spriteId,
colorReductionInfo.distinctColors, ColorQuantizer.MAX_INDEXED_COLORS);
}
final Color matte;
if (spriteImageDirective.matteColor != null) {
matte = spriteImageDirective.matteColor;
} else {
if (colorReductionInfo.hasPartialTransparency) {
messageLog.log(logLevel, MessageType.USING_WHITE_MATTE_COLOR_AS_DEFAULT, spriteImageDirective.spriteId);
}
matte = Color.WHITE;
}
return ColorQuantizer.quantize(sprite, matte);
}
}