|
@@ -1,14 +1,19 @@
|
|
|
package top.zibin.luban;
|
|
|
|
|
|
import android.text.TextUtils;
|
|
|
+import android.util.Log;
|
|
|
|
|
|
import java.io.File;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.List;
|
|
|
+import java.util.logging.Logger;
|
|
|
|
|
|
enum Checker {
|
|
|
SINGLE;
|
|
|
|
|
|
+ private final String TAG = "Luban";
|
|
|
+
|
|
|
private static List<String> format = new ArrayList<>();
|
|
|
private static final String JPG = "jpg";
|
|
|
private static final String JPEG = "jpeg";
|
|
@@ -16,6 +21,8 @@ enum Checker {
|
|
|
private static final String WEBP = "webp";
|
|
|
private static final String GIF = "gif";
|
|
|
|
|
|
+ private final byte[] JPEG_SIGNATURE = new byte[]{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF};
|
|
|
+
|
|
|
static {
|
|
|
format.add(JPG);
|
|
|
format.add(JPEG);
|
|
@@ -43,6 +50,113 @@ enum Checker {
|
|
|
return suffix.contains(JPG) || suffix.contains(JPEG);
|
|
|
}
|
|
|
|
|
|
+ boolean isJPG(byte[] data) {
|
|
|
+ if (data == null || data.length < 3) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ byte[] signatureB = new byte[]{data[0], data[1], data[2]};
|
|
|
+ return Arrays.equals(JPEG_SIGNATURE, signatureB);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
|
|
|
+ */
|
|
|
+ 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) {
|
|
|
+ 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.e(TAG, "Unsupported orientation");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ offset += 12;
|
|
|
+ length -= 12;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Log.e(TAG, "Orientation not found");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
String extSuffix(String path) {
|
|
|
if (TextUtils.isEmpty(path)) {
|
|
|
return ".jpg";
|
|
@@ -58,4 +172,19 @@ enum Checker {
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
+ private 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;
|
|
|
+ }
|
|
|
}
|