1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
/* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public abstract class GdkReader : PhotoFileReader {
protected GdkReader(string filepath, PhotoFileFormat file_format) {
base (filepath, file_format);
}
public override PhotoMetadata read_metadata() throws Error {
PhotoMetadata metadata = new PhotoMetadata();
metadata.read_from_file(get_file());
return metadata;
}
public override Gdk.Pixbuf unscaled_read() throws Error {
return new Gdk.Pixbuf.from_file(get_filepath());
}
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 abstract class GdkSniffer : PhotoFileSniffer {
private DetectedPhotoInformation detected = null;
private bool size_ready = false;
private bool area_prepared = false;
protected GdkSniffer(File file, PhotoFileSniffer.Options options) {
base (file, options);
}
public override DetectedPhotoInformation? sniff(out bool is_corrupted) throws Error {
detected = new DetectedPhotoInformation();
Gdk.PixbufLoader pixbuf_loader = new Gdk.PixbufLoader();
pixbuf_loader.size_prepared.connect(on_size_prepared);
pixbuf_loader.area_prepared.connect(on_area_prepared);
// valac chokes on the ternary operator here
Checksum? md5_checksum = null;
if (calc_md5)
md5_checksum = new Checksum(ChecksumType.MD5);
detected.metadata = new PhotoMetadata();
try {
detected.metadata.read_from_file(file);
} catch (Error err) {
// no metadata detected
detected.metadata = null;
}
if (calc_md5 && detected.metadata != null) {
detected.exif_md5 = detected.metadata.exif_hash();
detected.thumbnail_md5 = detected.metadata.thumbnail_hash();
}
// if no MD5, don't read as much, as the needed info will probably be gleaned
// in the first 8K to 16K
uint8[] buffer = calc_md5 ? new uint8[64 * 1024] : new uint8[8 * 1024];
size_t count = 0;
// loop through until all conditions we're searching for are met
FileInputStream fins = file.read(null);
for (;;) {
size_t bytes_read = fins.read(buffer, null);
if (bytes_read <= 0)
break;
count += bytes_read;
if (calc_md5)
md5_checksum.update(buffer, bytes_read);
// keep parsing the image until the size is discovered
if (!size_ready || !area_prepared)
pixbuf_loader.write(buffer[0:bytes_read]);
// if not searching for anything else, exit
if (!calc_md5 && size_ready && area_prepared)
break;
}
// PixbufLoader throws an error if you close it with an incomplete image, so trap this
try {
pixbuf_loader.close();
} catch (Error err) {
}
if (fins != null)
fins.close(null);
if (calc_md5)
detected.md5 = md5_checksum.get_string();
// if size and area are not ready, treat as corrupted file (entire file was read)
is_corrupted = !size_ready || !area_prepared;
return detected;
}
private void on_size_prepared(Gdk.PixbufLoader loader, int width, int height) {
detected.image_dim = Dimensions(width, height);
size_ready = true;
}
private void on_area_prepared(Gdk.PixbufLoader pixbuf_loader) {
Gdk.Pixbuf? pixbuf = pixbuf_loader.get_pixbuf();
if (pixbuf == null)
return;
detected.colorspace = pixbuf.get_colorspace();
detected.channels = pixbuf.get_n_channels();
detected.bits_per_channel = pixbuf.get_bits_per_sample();
unowned Gdk.PixbufFormat format = pixbuf_loader.get_format();
detected.format_name = format.get_name();
debug("Pixbuf detected format name: %s", detected.format_name);
detected.file_format = PhotoFileFormat.from_pixbuf_name(detected.format_name);
area_prepared = true;
}
}
|