/* * Universal Media Server, for streaming any media to DLNA * compatible renderers based on the http://www.ps3mediaserver.org. * Copyright (C) 2012 UMS developers. * * This program is a free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.pms.dlna; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.io.IOException; import java.io.InputStream; import javax.imageio.ImageIO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.drew.metadata.Metadata; import net.pms.image.Image; import net.pms.image.ImageFormat; import net.pms.image.ImageInfo; import net.pms.image.ImagesUtil; import net.pms.image.ImagesUtil.ScaleType; import net.pms.util.ParseException; /** * This class is simply a byte array for holding an {@link ImageIO} supported * image with some additional metadata restricted to the DLNA image media * format profiles {@code JPEG_*} and {@code PNG_*}. * * @see DLNAThumbnailInputStream * * @author Nadahar */ public class DLNAThumbnail extends DLNAImage { private static final Logger LOGGER = LoggerFactory.getLogger(DLNAImage.class); /* * Please note: This class is packed and stored in the database. Any changes * to the data structure (fields) will invalidate any instances already * stored, and will require a wipe of all rows with a stored instance. The * serialVersionUID value below should also be bumped. */ private static final long serialVersionUID = 1L; /** * Creates a new {@link DLNAThumbnail} instance. * * @param image the source {@link Image} in either JPEG or PNG format * adhering to the DLNA restrictions for color space and * compression. * @param profile the {@link DLNAImageProfile} this {@link DLNAImage} * adheres to. * @param copy whether this instance should be copied or shared. * @throws DLNAProfileException if the profile compliance check fails. */ public DLNAThumbnail( Image image, DLNAImageProfile profile, boolean copy ) throws DLNAProfileException { super(image, profile, copy); } /** * Creates a new {@link DLNAThumbnail} instance. * * @param bytes the source image in either JPEG or PNG format adhering to * the DLNA restrictions for color space and compression. * @param imageInfo the {@link ImageInfo} to store with this * {@link DLNAThumbnail}. * @param profile the {@link DLNAImageProfile} this {@link DLNAImage} * adheres to. * @param copy whether this instance should be copied or shared. * @throws DLNAProfileException if the profile compliance check fails. */ public DLNAThumbnail( byte[] bytes, ImageInfo imageInfo, DLNAImageProfile profile, boolean copy ) throws DLNAProfileException { super(bytes, imageInfo, profile, copy); } /** * Creates a new {@link DLNAThumbnail} instance. * * @param bytes the source image in either JPEG or PNG format adhering to * the DLNA restrictions for color space and compression. * @param width the width of the image. * @param height the height of the image. * @param format the {@link ImageFormat} of the image. * @param colorModel the {@link ColorModel} of the image. * @param metadata the {@link Metadata} instance describing the image. * @param profile the {@link DLNAImageProfile} this {@link DLNAImage} * adheres to. * @param copy whether this instance should be copied or shared. * @throws DLNAProfileException if the profile compliance check fails. * @throws ParseException if {@code format} is {@code null} and parsing the * format from {@code metadata} fails. */ public DLNAThumbnail( byte[] bytes, int width, int height, ImageFormat format, ColorModel colorModel, Metadata metadata, DLNAImageProfile profile, boolean copy ) throws DLNAProfileException, ParseException { super(bytes, width, height, format, colorModel, metadata, profile, copy); } /** * Creates a new {@link DLNAThumbnail} instance. * * @param bytes the source image in either JPEG or PNG format adhering to * the DLNA restrictions for color space and compression. * @param format the {@link ImageFormat} of the image. * @param bufferedImage the {@link BufferedImage} to get non- * {@link Metadata} metadata from. * @param metadata the {@link Metadata} instance describing the image. * @param profile the {@link DLNAImageProfile} this {@link DLNAImage} * adheres to. * @param copy whether this instance should be copied or shared. * @throws DLNAProfileException if the profile compliance check fails. * @throws ParseException if {@code format} is {@code null} and parsing the * format from {@code metadata} fails. */ public DLNAThumbnail( byte[] bytes, ImageFormat format, BufferedImage bufferedImage, Metadata metadata, DLNAImageProfile profile, boolean copy ) throws DLNAProfileException, ParseException { super(bytes, format, bufferedImage, metadata, profile, copy); } /** * Converts an {@link Image} to a {@link DLNAThumbnail}. Output format will * be the same as the source if the source is either JPEG or PNG. Further * restrictions on color space and compression is imposed and conversion * done if necessary. All other formats will be converted to a DLNA * compliant JPEG. * * @param inputImage the source {@link Image}. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail(Image inputImage) throws IOException { return toThumbnail(inputImage, 0, 0, null, ImageFormat.SOURCE, false); } /** * Converts an image to a {@link DLNAThumbnail}. Format support is limited * to that of {@link ImageIO}. Output format will be the same as source if * the source is either JPEG or PNG. Further restrictions on color space and * compression is imposed and conversion done if necessary. All other * formats will be converted to a DLNA compliant JPEG. Preserves aspect * ratio and rotates/flips the image according to Exif orientation. * <p> * <b> This method consumes and closes {@code inputStream}. </b> * * @param inputStream the source image in a supported format. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail(InputStream inputStream) throws IOException { return toThumbnail(inputStream, 0, 0, null, ImageFormat.SOURCE, false); } /** * Converts an image to a {@link DLNAThumbnail}. Format support is limited * to that of {@link ImageIO}. Output format will be the same as source if * the source is either JPEG or PNG. Further restrictions on color space and * compression is imposed and conversion done if necessary. All other * formats will be converted to a DLNA compliant JPEG. Preserves aspect * ratio and rotates/flips the image according to Exif orientation. * * @param inputByteArray the source image in a supported format. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail(byte[] inputByteArray) throws IOException { return toThumbnail(inputByteArray, 0, 0, null, ImageFormat.SOURCE, false); } /** * Converts an {@link Image} to a {@link DLNAThumbnail} adhering to * {@code outputProfile}. {@code outputProfile} is limited to JPEG or PNG * profiles. If {@code outputProfile} is a GIF profile, the image will be * converted to {@link DLNAImageProfile#JPEG_LRG}. * * @param inputImage the source {@link Image}. * @param outputProfile the {@link DLNAImageProfile} to adhere to for the * output. * @param padToSize whether padding should be used if source aspect doesn't * match target aspect. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail( Image inputImage, DLNAImageProfile outputProfile, boolean padToSize ) throws IOException { if (inputImage == null) { return null; } return (DLNAThumbnail) ImagesUtil.transcodeImage( inputImage, outputProfile, true, padToSize ); } /** * Converts an image to a {@link DLNAThumbnail} adhering to * {@code outputProfile}. Format support is limited to that of * {@link ImageIO}. {@code outputProfile} is limited to JPEG or PNG * profiles. If {@code outputProfile} is a GIF profile, the image will be * converted to {@link DLNAImageProfile#JPEG_LRG}. Preserves aspect ratio * and rotates/flips the image according to Exif orientation. * * <p> * <b> This method consumes and closes {@code inputStream}. </b> * * @param inputStream the source image in a supported format. * @param outputProfile the {@link DLNAImageProfile} to adhere to for the * output. * @param padToSize whether padding should be used if source aspect doesn't * match target aspect. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail( InputStream inputStream, DLNAImageProfile outputProfile, boolean padToSize ) throws IOException { if (inputStream == null) { return null; } return (DLNAThumbnail) ImagesUtil.transcodeImage( inputStream, outputProfile, true, padToSize ); } /** * Converts an image to a {@link DLNAThumbnail} adhering to * {@code outputProfile}. Format support is limited to that of * {@link ImageIO}. {@code outputProfile} is limited to JPEG or PNG * profiles. If {@code outputProfile} is a GIF profile, the image will be * converted to {@link DLNAImageProfile#JPEG_LRG}. Preserves aspect ratio * and rotates/flips the image according to Exif orientation. * * @param inputByteArray the source image in a supported format. * @param outputProfile the {@link DLNAImageProfile} to adhere to for the * output. * @param padToSize whether padding should be used if source aspect doesn't * match target aspect. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail( byte[] inputByteArray, DLNAImageProfile outputProfile, boolean padToSize ) throws IOException { if (inputByteArray == null) { return null; } return (DLNAThumbnail) ImagesUtil.transcodeImage( inputByteArray, outputProfile, true, padToSize ); } /** * Converts an {@link Image} to a {@link DLNAThumbnail}. Format support is * limited to that of {@link ImageIO}. {@code outputFormat} is limited to * JPEG or PNG format adhering to the DLNA restrictions for color space and * compression. If {@code outputFormat} doesn't qualify, the image will be * converted to a DLNA compliant JPEG. * * @param inputImage the source {@link Image}. * @param width the new width or 0 to disable scaling. * @param height the new height or 0 to disable scaling. * @param scaleType the {@link ScaleType} to use when scaling. * @param outputFormat the {@link ImageFormat} to generate or * {@link ImageFormat#SOURCE} to preserve source format. * @param padToSize whether padding should be used if source aspect doesn't * match target aspect. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail( Image inputImage, int width, int height, ScaleType scaleType, ImageFormat outputFormat, boolean padToSize ) throws IOException { if (inputImage == null) { return null; } return (DLNAThumbnail) ImagesUtil.transcodeImage( inputImage, width, height, scaleType, outputFormat, true, true, padToSize ); } /** * Converts an image to a {@link DLNAThumbnail}. Format support is limited * to that of {@link ImageIO}. {@code outputFormat} is limited to JPEG or * PNG format adhering to the DLNA restrictions for color space and * compression. If {@code outputFormat} doesn't qualify, the image will be * converted to a DLNA compliant JPEG. Preserves aspect ratio and * rotates/flips the image according to Exif orientation. * <p> * <b> This method consumes and closes {@code inputStream}. </b> * * @param inputStream the source image in a supported format. * @param width the new width or 0 to disable scaling. * @param height the new height or 0 to disable scaling. * @param scaleType the {@link ScaleType} to use when scaling. * @param outputFormat the {@link ImageFormat} to generate or * {@link ImageFormat#SOURCE} to preserve source format. * @param padToSize whether padding should be used if source aspect doesn't * match target aspect. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail( InputStream inputStream, int width, int height, ScaleType scaleType, ImageFormat outputFormat, boolean padToSize ) throws IOException { if (inputStream == null) { return null; } return (DLNAThumbnail) ImagesUtil.transcodeImage( inputStream, width, height, scaleType, outputFormat, true, true, padToSize ); } /** * Converts an image to a {@link DLNAThumbnail}. Format support is limited * to that of {@link ImageIO}. {@code outputFormat} is limited to JPEG or * PNG format adhering to the DLNA restrictions for color space and * compression. If {@code outputFormat} doesn't qualify, the image will be * converted to a DLNA compliant JPEG. Preserves aspect ratio and * rotates/flips the image according to Exif orientation. * * @param inputByteArray the source image in a supported format. * @param width the new width or 0 to disable scaling. * @param height the new height or 0 to disable scaling. * @param scaleType the {@link ScaleType} to use when scaling. * @param outputFormat the {@link ImageFormat} to generate or * {@link ImageFormat#SOURCE} to preserve source format. * @param padToSize whether padding should be used if source aspect doesn't * match target aspect. * @return The populated {@link DLNAThumbnail} or {@code null} if the source * image is {@code null}. * @throws IOException if the operation fails. */ public static DLNAThumbnail toThumbnail( byte[] inputByteArray, int width, int height, ScaleType scaleType, ImageFormat outputFormat, boolean padToSize) throws IOException { return (DLNAThumbnail) ImagesUtil.transcodeImage( inputByteArray, width, height, scaleType, outputFormat, true, true, padToSize ); } /** * Converts and scales the thumbnail according to the given * {@link DLNAImageProfile}. Preserves aspect ratio. Format support is * limited to that of {@link ImageIO}. * * @param outputProfile the {@link DLNAImageProfile} to adhere to for the output. * @param padToSize Whether padding should be used if source aspect doesn't * match target aspect. * @return The scaled and/or converted thumbnail, {@code null} if the * source is {@code null}. * @exception IOException if the operation fails. */ public DLNAThumbnail transcode( DLNAImageProfile outputProfile, boolean padToSize ) throws IOException { return (DLNAThumbnail) ImagesUtil.transcodeImage( this, outputProfile, true, padToSize); } @Override public DLNAThumbnail copy() { try { return new DLNAThumbnail(bytes, imageInfo, profile, true); } catch (DLNAProfileException e) { // Should be impossible LOGGER.error("Impossible situation in DLNAImage.copy(): {}", e.getMessage()); LOGGER.trace("", e); return null; } } }