[shotwell: 1/2] Add in initial AVIF support fixing #286
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell: 1/2] Add in initial AVIF support fixing #286
- Date: Sat, 2 Apr 2022 12:49:14 +0000 (UTC)
commit 23e16e8e4891cc54e3b46e8f23c343636d530ca4
Author: Mike Auty <mike auty gmail com>
Date: Fri Mar 4 23:46:49 2022 +0000
Add in initial AVIF support fixing #286
data/org.gnome.Shotwell-Viewer.desktop.in | 2 +-
po/POTFILES.in | 1 +
src/meson.build | 1 +
src/photos/AvifSupport.vala | 189 ++++++++++++++++++++++++++++++
src/photos/PhotoFileFormat.vala | 21 +++-
5 files changed, 211 insertions(+), 3 deletions(-)
---
diff --git a/data/org.gnome.Shotwell-Viewer.desktop.in b/data/org.gnome.Shotwell-Viewer.desktop.in
index 7e4f71e1..f1405de2 100644
--- a/data/org.gnome.Shotwell-Viewer.desktop.in
+++ b/data/org.gnome.Shotwell-Viewer.desktop.in
@@ -8,7 +8,7 @@ Icon=org.gnome.Shotwell
Terminal=false
NoDisplay=true
Type=Application
-MimeType=image/jpeg;image/jpg;image/pjpeg;image/png;image/tiff;image/x-3fr;image/x-adobe-dng;image/x-arw;image/x-bay;image/x-bmp;image/x-canon-cr2;image/x-canon-cr3;image/x-canon-crw;image/x-cap;image/x-cr2;image/x-crw;image/x-dcr;image/x-dcraw;image/x-dcs;image/x-dng;image/x-drf;image/x-eip;image/x-erf;image/x-fff;image/x-fuji-raf;image/x-iiq;image/x-k25;image/x-kdc;image/x-mef;image/x-minolta-mrw;image/x-mos;image/x-mrw;image/x-nef;image/x-nikon-nef;image/x-nrw;image/x-olympus-orf;image/x-orf;image/x-panasonic-raw;image/x-pef;image/x-pentax-pef;image/x-png;image/x-ptx;image/x-pxn;image/x-r3d;image/x-raf;image/x-raw;image/x-rw2;image/x-rwl;image/x-rwz;image/x-sigma-x3f;image/x-sony-arw;image/x-sony-sr2;image/x-sony-srf;image/x-sr2;image/x-srf;image/x-x3f;image/gif;image/webp;
+MimeType=image/jpeg;image/jpg;image/pjpeg;image/png;image/tiff;image/x-3fr;image/x-adobe-dng;image/x-arw;image/x-bay;image/x-bmp;image/x-canon-cr2;image/x-canon-cr3;image/x-canon-crw;image/x-cap;image/x-cr2;image/x-crw;image/x-dcr;image/x-dcraw;image/x-dcs;image/x-dng;image/x-drf;image/x-eip;image/x-erf;image/x-fff;image/x-fuji-raf;image/x-iiq;image/x-k25;image/x-kdc;image/x-mef;image/x-minolta-mrw;image/x-mos;image/x-mrw;image/x-nef;image/x-nikon-nef;image/x-nrw;image/x-olympus-orf;image/x-orf;image/x-panasonic-raw;image/x-pef;image/x-pentax-pef;image/x-png;image/x-ptx;image/x-pxn;image/x-r3d;image/x-raf;image/x-raw;image/x-rw2;image/x-rwl;image/x-rwz;image/x-sigma-x3f;image/x-sony-arw;image/x-sony-sr2;image/x-sony-srf;image/x-sr2;image/x-srf;image/x-x3f;image/gif;image/webp;image/avif;
Categories=Graphics;Viewer;Photography;GNOME;GTK;
X-GIO-NoFuse=true
X-GNOME-Gettext-Domain=shotwell
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 30506a1a..079c0cc9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -137,6 +137,7 @@ src/photos/PngSupport.vala
src/photos/RawSupport.vala
src/photos/TiffSupport.vala
src/photos/WebPSupport.vala
+src/photos/AvifSupport.vala
src/Photo.vala
src/Printing.vala
src/Properties.vala
diff --git a/src/meson.build b/src/meson.build
index 8b1ab4b5..d56387da 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -91,6 +91,7 @@ executable(
'photos/PngSupport.vala',
'photos/TiffSupport.vala',
'photos/WebPSupport.vala',
+ 'photos/AvifSupport.vala',
'plugins/Plugins.vala',
'plugins/StandardHostInterface.vala',
'plugins/ManifestWidget.vala',
diff --git a/src/photos/AvifSupport.vala b/src/photos/AvifSupport.vala
new file mode 100644
index 00000000..86d4f05b
--- /dev/null
+++ b/src/photos/AvifSupport.vala
@@ -0,0 +1,189 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ *
+ * This software is licensed under the GNU LGPL (version 2.1 or later).
+ * See the COPYING file in this distribution.
+ */
+
+class AvifFileFormatProperties : PhotoFileFormatProperties {
+ private static string[] KNOWN_EXTENSIONS = { "avif" };
+ private static string[] KNOWN_MIME_TYPES = { "image/avif" };
+
+ private static AvifFileFormatProperties instance = null;
+
+ public static void init() {
+ instance = new AvifFileFormatProperties();
+ }
+
+ public static AvifFileFormatProperties get_instance() {
+ return instance;
+ }
+
+ public override PhotoFileFormat get_file_format() {
+ return PhotoFileFormat.AVIF;
+ }
+
+ public override PhotoFileFormatFlags get_flags() {
+ return PhotoFileFormatFlags.NONE;
+ }
+
+ public override string get_user_visible_name() {
+ return _("AVIF");
+ }
+
+ public override string get_default_extension() {
+ return KNOWN_EXTENSIONS[0];
+ }
+
+ public override string[] get_known_extensions() {
+ return KNOWN_EXTENSIONS;
+ }
+
+ public override string get_default_mime_type() {
+ return KNOWN_MIME_TYPES[0];
+ }
+
+ public override string[] get_mime_types() {
+ return KNOWN_MIME_TYPES;
+ }
+}
+
+public class AvifSniffer : GdkSniffer {
+ private const uint8[] MAGIC_SEQUENCE = { 102, 116, 121, 112, 97, 118, 105, 102 };
+
+ public AvifSniffer(File file, PhotoFileSniffer.Options options) {
+ base (file, options);
+ }
+
+ private static bool is_avif_file(File file) throws Error {
+ FileInputStream instream = file.read(null);
+
+ // Read out first four bytes
+ uint8[] unknown_start = new uint8[4];
+
+ instream.read(unknown_start, null);
+
+ uint8[] file_lead_sequence = new uint8[MAGIC_SEQUENCE.length];
+
+ instream.read(file_lead_sequence, null);
+
+ for (int i = 0; i < MAGIC_SEQUENCE.length; i++) {
+ if (file_lead_sequence[i] != MAGIC_SEQUENCE[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
+ // Rely on GdkSniffer to detect corruption
+ is_corrupted = false;
+
+ if (!is_avif_file(file))
+ return null;
+
+ DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
+ if (detected == null)
+ return null;
+
+ return (detected.file_format == PhotoFileFormat.AVIF) ? detected : null;
+ }
+}
+
+public class AvifReader : GdkReader {
+ public AvifReader(string filepath) {
+ base (filepath, PhotoFileFormat.AVIF);
+ }
+
+ public override Gdk.Pixbuf scaled_read(Dimensions full, Dimensions scaled) throws Error {
+ Gdk.Pixbuf result = null;
+ /* if we encounter a situation where there are two orders of magnitude or more of
+ difference between the full image size and the scaled size, and if the full image
+ size has five or more decimal digits of precision, Gdk.Pixbuf.from_file_at_scale( ) can
+ fail due to what appear to be floating-point round-off issues. This isn't surprising,
+ since 32-bit floats only have 6-7 decimal digits of precision in their mantissa. In
+ this case, we prefetch the image at a larger scale and then downsample it to the
+ desired scale as a post-process step. This short-circuits Gdk.Pixbuf's buggy
+ scaling code. */
+ if (((full.width > 9999) || (full.height > 9999)) && ((scaled.width < 100) ||
+ (scaled.height < 100))) {
+ Dimensions prefetch_dimensions = full.get_scaled_by_constraint(1000,
+ ScaleConstraint.DIMENSIONS);
+
+ result = new Gdk.Pixbuf.from_file_at_scale(get_filepath(), prefetch_dimensions.width,
+ prefetch_dimensions.height, false);
+
+ result = result.scale_simple(scaled.width, scaled.height, Gdk.InterpType.HYPER);
+ } else {
+ result = new Gdk.Pixbuf.from_file_at_scale(get_filepath(), scaled.width,
+ scaled.height, false);
+ }
+
+ return result;
+ }
+}
+
+public class AvifWriter : PhotoFileWriter {
+ public AvifWriter(string filepath) {
+ base (filepath, PhotoFileFormat.AVIF);
+ }
+
+ public override void write(Gdk.Pixbuf pixbuf, Jpeg.Quality quality) throws Error {
+ pixbuf.save(get_filepath(), "avif", "quality", "90", null);
+ }
+}
+
+public class AvifMetadataWriter : PhotoFileMetadataWriter {
+ public AvifMetadataWriter(string filepath) {
+ base (filepath, PhotoFileFormat.AVIF);
+ }
+
+ public override void write_metadata(PhotoMetadata metadata) throws Error {
+ metadata.write_to_file(get_file());
+ }
+}
+
+public class AvifFileFormatDriver : PhotoFileFormatDriver {
+ private static AvifFileFormatDriver instance = null;
+
+ public static void init() {
+ instance = new AvifFileFormatDriver();
+ AvifFileFormatProperties.init();
+ }
+
+ public static AvifFileFormatDriver get_instance() {
+ return instance;
+ }
+
+ public override PhotoFileFormatProperties get_properties() {
+ return AvifFileFormatProperties.get_instance();
+ }
+
+ public override PhotoFileReader create_reader(string filepath) {
+ return new AvifReader(filepath);
+ }
+
+ public override bool can_write_image() {
+ return true;
+ }
+
+ public override bool can_write_metadata() {
+ return true;
+ }
+
+ public override PhotoFileWriter? create_writer(string filepath) {
+ return new AvifWriter(filepath);
+ }
+
+ public override PhotoFileMetadataWriter? create_metadata_writer(string filepath) {
+ return new AvifMetadataWriter(filepath);
+ }
+
+ public override PhotoFileSniffer create_sniffer(File file, PhotoFileSniffer.Options options) {
+ return new AvifSniffer(file, options);
+ }
+
+ public override PhotoMetadata create_metadata() {
+ return new PhotoMetadata();
+ }
+}
+
diff --git a/src/photos/PhotoFileFormat.vala b/src/photos/PhotoFileFormat.vala
index 94ca7521..c839b1c5 100644
--- a/src/photos/PhotoFileFormat.vala
+++ b/src/photos/PhotoFileFormat.vala
@@ -59,12 +59,13 @@ public enum PhotoFileFormat {
BMP,
GIF,
WEBP,
+ AVIF,
UNKNOWN;
// This is currently listed in the order of detection, that is, the file is examined from
// left to right. (See PhotoFileInterrogator.)
public static PhotoFileFormat[] get_supported() {
- return { JFIF, RAW, PNG, TIFF, BMP, GIF, WEBP };
+ return { JFIF, RAW, PNG, TIFF, BMP, GIF, WEBP, AVIF };
}
public static PhotoFileFormat[] get_writeable() {
@@ -146,6 +147,9 @@ public enum PhotoFileFormat {
case WEBP:
return 6;
+ case AVIF:
+ return 7;
+
case UNKNOWN:
default:
return -1;
@@ -176,6 +180,9 @@ public enum PhotoFileFormat {
case 6:
return WEBP;
+ case 7:
+ return AVIF;
+
default:
return UNKNOWN;
}
@@ -224,7 +231,10 @@ public enum PhotoFileFormat {
case "gif":
return PhotoFileFormat.GIF;
-
+
+ case "heif/avif":
+ return PhotoFileFormat.AVIF;
+
default:
return PhotoFileFormat.UNKNOWN;
}
@@ -260,6 +270,10 @@ public enum PhotoFileFormat {
Photos.WebpFileFormatDriver.init();
break;
+ case AVIF:
+ AvifFileFormatDriver.init();
+ break;
+
default:
error("Unsupported file format %s", this.to_string());
}
@@ -288,6 +302,9 @@ public enum PhotoFileFormat {
case WEBP:
return Photos.WebpFileFormatDriver.get_instance();
+ case AVIF:
+ return AvifFileFormatDriver.get_instance();
+
default:
error("Unsupported file format %s", this.to_string());
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]