2015-01-13 3 views
17

Picasso를 사용하여 내 응용 프로그램에서 웹에서 이미지를로드합니다. 일부 이미지는 90도 회전하여 표시되는 것으로 나타났습니다. 브라우저에서 이미지를 열면 이미지의 위치가 올바르게 표시됩니다. 이 이미지에는 EXIF ​​데이터가 있다고 가정합니다. 피카소에게 EXIF를 무시하도록 지시 할 수있는 방법이 있습니까?Android Picasso 자동 회전 이미지

+0

이상합니다. 나는 피카소가 EXIF에 관심을 기울 였다는 것을 깨닫지 못했다. 링크 할 수있는 예제 이미지가 있습니까? – CommonsWare

+0

예. 그것은 이상하고 기대하지도 않습니다. 사적인 서버에서 오는 것처럼 내 현재 이미지를 줄 수는 없습니다. 해상도 : X 2448 3264이 동향 : 90 ======= IPTC 데이터 회전 : =======를 는 EXIF ​​그래서이 확인된다 것을 그러나 확인 후는 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 inner 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 ° 회전을 반환하며이 솔루션이 작동합니다. 다른 경우도 검사해야합니다.

관련 문제