improved support for psd and other unrecognized formats
This commit is contained in:
parent
5029b19ebe
commit
2f29a970da
5 changed files with 108 additions and 82 deletions
|
@ -66,6 +66,10 @@ public class ImageByteStreamHandler implements EventChannel.StreamHandler {
|
|||
handler.post(() -> eventSink.endOfStream());
|
||||
}
|
||||
|
||||
// Supported image formats:
|
||||
// - Flutter (as of v1.20): JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP
|
||||
// - Android: https://developer.android.com/guide/topics/media/media-formats#image-formats
|
||||
// - Glide: https://github.com/bumptech/glide/blob/master/library/src/main/java/com/bumptech/glide/load/ImageHeaderParser.java
|
||||
private void getImage() {
|
||||
if (mimeType != null && mimeType.startsWith(MimeTypes.VIDEO)) {
|
||||
RequestOptions options = new RequestOptions()
|
||||
|
@ -91,42 +95,40 @@ public class ImageByteStreamHandler implements EventChannel.StreamHandler {
|
|||
} finally {
|
||||
Glide.with(activity).clear(target);
|
||||
}
|
||||
} else if (MimeTypes.DNG.equals(mimeType) || MimeTypes.HEIC.equals(mimeType) || MimeTypes.HEIF.equals(mimeType)) {
|
||||
// as of Flutter v1.20, Dart Image.memory cannot decode DNG/HEIC/HEIF images
|
||||
// so we convert the image on platform side first
|
||||
FutureTarget<Bitmap> target = Glide.with(activity)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.submit();
|
||||
try {
|
||||
Bitmap bitmap = target.get();
|
||||
if (bitmap != null) {
|
||||
bitmap = TransformationUtils.rotateImage(bitmap, orientationDegrees);
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
// we compress the bitmap because Dart Image.memory cannot decode the raw bytes
|
||||
// Bitmap.CompressFormat.PNG is slower than JPEG
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
||||
success(stream.toByteArray());
|
||||
} else {
|
||||
error("getImage-image-decode-null", "failed to get image from uri=" + uri, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
error("getImage-image-decode-exception", "failed to get image from uri=" + uri, e.getMessage());
|
||||
} finally {
|
||||
Glide.with(activity).clear(target);
|
||||
}
|
||||
} else {
|
||||
ContentResolver cr = activity.getContentResolver();
|
||||
if (MimeTypes.DNG.equals(mimeType) || MimeTypes.HEIC.equals(mimeType) || MimeTypes.HEIF.equals(mimeType)) {
|
||||
// as of Flutter v1.20, Dart Image.memory cannot decode DNG/HEIC/HEIF images
|
||||
// so we convert the image on platform side first
|
||||
FutureTarget<Bitmap> target = Glide.with(activity)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.submit();
|
||||
try {
|
||||
Bitmap bitmap = target.get();
|
||||
if (bitmap != null) {
|
||||
bitmap = TransformationUtils.rotateImage(bitmap, orientationDegrees);
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
// we compress the bitmap because Dart Image.memory cannot decode the raw bytes
|
||||
// Bitmap.CompressFormat.PNG is slower than JPEG
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
||||
success(stream.toByteArray());
|
||||
} else {
|
||||
error("getImage-image-decode-null", "failed to get image from uri=" + uri, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
error("getImage-image-decode-exception", "failed to get image from uri=" + uri, e.getMessage());
|
||||
} finally {
|
||||
Glide.with(activity).clear(target);
|
||||
}
|
||||
} else {
|
||||
try (InputStream is = cr.openInputStream(uri)) {
|
||||
if (is != null) {
|
||||
streamBytes(is);
|
||||
} else {
|
||||
error("getImage-image-read-null", "failed to get image from uri=" + uri, null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
error("getImage-image-read-exception", "failed to get image from uri=" + uri, e.getMessage());
|
||||
try (InputStream is = cr.openInputStream(uri)) {
|
||||
if (is != null) {
|
||||
streamBytes(is);
|
||||
} else {
|
||||
error("getImage-image-read-null", "failed to get image from uri=" + uri, null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
error("getImage-image-read-exception", "failed to get image from uri=" + uri, e.getMessage());
|
||||
}
|
||||
}
|
||||
endOfStream();
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.drew.metadata.exif.ExifIFD0Directory;
|
|||
import com.drew.metadata.jpeg.JpegDirectory;
|
||||
import com.drew.metadata.mp4.Mp4Directory;
|
||||
import com.drew.metadata.mp4.media.Mp4VideoDirectory;
|
||||
import com.drew.metadata.photoshop.PsdHeaderDirectory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -191,48 +192,69 @@ public class SourceImageEntry {
|
|||
try (InputStream is = StorageUtils.openInputStream(context, uri)) {
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(is);
|
||||
|
||||
if (MimeTypes.JPEG.equals(sourceMimeType)) {
|
||||
for (JpegDirectory dir : metadata.getDirectoriesOfType(JpegDirectory.class)) {
|
||||
if (dir.containsTag(JpegDirectory.TAG_IMAGE_WIDTH)) {
|
||||
width = dir.getInt(JpegDirectory.TAG_IMAGE_WIDTH);
|
||||
switch (sourceMimeType) {
|
||||
case MimeTypes.JPEG:
|
||||
for (JpegDirectory dir : metadata.getDirectoriesOfType(JpegDirectory.class)) {
|
||||
if (dir.containsTag(JpegDirectory.TAG_IMAGE_WIDTH)) {
|
||||
width = dir.getInt(JpegDirectory.TAG_IMAGE_WIDTH);
|
||||
}
|
||||
if (dir.containsTag(JpegDirectory.TAG_IMAGE_HEIGHT)) {
|
||||
height = dir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
|
||||
}
|
||||
}
|
||||
if (dir.containsTag(JpegDirectory.TAG_IMAGE_HEIGHT)) {
|
||||
height = dir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
|
||||
break;
|
||||
case MimeTypes.MP4:
|
||||
for (Mp4VideoDirectory dir : metadata.getDirectoriesOfType(Mp4VideoDirectory.class)) {
|
||||
if (dir.containsTag(Mp4VideoDirectory.TAG_WIDTH)) {
|
||||
width = dir.getInt(Mp4VideoDirectory.TAG_WIDTH);
|
||||
}
|
||||
if (dir.containsTag(Mp4VideoDirectory.TAG_HEIGHT)) {
|
||||
height = dir.getInt(Mp4VideoDirectory.TAG_HEIGHT);
|
||||
}
|
||||
}
|
||||
for (Mp4Directory dir : metadata.getDirectoriesOfType(Mp4Directory.class)) {
|
||||
if (dir.containsTag(Mp4Directory.TAG_DURATION)) {
|
||||
durationMillis = dir.getLong(Mp4Directory.TAG_DURATION);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MimeTypes.AVI:
|
||||
for (AviDirectory dir : metadata.getDirectoriesOfType(AviDirectory.class)) {
|
||||
if (dir.containsTag(AviDirectory.TAG_WIDTH)) {
|
||||
width = dir.getInt(AviDirectory.TAG_WIDTH);
|
||||
}
|
||||
if (dir.containsTag(AviDirectory.TAG_HEIGHT)) {
|
||||
height = dir.getInt(AviDirectory.TAG_HEIGHT);
|
||||
}
|
||||
if (dir.containsTag(AviDirectory.TAG_DURATION)) {
|
||||
durationMillis = dir.getLong(AviDirectory.TAG_DURATION);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MimeTypes.PSD:
|
||||
for (PsdHeaderDirectory dir : metadata.getDirectoriesOfType(PsdHeaderDirectory.class)) {
|
||||
if (dir.containsTag(PsdHeaderDirectory.TAG_IMAGE_WIDTH)) {
|
||||
width = dir.getInt(PsdHeaderDirectory.TAG_IMAGE_WIDTH);
|
||||
}
|
||||
if (dir.containsTag(PsdHeaderDirectory.TAG_IMAGE_HEIGHT)) {
|
||||
height = dir.getInt(PsdHeaderDirectory.TAG_IMAGE_HEIGHT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (ExifIFD0Directory dir : metadata.getDirectoriesOfType(ExifIFD0Directory.class)) {
|
||||
if (dir.containsTag(ExifIFD0Directory.TAG_IMAGE_WIDTH)) {
|
||||
width = dir.getInt(ExifIFD0Directory.TAG_IMAGE_WIDTH);
|
||||
}
|
||||
for (ExifIFD0Directory dir : metadata.getDirectoriesOfType(ExifIFD0Directory.class)) {
|
||||
if (dir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
|
||||
orientationDegrees = getOrientationDegreesForExifCode(dir.getInt(ExifIFD0Directory.TAG_ORIENTATION));
|
||||
}
|
||||
if (dir.containsTag(ExifIFD0Directory.TAG_DATETIME)) {
|
||||
sourceDateTakenMillis = dir.getDate(ExifIFD0Directory.TAG_DATETIME, null, TimeZone.getDefault()).getTime();
|
||||
}
|
||||
if (dir.containsTag(ExifIFD0Directory.TAG_IMAGE_HEIGHT)) {
|
||||
height = dir.getInt(ExifIFD0Directory.TAG_IMAGE_HEIGHT);
|
||||
}
|
||||
} else if (MimeTypes.MP4.equals(sourceMimeType)) {
|
||||
for (Mp4VideoDirectory dir : metadata.getDirectoriesOfType(Mp4VideoDirectory.class)) {
|
||||
if (dir.containsTag(Mp4VideoDirectory.TAG_WIDTH)) {
|
||||
width = dir.getInt(Mp4VideoDirectory.TAG_WIDTH);
|
||||
}
|
||||
if (dir.containsTag(Mp4VideoDirectory.TAG_HEIGHT)) {
|
||||
height = dir.getInt(Mp4VideoDirectory.TAG_HEIGHT);
|
||||
}
|
||||
if (dir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
|
||||
orientationDegrees = getOrientationDegreesForExifCode(dir.getInt(ExifIFD0Directory.TAG_ORIENTATION));
|
||||
}
|
||||
for (Mp4Directory dir : metadata.getDirectoriesOfType(Mp4Directory.class)) {
|
||||
if (dir.containsTag(Mp4Directory.TAG_DURATION)) {
|
||||
durationMillis = dir.getLong(Mp4Directory.TAG_DURATION);
|
||||
}
|
||||
}
|
||||
} else if (MimeTypes.AVI.equals(sourceMimeType)) {
|
||||
for (AviDirectory dir : metadata.getDirectoriesOfType(AviDirectory.class)) {
|
||||
if (dir.containsTag(AviDirectory.TAG_WIDTH)) {
|
||||
width = dir.getInt(AviDirectory.TAG_WIDTH);
|
||||
}
|
||||
if (dir.containsTag(AviDirectory.TAG_HEIGHT)) {
|
||||
height = dir.getInt(AviDirectory.TAG_HEIGHT);
|
||||
}
|
||||
if (dir.containsTag(AviDirectory.TAG_DURATION)) {
|
||||
durationMillis = dir.getLong(AviDirectory.TAG_DURATION);
|
||||
}
|
||||
if (dir.containsTag(ExifIFD0Directory.TAG_DATETIME)) {
|
||||
sourceDateTakenMillis = dir.getDate(ExifIFD0Directory.TAG_DATETIME, null, TimeZone.getDefault()).getTime();
|
||||
}
|
||||
}
|
||||
} catch (IOException | ImageProcessingException | MetadataException | NoClassDefFoundError e) {
|
||||
|
|
|
@ -180,20 +180,13 @@ public class MediaStoreImageProvider extends ImageProvider {
|
|||
// they are valid but miss some attributes, such as width, height, orientation
|
||||
SourceImageEntry entry = new SourceImageEntry(entryMap).fillPreCatalogMetadata(context);
|
||||
entryMap = entry.toMap();
|
||||
width = entry.width != null ? entry.width : 0;
|
||||
height = entry.height != null ? entry.height : 0;
|
||||
}
|
||||
|
||||
if ((width <= 0 || height <= 0) && needSize(mimeType)) {
|
||||
// this is probably not a real image, like "/storage/emulated/0", so we skip it
|
||||
Log.w(LOG_TAG, "failed to get size for uri=" + itemUri + ", path=" + path + ", mimeType=" + mimeType);
|
||||
} else {
|
||||
newEntryHandler.handleEntry(entryMap);
|
||||
if (newEntryCount % 30 == 0) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
newEntryCount++;
|
||||
newEntryHandler.handleEntry(entryMap);
|
||||
if (newEntryCount % 30 == 0) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
newEntryCount++;
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
|
|
|
@ -8,6 +8,7 @@ public class MimeTypes {
|
|||
public static final String HEIF = "image/heif";
|
||||
public static final String JPEG = "image/jpeg";
|
||||
public static final String PNG = "image/png";
|
||||
public static final String PSD = "image/x-photoshop";
|
||||
public static final String SVG = "image/svg+xml";
|
||||
public static final String WEBP = "image/webp";
|
||||
|
||||
|
|
|
@ -50,7 +50,15 @@ class MimeFilter extends CollectionFilter {
|
|||
};
|
||||
|
||||
static String displayType(String mime) {
|
||||
return mime.toUpperCase().replaceFirst(RegExp('.*/(X-)?'), '').replaceFirst('+XML', '').replaceFirst('VND.', '');
|
||||
final patterns = [
|
||||
RegExp('.*/'), // remove type, keep subtype
|
||||
RegExp('(X-|VND.)'), // noisy prefixes
|
||||
'+XML', // noisy suffix
|
||||
RegExp('ADOBE[-\.]'), // for DNG, PSD...
|
||||
];
|
||||
mime = mime.toUpperCase();
|
||||
patterns.forEach((pattern) => mime = mime.replaceFirst(pattern, ''));
|
||||
return mime;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in a new issue