Używam Picassa do ładowania obrazów z Internetu w mojej aplikacji. Zauważyłem, że niektóre obrazy są wyświetlane obrócone o 90 stopni, chociaż po otwarciu obrazu w przeglądarce widzę, że jest on prawidłowo ustawiony. Zakładam, że te obrazy mają dane EXIF. Czy istnieje sposób, aby nakazać Picassoowi zignorować EXIF?Android Picasso auto obraca zdjęcie
Odpowiedz
Czy możesz opublikować obraz, którego używasz? ponieważ ta thread powiedział, że orientacja exif dla obrazów załadowanych z sieci jest ignorowana (tylko dostawca zawartości i pliki lokalne).
Próbuję również wyświetlić ten image w picasso 2.5.2, prawdziwa orientacja obrazu jest zwrócona w prawo (dolny kod na obrazie jest skierowany w prawo). Orientacja exif jest 90deg zgodnie z ruchem wskazówek zegara. Spróbuj otworzyć go w chrome (chrome honoruje rotację exif), obraz będzie skierowany w dół (dolny kod w obrazie jest skierowany w dół).
Jak wiemy, Picasso obsługuje EXIF z lokalnej pamięci masowej, odbywa się to za pośrednictwem wewnętrznych aplikacji systemu Android. Zapewnienie tej samej funkcjonalności nie może być łatwe ze względu na możliwość korzystania z niestandardowych bibliotek ładowania HTTP. Moje rozwiązanie jest proste: musimy zastąpić buforowanie i zastosować rotację Exif, zanim element zostanie zbuforowany.
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();
Tutaj dodajemy NetworkInterceptor, który może przekształcić żądanie i odpowiedź, zanim zostanie zbuforowana.
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 Transformacja
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;
}
}
Rozwiązanie to eksperymentalne i muszą być badane na szczelność i prawdopodobnie lepsza. W większości przypadków urządzenia Samsung i iOs zwracają obrót o 90 °, a to rozwiązanie działa. Inne przypadki również muszą zostać przetestowane.
To dziwne. Nie zdawałem sobie sprawy, że Picasso zwrócił uwagę na EXIF. Czy masz przykładowe obrazy, które możesz połączyć? – CommonsWare
Tak. To dziwne i nieoczekiwane. Nie mogę podać mojego obecnego obrazu, ponieważ pochodzi on z prywatnego serwera. Ale po sprawdzeniu to EXIF Internecie widzę tak: Rozdzielczość: 3264 x 2448 Orientacja: obracać 90 ======= IPTC dane: ======= Więc to potwierdza, że jest EXIF odpowiedzialni za to. – Panos
Sprawdź linię # 162 https://github.com/square/picasso/blob/c8e79ce78c26e6ecdf3a0cf0a2efecd95f5ac4d7/picasso/src/main/java/com/squareup/picasso/BitmapHunter.java – Distwo