2015-01-13 28 views
17

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

+0

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

+0

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

+1

Sprawdź linię # 162 https://github.com/square/picasso/blob/c8e79ce78c26e6ecdf3a0cf0a2efecd95f5ac4d7/picasso/src/main/java/com/squareup/picasso/BitmapHunter.java – Distwo

Odpowiedz

0

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ół).

2

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.