diff options
Diffstat (limited to 'src/photos/JfifSupport.vala')
-rw-r--r-- | src/photos/JfifSupport.vala | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/photos/JfifSupport.vala b/src/photos/JfifSupport.vala new file mode 100644 index 0000000..12ac80a --- /dev/null +++ b/src/photos/JfifSupport.vala @@ -0,0 +1,236 @@ +/* Copyright 2010-2014 Yorba Foundation + * + * This software is licensed under the GNU LGPL (version 2.1 or later). + * See the COPYING file in this distribution. + */ + +public class JfifFileFormatDriver : PhotoFileFormatDriver { + private static JfifFileFormatDriver instance = null; + + public static void init() { + instance = new JfifFileFormatDriver(); + JfifFileFormatProperties.init(); + } + + public static JfifFileFormatDriver get_instance() { + return instance; + } + + public override PhotoFileFormatProperties get_properties() { + return JfifFileFormatProperties.get_instance(); + } + + public override PhotoFileReader create_reader(string filepath) { + return new JfifReader(filepath); + } + + public override PhotoMetadata create_metadata() { + return new PhotoMetadata(); + } + + 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 JfifWriter(filepath); + } + + public override PhotoFileMetadataWriter? create_metadata_writer(string filepath) { + return new JfifMetadataWriter(filepath); + } + + public override PhotoFileSniffer create_sniffer(File file, PhotoFileSniffer.Options options) { + return new JfifSniffer(file, options); + } +} + +public class JfifFileFormatProperties : PhotoFileFormatProperties { + private static string[] KNOWN_EXTENSIONS = { + "jpg", "jpeg", "jpe" + }; + + private static string[] KNOWN_MIME_TYPES = { + "image/jpeg" + }; + + private static JfifFileFormatProperties instance = null; + + public static void init() { + instance = new JfifFileFormatProperties(); + } + + public static JfifFileFormatProperties get_instance() { + return instance; + } + + public override PhotoFileFormat get_file_format() { + return PhotoFileFormat.JFIF; + } + + public override PhotoFileFormatFlags get_flags() { + return PhotoFileFormatFlags.NONE; + } + + public override string get_default_extension() { + return "jpg"; + } + + public override string get_user_visible_name() { + return _("JPEG"); + } + + 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 JfifSniffer : GdkSniffer { + public JfifSniffer(File file, PhotoFileSniffer.Options options) { + base (file, options); + } + + public override DetectedPhotoInformation? sniff() throws Error { + if (!Jpeg.is_jpeg(file)) + return null; + + DetectedPhotoInformation? detected = base.sniff(); + if (detected == null) + return null; + + return (detected.file_format == PhotoFileFormat.JFIF) ? detected : null; + } +} + +public class JfifReader : GdkReader { + public JfifReader(string filepath) { + base (filepath, PhotoFileFormat.JFIF); + } +} + +public class JfifWriter : PhotoFileWriter { + public JfifWriter(string filepath) { + base (filepath, PhotoFileFormat.JFIF); + } + + public override void write(Gdk.Pixbuf pixbuf, Jpeg.Quality quality) throws Error { + pixbuf.save(get_filepath(), "jpeg", "quality", quality.get_pct_text()); + } +} + +public class JfifMetadataWriter : PhotoFileMetadataWriter { + public JfifMetadataWriter(string filepath) { + base (filepath, PhotoFileFormat.JFIF); + } + + public override void write_metadata(PhotoMetadata metadata) throws Error { + metadata.write_to_file(get_file()); + } +} + +namespace Jpeg { + public const uint8 MARKER_PREFIX = 0xFF; + + public enum Marker { + // Could also be 0xFF according to spec + INVALID = 0x00, + + SOI = 0xD8, + EOI = 0xD9, + + APP0 = 0xE0, + APP1 = 0xE1; + + public uint8 get_byte() { + return (uint8) this; + } + } + + public enum Quality { + LOW = 50, + MEDIUM = 75, + HIGH = 90, + MAXIMUM = 100; + + public int get_pct() { + return (int) this; + } + + public string get_pct_text() { + return "%d".printf((int) this); + } + + public static Quality[] get_all() { + return { LOW, MEDIUM, HIGH, MAXIMUM }; + } + + public string? to_string() { + switch (this) { + case LOW: + return _("Low (%d%%)").printf((int) this); + + case MEDIUM: + return _("Medium (%d%%)").printf((int) this); + + case HIGH: + return _("High (%d%%)").printf((int) this); + + case MAXIMUM: + return _("Maximum (%d%%)").printf((int) this); + } + + warn_if_reached(); + + return null; + } + } + + public bool is_jpeg(File file) throws Error { + FileInputStream fins = file.read(null); + + Marker marker; + int segment_length = read_marker(fins, out marker); + + // for now, merely checking for SOI + return (marker == Marker.SOI) && (segment_length == 0); + } + + private int read_marker(FileInputStream fins, out Jpeg.Marker marker) throws Error { + marker = Jpeg.Marker.INVALID; + + DataInputStream dins = new DataInputStream(fins); + dins.set_byte_order(DataStreamByteOrder.BIG_ENDIAN); + + if (dins.read_byte() != Jpeg.MARKER_PREFIX) + return -1; + + marker = (Jpeg.Marker) dins.read_byte(); + if ((marker == Jpeg.Marker.SOI) || (marker == Jpeg.Marker.EOI)) { + // no length + return 0; + } + + uint16 length = dins.read_uint16(); + if (length < 2) { + debug("Invalid length %Xh at ofs %" + int64.FORMAT + "Xh", length, fins.tell() - 2); + + return -1; + } + + // account for two length bytes already read + return length - 2; + } +} + |