[shotwell/wip/phako/gif] Initial GIF support



commit f31c0a589a7718eb05525dfa0170593e1b5f02e5
Author: Jens Georg <mail jensge org>
Date:   Wed Mar 21 21:16:12 2018 +0100

    Initial GIF support

 src/meson.build                 |    1 +
 src/photos/GifSupport.vala      |  179 +++++++++++++++++++++++++++++++++++++++
 src/photos/PhotoFileFormat.vala |   23 +++++-
 3 files changed, 201 insertions(+), 2 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index ba260e7..6a901ef 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -58,6 +58,7 @@ executable('shotwell',
             'photos/PhotoMetadata.vala',
             'photos/GRaw.vala',
             'photos/GdkSupport.vala',
+            'photos/GifSupport.vala',
             'photos/JfifSupport.vala',
             'photos/BmpSupport.vala',
             'photos/RawSupport.vala',
diff --git a/src/photos/GifSupport.vala b/src/photos/GifSupport.vala
new file mode 100644
index 0000000..253c49c
--- /dev/null
+++ b/src/photos/GifSupport.vala
@@ -0,0 +1,179 @@
+/* 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.
+ */
+
+namespace Photos {
+
+class GifFileFormatProperties : PhotoFileFormatProperties {
+    private static string[] KNOWN_EXTENSIONS = { "gif" };
+    private static string[] KNOWN_MIME_TYPES = { "image/gif" };
+
+    private static GifFileFormatProperties instance = null;
+
+    public static void init() {
+        instance = new GifFileFormatProperties();
+    }
+    
+    public static GifFileFormatProperties get_instance() {
+        return instance;
+    }
+    
+    public override PhotoFileFormat get_file_format() {
+        return PhotoFileFormat.PNG;
+    }
+    
+    public override PhotoFileFormatFlags get_flags() {
+        return PhotoFileFormatFlags.NONE;
+    }
+
+    public override string get_user_visible_name() {
+        return _("GIF");
+    }
+
+    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 GifSniffer : GdkSniffer {
+    private const uint8[] MAGIC_SEQUENCE = { (uint8)'G', (uint8)'I', (uint8)'F', (uint8)'8' };
+
+    public GifSniffer(File file, PhotoFileSniffer.Options options) {
+        base (file, options);
+    }
+
+    private static bool is_gif_file(File file) throws Error {
+        FileInputStream instream = file.read(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_gif_file(file))
+            return null;
+        
+        DetectedPhotoInformation? detected = base.sniff(out is_corrupted);
+
+        if (detected == null)
+            return null;
+        
+        return (detected.file_format == PhotoFileFormat.GIF) ? detected : null;
+    }
+}
+
+public class GifReader : GdkReader {
+    public GifReader(string filepath) {
+        base (filepath, PhotoFileFormat.PNG);
+    }
+    
+    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 GifMetadataWriter : PhotoFileMetadataWriter {
+    public GifMetadataWriter(string filepath) {
+        base (filepath, PhotoFileFormat.GIF);
+    }
+    
+    public override void write_metadata(PhotoMetadata metadata) throws Error {
+        metadata.write_to_file(get_file());
+    }
+}
+
+public class GifFileFormatDriver : PhotoFileFormatDriver {
+    private static GifFileFormatDriver instance = null;
+    
+    public static void init() {
+        instance = new GifFileFormatDriver();
+        GifFileFormatProperties.init();
+    }
+    
+    public static GifFileFormatDriver get_instance() {
+        return instance;
+    }
+    
+    public override PhotoFileFormatProperties get_properties() {
+        return GifFileFormatProperties.get_instance();
+    }
+    
+    public override PhotoFileReader create_reader(string filepath) {
+        return new GifReader(filepath);
+    }
+    
+    public override bool can_write_image() {
+        return false;
+    }
+    
+    public override bool can_write_metadata() {
+        return true;
+    }
+    
+    public override PhotoFileWriter? create_writer(string filepath) {
+        return null;
+    }
+    
+    public override PhotoFileMetadataWriter? create_metadata_writer(string filepath) {
+        return new GifMetadataWriter(filepath);
+    }
+    
+    public override PhotoFileSniffer create_sniffer(File file, PhotoFileSniffer.Options options) {
+        return new GifSniffer(file, options);
+    }
+    
+    public override PhotoMetadata create_metadata() {
+        return new PhotoMetadata();
+    }
+}
+
+}
+
diff --git a/src/photos/PhotoFileFormat.vala b/src/photos/PhotoFileFormat.vala
index 725bd1d..e642008 100644
--- a/src/photos/PhotoFileFormat.vala
+++ b/src/photos/PhotoFileFormat.vala
@@ -57,12 +57,13 @@ public enum PhotoFileFormat {
     PNG,
     TIFF,
     BMP,
+    GIF,
     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 };
+        return { JFIF, RAW, PNG, TIFF, BMP, GIF };
     }
     
     public static PhotoFileFormat[] get_writeable() {
@@ -137,6 +138,9 @@ public enum PhotoFileFormat {
 
             case BMP:
                 return 4;
+
+            case GIF:
+                return 5;
             
             case UNKNOWN:
             default:
@@ -161,6 +165,9 @@ public enum PhotoFileFormat {
 
             case 4:
                 return BMP;
+
+            case 5:
+                return GIF;
                             
             default:
                 return UNKNOWN;
@@ -184,7 +191,9 @@ public enum PhotoFileFormat {
 
             case GPhoto.MIME.BMP:
                 return PhotoFileFormat.BMP;
-            
+
+            // GPhoto does not have GIF
+
             default:
                 // check file extension against those we support
                 return PhotoFileFormat.UNKNOWN;
@@ -205,6 +214,9 @@ public enum PhotoFileFormat {
             
             case "bmp":
                 return PhotoFileFormat.BMP;
+
+            case "gif":
+                return PhotoFileFormat.GIF;
             
             default:
                 return PhotoFileFormat.UNKNOWN;
@@ -233,6 +245,10 @@ public enum PhotoFileFormat {
                 Photos.BmpFileFormatDriver.init();
                 break;
 
+            case GIF:
+                Photos.GifFileFormatDriver.init();
+                break;
+
             default:
                 error("Unsupported file format %s", this.to_string());
         }
@@ -255,6 +271,9 @@ public enum PhotoFileFormat {
             case BMP:
                 return Photos.BmpFileFormatDriver.get_instance();
 
+            case GIF:
+                return Photos.GifFileFormatDriver.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]