summaryrefslogtreecommitdiff
path: root/src/photos/GRaw.vala
blob: 53c328c5b04886fe43a1c7aa92643beb00ed6e79 (plain)
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/* 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.
 */

namespace GRaw {

public const double HD_POWER = 2.222;
public const double HD_SLOPE = 4.5;

public const double SRGB_POWER = 2.4;
public const double SRGB_SLOPE = 12.92;

public enum Colorspace {
    RAW = 0,
    SRGB = 1,
    ADOBE = 2,
    WIDE = 3,
    PROPHOTO = 4,
    XYZ = 5
}

public errordomain Exception {
    UNSPECIFIED,
    UNSUPPORTED_FILE,
    NONEXISTANT_IMAGE,
    OUT_OF_ORDER_CALL,
    NO_THUMBNAIL,
    UNSUPPORTED_THUMBNAIL,
    OUT_OF_MEMORY,
    DATA_ERROR,
    IO_ERROR,
    CANCELLED_BY_CALLBACK,
    BAD_CROP,
    SYSTEM_ERROR
}

public enum Flip {
    FROM_SOURCE = -1,
    NONE = 0,
    UPSIDE_DOWN = 3,
    COUNTERCLOCKWISE = 5,
    CLOCKWISE = 6
}

public enum FujiRotate {
    USE = -1,
    DONT_USE = 0
}

public enum HighlightMode {
    CLIP = 0,
    UNCLIP = 1,
    BLEND = 2,
    REBUILD = 3
}

public enum InterpolationQuality {
    LINEAR = 0,
    VNG = 1,
    PPG = 2,
    AHD = 3
}

public class ProcessedImage {
    private LibRaw.ProcessedImage image;
    private Gdk.Pixbuf pixbuf = null;
    
    public ushort width {
        get {
            return image.width;
        }
    }
    
    public ushort height {
        get {
            return image.height;
        }
    }
    
    public ushort colors {
        get {
            return image.colors;
        }
    }
    
    public ushort bits {
        get {
            return image.bits;
        }
    }
    
    public uint8* data {
        get {
            return image.data;
        }
    }
    
    public uint data_size {
        get {
            return image.data_size;
        }
    }
    
    public ProcessedImage(LibRaw.Processor proc) throws Exception {
        LibRaw.Result result = LibRaw.Result.SUCCESS;
        image = proc.make_mem_image(ref result);
        throw_exception("ProcessedImage", result);
        assert(image != null);
        
        // A regular mem image comes back with raw RGB data ready for pixbuf (data buffer is shared
        // between the ProcessedImage and the Gdk.Pixbuf)
        pixbuf = new Gdk.Pixbuf.with_unowned_data(image.data, Gdk.Colorspace.RGB, false, image.bits,
            image.width, image.height, image.width * image.colors, null);
    }
    
    public ProcessedImage.from_thumb(LibRaw.Processor proc) throws Exception {
        LibRaw.Result result = LibRaw.Result.SUCCESS;
        image = proc.make_mem_thumb(ref result);
        throw_exception("ProcessedImage.from_thumb", result);
        assert(image != null);
        
        // A mem thumb comes back as the raw bytes from the data segment in the file -- this needs
        // to be decoded before being useful.  This will throw an error if the format is not
        // supported
        try {
            pixbuf = new Gdk.Pixbuf.from_stream(new MemoryInputStream.from_data(image.data, null),
                null);
        } catch (Error err) {
            throw new Exception.UNSUPPORTED_THUMBNAIL(err.message);
        }
        
        // fix up the ProcessedImage fields (which are unset when decoding the thumb)
        image.width = (ushort) pixbuf.width;
        image.height = (ushort) pixbuf.height;
        image.colors = (ushort) pixbuf.n_channels;
        image.bits = (ushort) pixbuf.bits_per_sample;
    }
    
    // This method returns a copy of a pixbuf representing the ProcessedImage.
    public Gdk.Pixbuf get_pixbuf_copy() {
        return pixbuf.copy();
    }
}

public class Processor {
    public LibRaw.OutputParams* output_params {
        get {
            return &proc.params;
        }
    }
    
    private LibRaw.Processor proc;
    
    public Processor(LibRaw.Options options = LibRaw.Options.NONE) {
        proc = new LibRaw.Processor(options);
    }
    
    public void adjust_sizes_info_only() throws Exception {
        throw_exception("adjust_sizes_info_only", proc.adjust_sizes_info_only());
    }
    
    public unowned LibRaw.ImageOther get_image_other() {
        return proc.get_image_other();
    }
    
    public unowned LibRaw.ImageParams get_image_params() {
        return proc.get_image_params();
    }
    
    public unowned LibRaw.ImageSizes get_sizes() {
        return proc.get_sizes();
    }
    
    public unowned LibRaw.Thumbnail get_thumbnail() {
        return proc.get_thumbnail();
    }
    
    public ProcessedImage make_mem_image() throws Exception {
        return new ProcessedImage(proc);
    }
    
    public ProcessedImage make_thumb_image() throws Exception {
        return new ProcessedImage.from_thumb(proc);
    }
    
    public void open_buffer(uint8[] buffer) throws Exception {
        throw_exception("open_buffer", proc.open_buffer(buffer));
    }
    
    public void open_file(string filename) throws Exception {
        throw_exception("open_file", proc.open_file(filename));
    }
    
    public void process() throws Exception {
        throw_exception("process", proc.process());
    }
    
    public void ppm_tiff_writer(string filename) throws Exception {
        throw_exception("ppm_tiff_writer", proc.ppm_tiff_writer(filename));
    }
    
    public void thumb_writer(string filename) throws Exception {
        throw_exception("thumb_writer", proc.thumb_writer(filename));
    }
    
    public void recycle() {
        proc.recycle();
    }
    
    public void unpack() throws Exception {
        throw_exception("unpack", proc.unpack());
    }
    
    public void unpack_thumb() throws Exception {
        throw_exception("unpack_thumb", proc.unpack_thumb());
    }
    
    // This configures output_params for reasonable settings for turning a RAW image into an 
    // RGB ProcessedImage suitable for display.  Tweaks can occur after this call and before
    // process().
    public void configure_for_rgb_display(bool half_size) {
        // Fields in comments are left to their defaults and/or should be modified by the caller.
        // These fields are set to reasonable defaults by libraw.
        
        // greybox
        output_params->set_chromatic_aberrations(1.0, 1.0);
        output_params->set_gamma_curve(GRaw.SRGB_POWER, GRaw.SRGB_SLOPE);
        // user_mul
        // shot_select
        // multi_out
        output_params->bright = 1.0f;
        // threshold
        output_params->half_size = half_size;
        // four_color_rgb
        output_params->highlight = GRaw.HighlightMode.CLIP;
        output_params->use_auto_wb = true;
        output_params->use_camera_wb = true;
        output_params->use_camera_matrix = true;
        output_params->output_color = GRaw.Colorspace.SRGB;
        // output_profile
        // camera_profile
        // bad_pixels
        // dark_frame
        output_params->output_bps = 8;
        // output_tiff
        output_params->user_flip = GRaw.Flip.FROM_SOURCE;
        output_params->user_qual = GRaw.InterpolationQuality.PPG;
        // user_black
        // user_sat
        // med_passes
        output_params->no_auto_bright = true;
        output_params->auto_bright_thr = 0.01f;
        output_params->use_fuji_rotate = GRaw.FujiRotate.USE;
    }
}

private void throw_exception(string caller, LibRaw.Result result) throws Exception {
    if (result == LibRaw.Result.SUCCESS)
        return;
    else if (result > 0)
        throw new Exception.SYSTEM_ERROR("%s: System error %d: %s", caller, (int) result, strerror(result));
    
    string msg = "%s: %s".printf(caller, result.to_string());
    
    switch (result) {
        case LibRaw.Result.UNSPECIFIED_ERROR:
            throw new Exception.UNSPECIFIED(msg);
        
        case LibRaw.Result.FILE_UNSUPPORTED:
            throw new Exception.UNSUPPORTED_FILE(msg);
        
        case LibRaw.Result.REQUEST_FOR_NONEXISTENT_IMAGE:
            throw new Exception.NONEXISTANT_IMAGE(msg);
        
        case LibRaw.Result.OUT_OF_ORDER_CALL:
            throw new Exception.OUT_OF_ORDER_CALL(msg);
        
        case LibRaw.Result.NO_THUMBNAIL:
            throw new Exception.NO_THUMBNAIL(msg);
        
        case LibRaw.Result.UNSUPPORTED_THUMBNAIL:
            throw new Exception.UNSUPPORTED_THUMBNAIL(msg);
        
        case LibRaw.Result.UNSUFFICIENT_MEMORY:
            throw new Exception.OUT_OF_MEMORY(msg);
        
        case LibRaw.Result.DATA_ERROR:
            throw new Exception.DATA_ERROR(msg);
        
        case LibRaw.Result.IO_ERROR:
            throw new Exception.IO_ERROR(msg);
        
        case LibRaw.Result.CANCELLED_BY_CALLBACK:
            throw new Exception.CANCELLED_BY_CALLBACK(msg);
        
        case LibRaw.Result.BAD_CROP:
            throw new Exception.BAD_CROP(msg);
        
        default:
            return;
    }
}

}