~ubuntu-branches/ubuntu/vivid/rawstudio/vivid

« back to all changes in this revision

Viewing changes to librawstudio/rs-utils.c

  • Committer: Bazaar Package Importer
  • Author(s): Bernd Zeimetz
  • Date: 2011-07-28 17:36:32 UTC
  • mfrom: (2.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20110728173632-5czluz9ye3c83zc5
Tags: 2.0-1
* [3750b2cf] Merge commit 'upstream/2.0'
* [63637468] Removing Patch, not necessary anymore.
* [2fb580dc] Add new build-dependencies.
* [c57d953b] Run dh_autoreconf due to patches in configure.in
* [13febe39] Add patch to remove the libssl requirement.
* [5ae773fe] Replace libjpeg62-dev by libjpeg8-dev :)
* [1969d755] Don't build static libraries.
* [7cfe0a2e] Add a patch to fix the plugin directory path.
  As plugins are shared libraries, they need to go into /usr/lib,
  not into /usr/share.
  Thanks to Andrew McMillan
* [c1d0d9dd] Don't install .la files for all plugins and libraries.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>, 
 
3
 * * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU General Public License
 
7
 * as published by the Free Software Foundation; either version 2
 
8
 * of the License, or (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
18
 */
 
19
 
 
20
#define _XOPEN_SOURCE 500 /* strptime() and realpath() */
 
21
#include <rawstudio.h>
 
22
#include <config.h>
 
23
#include <glib.h>
 
24
#include <glib/gstdio.h>
 
25
#ifdef WIN32
 
26
#include <pthread.h> /* MinGW WIN32 gmtime_r() */
 
27
#endif
 
28
#include <time.h>
 
29
#include <sys/types.h>
 
30
#include <sys/stat.h>
 
31
#include <fcntl.h>
 
32
#include <unistd.h>
 
33
#include <stdlib.h>
 
34
#include <string.h>
 
35
#include "conf_interface.h"
 
36
 
 
37
#define DOTDIR ".rawstudio"
 
38
 
 
39
/**
 
40
 * A version of atof() that isn't locale specific
 
41
 * @note This doesn't do any error checking!
 
42
 * @param str A NULL terminated string representing a number
 
43
 * @return The number represented by str or 0.0 if str is NULL
 
44
 */
 
45
gdouble
 
46
rs_atof(const gchar *str)
 
47
{
 
48
        gdouble result = 0.0f;
 
49
        gdouble div = 1.0f;
 
50
        gboolean point_passed = FALSE;
 
51
 
 
52
        gchar *ptr = (gchar *) str;
 
53
 
 
54
        while(str && *ptr)
 
55
        {
 
56
                if (g_ascii_isdigit(*ptr))
 
57
                {
 
58
                        result = result * 10.0f + g_ascii_digit_value(*ptr);
 
59
                        if (point_passed)
 
60
                                div *= 10.0f;
 
61
                }
 
62
                else if (*ptr == '-')
 
63
                        div *= -1.0f;
 
64
                else if (g_ascii_ispunct(*ptr))
 
65
                        point_passed = TRUE;
 
66
                ptr++;
 
67
        }
 
68
 
 
69
        return result / div;
 
70
}
 
71
 
 
72
/**
 
73
 * A convenience function to convert an EXIF timestamp to a unix timestamp.
 
74
 * @note This will only work until 2038 unless glib fixes its GTime
 
75
 * @param str A NULL terminated string containing a timestamp in the format "YYYY:MM:DD HH:MM:SS" (EXIF 2.2 section 4.6.4)
 
76
 * @return A unix timestamp or -1 on error
 
77
 */
 
78
GTime
 
79
rs_exiftime_to_unixtime(const gchar *str)
 
80
{
 
81
        struct tm *tm = g_new0(struct tm, 1);
 
82
        GTime timestamp = -1;
 
83
#ifndef WIN32 /* There is no strptime() in time.h in MinGW */
 
84
        if (strptime(str, "%Y:%m:%d %H:%M:%S", tm))
 
85
                timestamp = (GTime) mktime(tm);
 
86
#endif
 
87
 
 
88
        g_free(tm);
 
89
 
 
90
        return timestamp;
 
91
}
 
92
 
 
93
/**
 
94
 * A convenience function to convert an unix timestamp to an EXIF timestamp.
 
95
 * @note This will only work until 2038 unless glib fixes its GTime
 
96
 * @param timestamp A unix timestamp
 
97
 * @return A string formatted as specified in EXIF 2.2 section 4.6.4
 
98
 */
 
99
gchar *
 
100
rs_unixtime_to_exiftime(GTime timestamp)
 
101
{
 
102
        struct tm *tm = g_new0(struct tm, 1);
 
103
        time_t tt = (time_t) timestamp;
 
104
        gchar *result = g_new0(gchar, 20);
 
105
 
 
106
        gmtime_r(&tt, tm);
 
107
 
 
108
        if (strftime(result, 20, "%Y:%m:%d %H:%M:%S", tm) != 19)
 
109
        {
 
110
                g_free(result);
 
111
                result = NULL;
 
112
        }
 
113
 
 
114
        g_free(tm);
 
115
 
 
116
        return result;
 
117
}
 
118
 
 
119
/**
 
120
 * Constrains a box to fill a bounding box without changing aspect
 
121
 * @param target_width The width of the bounding box
 
122
 * @param target_height The height of the bounding box
 
123
 * @param width The input and output width
 
124
 * @param height The input and output height
 
125
 */
 
126
void
 
127
rs_constrain_to_bounding_box(gint target_width, gint target_height, gint *width, gint *height)
 
128
{
 
129
        gdouble target_aspect = ((gdouble)target_width) / ((gdouble)target_height);
 
130
        gdouble input_aspect = ((gdouble)*width) / ((gdouble)*height);
 
131
        gdouble scale;
 
132
 
 
133
        if (target_aspect < input_aspect)
 
134
                scale = ((gdouble) *width) / ((gdouble) target_width);
 
135
        else
 
136
                scale = ((gdouble) *height) / ((gdouble) target_height);
 
137
 
 
138
        *width = MIN((gint) ((gdouble)*width) / scale, target_width);
 
139
        *height = MIN((gint) ((gdouble)*height) / scale, target_height);
 
140
}
 
141
 
 
142
/**
 
143
 * Try to count the number of processor cores in a system.
 
144
 * @note This currently only works for systems with /proc/cpuinfo
 
145
 * @return The numver of cores or 1 if the system is unsupported
 
146
 */
 
147
gint
 
148
rs_get_number_of_processor_cores(void)
 
149
{
 
150
        static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 
151
 
 
152
        /* We assume processors will not be added/removed during our lifetime */
 
153
        static gint num = 0;
 
154
 
 
155
        if (num)
 
156
                return num;
 
157
 
 
158
        g_static_mutex_lock (&lock);
 
159
        if (num == 0)
 
160
        {
 
161
                /* Use a temporary for thread safety */
 
162
                gint temp_num = 0;
 
163
#if defined(_SC_NPROCESSORS_ONLN)
 
164
                /* Use the POSIX way of getting the number of processors */
 
165
                temp_num = sysconf(_SC_NPROCESSORS_ONLN);
 
166
#elif defined (__linux__) && (defined (__i386__) || defined (__x86_64__))
 
167
                /* Parse the /proc/cpuinfo exposed by Linux i386/amd64 kernels */
 
168
                GIOChannel *io;
 
169
                gchar *line;
 
170
 
 
171
                io = g_io_channel_new_file("/proc/cpuinfo", "r", NULL);
 
172
                if (io)
 
173
                {
 
174
                        /* Count the "processor"-lines, there should be one for each processor/core */
 
175
                        while (G_IO_STATUS_NORMAL == g_io_channel_read_line(io, &line, NULL, NULL, NULL))
 
176
                                if (line)
 
177
                                {
 
178
                                        if (g_str_has_prefix(line, "processor"))
 
179
                                                temp_num++;
 
180
                                        g_free(line);
 
181
                                }
 
182
                        g_io_channel_shutdown(io, FALSE, NULL);
 
183
                        g_io_channel_unref(io);
 
184
                }
 
185
#elif defined(_WIN32)
 
186
                /* Use pthread on windows */
 
187
                temp_num = pthread_num_processors_np();
 
188
#endif
 
189
                /* Be sure we have at least 1 processor and as sanity check, clamp to no more than 127 */
 
190
                temp_num = (temp_num <= 0) ? 1 : MIN(temp_num, 127);
 
191
                RS_DEBUG(PERFORMANCE, "Detected %d CPU cores.", temp_num);
 
192
                num = temp_num;
 
193
        }
 
194
        g_static_mutex_unlock (&lock);
 
195
 
 
196
        return num;
 
197
}
 
198
 
 
199
#if defined (__i386__) || defined (__x86_64__)
 
200
 
 
201
#define xgetbv(index,eax,edx)                                   \
 
202
   __asm__ (".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c" (index))
 
203
 
 
204
/**
 
205
 * Detect cpu features
 
206
 * @return A bitmask of @RSCpuFlags
 
207
 */
 
208
guint
 
209
rs_detect_cpu_features(void)
 
210
{
 
211
#define cpuid(cmd, eax, ecx, edx) \
 
212
  do { \
 
213
     eax = edx = 0;     \
 
214
     asm ( \
 
215
       "push %%"REG_b"\n\t"\
 
216
       "cpuid\n\t" \
 
217
       "pop %%"REG_b"\n\t" \
 
218
       : "=a" (eax), "=c" (ecx),  "=d" (edx) \
 
219
       : "0" (cmd) \
 
220
     ); \
 
221
} while(0)
 
222
        guint eax;
 
223
        guint edx;
 
224
        guint ecx;
 
225
        static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 
226
        static guint stored_cpuflags = -1;
 
227
 
 
228
        if (stored_cpuflags != -1)
 
229
                return stored_cpuflags;
 
230
 
 
231
        g_static_mutex_lock(&lock);
 
232
        if (stored_cpuflags == -1)
 
233
        {
 
234
                guint cpuflags = 0;
 
235
                /* Test cpuid presence comparing eflags */
 
236
                asm (
 
237
                        "push %%"REG_b"\n\t"
 
238
                        "pushf\n\t"
 
239
                        "pop %%"REG_a"\n\t"
 
240
                        "mov %%"REG_a", %%"REG_b"\n\t"
 
241
                        "xor $0x00200000, %%"REG_a"\n\t"
 
242
                        "push %%"REG_a"\n\t"
 
243
                        "popf\n\t"
 
244
                        "pushf\n\t"
 
245
                        "pop %%"REG_a"\n\t"
 
246
                        "cmp %%"REG_a", %%"REG_b"\n\t"
 
247
                        "je notfound\n\t"
 
248
                        "mov $1, %0\n\t"
 
249
                        "notfound:\n\t"
 
250
                        "pop %%"REG_b"\n\t"
 
251
                        : "=r" (eax)
 
252
                        :
 
253
                        : REG_a
 
254
 
 
255
                        );
 
256
 
 
257
                if (eax)
 
258
                {
 
259
                        guint std_dsc;
 
260
                        guint ext_dsc;
 
261
 
 
262
                        /* Get the standard level */
 
263
                        cpuid(0x00000000, std_dsc, ecx, edx);
 
264
 
 
265
                        if (std_dsc)
 
266
                        {
 
267
                                /* Request for standard features */
 
268
                                cpuid(0x00000001, std_dsc, ecx, edx);
 
269
 
 
270
                                if (edx & 0x00800000)
 
271
                                        cpuflags |= RS_CPU_FLAG_MMX;
 
272
                                if (edx & 0x02000000)
 
273
                                        cpuflags |= RS_CPU_FLAG_SSE;
 
274
                                if (edx & 0x04000000)
 
275
                                        cpuflags |= RS_CPU_FLAG_SSE2;
 
276
                                if (edx & 0x00008000)
 
277
                                        cpuflags |= RS_CPU_FLAG_CMOV;
 
278
 
 
279
                                if (ecx & 0x00000001)
 
280
                                        cpuflags |= RS_CPU_FLAG_SSE3;
 
281
                                if (ecx & 0x00000200)
 
282
                                        cpuflags |= RS_CPU_FLAG_SSSE3;
 
283
                                if (ecx & 0x00080000)
 
284
                                        cpuflags |= RS_CPU_FLAG_SSE4_1;
 
285
                                if (ecx & 0x00100000)
 
286
                                        cpuflags |= RS_CPU_FLAG_SSE4_2;
 
287
                                if ((ecx & 0x18000000) == 0x18000000)
 
288
                                {
 
289
                                        xgetbv(0, eax, edx);
 
290
                                                if ((eax & 0x6) == 0x6)
 
291
                                                        cpuflags |= RS_CPU_FLAG_AVX;
 
292
                                }
 
293
                        }
 
294
 
 
295
                        /* Is there extensions */
 
296
                        cpuid(0x80000000, ext_dsc, ecx, edx);
 
297
 
 
298
                        if (ext_dsc)
 
299
                        {
 
300
                                /* Request for extensions */
 
301
                                cpuid(0x80000001, eax, ecx, edx);
 
302
 
 
303
                                if (edx & 0x80000000)
 
304
                                        cpuflags |= RS_CPU_FLAG_3DNOW;
 
305
                                if (edx & 0x40000000)
 
306
                                        cpuflags |= RS_CPU_FLAG_3DNOW_EXT;
 
307
                                if (edx & 0x00400000)
 
308
                                        cpuflags |= RS_CPU_FLAG_AMD_ISSE;
 
309
                        }
 
310
 
 
311
                        /* ISSE is also implied in SSE */
 
312
                        if (cpuflags & RS_CPU_FLAG_SSE)
 
313
                                cpuflags |= RS_CPU_FLAG_AMD_ISSE;
 
314
                }
 
315
                stored_cpuflags = cpuflags;
 
316
        }
 
317
        g_static_mutex_unlock(&lock);
 
318
 
 
319
#define report(a, x) RS_DEBUG(PERFORMANCE, "CPU Feature: "a" = %d", !!(stored_cpuflags&x));
 
320
        report("MMX",RS_CPU_FLAG_MMX);
 
321
        report("SSE",RS_CPU_FLAG_SSE);
 
322
        report("CMOV",RS_CPU_FLAG_CMOV);
 
323
        report("3DNOW",RS_CPU_FLAG_3DNOW);
 
324
        report("3DNOW_EXT",RS_CPU_FLAG_3DNOW_EXT);
 
325
        report("Integer SSE",RS_CPU_FLAG_AMD_ISSE);
 
326
        report("SSE2",RS_CPU_FLAG_SSE2);
 
327
        report("SSE3",RS_CPU_FLAG_SSE3);
 
328
        report("SSSE3",RS_CPU_FLAG_SSSE3);
 
329
        report("SSE4.1",RS_CPU_FLAG_SSE4_1);
 
330
        report("SSE4.2",RS_CPU_FLAG_SSE4_2);
 
331
        report("AVX",RS_CPU_FLAG_AVX);
 
332
#undef report
 
333
 
 
334
        return(stored_cpuflags);
 
335
#undef cpuid
 
336
}
 
337
 
 
338
#else
 
339
guint
 
340
rs_detect_cpu_features()
 
341
{
 
342
        return 0;
 
343
}
 
344
#endif /* __i386__ || __x86_64__ */
 
345
 
 
346
/**
 
347
 * Return a path to the current config directory for Rawstudio - this is the
 
348
 * .rawstudio direcotry in home
 
349
 * @return A path to an existing directory
 
350
 */
 
351
const gchar *
 
352
rs_confdir_get(void)
 
353
{
 
354
        static gchar *dir = NULL;
 
355
        static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 
356
 
 
357
        g_static_mutex_lock(&lock);
 
358
        if (!dir)
 
359
        {
 
360
                const gchar *home = g_get_home_dir();
 
361
                dir = g_build_filename(home, ".rawstudio", NULL);
 
362
        }
 
363
 
 
364
        g_mkdir_with_parents(dir, 00755);
 
365
        g_static_mutex_unlock(&lock);
 
366
 
 
367
        return dir;
 
368
}
 
369
 
 
370
/**
 
371
 * Return a cache directory for filename
 
372
 * @param filename A complete path to a photo
 
373
 * @return A directory to hold the cache. This is guarenteed to exist
 
374
 */
 
375
gchar *
 
376
rs_dotdir_get(const gchar *filename)
 
377
{
 
378
        gchar *ret = NULL;
 
379
        gchar *directory;
 
380
        GString *dotdir;
 
381
        gboolean dotdir_is_local = FALSE;
 
382
 
 
383
        rs_conf_get_boolean(CONF_CACHEDIR_IS_LOCAL, &dotdir_is_local);
 
384
 
 
385
        if (g_file_test(filename, G_FILE_TEST_IS_DIR))
 
386
                directory = g_strdup(filename);
 
387
        else
 
388
                directory = g_path_get_dirname(filename);
 
389
        
 
390
        if (dotdir_is_local)
 
391
        {
 
392
                dotdir = g_string_new(g_get_home_dir());
 
393
                dotdir = g_string_append(dotdir, G_DIR_SEPARATOR_S);
 
394
                dotdir = g_string_append(dotdir, DOTDIR);
 
395
                dotdir = g_string_append(dotdir, G_DIR_SEPARATOR_S);
 
396
                dotdir = g_string_append(dotdir, directory);
 
397
        }
 
398
        else
 
399
        {
 
400
                dotdir = g_string_new(directory);
 
401
                dotdir = g_string_append(dotdir, G_DIR_SEPARATOR_S);
 
402
                dotdir = g_string_append(dotdir, DOTDIR);
 
403
        }
 
404
 
 
405
        if (!g_file_test(dotdir->str, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
 
406
        {
 
407
                if (g_mkdir_with_parents(dotdir->str, 0700) != 0)
 
408
                        ret = NULL;
 
409
                else
 
410
                        ret = dotdir->str;
 
411
        }
 
412
        else if (g_file_test(dotdir->str, G_FILE_TEST_IS_DIR))
 
413
                ret = dotdir->str;
 
414
        else 
 
415
                ret = NULL;
 
416
 
 
417
        /* If we for some reason cannot write to the current directory, */
 
418
        /* we save it to a new folder named as the md5 of the file content, */
 
419
        /* not particularly fast, but ensures that the images can be moved */
 
420
        if (ret == NULL)
 
421
        {
 
422
                g_string_free(dotdir, TRUE);
 
423
                g_free(directory);
 
424
                if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
 
425
                {
 
426
                        gchar* md5 = rs_file_checksum(filename);
 
427
                        ret = g_strdup_printf("%s/read-only-cache/%s", rs_confdir_get(), md5);
 
428
                        g_free(md5);
 
429
                        if (!g_file_test(ret, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
 
430
                        {
 
431
                                if (g_mkdir_with_parents(ret, 0700) != 0)
 
432
                                        ret = NULL;
 
433
                        }
 
434
                }
 
435
                return ret;
 
436
        }
 
437
        g_free(directory);
 
438
        g_string_free(dotdir, FALSE);
 
439
        return (ret);
 
440
}
 
441
 
 
442
/**
 
443
 * Normalize a RS_RECT, ie makes sure that x1 < x2 and y1<y2
 
444
 * @param in A RS_RECT to read values from
 
445
 * @param out A RS_RECT to write the values to (can be the same as in)
 
446
 */
 
447
void
 
448
rs_rect_normalize(RS_RECT *in, RS_RECT *out)
 
449
{
 
450
        gint n;
 
451
        gint x1,y1;
 
452
        gint x2,y2;
 
453
 
 
454
        x1 = in->x2;
 
455
        x2 = in->x1;
 
456
        y1 = in->y1;
 
457
        y2 = in->y2;
 
458
 
 
459
        if (x1>x2)
 
460
        {
 
461
                n = x1;
 
462
                x1 = x2;
 
463
                x2 = n;
 
464
        }
 
465
        if (y1>y2)
 
466
        {
 
467
                n = y1;
 
468
                y1 = y2;
 
469
                y2 = n;
 
470
        }
 
471
 
 
472
        out->x1 = x1;
 
473
        out->x2 = x2;
 
474
        out->y1 = y1;
 
475
        out->y2 = y2;
 
476
}
 
477
 
 
478
/**
 
479
 * Flip a RS_RECT
 
480
 * @param in A RS_RECT to read values from
 
481
 * @param out A RS_RECT to write the values to (can be the same as in)
 
482
 * @param w The width of the data OUTSIDE the RS_RECT
 
483
 * @param h The height of the data OUTSIDE the RS_RECT
 
484
 */
 
485
void
 
486
rs_rect_flip(RS_RECT *in, RS_RECT *out, gint w, gint h)
 
487
{
 
488
        gint x1,y1;
 
489
        gint x2,y2;
 
490
 
 
491
        x1 = in->x1;
 
492
        x2 = in->x2;
 
493
        y1 = h - in->y2 - 1;
 
494
        y2 = h - in->y1 - 1;
 
495
 
 
496
        out->x1 = x1;
 
497
        out->x2 = x2;
 
498
        out->y1 = y1;
 
499
        out->y2 = y2;
 
500
        rs_rect_normalize(out, out);
 
501
}
 
502
 
 
503
/**
 
504
 * Mirrors a RS_RECT
 
505
 * @param in A RS_RECT to read values from
 
506
 * @param out A RS_RECT to write the values to (can be the same as in)
 
507
 * @param w The width of the data OUTSIDE the RS_RECT
 
508
 * @param h The height of the data OUTSIDE the RS_RECT
 
509
 */
 
510
void
 
511
rs_rect_mirror(RS_RECT *in, RS_RECT *out, gint w, gint h)
 
512
{
 
513
        gint x1,y1;
 
514
        gint x2,y2;
 
515
 
 
516
        x1 = w - in->x2 - 1;
 
517
        x2 = w - in->x1 - 1;
 
518
        y1 = in->y1;
 
519
        y2 = in->y2;
 
520
 
 
521
        out->x1 = x1;
 
522
        out->x2 = x2;
 
523
        out->y1 = y1;
 
524
        out->y2 = y2;
 
525
        rs_rect_normalize(out, out);
 
526
}
 
527
 
 
528
/**
 
529
 * Rotate a RS_RECT in 90 degrees steps
 
530
 * @param in A RS_RECT to read values from
 
531
 * @param out A RS_RECT to write the values to (can be the same as in)
 
532
 * @param w The width of the data OUTSIDE the RS_RECT
 
533
 * @param h The height of the data OUTSIDE the RS_RECT
 
534
 * @param quarterturns How many times to turn the rect clockwise
 
535
 */
 
536
void
 
537
rs_rect_rotate(RS_RECT *in, RS_RECT *out, gint w, gint h, gint quarterturns)
 
538
{
 
539
        gint x1,y1;
 
540
        gint x2,y2;
 
541
 
 
542
        x1 = in->x2;
 
543
        x2 = in->x1;
 
544
        y1 = in->y1;
 
545
        y2 = in->y2;
 
546
 
 
547
        switch(quarterturns)
 
548
        {
 
549
                case 1:
 
550
                        x1 = h - in->y1-1;
 
551
                        x2 = h - in->y2-1;
 
552
                        y1 = in->x1;
 
553
                        y2 = in->x2;
 
554
                        break;
 
555
                case 2:
 
556
                        x1 = w - in->x1 - 1;
 
557
                        x2 = w - in->x2 - 1;
 
558
                        y1 = h - in->y1 - 1;
 
559
                        y2 = h - in->y2 - 1;
 
560
                        break;
 
561
                case 3:
 
562
                        x1 = in->y1;
 
563
                        x2 = in->y2;
 
564
                        y1 = w - in->x1 - 1;
 
565
                        y2 = w - in->x2 - 1;
 
566
                        break;
 
567
        }
 
568
 
 
569
        out->x1 = x1;
 
570
        out->x2 = x2;
 
571
        out->y1 = y1;
 
572
        out->y2 = y2;
 
573
        rs_rect_normalize(out, out);
 
574
}
 
575
 
 
576
/**
 
577
 * Reset a property on a GObject to it's default
 
578
 * @param object A GObject
 
579
 * @param property_name A name of a property installed in object's class
 
580
 */
 
581
void
 
582
rs_object_class_property_reset(GObject *object, const gchar *property_name)
 
583
{
 
584
        GObjectClass *klass = G_OBJECT_GET_CLASS(object);
 
585
        GParamSpec *spec;
 
586
        GValue value = {0};
 
587
 
 
588
        spec = g_object_class_find_property(klass, property_name);
 
589
        g_assert(spec != NULL);
 
590
 
 
591
        g_value_init(&value, spec->value_type);
 
592
 
 
593
        g_param_value_set_default(spec, &value);
 
594
        g_object_set_property(object, spec->name, &value);
 
595
 
 
596
        g_value_unset(&value);
 
597
}
 
598
 
 
599
/**
 
600
 * Check (and complain if needed) the Rawstudio install
 
601
 */
 
602
void
 
603
check_install(void)
 
604
{
 
605
#define TEST_FILE_ACCESS(path) do { if (g_access(path, R_OK)!=0) g_debug("Cannot access %s\n", path);} while (0)
 
606
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "icons" G_DIR_SEPARATOR_S PACKAGE ".png");
 
607
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "overlay_priority1.png");
 
608
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "overlay_priority2.png");
 
609
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "overlay_priority3.png");
 
610
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "overlay_deleted.png");
 
611
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "overlay_exported.png");
 
612
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "transform_flip.png");
 
613
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "transform_mirror.png");
 
614
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "transform_90.png");
 
615
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "transform_180.png");
 
616
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "transform_270.png");
 
617
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "cursor-color-picker.png");
 
618
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "cursor-crop.png");
 
619
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "cursor-rotate.png");
 
620
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "tool-color-picker.png");
 
621
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "tool-crop.png");
 
622
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "tool-rotate.png");
 
623
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "ui.xml");
 
624
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "rawstudio.gtkrc");
 
625
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "profiles" G_DIR_SEPARATOR_S "generic_camera_profile.icc");
 
626
        TEST_FILE_ACCESS(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "profiles" G_DIR_SEPARATOR_S "sRGB.icc");
 
627
#undef TEST_FILE_ACCESS
 
628
}
 
629
 
 
630
/* Rewritten from Exiftools - lib/Image/ExifTool/Canon.pm*/
 
631
gfloat
 
632
CanonEv(gint val)
 
633
{
 
634
        gfloat sign;
 
635
        gfloat frac;
 
636
 
 
637
        /* temporarily make the number positive */
 
638
        if (val < 0)
 
639
        {
 
640
                val = -val;
 
641
                sign = -1.0;
 
642
        }
 
643
        else
 
644
        {
 
645
                sign = 1.0;
 
646
        }
 
647
 
 
648
        gint ifrac = val & 0x1f;
 
649
 
 
650
        /* remove fraction */
 
651
        val -= ifrac;
 
652
 
 
653
        /* Convert 1/3 and 2/3 codes */
 
654
        if (ifrac == 0x0c)
 
655
                frac = 32.0 / 3.0; /* 0x20 / 3 */
 
656
        else if (ifrac == 0x14)
 
657
                frac = 64.0 / 3.0; /* 0x40 / 3 */
 
658
        else
 
659
                frac = (gfloat) ifrac;
 
660
 
 
661
        return sign * (((gfloat)val) + frac) / 32.0;
 
662
}
 
663
 
 
664
/**
 
665
 * Split a char * with a given delimiter
 
666
 * @param str The gchar * to be splitted
 
667
 * @param delimiters The gchar * to be used as delimiter (can be more than 1 char)
 
668
 * @return A GList consisting of the different parts of the input string, must be freed using g_free() and g_list_free().
 
669
 */
 
670
GList *
 
671
rs_split_string(const gchar *str, const gchar *delimiters) {
 
672
        gchar **temp = g_strsplit_set(str, delimiters, 0);
 
673
 
 
674
        int i = 0;
 
675
        GList *glist = NULL;
 
676
        while (temp[i])
 
677
        {
 
678
                gchar* text = (gchar *) temp[i];
 
679
                if (text[0] != 0)
 
680
                        glist = g_list_append(glist, text);
 
681
                else
 
682
                        g_free(text);
 
683
                i++;
 
684
        }
 
685
        g_free(temp);
 
686
        return glist;
 
687
}
 
688
 
 
689
gchar *
 
690
rs_file_checksum(const gchar *filename)
 
691
{
 
692
        gchar *checksum = NULL;
 
693
        struct stat st;
 
694
        gint fd = open(filename, O_RDONLY);
 
695
 
 
696
        if (fd > 0)
 
697
        {
 
698
                fstat(fd, &st);
 
699
 
 
700
                gint offset = 0;
 
701
                gint length = st.st_size;
 
702
 
 
703
                /* If the file is bigger than 2 KiB, we sample 1 KiB in the middle of the file */
 
704
                if (st.st_size > 2048)
 
705
                {
 
706
                        offset = st.st_size/2;
 
707
                        length = 1024;
 
708
                }
 
709
 
 
710
                guchar buffer[length];
 
711
 
 
712
                lseek(fd, offset, SEEK_SET);
 
713
                gint bytes_read = read(fd, buffer, length);
 
714
 
 
715
                close(fd);
 
716
 
 
717
                if (bytes_read == length)
 
718
                        checksum = g_compute_checksum_for_data(G_CHECKSUM_MD5, buffer, length);
 
719
        }
 
720
 
 
721
        return checksum;
 
722
}
 
723
 
 
724
const gchar *
 
725
rs_human_aperture(gdouble aperture)
 
726
{
 
727
        gchar *ret = NULL;
 
728
 
 
729
        if (aperture < 8)
 
730
                ret = g_strdup_printf("f/%.1f", aperture);
 
731
        else
 
732
                ret = g_strdup_printf("f/%.0f", aperture);
 
733
 
 
734
        return ret;
 
735
}
 
736
 
 
737
const gchar *
 
738
rs_human_focal(gdouble min, gdouble max)
 
739
{
 
740
        gchar *ret = NULL;
 
741
 
 
742
        if (min == max)
 
743
                ret = g_strdup_printf("%.0fmm", max);
 
744
        else
 
745
                ret = g_strdup_printf("%.0f-%.0fmm", min, max);
 
746
        return ret;
 
747
}
 
748
 
 
749
gchar *
 
750
rs_normalize_path(const gchar *path)
 
751
{
 
752
#ifdef PATH_MAX
 
753
        gint path_max = PATH_MAX;
 
754
#else
 
755
        gint path_max = pathconf(path, _PC_PATH_MAX);
 
756
        if (path_max <= 0)
 
757
                path_max = 4096;
 
758
#endif
 
759
        gchar *buffer = g_new0(gchar, path_max);
 
760
 
 
761
        gchar *ret = NULL;
 
762
#ifdef WIN32
 
763
        int length = GetFullPathName(path, path_max, buffer, NULL);
 
764
        if(length == 0){
 
765
          g_error("Error normalizing path: %s\n", path);
 
766
        }
 
767
        ret = buffer;
 
768
#else
 
769
        ret = realpath(path, buffer);
 
770
#endif
 
771
 
 
772
        if (ret == NULL)
 
773
                g_free(buffer);
 
774
 
 
775
        return ret;
 
776
}
 
777
 
 
778
/**
 
779
 * Copy a file from one location to another
 
780
 * @param source An absolute path to a source file
 
781
 * @param deastination An absolute path to a destination file (not folder), will be overwritten if exists
 
782
 * @return TRUE on success, FALSE on failure
 
783
 */
 
784
gboolean
 
785
rs_file_copy(const gchar *source, const gchar *destination)
 
786
{
 
787
        gboolean ret = FALSE;
 
788
        const gint buffer_size = 1024*1024;
 
789
        gint source_fd, destination_fd;
 
790
        gint bytes_read, bytes_written;
 
791
        struct stat st;
 
792
        mode_t default_mode = 00666; /* We set this relaxed to respect the users umask */
 
793
 
 
794
        g_return_val_if_fail(source != NULL, FALSE);
 
795
        g_return_val_if_fail(source[0] != '\0', FALSE);
 
796
        g_return_val_if_fail(g_path_is_absolute(source), FALSE);
 
797
 
 
798
        g_return_val_if_fail(destination != NULL, FALSE);
 
799
        g_return_val_if_fail(destination[0] != '\0', FALSE);
 
800
        g_return_val_if_fail(g_path_is_absolute(destination), FALSE);
 
801
 
 
802
        source_fd = open(source, O_RDONLY);
 
803
        if (source_fd > 0)
 
804
        {
 
805
                /* Try to copy permissions too */
 
806
                if (fstat(source_fd, &st) == 0)
 
807
                        default_mode = st.st_mode;
 
808
                destination_fd = creat(destination, default_mode);
 
809
 
 
810
                if (destination_fd > 0)
 
811
                {
 
812
                        gpointer buffer = g_malloc(buffer_size);
 
813
                        do {
 
814
                                bytes_read = read(source_fd, buffer, buffer_size);
 
815
                                bytes_written = write(destination_fd, buffer, bytes_read);
 
816
                                if (bytes_written != bytes_read)
 
817
                                        g_warning("%s was truncated", destination);
 
818
                        } while(bytes_read > 0);
 
819
                        g_free(buffer);
 
820
 
 
821
                        ret = TRUE;
 
822
 
 
823
                        close(destination_fd);
 
824
                }
 
825
                close(source_fd);
 
826
        }
 
827
 
 
828
        return ret;
 
829
}
 
830
 
 
831
/**
 
832
 * Removes tailing spaces from a gchar *
 
833
 * @param str A gchar * to have tailing spaces removed
 
834
 * @param inplace Set to TRUE if string should be edited inplace
 
835
 * @return A gchar * with tailing spaces removed
 
836
 */
 
837
gchar *
 
838
rs_remove_tailing_spaces(gchar *str, gboolean inplace)
 
839
{
 
840
        gint i;
 
841
        gchar *ret = str;
 
842
 
 
843
        g_return_val_if_fail(str != NULL, NULL);
 
844
 
 
845
        if (!inplace)
 
846
                ret = g_strdup(str);
 
847
 
 
848
        for(i = strlen(ret)-1; i > 0; i--)
 
849
        {
 
850
                if (ret[i] == 0x20)
 
851
                        ret[i] = 0x00;
 
852
                else
 
853
                        i = 0;
 
854
        }
 
855
 
 
856
        return ret;
 
857
}