提交 52985d7c 编写于 作者: S Sam Judd

Handle partial reads/skips in ImageHeaderParser.

Fixes #387
上级 02901005
package com.bumptech.glide.load.resource.bitmap;
import static com.google.common.truth.Truth.assertThat;
import static com.bumptech.glide.load.resource.bitmap.ImageHeaderParser.ImageType;
import static org.junit.Assert.assertEquals;
......@@ -7,13 +8,16 @@ import com.bumptech.glide.testutil.TestResourceUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
@RunWith(JUnit4.class)
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, emulateSdk = 18)
public class ImageHeaderParserTest {
private static final byte[] PNG_HEADER_WITH_IHDR_CHUNK = new byte[] {(byte) 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a,
......@@ -84,10 +88,82 @@ public class ImageHeaderParserTest {
assertEquals(-1, parser.getOrientation());
}
// Test for #387.
@Test
public void testHandlesPartialReads() throws IOException {
InputStream is = TestResourceUtil.openResource(getClass(), "issue387_rotated_jpeg.jpg");
ImageHeaderParser parser = new ImageHeaderParser(new PartialReadInputStream(is));
assertThat(parser.getOrientation()).isEqualTo(6);
}
// Test for #387.
@Test
public void testHandlesPartialSkips() throws IOException {
InputStream is = TestResourceUtil.openResource(getClass(), "issue387_rotated_jpeg.jpg");
ImageHeaderParser parser = new ImageHeaderParser(new PartialSkipInputStream(is));
assertThat(parser.getOrientation()).isEqualTo(6);
}
@Test
public void testHandlesSometimesZeroSkips() throws IOException {
InputStream is = new ByteArrayInputStream(new byte[] { (byte) 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a });
ImageHeaderParser parser = new ImageHeaderParser(new SometimesZeroSkipInputStream(is));
assertEquals(ImageType.PNG, parser.getType());
}
private static byte[] generatePngHeaderWithIhdr(int bitDepth) {
byte[] result = new byte[PNG_HEADER_WITH_IHDR_CHUNK.length];
System.arraycopy(PNG_HEADER_WITH_IHDR_CHUNK, 0, result, 0, PNG_HEADER_WITH_IHDR_CHUNK.length);
result[result.length - 1] = (byte) bitDepth;
return result;
}
private static class SometimesZeroSkipInputStream extends FilterInputStream {
boolean returnZeroFlag = true;
protected SometimesZeroSkipInputStream(InputStream in) {
super(in);
}
@Override
public long skip(long byteCount) throws IOException {
if (returnZeroFlag) {
return 0;
}
returnZeroFlag = !returnZeroFlag;
return super.skip(byteCount);
}
}
private static class PartialSkipInputStream extends FilterInputStream {
protected PartialSkipInputStream(InputStream in) {
super(in);
}
@Override
public long skip(long byteCount) throws IOException {
long toActuallySkip = byteCount / 2;
if (byteCount == 1) {
toActuallySkip = 1;
}
return super.skip(toActuallySkip);
}
}
private static class PartialReadInputStream extends FilterInputStream {
protected PartialReadInputStream(InputStream in) {
super(in);
}
@Override
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
int toActuallyRead = byteCount / 2;
if (byteCount == 1) {
toActuallyRead = 1;
}
return super.read(buffer, byteOffset, toActuallyRead);
}
}
}
\ No newline at end of file
package com.bumptech.glide.resize.load;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
......@@ -41,6 +42,12 @@ public class ExifTest {
}
}
@Test
public void testIssue387() throws IOException {
InputStream is = TestResourceUtil.openResource(getClass(), "issue387_rotated_jpeg.jpg");
assertThat(new ImageHeaderParser(is).getOrientation()).isEqualTo(6);
}
@Test
public void testLandscape() throws IOException {
for (int i = 1; i <= 8; i++) {
......
......@@ -23,7 +23,7 @@ public class ImageHeaderParser {
/**
* The format of the image data including whether or not the image may include transparent pixels.
*/
public static enum ImageType {
public enum ImageType {
/** GIF type. */
GIF(true),
/** JPG type. */
......@@ -171,18 +171,25 @@ public class ImageHeaderParser {
segmentLength = streamReader.getUInt16() - 2;
if (segmentType != EXIF_SEGMENT_TYPE) {
if (segmentLength != streamReader.skip(segmentLength)) {
long skipped = streamReader.skip(segmentLength);
if (skipped != segmentLength) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unable to skip enough data for type=" + segmentType);
Log.d(TAG, "Unable to skip enough data"
+ ", type: " + segmentType
+ ", wanted to skip: " + segmentLength
+ ", but actually skipped: " + skipped);
}
return null;
}
} else {
byte[] segmentData = new byte[segmentLength];
if (segmentLength != streamReader.read(segmentData)) {
int read = streamReader.read(segmentData);
if (read != segmentLength) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unable to read segment data for type=" + segmentType + " length=" + segmentLength);
Log.d(TAG, "Unable to read segment data"
+ ", type: " + segmentType
+ ", length: " + segmentLength
+ ", actually read: " + read);
}
return null;
} else {
......@@ -229,7 +236,7 @@ public class ImageHeaderParser {
// 12 is max format code.
if (formatCode < 1 || formatCode > 12) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got invalid format code = " + formatCode);
Log.d(TAG, "Got invalid format code=" + formatCode);
}
continue;
}
......@@ -244,7 +251,7 @@ public class ImageHeaderParser {
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got tagIndex=" + i + " tagType=" + tagType + " formatCode =" + formatCode
Log.d(TAG, "Got tagIndex=" + i + " tagType=" + tagType + " formatCode=" + formatCode
+ " componentCount=" + componentCount);
}
......@@ -332,11 +339,39 @@ public class ImageHeaderParser {
}
public long skip(long total) throws IOException {
return is.skip(total);
if (total < 0) {
return 0;
}
long skipped;
long toSkip = total;
while (toSkip > 0) {
skipped = is.skip(toSkip);
if (skipped > 0) {
toSkip -= skipped;
} else {
// Skip has no specific contract as to what happens when you reach the end of
// the stream. To differentiate between temporarily not having more data and
// having finished the stream, we read a single byte when we fail to skip any
// amount of data.
int testEofByte = is.read();
if (testEofByte == -1) {
break;
} else {
toSkip--;
}
}
}
return total - toSkip;
}
public int read(byte[] buffer) throws IOException {
return is.read(buffer);
int toRead = buffer.length;
int read;
while (toRead > 0 && ((read = is.read(buffer, buffer.length - toRead, toRead)) != -1)) {
toRead -= read;
}
return buffer.length - toRead;
}
public int getByte() throws IOException {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册