From 7d8b374c1ded2dd7df615327b9c173eeda968691 Mon Sep 17 00:00:00 2001 From: Alexey Kulish Date: Thu, 14 Apr 2016 15:30:38 +0300 Subject: [PATCH] Fix photo for 4.4 --- .../AudioVideoNoteRecordingMenu.java | 2 +- ...AudioVideoNoteRecordingMenuFullScreen.java | 149 +++++++++++++++++- .../audionotes/AudioVideoNotesPlugin.java | 23 ++- 3 files changed, 153 insertions(+), 21 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java index 07ae56b634..b6ef3e258d 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenu.java @@ -326,7 +326,7 @@ public class AudioVideoNoteRecordingMenu { } } - public void showFinalPhoto(final byte[] data, long duration) { + public void showFinalPhoto(final byte[] jpeg, long duration) { } public void hideFinalPhoto() { diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenuFullScreen.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenuFullScreen.java index 596eb23cb7..35db16b200 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenuFullScreen.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteRecordingMenuFullScreen.java @@ -2,12 +2,14 @@ package net.osmand.plus.audionotes; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Matrix; import android.support.v4.animation.AnimatorCompatHelper; import android.support.v4.animation.AnimatorUpdateListenerCompat; import android.support.v4.animation.ValueAnimatorCompat; import android.support.v4.app.Fragment; import android.support.v4.view.ViewCompat; import android.util.DisplayMetrics; +import android.view.Display; import android.view.View; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; @@ -104,8 +106,8 @@ public class AudioVideoNoteRecordingMenuFullScreen extends AudioVideoNoteRecordi return null; } - public void showFinalPhoto(final byte[] data, long duration) { - setImage(data); + public void showFinalPhoto(final byte[] jpeg, long duration) { + setImage(jpeg); imageview.setVisibility(View.VISIBLE); viewfinder.setVisibility(View.GONE); @@ -148,13 +150,144 @@ public class AudioVideoNoteRecordingMenuFullScreen extends AudioVideoNoteRecordi animatorCompat.cancel(); } - private void setImage(final byte[] data) { - Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length); + private void setImage(final byte[] jpeg) { + Bitmap bmp = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length); DisplayMetrics dm = new DisplayMetrics(); - getMapActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); + Display display = getMapActivity().getWindowManager().getDefaultDisplay(); + display.getMetrics(dm); - //imageview.setMinimumHeight(dm.heightPixels); - //imageview.setMinimumWidth(dm.widthPixels); - imageview.setImageBitmap(bm); + int imageOrientation = getOrientation(jpeg); + + imageview.setMinimumHeight(dm.heightPixels); + imageview.setMinimumWidth(dm.widthPixels); + bmp = rotateBitmap(bmp, imageOrientation, dm.widthPixels, dm.heightPixels); + imageview.setImageBitmap(bmp); + } + + private static Bitmap rotateBitmap(Bitmap src, int angle, int screenWidth, int screenHeight) { + float srcWidth = (float) src.getWidth(); + float srcHeight = (float) src.getHeight(); + float srcRes[] = {srcWidth, srcHeight}; + + Matrix mat = new Matrix(); + mat.setRotate(angle); + mat.mapPoints(srcRes); + srcWidth = Math.abs(srcRes[0]); + srcHeight = Math.abs(srcRes[1]); + float k = Math.min(screenWidth / srcWidth, screenHeight / srcHeight); + mat.reset(); + mat.preScale(k, k); + mat.postRotate(angle); + + return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), mat, true); + } + + // 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) { + 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) { + 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) { + 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; + } + return 0; + } + offset += 12; + length -= 12; + } + } + + 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; } } diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index 9ff0bad60b..e7b7c51011 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -161,7 +161,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { private static File mediaRecFile; private static MediaRecorder mediaRec; private File lastTakingPhoto; - private byte[] photoRawData; + private byte[] photoJpegData; private Timer photoTimer; private Camera cam; private List mSupportedPreviewSizes; @@ -1297,14 +1297,13 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { private void internalShoot() { if (!autofocus) { - cam.takePicture(null, null, new AudioVideoPhotoHandler()); + cam.takePicture(null, null, new JpegPhotoHandler()); } else { cam.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { - cam.cancelAutoFocus(); try { - cam.takePicture(null, null, new AudioVideoPhotoHandler()); + cam.takePicture(null, null, new JpegPhotoHandler()); } catch (Exception e) { logErr(e); closeRecordingMenu(); @@ -1389,7 +1388,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { recordingDone = true; if (cam != null && lastTakingPhoto != null) { try { - cam.takePicture(null, null, new AudioVideoPhotoHandler()); + cam.takePicture(null, null, new JpegPhotoHandler()); } catch (RuntimeException e) { closeRecordingMenu(); closeCamera(); @@ -1893,14 +1892,14 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { } } - public class AudioVideoPhotoHandler implements PictureCallback { + public class JpegPhotoHandler implements PictureCallback { - public AudioVideoPhotoHandler() { + public JpegPhotoHandler() { } @Override public void onPictureTaken(final byte[] data, Camera camera) { - photoRawData = data; + photoJpegData = data; if (AV_PHOTO_PLAY_SOUND.get()) { if (sp != null && shotId != 0) { @@ -1937,7 +1936,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { public synchronized void shootAgain() { cancelPhotoTimer(); - photoRawData = null; + photoJpegData = null; if (cam != null) { try { cam.cancelAutoFocus(); @@ -1960,18 +1959,18 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { public synchronized void finishPhotoRecording(boolean cancel) { cancelPhotoTimer(); - if (photoRawData != null && photoRawData.length > 0 && lastTakingPhoto != null) { + if (photoJpegData != null && photoJpegData.length > 0 && lastTakingPhoto != null) { try { if (!cancel) { FileOutputStream fos = new FileOutputStream(lastTakingPhoto); - fos.write(photoRawData); + fos.write(photoJpegData); fos.close(); indexFile(true, lastTakingPhoto); } } catch (Exception error) { logErr(error); } finally { - photoRawData = null; + photoJpegData = null; closeRecordingMenu(); if (!cancel) { finishRecording();