Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
沉迷打码的小凳子
glide
提交
ff503990
G
glide
项目概览
沉迷打码的小凳子
/
glide
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
glide
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
ff503990
编写于
4月 12, 2015
作者:
S
Sam Judd
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Insert exif orientation into thumbnail streams.
Fixes #400
上级
f20687d0
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
278 addition
and
34 deletion
+278
-34
library/src/androidTest/java/com/bumptech/glide/load/data/ExifOrientationStreamTest.java
...m/bumptech/glide/load/data/ExifOrientationStreamTest.java
+41
-0
library/src/androidTest/java/com/bumptech/glide/load/data/MediaStoreThumbFetcherTest.java
.../bumptech/glide/load/data/MediaStoreThumbFetcherTest.java
+4
-2
library/src/androidTest/java/com/bumptech/glide/load/data/ThumbnailStreamOpenerTest.java
...m/bumptech/glide/load/data/ThumbnailStreamOpenerTest.java
+6
-6
library/src/main/java/com/bumptech/glide/load/data/ExifOrientationStream.java
...a/com/bumptech/glide/load/data/ExifOrientationStream.java
+133
-0
library/src/main/java/com/bumptech/glide/load/data/MediaStoreThumbFetcher.java
.../com/bumptech/glide/load/data/MediaStoreThumbFetcher.java
+94
-26
未找到文件。
library/src/androidTest/java/com/bumptech/glide/load/data/ExifOrientationStreamTest.java
0 → 100644
浏览文件 @
ff503990
package
com.bumptech.glide.load.data
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
com.bumptech.glide.load.resource.bitmap.ImageHeaderParser
;
import
com.bumptech.glide.testutil.TestResourceUtil
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.annotation.Config
;
import
java.io.IOException
;
import
java.io.InputStream
;
@RunWith
(
RobolectricTestRunner
.
class
)
@Config
(
manifest
=
Config
.
NONE
,
emulateSdk
=
18
)
public
class
ExifOrientationStreamTest
{
private
InputStream
openOrientationExample
(
boolean
isLandscape
,
int
item
)
{
String
filePrefix
=
isLandscape
?
"Landscape"
:
"Portrait"
;
return
TestResourceUtil
.
openResource
(
getClass
(),
"exif-orientation-examples/"
+
filePrefix
+
"_"
+
item
+
".jpg"
);
}
@Test
public
void
testIncludesGivenExifOrientation
()
throws
IOException
{
for
(
int
i
=
0
;
i
<
8
;
i
++)
{
for
(
int
j
=
0
;
j
<
8
;
j
++)
{
InputStream
toWrap
=
openOrientationExample
(
true
/*isLandscape*/
,
j
+
1
);
InputStream
wrapped
=
new
ExifOrientationStream
(
toWrap
,
i
);
ImageHeaderParser
parser
=
new
ImageHeaderParser
(
wrapped
);
assertThat
(
parser
.
getOrientation
()).
isEqualTo
(
i
);
toWrap
=
openOrientationExample
(
false
/*isLandscape*/
,
j
+
1
);
wrapped
=
new
ExifOrientationStream
(
toWrap
,
i
);
parser
=
new
ImageHeaderParser
(
wrapped
);
assertThat
(
parser
.
getOrientation
()).
isEqualTo
(
i
);
}
}
}
}
\ No newline at end of file
library/src/androidTest/java/com/bumptech/glide/load/data/MediaStoreThumbFetcherTest.java
浏览文件 @
ff503990
...
...
@@ -2,6 +2,7 @@ package com.bumptech.glide.load.data;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
mockito
.
Matchers
.
any
;
import
static
org
.
mockito
.
Matchers
.
anyInt
;
import
static
org
.
mockito
.
Matchers
.
eq
;
...
...
@@ -38,10 +39,11 @@ public class MediaStoreThumbFetcherTest {
public
void
testReturnsInputStreamFromThumbnailOpener
()
throws
Exception
{
InputStream
expected
=
new
ByteArrayInputStream
(
new
byte
[
0
]);
when
(
harness
.
thumbnailFetcher
.
open
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
expected
);
when
(
harness
.
thumbnailFetcher
.
open
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
expected
);
InputStream
result
=
harness
.
get
().
loadData
(
Priority
.
LOW
);
assert
Equals
(
expected
,
result
);
assert
NotNull
(
result
);
}
@Test
...
...
library/src/androidTest/java/com/bumptech/glide/load/data/ThumbnailStreamOpenerTest.java
浏览文件 @
ff503990
...
...
@@ -38,14 +38,14 @@ public class ThumbnailStreamOpenerTest {
@Test
public
void
testReturnsNullIfCursorIsNull
()
throws
FileNotFoundException
{
when
(
harness
.
query
.
query
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
null
);
when
(
harness
.
query
.
query
Path
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
null
);
assertNull
(
harness
.
get
()
.
open
(
Robolectric
.
application
,
harness
.
uri
));
}
@Test
public
void
testReturnsNullIfCursorIsEmpty
()
throws
FileNotFoundException
{
when
(
harness
.
query
.
query
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
when
(
harness
.
query
.
query
Path
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
new
MatrixCursor
(
new
String
[
1
]));
assertNull
(
harness
.
get
()
.
open
(
Robolectric
.
application
,
harness
.
uri
));
...
...
@@ -55,7 +55,7 @@ public class ThumbnailStreamOpenerTest {
public
void
testReturnsNullIfCursorHasEmptyPath
()
throws
FileNotFoundException
{
MatrixCursor
cursor
=
new
MatrixCursor
(
new
String
[
1
]);
cursor
.
addRow
(
new
Object
[]{
""
});
when
(
harness
.
query
.
query
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
cursor
);
when
(
harness
.
query
.
query
Path
(
eq
(
Robolectric
.
application
),
eq
(
harness
.
uri
))).
thenReturn
(
cursor
);
assertNull
(
harness
.
get
()
.
open
(
Robolectric
.
application
,
harness
.
uri
));
}
...
...
@@ -93,7 +93,7 @@ public class ThumbnailStreamOpenerTest {
MediaStoreThumbFetcher
.
VideoThumbnailQuery
query
=
new
MediaStoreThumbFetcher
.
VideoThumbnailQuery
();
TestCursor
testCursor
=
new
SimpleTestCursor
();
Robolectric
.
shadowOf
(
Robolectric
.
application
.
getContentResolver
()).
setCursor
(
queryUri
,
testCursor
);
assertEquals
(
testCursor
,
query
.
query
(
Robolectric
.
application
,
harness
.
uri
));
assertEquals
(
testCursor
,
query
.
query
Path
(
Robolectric
.
application
,
harness
.
uri
));
}
@Test
...
...
@@ -102,7 +102,7 @@ public class ThumbnailStreamOpenerTest {
MediaStoreThumbFetcher
.
ImageThumbnailQuery
query
=
new
MediaStoreThumbFetcher
.
ImageThumbnailQuery
();
TestCursor
testCursor
=
new
SimpleTestCursor
();
Robolectric
.
shadowOf
(
Robolectric
.
application
.
getContentResolver
()).
setCursor
(
queryUri
,
testCursor
);
assertEquals
(
testCursor
,
query
.
query
(
Robolectric
.
application
,
harness
.
uri
));
assertEquals
(
testCursor
,
query
.
query
Path
(
Robolectric
.
application
,
harness
.
uri
));
}
private
static
class
Harness
{
...
...
@@ -114,7 +114,7 @@ public class ThumbnailStreamOpenerTest {
public
Harness
()
{
cursor
.
addRow
(
new
String
[]
{
file
.
getAbsolutePath
()
});
when
(
query
.
query
(
eq
(
Robolectric
.
application
),
eq
(
uri
))).
thenReturn
(
cursor
);
when
(
query
.
query
Path
(
eq
(
Robolectric
.
application
),
eq
(
uri
))).
thenReturn
(
cursor
);
when
(
service
.
get
(
eq
(
file
.
getAbsolutePath
()))).
thenReturn
(
file
);
when
(
service
.
exists
(
eq
(
file
))).
thenReturn
(
true
);
when
(
service
.
length
(
eq
(
file
))).
thenReturn
(
1L
);
...
...
library/src/main/java/com/bumptech/glide/load/data/ExifOrientationStream.java
0 → 100644
浏览文件 @
ff503990
package
com.bumptech.glide.load.data
;
import
java.io.FilterInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
/**
* Adds an exif segment with an orientation attribute to a wrapped {@link InputStream} containing
* image data.
*
* <p>This class assumes that the wrapped stream contains an image format that can contain
* exif information and performs no verification. </p>
*/
public
class
ExifOrientationStream
extends
FilterInputStream
{
/** Allow two bytes for the file format. */
private
static
final
int
SEGMENT_START_POSITION
=
2
;
private
static
final
byte
[]
EXIF_SEGMENT
=
new
byte
[]
{
/** segment start id. */
(
byte
)
0xFF
,
/** segment type. */
(
byte
)
0xE1
,
/** segmentLength. */
0x00
,
(
byte
)
0x1C
,
/** exif identifier. */
0x45
,
0x78
,
0x69
,
0x66
,
0x00
,
0x00
,
/** mototorola byte order (big endian). */
(
byte
)
0x4D
,
(
byte
)
0x4D
,
/** filler? */
0x00
,
0x00
,
/** first id offset. */
0x00
,
0x00
,
0x00
,
0x08
,
/** tagCount. */
0x00
,
0x01
,
/** exif tag type. */
0x01
,
0x12
,
/** 2 byte format. */
0x00
,
0x02
,
/** component count. */
0x00
,
0x00
,
0x00
,
0x01
,
/** 2 byte orientation value, the first byte of which is always 0. */
0x00
,
};
private
static
final
int
SEGMENT_LENGTH
=
EXIF_SEGMENT
.
length
;
private
static
final
int
ORIENTATION_POSITION
=
SEGMENT_LENGTH
+
SEGMENT_START_POSITION
;
private
final
byte
orientation
;
private
int
position
;
public
ExifOrientationStream
(
InputStream
in
,
int
orientation
)
{
super
(
in
);
if
(
orientation
<
-
1
||
orientation
>
8
)
{
throw
new
IllegalArgumentException
(
"Cannot add invalid orientation: "
+
orientation
);
}
this
.
orientation
=
(
byte
)
orientation
;
}
@Override
public
boolean
markSupported
()
{
return
false
;
}
@Override
public
void
mark
(
int
readlimit
)
{
throw
new
UnsupportedOperationException
();
}
@Override
public
int
read
()
throws
IOException
{
final
int
result
;
if
(
position
<
SEGMENT_START_POSITION
||
position
>
ORIENTATION_POSITION
)
{
result
=
super
.
read
();
}
else
if
(
position
==
ORIENTATION_POSITION
)
{
result
=
orientation
;
}
else
{
result
=
EXIF_SEGMENT
[
position
-
SEGMENT_START_POSITION
]
&
0xFF
;
}
if
(
result
!=
-
1
)
{
position
++;
}
return
result
;
}
@Override
public
int
read
(
byte
[]
buffer
,
int
byteOffset
,
int
byteCount
)
throws
IOException
{
int
read
;
if
(
position
>
ORIENTATION_POSITION
)
{
read
=
super
.
read
(
buffer
,
byteOffset
,
byteCount
);
}
else
if
(
position
==
ORIENTATION_POSITION
)
{
buffer
[
byteOffset
]
=
orientation
;
read
=
1
;
}
else
if
(
position
<
SEGMENT_START_POSITION
)
{
read
=
super
.
read
(
buffer
,
byteOffset
,
SEGMENT_START_POSITION
-
position
);
}
else
{
read
=
Math
.
min
(
ORIENTATION_POSITION
-
position
,
byteCount
);
System
.
arraycopy
(
EXIF_SEGMENT
,
position
-
SEGMENT_START_POSITION
,
buffer
,
byteOffset
,
read
);
}
if
(
read
>
0
)
{
position
+=
read
;
}
return
read
;
}
@Override
public
long
skip
(
long
byteCount
)
throws
IOException
{
long
skipped
=
super
.
skip
(
byteCount
);
if
(
skipped
>
0
)
{
position
+=
skipped
;
}
return
skipped
;
}
@Override
public
void
reset
()
throws
IOException
{
throw
new
UnsupportedOperationException
();
}
}
library/src/main/java/com/bumptech/glide/load/data/MediaStoreThumbFetcher.java
浏览文件 @
ff503990
...
...
@@ -6,8 +6,10 @@ import android.database.Cursor;
import
android.net.Uri
;
import
android.provider.MediaStore
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
com.bumptech.glide.Priority
;
import
com.bumptech.glide.load.resource.bitmap.ImageHeaderParser
;
import
java.io.File
;
import
java.io.FileNotFoundException
;
...
...
@@ -22,6 +24,7 @@ import java.io.InputStream;
* {@link android.provider.MediaStore.Video.Thumbnails}.
*/
public
class
MediaStoreThumbFetcher
implements
DataFetcher
<
InputStream
>
{
private
static
final
String
TAG
=
"MediaStoreThumbFetcher"
;
private
static
final
int
MINI_WIDTH
=
512
;
private
static
final
int
MINI_HEIGHT
=
384
;
private
static
final
ThumbnailStreamOpenerFactory
DEFAULT_FACTORY
=
new
ThumbnailStreamOpenerFactory
();
...
...
@@ -54,14 +57,35 @@ public class MediaStoreThumbFetcher implements DataFetcher<InputStream> {
ThumbnailStreamOpener
fetcher
=
factory
.
build
(
mediaStoreUri
,
width
,
height
);
if
(
fetcher
!=
null
)
{
inputStream
=
fetcher
.
open
(
context
,
mediaStoreUri
);
inputStream
=
openThumbInputStream
(
fetcher
);
}
if
(
inputStream
!=
null
)
{
return
inputStream
;
}
else
{
return
defaultFetcher
.
loadData
(
priority
);
if
(
inputStream
==
null
)
{
inputStream
=
defaultFetcher
.
loadData
(
priority
);
}
return
inputStream
;
}
private
InputStream
openThumbInputStream
(
ThumbnailStreamOpener
fetcher
)
{
InputStream
result
=
null
;
try
{
result
=
fetcher
.
open
(
context
,
mediaStoreUri
);
}
catch
(
FileNotFoundException
e
)
{
if
(
Log
.
isLoggable
(
TAG
,
Log
.
DEBUG
))
{
Log
.
d
(
TAG
,
"Failed to find thumbnail file"
,
e
);
}
}
int
orientation
=
-
1
;
if
(
result
!=
null
)
{
orientation
=
fetcher
.
getOrientation
(
context
,
mediaStoreUri
);
}
if
(
orientation
!=
-
1
)
{
result
=
new
ExifOrientationStream
(
result
,
orientation
);
}
return
result
;
}
@Override
...
...
@@ -111,7 +135,7 @@ public class MediaStoreThumbFetcher implements DataFetcher<InputStream> {
}
interface
ThumbnailQuery
{
Cursor
query
(
Context
context
,
Uri
uri
);
Cursor
query
Path
(
Context
context
,
Uri
uri
);
}
static
class
ThumbnailStreamOpener
{
...
...
@@ -128,20 +152,36 @@ public class MediaStoreThumbFetcher implements DataFetcher<InputStream> {
this
.
query
=
query
;
}
public
int
getOrientation
(
Context
context
,
Uri
uri
)
{
int
orientation
=
-
1
;
InputStream
is
=
null
;
try
{
is
=
context
.
getContentResolver
().
openInputStream
(
uri
);
orientation
=
new
ImageHeaderParser
(
is
).
getOrientation
();
}
catch
(
IOException
e
)
{
if
(
Log
.
isLoggable
(
TAG
,
Log
.
DEBUG
))
{
Log
.
d
(
TAG
,
"Failed to open uri: "
+
uri
,
e
);
}
}
finally
{
if
(
is
!=
null
)
{
try
{
is
.
close
();
}
catch
(
IOException
e
)
{
// Ignored.
}
}
}
return
orientation
;
}
public
InputStream
open
(
Context
context
,
Uri
uri
)
throws
FileNotFoundException
{
Uri
thumbnailUri
=
null
;
InputStream
inputStream
=
null
;
final
Cursor
cursor
=
query
.
query
(
context
,
uri
);
final
Cursor
cursor
=
query
.
query
Path
(
context
,
uri
);
try
{
if
(
cursor
!=
null
&&
cursor
.
moveToFirst
())
{
String
path
=
cursor
.
getString
(
0
);
if
(!
TextUtils
.
isEmpty
(
path
))
{
File
file
=
service
.
get
(
path
);
if
(
service
.
exists
(
file
)
&&
service
.
length
(
file
)
>
0
)
{
thumbnailUri
=
Uri
.
fromFile
(
file
);
}
}
thumbnailUri
=
parseThumbUri
(
cursor
);
}
}
finally
{
if
(
cursor
!=
null
)
{
...
...
@@ -153,29 +193,57 @@ public class MediaStoreThumbFetcher implements DataFetcher<InputStream> {
}
return
inputStream
;
}
private
Uri
parseThumbUri
(
Cursor
cursor
)
{
Uri
result
=
null
;
String
path
=
cursor
.
getString
(
0
);
if
(!
TextUtils
.
isEmpty
(
path
))
{
File
file
=
service
.
get
(
path
);
if
(
service
.
exists
(
file
)
&&
service
.
length
(
file
)
>
0
)
{
result
=
Uri
.
fromFile
(
file
);
}
}
return
result
;
}
}
static
class
ImageThumbnailQuery
implements
ThumbnailQuery
{
private
static
final
String
[]
PATH_PROJECTION
=
{
MediaStore
.
Images
.
Thumbnails
.
DATA
,
};
private
static
final
String
PATH_SELECTION
=
MediaStore
.
Images
.
Thumbnails
.
KIND
+
" = "
+
MediaStore
.
Images
.
Thumbnails
.
MINI_KIND
+
" AND "
+
MediaStore
.
Images
.
Thumbnails
.
IMAGE_ID
+
" = ?"
;
@Override
public
Cursor
query
(
Context
context
,
Uri
uri
)
{
String
id
=
uri
.
getLastPathSegment
();
return
context
.
getContentResolver
().
query
(
MediaStore
.
Images
.
Thumbnails
.
EXTERNAL_CONTENT_URI
,
new
String
[]
{
MediaStore
.
Images
.
Thumbnails
.
DATA
},
MediaStore
.
Images
.
Thumbnails
.
IMAGE_ID
+
" = ? AND "
+
MediaStore
.
Images
.
Thumbnails
.
KIND
+
" = ?"
,
new
String
[]
{
id
,
String
.
valueOf
(
MediaStore
.
Images
.
Thumbnails
.
MINI_KIND
)
},
null
);
public
Cursor
queryPath
(
Context
context
,
Uri
uri
)
{
String
imageId
=
uri
.
getLastPathSegment
();
return
context
.
getContentResolver
().
query
(
MediaStore
.
Images
.
Thumbnails
.
EXTERNAL_CONTENT_URI
,
PATH_PROJECTION
,
PATH_SELECTION
,
new
String
[]
{
imageId
},
null
/*sortOrder*/
);
}
}
static
class
VideoThumbnailQuery
implements
ThumbnailQuery
{
private
static
final
String
[]
PATH_PROJECTION
=
{
MediaStore
.
Video
.
Thumbnails
.
DATA
};
private
static
final
String
PATH_SELECTION
=
MediaStore
.
Video
.
Thumbnails
.
KIND
+
" = "
+
MediaStore
.
Video
.
Thumbnails
.
MINI_KIND
+
" AND "
+
MediaStore
.
Video
.
Thumbnails
.
VIDEO_ID
+
" = ?"
;
@Override
public
Cursor
query
(
Context
context
,
Uri
uri
)
{
String
id
=
uri
.
getLastPathSegment
();
return
context
.
getContentResolver
().
query
(
MediaStore
.
Video
.
Thumbnails
.
EXTERNAL_CONTENT_URI
,
new
String
[]
{
MediaStore
.
Video
.
Thumbnails
.
DATA
},
MediaStore
.
Video
.
Thumbnails
.
VIDEO_ID
+
" = ? AND "
+
MediaStore
.
Video
.
Thumbnails
.
KIND
+
" = ?"
,
new
String
[]
{
id
,
String
.
valueOf
(
MediaStore
.
Video
.
Thumbnails
.
MINI_KIND
)
},
null
);
public
Cursor
queryPath
(
Context
context
,
Uri
uri
)
{
String
videoId
=
uri
.
getLastPathSegment
();
return
context
.
getContentResolver
().
query
(
MediaStore
.
Video
.
Thumbnails
.
EXTERNAL_CONTENT_URI
,
PATH_PROJECTION
,
PATH_SELECTION
,
new
String
[]
{
videoId
},
null
/*sortOrder*/
);
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录