All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.drew.imaging.jpeg.JpegSegmentReader Maven / Gradle / Ivy

Go to download

Java library for extracting EXIF, IPTC, XMP, ICC and other metadata from image and video files.

There is a newer version: 2.19.0
Show newest version
/*
 * Copyright 2002-2015 Drew Noakes
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 * More information about this project is available at:
 *
 *    https://drewnoakes.com/code/exif/
 *    https://github.com/drewnoakes/metadata-extractor
 */
package com.drew.imaging.jpeg;

import com.drew.lang.SequentialReader;
import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.lang.annotations.Nullable;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * Performs read functions of JPEG files, returning specific file segments.
 * 

* JPEG files are composed of a sequence of consecutive JPEG 'segments'. Each is identified by one of a set of byte * values, modelled in the {@link JpegSegmentType} enumeration. Use readSegments to read out the some * or all segments into a {@link JpegSegmentData} object, from which the raw JPEG segment byte arrays may be accessed. * * @author Drew Noakes https://drewnoakes.com */ public class JpegSegmentReader { /** * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet). */ private static final byte SEGMENT_SOS = (byte) 0xDA; /** * Private, because one wouldn't search for it. */ private static final byte MARKER_EOI = (byte) 0xD9; /** * Processes the provided JPEG data, and extracts the specified JPEG segments into a {@link JpegSegmentData} object. *

* Will not return SOS (start of scan) or EOI (end of image) segments. * * @param file a {@link File} from which the JPEG data will be read. * @param segmentTypes the set of JPEG segments types that are to be returned. If this argument is null * then all found segment types are returned. */ @NotNull public static JpegSegmentData readSegments(@NotNull File file, @Nullable Iterable segmentTypes) throws JpegProcessingException, IOException { FileInputStream stream = null; try { stream = new FileInputStream(file); return readSegments(new StreamReader(stream), segmentTypes); } finally { if (stream != null) { stream.close(); } } } /** * Processes the provided JPEG data, and extracts the specified JPEG segments into a {@link JpegSegmentData} object. *

* Will not return SOS (start of scan) or EOI (end of image) segments. * * @param reader a {@link SequentialReader} from which the JPEG data will be read. It must be positioned at the * beginning of the JPEG data stream. * @param segmentTypes the set of JPEG segments types that are to be returned. If this argument is null * then all found segment types are returned. */ @NotNull public static JpegSegmentData readSegments(@NotNull final SequentialReader reader, @Nullable Iterable segmentTypes) throws JpegProcessingException, IOException { // Must be big-endian assert (reader.isMotorolaByteOrder()); // first two bytes should be JPEG magic number final int magicNumber = reader.getUInt16(); if (magicNumber != 0xFFD8) { throw new JpegProcessingException("JPEG data is expected to begin with 0xFFD8 (ÿØ) not 0x" + Integer.toHexString(magicNumber)); } Set segmentTypeBytes = null; if (segmentTypes != null) { segmentTypeBytes = new HashSet(); for (JpegSegmentType segmentType : segmentTypes) { segmentTypeBytes.add(segmentType.byteValue); } } JpegSegmentData segmentData = new JpegSegmentData(); do { // Find the segment marker. Markers are zero or more 0xFF bytes, followed // by a 0xFF and then a byte not equal to 0x00 or 0xFF. final short segmentIdentifier = reader.getUInt8(); // We must have at least one 0xFF byte if (segmentIdentifier != 0xFF) throw new JpegProcessingException("Expected JPEG segment start identifier 0xFF, not 0x" + Integer.toHexString(segmentIdentifier).toUpperCase()); // Read until we have a non-0xFF byte. This identifies the segment type. byte segmentType = reader.getInt8(); while (segmentType == (byte)0xFF) segmentType = reader.getInt8(); if (segmentType == 0) throw new JpegProcessingException("Expected non-zero byte as part of JPEG marker identifier"); if (segmentType == SEGMENT_SOS) { // The 'Start-Of-Scan' segment's length doesn't include the image data, instead would // have to search for the two bytes: 0xFF 0xD9 (EOI). // It comes last so simply return at this point return segmentData; } if (segmentType == MARKER_EOI) { // the 'End-Of-Image' segment -- this should never be found in this fashion return segmentData; } // next 2-bytes are : [high-byte] [low-byte] int segmentLength = reader.getUInt16(); // segment length includes size bytes, so subtract two segmentLength -= 2; if (segmentLength < 0) throw new JpegProcessingException("JPEG segment size would be less than zero"); // Check whether we are interested in this segment if (segmentTypeBytes == null || segmentTypeBytes.contains(segmentType)) { byte[] segmentBytes = reader.getBytes(segmentLength); assert (segmentLength == segmentBytes.length); segmentData.addSegment(segmentType, segmentBytes); } else { // Some if the JPEG is truncated, just return what data we've already gathered if (!reader.trySkip(segmentLength)) { return segmentData; } } } while (true); } private JpegSegmentReader() throws Exception { throw new Exception("Not intended for instantiation."); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy