1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2009 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 #include <config.h>
24 #include <glib.h>
25 #include <gthumb.h>
26 #include <cairo.h>
27 #include <gtk/gtk.h>
28 #include <libraw.h>
29 #include "main.h"
30 #include "gth-metadata-provider-raw.h"
31 
32 
33 typedef enum {
34 	RAW_OUTPUT_COLOR_RAW = 0,
35 	RAW_OUTPUT_COLOR_SRGB = 1,
36 	RAW_OUTPUT_COLOR_ADOBE = 2,
37 	RAW_OUTPUT_COLOR_WIDE = 3,
38 	RAW_OUTPUT_COLOR_PROPHOTO = 4,
39 	RAW_OUTPUT_COLOR_XYZ = 5
40 } RawOutputColor;
41 
42 
43 static void
_libraw_set_gerror(GError ** error,int error_code)44 _libraw_set_gerror (GError **error,
45 		    int      error_code)
46 {
47 	g_set_error_literal (error,
48 			     G_IO_ERROR,
49 			     G_IO_ERROR_FAILED,
50 			     libraw_strerror (error_code));
51 }
52 
53 
54 static GthImage *
_libraw_read_jpeg_data(void * buffer,gsize buffer_size,int requested_size,GCancellable * cancellable,GError ** error)55 _libraw_read_jpeg_data (void           *buffer,
56 			gsize           buffer_size,
57 			int             requested_size,
58 			GCancellable   *cancellable,
59 			GError        **error)
60 {
61 	GthImageLoaderFunc  loader_func;
62 	GInputStream       *istream;
63 	GthImage           *image;
64 
65 	loader_func = gth_main_get_image_loader_func ("image/jpeg", GTH_IMAGE_FORMAT_CAIRO_SURFACE);
66 	if (loader_func == NULL)
67 		return NULL;
68 
69 	istream = g_memory_input_stream_new_from_data (buffer, buffer_size, NULL);
70 	if (istream == NULL)
71 		return NULL;
72 
73 	image = loader_func (istream,
74 			     NULL,
75 			     requested_size,
76 			     NULL,
77 			     NULL,
78 			     NULL,
79 			     NULL,
80 			     cancellable,
81 			     error);
82 
83 	g_object_unref (istream);
84 
85 	return image;
86 }
87 
88 
89 static cairo_surface_t *
_cairo_surface_create_from_ppm(int width,int height,int colors,int bits,guchar * buffer,gsize buffer_size)90 _cairo_surface_create_from_ppm (int     width,
91 				int     height,
92 				int     colors,
93 				int     bits,
94 				guchar *buffer,
95 				gsize   buffer_size)
96 {
97 	cairo_surface_t *surface;
98 	int              stride;
99 	guchar          *buffer_p;
100 	int              r, c;
101 	guchar          *row;
102 	guchar          *column;
103 	guint32          pixel;
104 
105 	if (bits != 8)
106 		return NULL;
107 
108 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
109 	stride = cairo_image_surface_get_stride (surface);
110 
111 	buffer_p = buffer;
112 	row = _cairo_image_surface_flush_and_get_data (surface);
113 	for (r = 0; r < height; r++) {
114 		column = row;
115 		for (c = 0; c < width; c++) {
116 			switch (colors) {
117 			case 4:
118 				pixel = CAIRO_RGBA_TO_UINT32 (buffer_p[0], buffer_p[1], buffer_p[2], buffer_p[3]);
119 				break;
120 			case 3:
121 				pixel = CAIRO_RGBA_TO_UINT32 (buffer_p[0], buffer_p[1], buffer_p[2], 0xff);
122 				break;
123 			case 1:
124 				pixel = CAIRO_RGBA_TO_UINT32 (buffer_p[0], buffer_p[0], buffer_p[0], 0xff);
125 				break;
126 			default:
127 				g_assert_not_reached ();
128 			}
129 
130 			memcpy (column, &pixel, sizeof (guint32));
131 
132 			column += 4;
133 			buffer_p += colors;
134 		}
135 		row += stride;
136 	}
137 
138 	cairo_surface_mark_dirty (surface);
139 
140 	return surface;
141 }
142 
143 
144 static GthImage *
_libraw_read_bitmap_data(int width,int height,int colors,int bits,guchar * buffer,gsize buffer_size)145 _libraw_read_bitmap_data (int     width,
146 			  int     height,
147 			  int     colors,
148 			  int     bits,
149 			  guchar *buffer,
150 			  gsize   buffer_size)
151 {
152 	GthImage        *image = NULL;
153 	cairo_surface_t *surface = NULL;
154 
155 	surface = _cairo_surface_create_from_ppm (width, height, colors, bits, buffer, buffer_size);
156 	if (surface != NULL) {
157 		image = gth_image_new_for_surface (surface);
158 		cairo_surface_destroy (surface);
159 	}
160 
161 	return image;
162 }
163 
164 
165 static GthTransform
_libraw_get_tranform(libraw_data_t * raw_data)166 _libraw_get_tranform (libraw_data_t *raw_data)
167 {
168 	GthTransform transform;
169 
170 	switch (raw_data->sizes.flip) {
171 	case 3:
172 		transform = GTH_TRANSFORM_ROTATE_180;
173 		break;
174 	case 5:
175 		transform = GTH_TRANSFORM_ROTATE_270;
176 		break;
177 	case 6:
178 		transform = GTH_TRANSFORM_ROTATE_90;
179 		break;
180 	default:
181 		transform = GTH_TRANSFORM_NONE;
182 		break;
183 	}
184 
185 	return transform;
186 }
187 
188 
189 static int
_libraw_progress_cb(void * callback_data,enum LibRaw_progress stage,int iteration,int expected)190 _libraw_progress_cb (void                 *callback_data,
191 		     enum LibRaw_progress  stage,
192 		     int                   iteration,
193 		     int                   expected)
194 {
195 	return g_cancellable_is_cancelled ((GCancellable *) callback_data) ? 1 : 0;
196 }
197 
198 
199 static GthImage *
_cairo_image_surface_create_from_raw(GInputStream * istream,GthFileData * file_data,int requested_size,int * original_width,int * original_height,gboolean * loaded_original,gpointer user_data,GCancellable * cancellable,GError ** error)200 _cairo_image_surface_create_from_raw (GInputStream  *istream,
201 				      GthFileData   *file_data,
202 				      int            requested_size,
203 				      int           *original_width,
204 				      int           *original_height,
205 				      gboolean      *loaded_original,
206 				      gpointer       user_data,
207 				      GCancellable  *cancellable,
208 				      GError       **error)
209 {
210 	libraw_data_t *raw_data;
211 	int            result;
212 	void          *buffer = NULL;
213 	size_t         size;
214 	GthImage      *image = NULL;
215 
216 	raw_data = libraw_init (LIBRAW_OPIONS_NO_MEMERR_CALLBACK | LIBRAW_OPIONS_NO_DATAERR_CALLBACK);
217 	if (raw_data == NULL) {
218 		_libraw_set_gerror (error, errno);
219 		goto fatal_error;
220 	}
221 
222 	libraw_set_progress_handler (raw_data, _libraw_progress_cb, cancellable);
223 
224 	if (! _g_input_stream_read_all (istream, &buffer, &size, cancellable, error))
225 		goto fatal_error;
226 
227 	raw_data->params.output_tiff = FALSE;
228 	raw_data->params.use_camera_wb = TRUE;
229 	raw_data->params.use_rawspeed = TRUE;
230 	raw_data->params.highlight = FALSE;
231 	raw_data->params.use_camera_matrix = TRUE;
232 	raw_data->params.output_color = RAW_OUTPUT_COLOR_SRGB;
233 	raw_data->params.output_bps = 8;
234 	raw_data->params.half_size = (requested_size > 0);
235 
236 	result = libraw_open_buffer (raw_data, buffer, size);
237 	if (LIBRAW_FATAL_ERROR (result)) {
238 		_libraw_set_gerror (error, result);
239 		goto fatal_error;
240 	}
241 
242 
243 	if (requested_size > 0) {
244 
245 		if (loaded_original != NULL)
246 			*loaded_original = FALSE;
247 
248 		/* read the thumbnail */
249 
250 		result = libraw_unpack_thumb (raw_data);
251 		if (result != LIBRAW_SUCCESS) {
252 			_libraw_set_gerror (error, result);
253 			goto fatal_error;
254 		}
255 
256 		switch (raw_data->thumbnail.tformat) {
257 		case LIBRAW_THUMBNAIL_JPEG:
258 			image = _libraw_read_jpeg_data (raw_data->thumbnail.thumb,
259 							raw_data->thumbnail.tlength,
260 							requested_size,
261 							cancellable,
262 							error);
263 			break;
264 		case LIBRAW_THUMBNAIL_BITMAP:
265 			if ((raw_data->thumbnail.tcolors > 0) && (raw_data->thumbnail.tcolors <= 4)) {
266 				image = _libraw_read_bitmap_data (raw_data->thumbnail.twidth,
267 								  raw_data->thumbnail.theight,
268 								  raw_data->thumbnail.tcolors,
269 								  8,
270 								  (guchar *) raw_data->thumbnail.thumb,
271 								  raw_data->thumbnail.tlength);
272 			}
273 			else
274 				g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unsupported data format");
275 			break;
276 		default:
277 			g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unsupported data format");
278 			break;
279 		}
280 
281 		if ((image != NULL) && (_libraw_get_tranform (raw_data) != GTH_TRANSFORM_NONE)) {
282 			cairo_surface_t *surface;
283 			cairo_surface_t *rotated;
284 
285 			surface = gth_image_get_cairo_surface (image);
286 			rotated = _cairo_image_surface_transform (surface, _libraw_get_tranform (raw_data));
287 			gth_image_set_cairo_surface (image, rotated);
288 
289 			cairo_surface_destroy (rotated);
290 			cairo_surface_destroy (surface);
291 		}
292 
293 		/* get the original size */
294 
295 		if ((original_width != NULL) && (original_height != NULL)) {
296 			libraw_close (raw_data);
297 
298 			raw_data = libraw_init (LIBRAW_OPIONS_NO_MEMERR_CALLBACK | LIBRAW_OPIONS_NO_DATAERR_CALLBACK);
299 			if (raw_data == NULL)
300 				goto fatal_error;
301 
302 			result = libraw_open_buffer (raw_data, buffer, size);
303 			if (LIBRAW_FATAL_ERROR (result))
304 				goto fatal_error;
305 
306 			result = libraw_unpack (raw_data);
307 			if (result != LIBRAW_SUCCESS) {
308 				_libraw_set_gerror (error, result);
309 				goto fatal_error;
310 			}
311 
312 			result = libraw_adjust_sizes_info_only (raw_data);
313 			if (result != LIBRAW_SUCCESS) {
314 				_libraw_set_gerror (error, result);
315 				goto fatal_error;
316 			}
317 
318 			*original_width = raw_data->sizes.iwidth;
319 			*original_height = raw_data->sizes.iheight;
320 		}
321 	} else {
322 		/* read the image */
323 
324 		libraw_processed_image_t *processed_image;
325 
326 		result = libraw_unpack (raw_data);
327 		if (result != LIBRAW_SUCCESS) {
328 			_libraw_set_gerror (error, result);
329 			goto fatal_error;
330 		}
331 
332 		result = libraw_dcraw_process (raw_data);
333 		if (result != LIBRAW_SUCCESS) {
334 			_libraw_set_gerror (error, result);
335 			goto fatal_error;
336 		}
337 
338 		processed_image = libraw_dcraw_make_mem_image (raw_data, &result);
339 		if (result != LIBRAW_SUCCESS) {
340 			_libraw_set_gerror (error, result);
341 			goto fatal_error;
342 		}
343 
344 		switch (processed_image->type) {
345 		case LIBRAW_IMAGE_JPEG:
346 			image = _libraw_read_jpeg_data (processed_image->data,
347 							processed_image->data_size,
348 							-1,
349 							cancellable,
350 							error);
351 			break;
352 		case LIBRAW_IMAGE_BITMAP:
353 			image = _libraw_read_bitmap_data (processed_image->width,
354 							  processed_image->height,
355 							  processed_image->colors,
356 							  processed_image->bits,
357 							  processed_image->data,
358 							  processed_image->data_size);
359 			break;
360 		default:
361 			g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unsupported data format");
362 			break;
363 		}
364 
365 		libraw_dcraw_clear_mem (processed_image);
366 
367 		/* get the original size */
368 
369 		if ((original_width != NULL) && (original_height != NULL)) {
370 			result = libraw_adjust_sizes_info_only (raw_data);
371 			if (result != LIBRAW_SUCCESS) {
372 				_libraw_set_gerror (error, result);
373 				goto fatal_error;
374 			}
375 
376 			*original_width = raw_data->sizes.iwidth;
377 			*original_height = raw_data->sizes.iheight;
378 		}
379 	}
380 
381 	fatal_error:
382 
383 	if (raw_data != NULL)
384 		libraw_close (raw_data);
385 	g_free (buffer);
386 
387 	return image;
388 }
389 
390 
391 G_MODULE_EXPORT void
gthumb_extension_activate(void)392 gthumb_extension_activate (void)
393 {
394 	GList *mime_types;
395 	mime_types = g_content_types_get_registered ();
396 
397 	GList *l = mime_types;
398 	while (l != NULL) {
399 		GList *next = l->next;
400 		if (!_g_mime_type_is_raw (l->data)) {
401 			g_free (l->data);
402 			mime_types = g_list_delete_link (mime_types, l);
403 		}
404 		l = next;
405 	}
406 
407 	int count_of_raw_types, i;
408 	count_of_raw_types = g_list_length (mime_types);
409 
410 	gchar *raw_mime_types[count_of_raw_types+1];
411 
412 	i = 0;
413 	l = mime_types;
414 	while (l != NULL) {
415 		GList *next = l->next;
416 		raw_mime_types[i] = (gchar *) l->data;
417 		i++;
418 		l = next;
419 	}
420 	raw_mime_types[i] = NULL;
421 
422 	gth_main_register_metadata_provider (GTH_TYPE_METADATA_PROVIDER_RAW);
423 	gth_main_register_image_loader_func_v (_cairo_image_surface_create_from_raw,
424 					       GTH_IMAGE_FORMAT_CAIRO_SURFACE,
425 					       (const gchar **) raw_mime_types);
426 
427 	g_list_free_full (mime_types, g_free);
428 }
429 
430 
431 G_MODULE_EXPORT void
gthumb_extension_deactivate(void)432 gthumb_extension_deactivate (void)
433 {
434 }
435 
436 
437 G_MODULE_EXPORT gboolean
gthumb_extension_is_configurable(void)438 gthumb_extension_is_configurable (void)
439 {
440 	return FALSE;
441 }
442 
443 
444 G_MODULE_EXPORT void
gthumb_extension_configure(GtkWindow * parent)445 gthumb_extension_configure (GtkWindow *parent)
446 {
447 }
448