mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 11:05:29 -04:00
TMI-18: Fix for images/thumbnails get inverted colors.
This commit is contained in:
parent
24db7e847c
commit
c16ffaca13
@ -71,6 +71,7 @@ import java.util.List;
|
||||
public class JPEGImageReader extends ImageReaderBase {
|
||||
// TODO: Fix the (stream) metadata inconsistency issues.
|
||||
// - Sun JPEGMetadata class does not (and can not be made to) support CMYK data.. We need to create all new metadata classes.. :-/
|
||||
// TODO: Split thumbnail reading into separate class
|
||||
|
||||
private final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.jpeg.debug"));
|
||||
|
||||
@ -306,7 +307,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
// NOTE: Reading the metadata here chokes on some images. Instead, parse the Adobe App14 segment and read transform directly
|
||||
|
||||
// TODO: If cmyk and no ICC profile, just use FastCMYKToRGB, without attempting loading Generic CMYK profile first?
|
||||
// TODO: Also, don't get generic CMYK if we already have a profile...
|
||||
// TODO: Don't get generic CMYK if we already have a CMYK profile...
|
||||
srcCs = ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK);
|
||||
imageTypes = Arrays.asList(
|
||||
ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR),
|
||||
@ -544,83 +545,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
|
||||
CompoundDirectory exifMetadata = (CompoundDirectory) new EXIFReader().read(stream);
|
||||
|
||||
if (exifMetadata.directoryCount() == 2) {
|
||||
Directory ifd1 = exifMetadata.getDirectory(1);
|
||||
Entry compression = ifd1.getEntryById(TIFF.TAG_COMPRESSION);
|
||||
if (compression != null && compression.getValue().equals(1)) {
|
||||
// Read ImageWidth, ImageLength (height) and BitsPerSample (=8 8 8, always)
|
||||
// PhotometricInterpretation (2=RGB, 6=YCbCr), SamplesPerPixel (=3, always),
|
||||
Entry width = ifd1.getEntryById(TIFF.TAG_IMAGE_WIDTH);
|
||||
Entry height = ifd1.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
|
||||
|
||||
if (width == null || height == null) {
|
||||
throw new IIOException("Missing dimensions for RAW EXIF thumbnail");
|
||||
}
|
||||
|
||||
Entry bitsPerSample = ifd1.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
|
||||
Entry samplesPerPixel = ifd1.getEntryById(TIFF.TAG_SAMPLES_PER_PIXELS);
|
||||
Entry photometricInterpretation = ifd1.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);
|
||||
|
||||
// Required
|
||||
int w = ((Number) width.getValue()).intValue();
|
||||
int h = ((Number) height.getValue()).intValue();
|
||||
|
||||
if (bitsPerSample != null) {
|
||||
int[] bpp = (int[]) bitsPerSample.getValue();
|
||||
if (!Arrays.equals(bpp, new int[]{8, 8, 8})) {
|
||||
throw new IIOException("Unknown bits per sample for RAW EXIF thumbnail: " + bitsPerSample.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
if (samplesPerPixel != null && (Integer) samplesPerPixel.getValue() != 3) {
|
||||
throw new IIOException("Unknown samples per pixel for RAW EXIF thumbnail: " + samplesPerPixel.getValueAsString());
|
||||
}
|
||||
|
||||
int interpretation = photometricInterpretation != null ? ((Number) photometricInterpretation.getValue()).intValue() : 2;
|
||||
|
||||
// IFD1 should contain strip offsets for uncompressed images
|
||||
Entry offset = ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS);
|
||||
if (offset != null) {
|
||||
stream.seek(((Number) offset.getValue()).longValue());
|
||||
|
||||
// Read raw image data, either RGB or YCbCr
|
||||
int thumbSize = w * h * 3;
|
||||
byte[] thumbData = readFully(stream, thumbSize);
|
||||
|
||||
switch (interpretation) {
|
||||
case 2:
|
||||
// RGB
|
||||
break;
|
||||
case 6:
|
||||
// YCbCr
|
||||
for (int i = 0, thumbDataLength = thumbData.length; i < thumbDataLength; i++) {
|
||||
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unknown photometric interpretation for RAW EXIF thumbnail: " + interpretation);
|
||||
}
|
||||
|
||||
thumbnails.add(readRawThumbnail(thumbData, thumbData.length, 0, w, h));
|
||||
}
|
||||
}
|
||||
else if (compression == null || compression.getValue().equals(6)) {
|
||||
Entry jpegOffset = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
|
||||
// IFD1 should contain jpeg offset for JPEG thumbnail
|
||||
if (jpegOffset != null) {
|
||||
stream.seek(((Number) jpegOffset.getValue()).longValue());
|
||||
InputStream adapter = IIOUtil.createStreamAdapter(stream);
|
||||
BufferedImage exifThumb = ImageIO.read(adapter);
|
||||
|
||||
if (exifThumb != null) {
|
||||
thumbnails.add(exifThumb);
|
||||
}
|
||||
|
||||
adapter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
extractEXIFThumbnails(stream, exifMetadata);
|
||||
|
||||
return exifMetadata;
|
||||
}
|
||||
@ -628,6 +553,102 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void extractEXIFThumbnails(ImageInputStream stream, CompoundDirectory exifMetadata) throws IOException {
|
||||
if (exifMetadata.directoryCount() == 2) {
|
||||
Directory ifd1 = exifMetadata.getDirectory(1);
|
||||
Entry compression = ifd1.getEntryById(TIFF.TAG_COMPRESSION);
|
||||
|
||||
if (compression != null && compression.getValue().equals(1)) { // 1 = no compression
|
||||
// Read ImageWidth, ImageLength (height) and BitsPerSample (=8 8 8, always)
|
||||
// PhotometricInterpretation (2=RGB, 6=YCbCr), SamplesPerPixel (=3, always),
|
||||
Entry width = ifd1.getEntryById(TIFF.TAG_IMAGE_WIDTH);
|
||||
Entry height = ifd1.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
|
||||
|
||||
if (width == null || height == null) {
|
||||
throw new IIOException("Missing dimensions for RAW EXIF thumbnail");
|
||||
}
|
||||
|
||||
Entry bitsPerSample = ifd1.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
|
||||
Entry samplesPerPixel = ifd1.getEntryById(TIFF.TAG_SAMPLES_PER_PIXELS);
|
||||
Entry photometricInterpretation = ifd1.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);
|
||||
|
||||
// Required
|
||||
int w = ((Number) width.getValue()).intValue();
|
||||
int h = ((Number) height.getValue()).intValue();
|
||||
|
||||
if (bitsPerSample != null) {
|
||||
int[] bpp = (int[]) bitsPerSample.getValue();
|
||||
if (!Arrays.equals(bpp, new int[] {8, 8, 8})) {
|
||||
throw new IIOException("Unknown bits per sample for RAW EXIF thumbnail: " + bitsPerSample.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
if (samplesPerPixel != null && (Integer) samplesPerPixel.getValue() != 3) {
|
||||
throw new IIOException("Unknown samples per pixel for RAW EXIF thumbnail: " + samplesPerPixel.getValueAsString());
|
||||
}
|
||||
|
||||
int interpretation = photometricInterpretation != null ? ((Number) photometricInterpretation.getValue()).intValue() : 2;
|
||||
|
||||
// IFD1 should contain strip offsets for uncompressed images
|
||||
Entry offset = ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS);
|
||||
if (offset != null) {
|
||||
stream.seek(((Number) offset.getValue()).longValue());
|
||||
|
||||
// Read raw image data, either RGB or YCbCr
|
||||
int thumbSize = w * h * 3;
|
||||
byte[] thumbData = readFully(stream, thumbSize);
|
||||
|
||||
switch (interpretation) {
|
||||
case 2:
|
||||
// RGB
|
||||
break;
|
||||
case 6:
|
||||
// YCbCr
|
||||
for (int i = 0, thumbDataLength = thumbData.length; i < thumbDataLength; i += 3) {
|
||||
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unknown photometric interpretation for RAW EXIF thumbnail: " + interpretation);
|
||||
}
|
||||
|
||||
thumbnails.add(readRawThumbnail(thumbData, thumbData.length, 0, w, h));
|
||||
}
|
||||
}
|
||||
else if (compression == null || compression.getValue().equals(6)) { // 6 = JPEG compression
|
||||
// IFD1 should contain JPEG offset for JPEG thumbnail
|
||||
Entry jpegOffset = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
|
||||
if (jpegOffset != null) {
|
||||
stream.seek(((Number) jpegOffset.getValue()).longValue());
|
||||
InputStream input = IIOUtil.createStreamAdapter(stream);
|
||||
|
||||
// For certain EXIF files (encoded with TIFF.TAG_YCBCR_POSITIONING = 2?), we need
|
||||
// EXIF information to read the thumbnail correctly (otherwise the colors are messed up).
|
||||
|
||||
// HACK: Splice empty EXIF information into the thumbnail stream
|
||||
byte[] fakeEmptyExif = {
|
||||
// SOI (from original data)
|
||||
(byte) input.read(), (byte) input.read(),
|
||||
// APP1 + len (016) + 'Exif' + 0-term + pad
|
||||
(byte) 0xFF, (byte) 0xE1, 0, 16, 'E', 'x', 'i', 'f', 0, 0,
|
||||
// Big-endian BOM (MM), TIFF magic (042), offset (0000)
|
||||
'M', 'M', 0, 42, 0, 0, 0, 0,
|
||||
};
|
||||
input = new SequenceInputStream(new ByteArrayInputStream(fakeEmptyExif), input);
|
||||
|
||||
BufferedImage exifThumb = ImageIO.read(input);
|
||||
|
||||
if (exifThumb != null) {
|
||||
thumbnails.add(exifThumb);
|
||||
}
|
||||
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<JPEGSegment> getAppSegments(final int marker, final String identifier) throws IOException {
|
||||
initHeader();
|
||||
|
||||
@ -1362,14 +1383,19 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
for (final String arg : args) {
|
||||
// File file = new File(args[0]);
|
||||
File file = new File(arg);
|
||||
|
||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
||||
if (input == null) {
|
||||
System.err.println("Could not read file: " + file);
|
||||
continue;
|
||||
}
|
||||
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||
|
||||
if (!readers.hasNext()) {
|
||||
System.err.println("No reader for: " + file);
|
||||
System.exit(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
ImageReader reader = readers.next();
|
||||
@ -1411,7 +1437,6 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
reader.setInput(input);
|
||||
|
||||
try {
|
||||
@ -1452,6 +1477,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
int numThumbnails = reader.getNumThumbnails(0);
|
||||
for (int i = 0; i < numThumbnails; i++) {
|
||||
BufferedImage thumbnail = reader.readThumbnail(0, i);
|
||||
// System.err.println("thumbnail: " + thumbnail);
|
||||
showIt(thumbnail, String.format("Thumbnail: %s [%d x %d]", file.getName(), thumbnail.getWidth(), thumbnail.getHeight()));
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
@ -141,7 +140,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
||||
}
|
||||
|
||||
private static boolean isAppSegmentWithId(String segmentId, ImageInputStream stream) throws IOException {
|
||||
Validate.notNull(segmentId, "segmentId");
|
||||
notNull(segmentId, "segmentId");
|
||||
|
||||
stream.mark();
|
||||
|
||||
@ -160,7 +159,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
||||
|
||||
static String asNullTerminatedAsciiString(final byte[] data, final int offset) {
|
||||
for (int i = 0; i < data.length - offset; i++) {
|
||||
if (data[i] == 0 || i > 255) {
|
||||
if (data[offset + i] == 0 || i > 255) {
|
||||
return asAsciiString(data, offset, offset + i);
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* JPEGImageReaderTest
|
||||
@ -135,9 +136,13 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
byte[] expectedData = {34, 37, 34, 47, 47, 44, 22, 26, 28, 23, 26, 28, 20, 23, 26, 20, 22, 25, 22, 25, 27, 18, 21, 24};
|
||||
|
||||
assertEquals(expectedData.length, data.length);
|
||||
|
||||
for (int i = 0; i < expectedData.length; i++) {
|
||||
assertEquals(expectedData[i], data[i], 5);
|
||||
|
||||
assertJPEGPixelsEqual(expectedData, data, 0);
|
||||
}
|
||||
|
||||
private static void assertJPEGPixelsEqual(byte[] expected, byte[] actual, int actualOffset) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i], actual[i + actualOffset], 5);
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,4 +338,65 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
assertEquals(96, thumbnail.getWidth());
|
||||
assertEquals(72, thumbnail.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvertedColors() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/exif-jpeg-thumbnail-sony-dsc-p150-inverted-colors.jpg")));
|
||||
|
||||
assertEquals(2437, reader.getWidth(0));
|
||||
assertEquals(1662, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(0, 0, reader.getWidth(0), 8));
|
||||
BufferedImage strip = reader.read(0, param);
|
||||
|
||||
assertNotNull(strip);
|
||||
assertEquals(2437, strip.getWidth());
|
||||
assertEquals(8, strip.getHeight());
|
||||
|
||||
int[] expectedRGB = new int[] {
|
||||
0xffe9d0bc, 0xfff3decd, 0xfff5e6d3, 0xfff8ecdc, 0xfff8f0e5, 0xffe3ceb9, 0xff6d3923, 0xff5a2d18,
|
||||
0xff00170b, 0xff131311, 0xff52402c, 0xff624a30, 0xff6a4f34, 0xfffbf8f1, 0xfff4efeb, 0xffefeae6,
|
||||
0xffebe6e2, 0xffe3e0d9, 0xffe1d6d0, 0xff10100e
|
||||
};
|
||||
|
||||
// Validate strip colors
|
||||
for (int i = 0; i < strip.getWidth() / 128; i++) {
|
||||
int actualRGB = strip.getRGB(i * 128, 4);
|
||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
||||
assertEquals((actualRGB) & 0xff, (expectedRGB[i]) & 0xff, 5);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailInvertedColors() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/exif-jpeg-thumbnail-sony-dsc-p150-inverted-colors.jpg")));
|
||||
|
||||
assertTrue(reader.hasThumbnails(0));
|
||||
assertEquals(1, reader.getNumThumbnails(0));
|
||||
assertEquals(160, reader.getThumbnailWidth(0, 0));
|
||||
assertEquals(109, reader.getThumbnailHeight(0, 0));
|
||||
|
||||
BufferedImage thumbnail = reader.readThumbnail(0, 0);
|
||||
assertNotNull(thumbnail);
|
||||
assertEquals(160, thumbnail.getWidth());
|
||||
assertEquals(109, thumbnail.getHeight());
|
||||
|
||||
int[] expectedRGB = new int[] {
|
||||
0xffefd5c4, 0xffead3b1, 0xff55392d, 0xff55403b, 0xff6d635a, 0xff7b726b, 0xff68341f, 0xff5c2f1c,
|
||||
0xff250f12, 0xff6d7c77, 0xff414247, 0xff6a4f3a, 0xff6a4e39, 0xff564438, 0xfffcf7f1, 0xffefece7,
|
||||
0xfff0ebe7, 0xff464040, 0xffe3deda, 0xffd4cfc9,
|
||||
};
|
||||
|
||||
// Validate strip colors
|
||||
for (int i = 0; i < thumbnail.getWidth() / 8; i++) {
|
||||
int actualRGB = thumbnail.getRGB(i * 8, 4);
|
||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
||||
assertEquals((actualRGB) & 0xff, (expectedRGB[i]) & 0xff, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1013 KiB |
@ -59,6 +59,10 @@ final class EXIFEntry extends AbstractEntry {
|
||||
switch ((Integer) getIdentifier()) {
|
||||
case TIFF.TAG_EXIF_IFD:
|
||||
return "EXIF";
|
||||
case TIFF.TAG_INTEROP_IFD:
|
||||
return "Interoperability";
|
||||
case TIFF.TAG_GPS_IFD:
|
||||
return "GPS";
|
||||
case TIFF.TAG_XMP:
|
||||
return "XMP";
|
||||
case TIFF.TAG_IPTC:
|
||||
@ -112,6 +116,10 @@ final class EXIFEntry extends AbstractEntry {
|
||||
return "HostComputer";
|
||||
case TIFF.TAG_COPYRIGHT:
|
||||
return "Copyright";
|
||||
case TIFF.TAG_YCBCR_SUB_SAMPLING:
|
||||
return "YCbCrSubSampling";
|
||||
case TIFF.TAG_YCBCR_POSITIONING:
|
||||
return "YCbCrPositioning";
|
||||
|
||||
case EXIF.TAG_EXPOSURE_TIME:
|
||||
return "ExposureTime";
|
||||
@ -121,6 +129,55 @@ final class EXIFEntry extends AbstractEntry {
|
||||
return "ExposureProgram";
|
||||
case EXIF.TAG_ISO_SPEED_RATINGS:
|
||||
return "ISOSpeedRatings";
|
||||
case EXIF.TAG_SHUTTER_SPEED_VALUE:
|
||||
return "ShutterSpeedValue";
|
||||
case EXIF.TAG_APERTURE_VALUE:
|
||||
return "ApertureValue";
|
||||
case EXIF.TAG_BRIGHTNESS_VALUE:
|
||||
return "BrightnessValue";
|
||||
case EXIF.TAG_EXPOSURE_BIAS_VALUE:
|
||||
return "ExposureBiasValue";
|
||||
case EXIF.TAG_MAX_APERTURE_VALUE:
|
||||
return "MaxApertureValue";
|
||||
case EXIF.TAG_SUBJECT_DISTANCE:
|
||||
return "SubjectDistance";
|
||||
case EXIF.TAG_METERING_MODE:
|
||||
return "MeteringMode";
|
||||
case EXIF.TAG_LIGHT_SOURCE:
|
||||
return "LightSource";
|
||||
case EXIF.TAG_FLASH:
|
||||
return "Flash";
|
||||
case EXIF.TAG_FOCAL_LENGTH:
|
||||
return "FocalLength";
|
||||
case EXIF.TAG_FILE_SOURCE:
|
||||
return "FileSource";
|
||||
case EXIF.TAG_SCENE_TYPE:
|
||||
return "SceneType";
|
||||
case EXIF.TAG_CFA_PATTERN:
|
||||
return "CFAPattern";
|
||||
case EXIF.TAG_CUSTOM_RENDERED:
|
||||
return "CustomRendered";
|
||||
case EXIF.TAG_EXPOSURE_MODE:
|
||||
return "ExposureMode";
|
||||
case EXIF.TAG_WHITE_BALANCE:
|
||||
return "WhiteBalance";
|
||||
case EXIF.TAG_DIGITAL_ZOOM_RATIO:
|
||||
return "DigitalZoomRation";
|
||||
case EXIF.TAG_FOCAL_LENGTH_IN_35_MM_FILM:
|
||||
return "FocalLengthIn35mmFilm";
|
||||
case EXIF.TAG_SCENE_CAPTURE_TYPE:
|
||||
return "SceneCaptureType";
|
||||
case EXIF.TAG_GAIN_CONTROL:
|
||||
return "GainControl";
|
||||
case EXIF.TAG_CONTRAST:
|
||||
return "Contrast";
|
||||
case EXIF.TAG_SATURATION:
|
||||
return "Saturation";
|
||||
case EXIF.TAG_SHARPNESS:
|
||||
return "Sharpness";
|
||||
|
||||
case EXIF.TAG_FLASHPIX_VERSION:
|
||||
return "FlashpixVersion";
|
||||
|
||||
case EXIF.TAG_EXIF_VERSION:
|
||||
return "ExifVersion";
|
||||
@ -133,6 +190,11 @@ final class EXIFEntry extends AbstractEntry {
|
||||
case EXIF.TAG_USER_COMMENT:
|
||||
return "UserComment";
|
||||
|
||||
case EXIF.TAG_COMPONENTS_CONFIGURATION:
|
||||
return "ComponentsConfiguration";
|
||||
case EXIF.TAG_COMPRESSED_BITS_PER_PIXEL:
|
||||
return "CompressedBitsPerPixel";
|
||||
|
||||
case EXIF.TAG_COLOR_SPACE:
|
||||
return "ColorSpace";
|
||||
case EXIF.TAG_PIXEL_X_DIMENSION:
|
||||
|
@ -133,7 +133,7 @@ public final class JPEGSegmentUtil {
|
||||
|
||||
static String asNullTerminatedAsciiString(final byte[] data, final int offset) {
|
||||
for (int i = 0; i < data.length - offset; i++) {
|
||||
if (data[i] == 0 || i > 255) {
|
||||
if (data[offset + i] == 0 || i > 255) {
|
||||
return asAsciiString(data, offset, offset + i);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user