Search in sources :

Example 1 with ExifOrientation

use of net.pms.image.ExifOrientation in project UniversalMediaServer by UniversalMediaServer.

the class DCRaw method getThumbnail.

/**
 * Extracts or generates a thumbnail for {@code fileName}.
 *
 * @param params the {@link OutputParams} to use. Can be {@code null}.
 * @param fileName the path of the image file to process.
 * @param imageInfo the {@link ImageInfo} for the image file.
 * @return A byte array containing the thumbnail or {@code null}.
 * @throws IOException if an IO error occurs.
 */
@Override
public byte[] getThumbnail(OutputParams params, String fileName, ImageInfo imageInfo) {
    boolean trace = LOGGER.isTraceEnabled();
    if (trace) {
        LOGGER.trace("Extracting thumbnail from \"{}\" with DCRaw", fileName);
    }
    if (params == null) {
        params = new OutputParams(PMS.getConfiguration());
    }
    // Use device-specific pms conf
    PmsConfiguration configuration = PMS.getConfiguration(params);
    params.log = false;
    // This is a wild guess at a decent buffer size for an embedded thumbnail.
    // Every time the buffer has to grow, the whole buffer must be copied in memory.
    params.outputByteArrayStreamBufferSize = 150000;
    // First try to get the embedded thumbnail
    String[] cmdArray = new String[6];
    cmdArray[0] = configuration.getDCRawPath();
    cmdArray[1] = "-e";
    cmdArray[2] = "-c";
    cmdArray[3] = "-M";
    cmdArray[4] = "-w";
    cmdArray[5] = fileName;
    ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, true, params, false, true);
    pw.runInSameThread();
    byte[] bytes = pw.getOutputByteArray().toByteArray();
    List<String> results = pw.getResults();
    if (bytes.length > 0) {
        // DCRaw doesn't seem to apply Exif Orientation to embedded thumbnails, handle it
        boolean isJPEG = (bytes[0] & 0xFF) == 0xFF && (bytes[1] & 0xFF) == 0xD8;
        ExifOrientation thumbnailOrientation = null;
        Dimension jpegResolution = null;
        int exifOrientationOffset = -1;
        if (isJPEG) {
            try {
                ByteArrayReader reader = new ByteArrayReader(bytes);
                exifOrientationOffset = ImagesUtil.getJPEGExifIFDTagOffset(0x112, reader);
                jpegResolution = ImagesUtil.getJPEGResolution(reader);
            } catch (IOException e) {
                exifOrientationOffset = -1;
                LOGGER.debug("Unexpected error while trying to find Exif orientation offset in embedded thumbnail for \"{}\": {}", fileName, e.getMessage());
                LOGGER.trace("", e);
            }
            if (exifOrientationOffset > 0) {
                thumbnailOrientation = ExifOrientation.typeOf(bytes[exifOrientationOffset]);
            } else {
                LOGGER.debug("Couldn't find Exif orientation in the thumbnail extracted from \"{}\"", fileName);
            }
        }
        ExifOrientation imageOrientation = imageInfo instanceof ExifInfo ? ((ExifInfo) imageInfo).getOriginalExifOrientation() : null;
        if (imageOrientation != null && imageOrientation != thumbnailOrientation) {
            if (thumbnailOrientation != null) {
                if (imageInfo.getWidth() > 0 && imageInfo.getHeight() > 0 && jpegResolution != null && jpegResolution.getWidth() > 0 && jpegResolution.getHeight() > 0) {
                    // Try to determine which orientation to trust
                    double imageAspect, thumbnailAspect;
                    if (ImagesUtil.isExifAxesSwapNeeded(imageOrientation)) {
                        imageAspect = (double) imageInfo.getHeight() / imageInfo.getWidth();
                    } else {
                        imageAspect = (double) imageInfo.getWidth() / imageInfo.getHeight();
                    }
                    if (ImagesUtil.isExifAxesSwapNeeded(thumbnailOrientation)) {
                        thumbnailAspect = (double) jpegResolution.getHeight() / jpegResolution.getWidth();
                    } else {
                        thumbnailAspect = (double) jpegResolution.getWidth() / jpegResolution.getHeight();
                    }
                    if (Math.abs(imageAspect - thumbnailAspect) > 0.001d) {
                        // The image and the thumbnail seems to have different aspect ratios, use that of the image
                        bytes[exifOrientationOffset] = (byte) imageOrientation.getValue();
                    }
                }
            } else if (imageOrientation != ExifOrientation.TOP_LEFT) {
                // Apply the orientation to the thumbnail
                try {
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    Thumbnails.of(new ByteArrayInputStream(bytes)).scale(1.0d).addFilter(ExifFilterUtils.getFilterForOrientation(imageOrientation.getThumbnailatorOrientation())).outputFormat(// PNG here to avoid further degradation from rotation
                    "PNG").outputQuality(1.0f).toOutputStream(outputStream);
                    bytes = outputStream.toByteArray();
                } catch (IOException e) {
                    LOGGER.error("Unexpected error when trying to rotate thumbnail for \"{}\" - cancelling rotation: {}", fileName, e.getMessage());
                    LOGGER.trace("", e);
                }
            }
        }
    }
    if (bytes.length == 0 || !results.isEmpty() && results.get(0).contains("has no thumbnail")) {
        // No embedded thumbnail retrieved, generate thumbnail from the actual file
        if (trace) {
            LOGGER.trace("No embedded thumbnail found in \"{}\", " + "trying to generate thumbnail from the image itself", fileName);
        }
        params.outputByteArrayStreamBufferSize = imageInfo != null && imageInfo.getSize() != ImageInfo.SIZE_UNKNOWN ? (int) imageInfo.getSize() / 4 : 500000;
        cmdArray[1] = "-h";
        pw = new ProcessWrapperImpl(cmdArray, true, params);
        pw.runInSameThread();
        bytes = pw.getOutputByteArray().toByteArray();
    }
    if (trace && (bytes == null || bytes.length == 0)) {
        LOGGER.trace("Failed to generate thumbnail with DCRaw for image \"{}\"", fileName);
    }
    return bytes != null && bytes.length > 0 ? bytes : null;
}
Also used : Dimension(java.awt.Dimension) IOException(java.io.IOException) ByteArrayOutputStream(java.io.ByteArrayOutputStream) PmsConfiguration(net.pms.configuration.PmsConfiguration) ByteArrayInputStream(java.io.ByteArrayInputStream) ExifInfo(net.pms.image.ExifInfo) OutputParams(net.pms.io.OutputParams) ExifOrientation(net.pms.image.ExifOrientation) ByteArrayReader(com.drew.lang.ByteArrayReader) ProcessWrapperImpl(net.pms.io.ProcessWrapperImpl)

Aggregations

ByteArrayReader (com.drew.lang.ByteArrayReader)1 Dimension (java.awt.Dimension)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 ByteArrayOutputStream (java.io.ByteArrayOutputStream)1 IOException (java.io.IOException)1 PmsConfiguration (net.pms.configuration.PmsConfiguration)1 ExifInfo (net.pms.image.ExifInfo)1 ExifOrientation (net.pms.image.ExifOrientation)1 OutputParams (net.pms.io.OutputParams)1 ProcessWrapperImpl (net.pms.io.ProcessWrapperImpl)1