diff options
Diffstat (limited to 'src/photos/JfifSupport.vala')
-rw-r--r-- | src/photos/JfifSupport.vala | 108 |
1 files changed, 92 insertions, 16 deletions
diff --git a/src/photos/JfifSupport.vala b/src/photos/JfifSupport.vala index 5ea64a5..0de45f8 100644 --- a/src/photos/JfifSupport.vala +++ b/src/photos/JfifSupport.vala @@ -103,17 +103,78 @@ public class JfifSniffer : GdkSniffer { } public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error { - // Rely on GdkSniffer to detect corruption is_corrupted = false; - - if (!Jpeg.is_jpeg(file)) - return null; - - DetectedPhotoInformation? detected = base.sniff(out is_corrupted); - if (detected == null) + if (!calc_md5) { + return fast_sniff (out is_corrupted); + } else { + if (!Jpeg.is_jpeg(file)) { + return null; + } + + // Rely on GdkSniffer to detect corruption + + DetectedPhotoInformation? detected = base.sniff(out is_corrupted); + if (detected == null) + return null; + + return (detected.file_format == PhotoFileFormat.JFIF) ? detected : null; + } + } + + private DetectedPhotoInformation? fast_sniff(out bool is_corrupted) throws Error { + is_corrupted = false; + var detected = new DetectedPhotoInformation(); + + detected.metadata = new PhotoMetadata(); + try { + detected.metadata.read_from_file(file); + } catch (Error err) { + // no metadata detected + detected.metadata = null; + } + + var fins = file.read(null); + var dins = new DataInputStream(fins); + dins.set_byte_order(DataStreamByteOrder.BIG_ENDIAN); + var seekable = (Seekable) dins; + + var marker = Jpeg.Marker.INVALID; + var length = Jpeg.read_marker_2(dins, out marker); + + if (marker != Jpeg.Marker.SOI) { return null; - - return (detected.file_format == PhotoFileFormat.JFIF) ? detected : null; + } + + length = Jpeg.read_marker_2(dins, out marker); + while (!marker.is_sof() && length > 0) { + seekable.seek(length, SeekType.CUR, null); + length = Jpeg.read_marker_2(dins, out marker); + } + + if (marker.is_sof()) { + if (length < 6) { + is_corrupted = true; + return null; + } + + // Skip precision + dins.read_byte(); + + // Next two 16 bytes are image dimensions + uint16 height = dins.read_uint16(); + uint16 width = dins.read_uint16(); + + detected.image_dim = Dimensions(width, height); + detected.colorspace = Gdk.Colorspace.RGB; + detected.channels = 3; + detected.bits_per_channel = 8; + detected.format_name = "jpeg"; + detected.file_format = PhotoFileFormat.from_pixbuf_name(detected.format_name); + } else { + is_corrupted = true; + } + + return detected; } } @@ -159,6 +220,16 @@ namespace Jpeg { public uint8 get_byte() { return (uint8) this; } + + public bool is_sof() { + // FFCn is SOF unless n is a multiple of 4 > 0 (FFC4, FFC8, FFCC) + if ((this & 0xC0) != 0xC0) { + return false; + } + + var variant = this & 0x0F; + return variant == 0 || variant % 4 != 0; + } } public enum Quality { @@ -219,12 +290,9 @@ namespace Jpeg { return is_jpeg_stream(mins); } - private int read_marker(InputStream fins, out Jpeg.Marker marker) throws Error { + private int32 read_marker_2(DataInputStream dins, 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; @@ -235,9 +303,10 @@ namespace Jpeg { } uint16 length = dins.read_uint16(); - if (length < 2 && fins is Seekable) { + var seekable = dins as Seekable; + if (length < 2 && dins != null) { debug("Invalid length %Xh at ofs %" + int64.FORMAT + "Xh", length, - (fins as Seekable).tell() - 2); + seekable.tell() - 2); return -1; } @@ -245,5 +314,12 @@ namespace Jpeg { // account for two length bytes already read return length - 2; } + + private int read_marker(InputStream fins, out Jpeg.Marker marker) throws Error { + DataInputStream dins = new DataInputStream(fins); + dins.set_byte_order(DataStreamByteOrder.BIG_ENDIAN); + + return read_marker_2(dins, out marker); + } } |