[8132] | 1 | /*
|
---|
[15217] | 2 | * Copyright 2002-2019 Drew Noakes and contributors
|
---|
[8132] | 3 | *
|
---|
| 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
| 5 | * you may not use this file except in compliance with the License.
|
---|
| 6 | * You may obtain a copy of the License at
|
---|
| 7 | *
|
---|
| 8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
| 9 | *
|
---|
| 10 | * Unless required by applicable law or agreed to in writing, software
|
---|
| 11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
| 13 | * See the License for the specific language governing permissions and
|
---|
| 14 | * limitations under the License.
|
---|
| 15 | *
|
---|
| 16 | * More information about this project is available at:
|
---|
| 17 | *
|
---|
| 18 | * https://drewnoakes.com/code/exif/
|
---|
| 19 | * https://github.com/drewnoakes/metadata-extractor
|
---|
| 20 | */
|
---|
| 21 | package com.drew.metadata.jpeg;
|
---|
| 22 |
|
---|
| 23 | import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
|
---|
| 24 | import com.drew.imaging.jpeg.JpegSegmentType;
|
---|
| 25 | import com.drew.lang.SequentialByteArrayReader;
|
---|
| 26 | import com.drew.lang.SequentialReader;
|
---|
| 27 | import com.drew.lang.annotations.NotNull;
|
---|
| 28 | import com.drew.metadata.Metadata;
|
---|
| 29 |
|
---|
| 30 | import java.io.IOException;
|
---|
| 31 | import java.util.Arrays;
|
---|
| 32 |
|
---|
| 33 | /**
|
---|
| 34 | * Decodes JPEG SOFn data, populating a {@link Metadata} object with tag values in a {@link JpegDirectory}.
|
---|
| 35 | *
|
---|
| 36 | * @author Drew Noakes https://drewnoakes.com
|
---|
| 37 | * @author Darrell Silver http://www.darrellsilver.com
|
---|
| 38 | */
|
---|
| 39 | public class JpegReader implements JpegSegmentMetadataReader
|
---|
| 40 | {
|
---|
| 41 | @NotNull
|
---|
| 42 | public Iterable<JpegSegmentType> getSegmentTypes()
|
---|
| 43 | {
|
---|
| 44 | // NOTE that some SOFn values do not exist
|
---|
| 45 | return Arrays.asList(
|
---|
| 46 | JpegSegmentType.SOF0,
|
---|
| 47 | JpegSegmentType.SOF1,
|
---|
| 48 | JpegSegmentType.SOF2,
|
---|
| 49 | JpegSegmentType.SOF3,
|
---|
| 50 | // JpegSegmentType.SOF4,
|
---|
| 51 | JpegSegmentType.SOF5,
|
---|
| 52 | JpegSegmentType.SOF6,
|
---|
| 53 | JpegSegmentType.SOF7,
|
---|
[13061] | 54 | // JpegSegmentType.JPG,
|
---|
[8132] | 55 | JpegSegmentType.SOF9,
|
---|
| 56 | JpegSegmentType.SOF10,
|
---|
| 57 | JpegSegmentType.SOF11,
|
---|
| 58 | // JpegSegmentType.SOF12,
|
---|
| 59 | JpegSegmentType.SOF13,
|
---|
| 60 | JpegSegmentType.SOF14,
|
---|
| 61 | JpegSegmentType.SOF15
|
---|
| 62 | );
|
---|
| 63 | }
|
---|
| 64 |
|
---|
[8243] | 65 | public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
|
---|
[8132] | 66 | {
|
---|
[8243] | 67 | for (byte[] segmentBytes : segments) {
|
---|
| 68 | extract(segmentBytes, metadata, segmentType);
|
---|
| 69 | }
|
---|
[8132] | 70 | }
|
---|
| 71 |
|
---|
[8243] | 72 | public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType)
|
---|
[8132] | 73 | {
|
---|
[8243] | 74 | JpegDirectory directory = new JpegDirectory();
|
---|
| 75 | metadata.addDirectory(directory);
|
---|
[8132] | 76 |
|
---|
| 77 | // The value of TAG_COMPRESSION_TYPE is determined by the segment type found
|
---|
| 78 | directory.setInt(JpegDirectory.TAG_COMPRESSION_TYPE, segmentType.byteValue - JpegSegmentType.SOF0.byteValue);
|
---|
| 79 |
|
---|
| 80 | SequentialReader reader = new SequentialByteArrayReader(segmentBytes);
|
---|
| 81 |
|
---|
| 82 | try {
|
---|
| 83 | directory.setInt(JpegDirectory.TAG_DATA_PRECISION, reader.getUInt8());
|
---|
| 84 | directory.setInt(JpegDirectory.TAG_IMAGE_HEIGHT, reader.getUInt16());
|
---|
| 85 | directory.setInt(JpegDirectory.TAG_IMAGE_WIDTH, reader.getUInt16());
|
---|
| 86 | short componentCount = reader.getUInt8();
|
---|
| 87 | directory.setInt(JpegDirectory.TAG_NUMBER_OF_COMPONENTS, componentCount);
|
---|
| 88 |
|
---|
| 89 | // for each component, there are three bytes of data:
|
---|
| 90 | // 1 - Component ID: 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
|
---|
| 91 | // 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal
|
---|
| 92 | // 3 - Quantization table number
|
---|
| 93 | for (int i = 0; i < (int)componentCount; i++) {
|
---|
| 94 | final int componentId = reader.getUInt8();
|
---|
| 95 | final int samplingFactorByte = reader.getUInt8();
|
---|
| 96 | final int quantizationTableNumber = reader.getUInt8();
|
---|
| 97 | final JpegComponent component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber);
|
---|
| 98 | directory.setObject(JpegDirectory.TAG_COMPONENT_DATA_1 + i, component);
|
---|
| 99 | }
|
---|
| 100 | } catch (IOException ex) {
|
---|
| 101 | directory.addError(ex.getMessage());
|
---|
| 102 | }
|
---|
| 103 | }
|
---|
| 104 | }
|
---|