2015-01-13 3 views
17

Я использую Picasso для загрузки изображений из Интернета в моем приложении. Я заметил, что некоторые изображения показаны с поворотом на 90 градусов, хотя, когда я открываю изображение в своем браузере, я вижу, что он правильно расположен. Я предполагаю, что эти изображения имеют данные EXIF. Есть ли способ дать указание Пикассо игнорировать EXIF?Android Picasso auto вращает изображение

+0

Это странно. Я не понимал, что Пикассо обратил внимание на EXIF. У вас есть примеры изображений, на которые вы можете ссылаться? – CommonsWare

+0

Да. Это странно и не ожидается. Я не могу дать вам мой текущий образ, поскольку он исходит от частного сервера. Но после проверки его EXIF ​​онлайн Я вижу это: Разрешение: 3264 х 2448 Ориентация: поворот на 90 ======= IPTC данные: ======= Так что подтверждается, что EXIF ​​является Ответственный за это. – Panos

+1

Проверить строку # 162 https://github.com/square/picasso/blob/c8e79ce78c26e6ecdf3a0cf0a2efecd95f5ac4d7/picasso/src/main/java/com/squareup/picasso/BitmapHunter.java – Distwo

ответ

0

Можете ли вы разместить изображение, которое используете? , потому что как это thread сказал, ориентация exif для изображений, загруженных из Интернета, игнорируется (только контент-провайдер и локальные файлы).

Я также пытаюсь отобразить это image в picasso 2.5.2, реальная ориентация изображения обращена к правде (нижний код на изображении обращен вправо). Ориентация exif - 90 градусов по часовой стрелке. Попробуйте открыть его в хроме (хром выполняет поправку exif), изображение будет обращено вниз (нижний код на изображении обращен вниз).

2

Как известно, Picasso поддерживает EXIF ​​с локального хранилища, это делается через внутренние устройства Android. Обеспечение такой же функциональности не может быть легко осуществлено из-за возможности использования пользовательских библиотек загрузки 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; 
    } 
} 

Это решение является экспериментальным и должны быть проверены на наличие утечек и, вероятно, улучшилось. В большинстве случаев устройства Samsung и iOs возвращают вращение на 90 градусов, и это решение работает. Другие случаи также должны быть проверены.

Смежные вопросы