2015-01-13 28 views
17

Tôi đang sử dụng Picasso để tải hình ảnh từ web trong ứng dụng của tôi. Tôi đã nhận thấy rằng một số hình ảnh được hiển thị xoay 90 độ mặc dù khi tôi mở hình ảnh trong trình duyệt của mình, tôi thấy nó được định vị chính xác. Tôi cho rằng những hình ảnh này có dữ liệu EXIF. Có cách nào để hướng dẫn Picasso bỏ qua EXIF ​​không?Android Picasso tự động xoay hình ảnh

+0

Thật kỳ quặc. Tôi không nhận ra Picasso chú ý đến EXIF. Bạn có bất kỳ hình ảnh mẫu nào mà bạn có thể liên kết tới không? – CommonsWare

+0

Có. Đó là kỳ lạ và không mong đợi. Tôi không thể cung cấp cho bạn hình ảnh hiện tại của tôi vì nó đến từ một máy chủ riêng tư. Nhưng sau khi kiểm tra nó EXIF ​​online Tôi thấy điều này: Độ phân giải: 3264 x 2448 Định hướng: xoay 90 ======= IPTC dữ liệu: ======= Vì vậy, nó đã được khẳng định là EXIF chịu trách nhiệm cho điều đó. – Panos

+1

Dòng kiểm tra # 162 https://github.com/square/picasso/blob/c8e79ce78c26e6ecdf3a0cf0a2efecd95f5ac4d7/picasso/src/main/java/com/squareup/picasso/BitmapHunter.java – Distwo

Trả lời

0

Bạn có thể đăng hình ảnh bạn đang sử dụng không? vì vì điều này thread cho biết, định dạng exif cho hình ảnh được tải từ web bị bỏ qua (chỉ nhà cung cấp nội dung và tệp cục bộ).

Tôi cũng cố gắng hiển thị số này image trong picasso 2.5.2, hướng thực của hình ảnh đang đối mặt với quyền (mã dưới cùng trong hình ảnh hướng về bên phải). Hướng exif, là 90deg theo chiều kim đồng hồ. Hãy thử mở nó trong chrome (chrome là tôn vinh exif quay), hình ảnh sẽ phải đối mặt xuống (mã dưới cùng trong hình ảnh đang phải đối mặt xuống).

2

Như chúng ta biết, Picasso hỗ trợ EXIF ​​từ bộ nhớ cục bộ, điều này được thực hiện thông qua Utils bên trong Android. Cung cấp chức năng tương tự không thể thực hiện dễ dàng do khả năng sử dụng thư viện tải tùy chỉnh của Http. Giải pháp của tôi rất đơn giản: chúng ta phải ghi đè bộ nhớ đệm và áp dụng xoay Exif trước khi mục được lưu trong bộ nhớ cache.

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(); 

Ở đây chúng tôi thêm NetworkInterceptor có thể chuyển đổi yêu cầu và phản hồi trước khi được lưu vào bộ nhớ cache.

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 chuyển đổi:

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; 
    } 
} 

giải pháp này là thử nghiệm và phải được kiểm tra rò rỉ và có lẽ được cải thiện. Trong hầu hết các trường hợp, các thiết bị Samsung và iOs quay trở lại quay 90 DEG và giải pháp này hoạt động. Các trường hợp khác cũng phải được kiểm tra.