我正在使用Picasso在我的应用程序中从Web加载图像。我注意到一些图像显示旋转了90度,但是当我在浏览器中打开图像时,我看到它正确定位。我假设这些图像有EXIF数据。有没有办法指示毕加索忽略EXIF?
答案 0 :(得分:2)
众所周知,Picasso支持EXIF来自本地存储,这是通过Android内置Utils完成的。由于能够使用自定义Http加载库,因此无法轻松提供相同的功能。 我的解决方案很简单:我们必须覆盖缓存并在缓存项目之前应用Exif轮换。
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(chain -> {
Response originalResponse = chain.proceed(chain.request());
byte[] body = originalResponse.body().bytes();
ResponseBody newBody = ResponseBody
.create(originalResponse.body().contentType(), ImageUtils.processImage(body));
return originalResponse.newBuilder().body(newBody).build();
})
.cache(cache)
.build();
这里我们添加了NetworkInterceptor,它可以在缓存之前转换请求和响应。
public class ImageUtils {
public static byte[] processImage(byte[] originalImg) {
int orientation = Exif.getOrientation(originalImg);
if (orientation != 0) {
Bitmap bmp = BitmapFactory.decodeByteArray(originalImg, 0, originalImg.length);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
rotateImage(orientation, bmp).compress(Bitmap.CompressFormat.PNG, 100, stream);
return stream.toByteArray();
}
return originalImg;
}
private static Bitmap rotateImage(int angle, Bitmap bitmapSrc) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(bitmapSrc, 0, 0,
bitmapSrc.getWidth(), bitmapSrc.getHeight(), matrix, true);
}
}
Exif转换:
public class Exif {
private static final String TAG = "Exif";
// Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
public static int getOrientation(byte[] jpeg) {
if (jpeg == null) {
return 0;
}
int offset = 0;
int length = 0;
// ISO/IEC 10918-1:1993(E)
while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
int marker = jpeg[offset] & 0xFF;
// Check if the marker is a padding.
if (marker == 0xFF) {
continue;
}
offset++;
// Check if the marker is SOI or TEM.
if (marker == 0xD8 || marker == 0x01) {
continue;
}
// Check if the marker is EOI or SOS.
if (marker == 0xD9 || marker == 0xDA) {
break;
}
// Get the length and check if it is reasonable.
length = pack(jpeg, offset, 2, false);
if (length < 2 || offset + length > jpeg.length) {
Log.e(TAG, "Invalid length");
return 0;
}
// Break if the marker is EXIF in APP1.
if (marker == 0xE1 && length >= 8 &&
pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
pack(jpeg, offset + 6, 2, false) == 0) {
offset += 8;
length -= 8;
break;
}
// Skip other markers.
offset += length;
length = 0;
}
// JEITA CP-3451 Exif Version 2.2
if (length > 8) {
// Identify the byte order.
int tag = pack(jpeg, offset, 4, false);
if (tag != 0x49492A00 && tag != 0x4D4D002A) {
Log.e(TAG, "Invalid byte order");
return 0;
}
boolean littleEndian = (tag == 0x49492A00);
// Get the offset and check if it is reasonable.
int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
if (count < 10 || count > length) {
Log.e(TAG, "Invalid offset");
return 0;
}
offset += count;
length -= count;
// Get the count and go through all the elements.
count = pack(jpeg, offset - 2, 2, littleEndian);
while (count-- > 0 && length >= 12) {
// Get the tag and check if it is orientation.
tag = pack(jpeg, offset, 2, littleEndian);
if (tag == 0x0112) {
// We do not really care about type and count, do we?
int orientation = pack(jpeg, offset + 8, 2, littleEndian);
switch (orientation) {
case 1:
return 0;
case 3:
return 180;
case 6:
return 90;
case 8:
return 270;
}
Log.i(TAG, "Unsupported orientation");
return 0;
}
offset += 12;
length -= 12;
}
}
Log.i(TAG, "Orientation not found");
return 0;
}
private static int pack(byte[] bytes, int offset, int length,
boolean littleEndian) {
int step = 1;
if (littleEndian) {
offset += length - 1;
step = -1;
}
int value = 0;
while (length-- > 0) {
value = (value << 8) | (bytes[offset] & 0xFF);
offset += step;
}
return value;
}
}
此解决方案是实验性的,必须进行泄漏测试并可能进行改进。在大多数情况下,三星和iO设备返回90°旋转,这个解决方案有效。其他情况也必须进行测试。
答案 1 :(得分:0)
您可以发布您正在使用的图片吗? 因为正如thread所述,从web加载的图像的exif方向被忽略(仅限内容提供者和本地文件)。
我也尝试在picasso 2.5.2中显示此image,图像的真实方向面向右侧(图像中的底部代码朝向右侧)。 exif方向,顺时针90deg。尝试用chrome打开它(chrome表示exif旋转),图像将面朝下(图像中的底部代码朝下)。
答案 2 :(得分:0)
基于@ph0en1x 响应,此版本使用google exif 库和kotlin:将此拦截器添加到picasso 使用的okhttpclient
addNetworkInterceptor {
val response = it.proceed(it.request())
val body = response.body
if (body?.contentType()?.type == "image") {
val bytes = body.bytes()
val degrees = bytes.inputStream().use { input ->
when (ExifInterface(input).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) {
ExifInterface.ORIENTATION_ROTATE_270 -> 270
ExifInterface.ORIENTATION_ROTATE_180 -> 180
ExifInterface.ORIENTATION_ROTATE_90 -> 90
else -> 0
}
}
if (degrees != 0) {
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
ByteArrayOutputStream().use { output ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply { postRotate(degrees.toFloat()) }, true)
.compress(Bitmap.CompressFormat.PNG, 100, output)
response.newBuilder().body(output.toByteArray().toResponseBody(body.contentType())).build()
}
} else
response.newBuilder().body(bytes.toResponseBody(body.contentType())).build()
} else
response
}