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

« back to all changes in this revision

Viewing changes to plugins/colorspace-transform/colorspace_transform.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
/* Plugin tmpl version 5 */
 
21
 
 
22
#include <rawstudio.h>
 
23
#include <lcms.h>
 
24
#include "rs-cmm.h"
 
25
#include "colorspace_transform.h"
 
26
 
 
27
 
 
28
struct _RSColorspaceTransform {
 
29
        RSFilter parent;
 
30
        gfloat premul[4];
 
31
        gboolean has_premul;
 
32
 
 
33
        RSCmm *cmm;
 
34
};
 
35
 
 
36
struct _RSColorspaceTransformClass {
 
37
        RSFilterClass parent_class;
 
38
};
 
39
 
 
40
RS_DEFINE_FILTER(rs_colorspace_transform, RSColorspaceTransform)
 
41
 
 
42
enum {
 
43
        PROP_0,
 
44
        PROP_CHANGEME
 
45
};
 
46
 
 
47
static RSFilterResponse *get_image(RSFilter *filter, const RSFilterRequest *request);
 
48
static RSFilterResponse *get_image8(RSFilter *filter, const RSFilterRequest *request);
 
49
static gboolean convert_colorspace16(RSColorspaceTransform *colorspace_transform, RS_IMAGE16 *input_image, RS_IMAGE16 *output_image, RSColorSpace *input_space, RSColorSpace *output_space);
 
50
static void convert_colorspace8(RSColorspaceTransform *colorspace_transform, RS_IMAGE16 *input_image, GdkPixbuf *output_image, RSColorSpace *input_space, RSColorSpace *output_space, GdkRectangle *roi);
 
51
 
 
52
static RSFilterClass *rs_colorspace_transform_parent_class = NULL;
 
53
 
 
54
/* SSE2 optimized functions */
 
55
extern void transform8_srgb_sse2(ThreadInfo* t);
 
56
extern void transform8_otherrgb_sse2(ThreadInfo* t);
 
57
extern gboolean cst_has_sse2(void);
 
58
 
 
59
G_MODULE_EXPORT void
 
60
rs_plugin_load(RSPlugin *plugin)
 
61
{
 
62
        rs_colorspace_transform_get_type(G_TYPE_MODULE(plugin));
 
63
}
 
64
 
 
65
static void
 
66
rs_colorspace_transform_class_init(RSColorspaceTransformClass *klass)
 
67
{
 
68
        RSFilterClass *filter_class = RS_FILTER_CLASS (klass);
 
69
 
 
70
        rs_colorspace_transform_parent_class = g_type_class_peek_parent (klass);
 
71
 
 
72
        filter_class->name = "ColorspaceTransform filter";
 
73
        filter_class->get_image = get_image;
 
74
        filter_class->get_image8 = get_image8;
 
75
}
 
76
 
 
77
static void
 
78
rs_colorspace_transform_init(RSColorspaceTransform *colorspace_transform)
 
79
{
 
80
        /* FIXME: unref this at some point */
 
81
        colorspace_transform->cmm = rs_cmm_new();
 
82
        rs_cmm_set_num_threads(colorspace_transform->cmm, rs_get_number_of_processor_cores());
 
83
}
 
84
 
 
85
static RSFilterResponse *
 
86
get_image(RSFilter *filter, const RSFilterRequest *request)
 
87
{
 
88
        RSColorspaceTransform *colorspace_transform = RS_COLORSPACE_TRANSFORM(filter);
 
89
        RSFilterResponse *previous_response;
 
90
        RSFilterResponse *response;
 
91
        RS_IMAGE16 *input;
 
92
        RS_IMAGE16 *output = NULL;
 
93
        int i;
 
94
 
 
95
        previous_response = rs_filter_get_image(filter->previous, request);
 
96
        input = rs_filter_response_get_image(previous_response);
 
97
        if (!RS_IS_IMAGE16(input))
 
98
                return previous_response;
 
99
 
 
100
        RSColorSpace *input_space = rs_filter_param_get_object_with_type(RS_FILTER_PARAM(previous_response), "colorspace", RS_TYPE_COLOR_SPACE);
 
101
        RSColorSpace *output_space = rs_filter_param_get_object_with_type(RS_FILTER_PARAM(request), "colorspace", RS_TYPE_COLOR_SPACE);
 
102
 
 
103
        for( i = 0; i < 4; i++)
 
104
                colorspace_transform->premul[i] = 1.0f;
 
105
 
 
106
        if (input_space && output_space && (input_space != output_space))
 
107
        {
 
108
                gboolean is_premultiplied = FALSE;
 
109
                rs_filter_param_get_boolean(RS_FILTER_PARAM(previous_response), "is-premultiplied", &is_premultiplied);
 
110
 
 
111
                if (!is_premultiplied)
 
112
                        colorspace_transform->has_premul = rs_filter_param_get_float4(RS_FILTER_PARAM(request), "premul", colorspace_transform->premul);
 
113
                rs_cmm_set_premul(colorspace_transform->cmm, colorspace_transform->premul);
 
114
 
 
115
                output = rs_image16_copy(input, FALSE);
 
116
 
 
117
                if (convert_colorspace16(colorspace_transform, input, output, input_space, output_space))
 
118
                {
 
119
                        /* Image was converted */
 
120
                        response = rs_filter_response_clone(previous_response);
 
121
                        g_object_unref(previous_response);
 
122
                        if (colorspace_transform->has_premul)
 
123
                                rs_filter_param_set_boolean(RS_FILTER_PARAM(response), "is-premultiplied", TRUE);
 
124
                        rs_filter_param_set_object(RS_FILTER_PARAM(response), "colorspace", output_space);
 
125
                        rs_filter_response_set_image(response, output);
 
126
                        g_object_unref(output);
 
127
                        g_object_unref(input);
 
128
                        return response;
 
129
                } else
 
130
                {
 
131
                        /* No conversion was needed */
 
132
                        g_object_unref(input);
 
133
                        g_object_unref(output);
 
134
                        return previous_response;
 
135
                }
 
136
        }
 
137
        else
 
138
        {
 
139
                g_object_unref(input);
 
140
                return previous_response;
 
141
        }
 
142
 
 
143
}
 
144
 
 
145
static RSFilterResponse *
 
146
get_image8(RSFilter *filter, const RSFilterRequest *request)
 
147
{
 
148
        RSColorspaceTransform *colorspace_transform = RS_COLORSPACE_TRANSFORM(filter);
 
149
        RSFilterResponse *previous_response;
 
150
        RSFilterResponse *response;
 
151
        RS_IMAGE16 *input;
 
152
        GdkPixbuf *output = NULL;
 
153
        GdkRectangle *roi;
 
154
        int i;
 
155
 
 
156
        previous_response = rs_filter_get_image(filter->previous, request);
 
157
        input = rs_filter_response_get_image(previous_response);
 
158
        if (!RS_IS_IMAGE16(input))
 
159
                return previous_response;
 
160
 
 
161
        roi = rs_filter_request_get_roi(request);
 
162
        RSColorSpace *input_space = rs_filter_param_get_object_with_type(RS_FILTER_PARAM(previous_response), "colorspace", RS_TYPE_COLOR_SPACE);
 
163
        RSColorSpace *output_space = rs_filter_param_get_object_with_type(RS_FILTER_PARAM(request), "colorspace", RS_TYPE_COLOR_SPACE);
 
164
 
 
165
        response = rs_filter_response_clone(previous_response);
 
166
        g_object_unref(previous_response);
 
167
 
 
168
        for( i = 0; i < 4; i++)
 
169
                colorspace_transform->premul[i] = 1.0f;
 
170
 
 
171
        gboolean is_premultiplied = FALSE;
 
172
        rs_filter_param_get_boolean(RS_FILTER_PARAM(response), "is-premultiplied", &is_premultiplied);
 
173
 
 
174
        if (!is_premultiplied)
 
175
                if ((colorspace_transform->has_premul = rs_filter_param_get_float4(RS_FILTER_PARAM(request), "premul", colorspace_transform->premul)))
 
176
                        rs_cmm_set_premul(colorspace_transform->cmm, colorspace_transform->premul);
 
177
 
 
178
        if (colorspace_transform->has_premul)
 
179
                rs_filter_param_set_boolean(RS_FILTER_PARAM(response), "is-premultiplied", TRUE);
 
180
 
 
181
#if 0
 
182
        printf("\033[33m8 input_space: %s\033[0m\n", (input_space) ? G_OBJECT_TYPE_NAME(input_space) : "none");
 
183
        printf("\033[33m8 output_space: %s\n\033[0m", (output_space) ? G_OBJECT_TYPE_NAME(output_space) : "none");
 
184
#endif
 
185
 
 
186
        output = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, input->w, input->h);
 
187
 
 
188
        /* Process output */
 
189
        convert_colorspace8(colorspace_transform, input, output, input_space, output_space, roi);
 
190
 
 
191
        rs_filter_response_set_image8(response, output);
 
192
        rs_filter_param_set_object(RS_FILTER_PARAM(response), "colorspace", output_space);
 
193
        g_object_unref(output);
 
194
        g_object_unref(input);
 
195
        return response;
 
196
}
 
197
 
 
198
static void
 
199
transform8_c(ThreadInfo* t)
 
200
{
 
201
        gint row;
 
202
        gint r,g,b;
 
203
        gint width;
 
204
        RS_MATRIX3Int mati;
 
205
        RS_IMAGE16 *input = t->input;
 
206
        GdkPixbuf *output = (GdkPixbuf *)t->output;
 
207
        guchar *table8 = t->table8;
 
208
        
 
209
        g_assert(RS_IS_IMAGE16(input));
 
210
        g_assert(GDK_IS_PIXBUF(output));
 
211
        gint o_channels = gdk_pixbuf_get_n_channels(output);
 
212
 
 
213
        matrix3_to_matrix3int(t->matrix, &mati);
 
214
 
 
215
        for(row=t->start_y ; row<t->end_y ; row++)
 
216
        {
 
217
                gushort *i = GET_PIXEL(input, t->start_x, row);
 
218
                guchar *o = GET_PIXBUF_PIXEL(output, t->start_x, row);
 
219
 
 
220
                width = t->end_x - t->start_x;
 
221
 
 
222
                while(width-- > 0)
 
223
                {
 
224
                        r =
 
225
                                ( i[R] * mati.coeff[0][0]
 
226
                                + i[G] * mati.coeff[0][1]
 
227
                                + i[B] * mati.coeff[0][2]
 
228
                                + MATRIX_RESOLUTION_ROUNDER ) >> MATRIX_RESOLUTION;
 
229
                        g =
 
230
                                ( i[R] * mati.coeff[1][0]
 
231
                                + i[G] * mati.coeff[1][1]
 
232
                                + i[B] * mati.coeff[1][2]
 
233
                                + MATRIX_RESOLUTION_ROUNDER ) >> MATRIX_RESOLUTION;
 
234
                        b =
 
235
                                ( i[R] * mati.coeff[2][0]
 
236
                                + i[G] * mati.coeff[2][1]
 
237
                                + i[B] * mati.coeff[2][2]
 
238
                                + MATRIX_RESOLUTION_ROUNDER ) >> MATRIX_RESOLUTION;
 
239
 
 
240
                        r = CLAMP(r, 0, 65535);
 
241
                        g = CLAMP(g, 0, 65535);
 
242
                        b = CLAMP(b, 0, 65535);
 
243
 
 
244
                        o[R] = table8[r];
 
245
                        o[G] = table8[g];
 
246
                        o[B] = table8[b];
 
247
                        o[3] = 255;
 
248
 
 
249
                        i += input->pixelsize;
 
250
                        o += o_channels;
 
251
                }
 
252
        }
 
253
}
 
254
 
 
255
 
 
256
static void
 
257
transform16_c(gushort* __restrict input, gushort* __restrict output, gint num_pixels, const gint pixelsize, RS_MATRIX3 *matrix)
 
258
{
 
259
        gint r,g,b;
 
260
        RS_MATRIX3Int mati;
 
261
 
 
262
        matrix3_to_matrix3int(matrix, &mati);
 
263
 
 
264
        while(num_pixels--)
 
265
        {
 
266
                r =
 
267
                        ( input[R] * mati.coeff[0][0]
 
268
                        + input[G] * mati.coeff[0][1]
 
269
                        + input[B] * mati.coeff[0][2]
 
270
                        + MATRIX_RESOLUTION_ROUNDER ) >> MATRIX_RESOLUTION;
 
271
                g =
 
272
                        ( input[R] * mati.coeff[1][0]
 
273
                        + input[G] * mati.coeff[1][1]
 
274
                        + input[B] * mati.coeff[1][2]
 
275
                        + MATRIX_RESOLUTION_ROUNDER ) >> MATRIX_RESOLUTION;
 
276
                b =
 
277
                        ( input[R] * mati.coeff[2][0]
 
278
                        + input[G] * mati.coeff[2][1]
 
279
                        + input[B] * mati.coeff[2][2]
 
280
                        + MATRIX_RESOLUTION_ROUNDER ) >> MATRIX_RESOLUTION;
 
281
 
 
282
                r = CLAMP(r, 0, 65535);
 
283
                g = CLAMP(g, 0, 65535);
 
284
                b = CLAMP(b, 0, 65535);
 
285
 
 
286
                output[R] = r;
 
287
                output[G] = g;
 
288
                output[B] = b;
 
289
 
 
290
                input += pixelsize;
 
291
                output += pixelsize;
 
292
        }
 
293
}
 
294
 
 
295
static gboolean
 
296
convert_colorspace16(RSColorspaceTransform *colorspace_transform, RS_IMAGE16 *input_image, RS_IMAGE16 *output_image, RSColorSpace *input_space, RSColorSpace *output_space)
 
297
{
 
298
        g_assert(RS_IS_IMAGE16(input_image));
 
299
        g_assert(RS_IS_IMAGE16(output_image));
 
300
        g_assert(RS_IS_COLOR_SPACE(input_space));
 
301
        g_assert(RS_IS_COLOR_SPACE(output_space));
 
302
 
 
303
        /* If input/output-image doesn't differ, return no transformation needed */
 
304
        if (input_space == output_space && !colorspace_transform->has_premul)
 
305
                return FALSE;
 
306
 
 
307
        /* A few sanity checks */
 
308
        g_assert(input_image->w == output_image->w);
 
309
        g_assert(input_image->h == output_image->h);
 
310
 
 
311
        /* If a CMS is needed, do the transformation using LCMS */
 
312
        if (RS_COLOR_SPACE_REQUIRES_CMS(input_space) || RS_COLOR_SPACE_REQUIRES_CMS(output_space))
 
313
        {
 
314
                const RSIccProfile *i, *o;
 
315
 
 
316
                i = rs_color_space_get_icc_profile(input_space, TRUE);
 
317
                o = rs_color_space_get_icc_profile(output_space, TRUE);
 
318
 
 
319
                rs_cmm_set_input_profile(colorspace_transform->cmm, i);
 
320
                rs_cmm_set_output_profile(colorspace_transform->cmm, o);
 
321
 
 
322
                rs_cmm_transform16(colorspace_transform->cmm, input_image, output_image);
 
323
        }
 
324
 
 
325
        /* If we get here, we can transform using simple vector math */
 
326
        else
 
327
        {
 
328
                RS_VECTOR3 vec = {{colorspace_transform->premul[0]},{colorspace_transform->premul[1]},{colorspace_transform->premul[2]}};
 
329
                const RS_MATRIX3 mul_vec = vector3_as_diagonal(&vec);
 
330
                const RS_MATRIX3 a = rs_color_space_get_matrix_from_pcs(input_space);
 
331
                RS_MATRIX3 a_premul;
 
332
                matrix3_multiply(&a, &mul_vec, &a_premul);
 
333
                const RS_MATRIX3 b = rs_color_space_get_matrix_to_pcs(output_space);
 
334
                RS_MATRIX3 mat;
 
335
                matrix3_multiply(&b, &a_premul, &mat);
 
336
 
 
337
                transform16_c(
 
338
                        GET_PIXEL(input_image, 0, 0),
 
339
                        GET_PIXEL(output_image, 0, 0),
 
340
                        input_image->h * input_image->pitch,
 
341
                        input_image->pixelsize,
 
342
                        &mat);
 
343
        }
 
344
        return TRUE;
 
345
}
 
346
 
 
347
gpointer
 
348
start_single_cs8_transform_thread(gpointer _thread_info)
 
349
{
 
350
        ThreadInfo* t = _thread_info;
 
351
        RS_IMAGE16 *input_image = t->input; 
 
352
        GdkPixbuf *output = (GdkPixbuf*) t->output;
 
353
        RSColorSpace *input_space = t->input_space;
 
354
        RSColorSpace *output_space = t->output_space;
 
355
 
 
356
        g_assert(RS_IS_IMAGE16(input_image));
 
357
        g_assert(GDK_IS_PIXBUF(output));
 
358
        g_assert(RS_IS_COLOR_SPACE(input_space));
 
359
        g_assert(RS_IS_COLOR_SPACE(output_space));
 
360
 
 
361
        gboolean sse2_available = (!!(rs_detect_cpu_features() & RS_CPU_FLAG_SSE2)) && cst_has_sse2();
 
362
 
 
363
        if (sse2_available && rs_color_space_new_singleton("RSSrgb") == output_space)
 
364
        {
 
365
                transform8_srgb_sse2(t);
 
366
                return (NULL);
 
367
        }
 
368
        if (sse2_available && rs_color_space_new_singleton("RSAdobeRGB") == output_space)
 
369
        {
 
370
                t->output_gamma = 1.0 / 2.19921875;
 
371
                transform8_otherrgb_sse2(t);
 
372
                return (NULL);
 
373
        }
 
374
        if (sse2_available && rs_color_space_new_singleton("RSProphoto") == output_space)
 
375
        {
 
376
                t->output_gamma = 1.0 / 1.8;
 
377
                transform8_otherrgb_sse2(t);
 
378
                return (NULL);
 
379
        }
 
380
        
 
381
        /* Fall back to C-functions */
 
382
        /* Calculate our gamma table */
 
383
        const RS1dFunction *input_gamma = rs_color_space_get_gamma_function(input_space);
 
384
        const RS1dFunction *output_gamma = rs_color_space_get_gamma_function(output_space);
 
385
        guchar table8[65536];
 
386
        gint i;
 
387
        for(i=0;i<65536;i++)
 
388
        {
 
389
                gdouble nd = ((gdouble) i) * (1.0/65535.0);
 
390
 
 
391
                nd = rs_1d_function_evaluate_inverse(input_gamma, nd);
 
392
                nd = rs_1d_function_evaluate(output_gamma, nd);
 
393
 
 
394
                /* 8 bit output */
 
395
                gint res = (gint) (nd*255.0 + 0.5f);
 
396
                _CLAMP255(res);
 
397
                table8[i] = res;
 
398
        }
 
399
        t->table8 = table8;
 
400
        transform8_c(t);
 
401
        return (NULL);
 
402
}
 
403
 
 
404
static void
 
405
convert_colorspace8(RSColorspaceTransform *colorspace_transform, RS_IMAGE16 *input_image, GdkPixbuf *output_image, RSColorSpace *input_space, RSColorSpace *output_space, GdkRectangle *_roi)
 
406
{
 
407
        g_assert(RS_IS_IMAGE16(input_image));
 
408
        g_assert(GDK_IS_PIXBUF(output_image));
 
409
        g_assert(RS_IS_COLOR_SPACE(input_space));
 
410
        g_assert(RS_IS_COLOR_SPACE(output_space));
 
411
 
 
412
        /* A few sanity checks */
 
413
        g_assert(input_image->w == gdk_pixbuf_get_width(output_image));
 
414
        g_assert(input_image->h == gdk_pixbuf_get_height(output_image));
 
415
 
 
416
        GdkRectangle *roi = _roi;
 
417
        if (!roi) 
 
418
        {
 
419
                roi = g_new(GdkRectangle, 1);
 
420
                roi->x = 0;
 
421
                roi->y = 0;
 
422
                roi->width = input_image->w;
 
423
                roi->height = input_image->h;
 
424
        }
 
425
 
 
426
        /* If a CMS is needed, do the transformation using LCMS */
 
427
        if (RS_COLOR_SPACE_REQUIRES_CMS(input_space) || RS_COLOR_SPACE_REQUIRES_CMS(output_space))
 
428
        {
 
429
                const RSIccProfile *i, *o;
 
430
 
 
431
                i = rs_color_space_get_icc_profile(input_space, TRUE);
 
432
                o = rs_color_space_get_icc_profile(output_space, FALSE);
 
433
 
 
434
                rs_cmm_set_input_profile(colorspace_transform->cmm, i);
 
435
                rs_cmm_set_output_profile(colorspace_transform->cmm, o);
 
436
 
 
437
                rs_cmm_transform8(colorspace_transform->cmm, input_image, output_image);
 
438
        }
 
439
 
 
440
        /* If we get here, we can transform using simple vector math and a lookup table */
 
441
        else
 
442
        {
 
443
                const RS_VECTOR3 vec = {{colorspace_transform->premul[0]},{colorspace_transform->premul[1]},{colorspace_transform->premul[2]}};
 
444
                const RS_MATRIX3 mul_vec = vector3_as_diagonal(&vec);
 
445
                const RS_MATRIX3 a = rs_color_space_get_matrix_from_pcs(input_space);
 
446
                RS_MATRIX3 a_premul;
 
447
                matrix3_multiply(&a, &mul_vec, &a_premul);
 
448
                const RS_MATRIX3 b = rs_color_space_get_matrix_to_pcs(output_space);
 
449
                RS_MATRIX3 mat;
 
450
                matrix3_multiply(&b, &a_premul, &mat);
 
451
 
 
452
 
 
453
                gint i;
 
454
                guint y_offset, y_per_thread, threaded_h;
 
455
                const guint threads = rs_get_number_of_processor_cores();
 
456
                ThreadInfo *t = g_new(ThreadInfo, threads);
 
457
 
 
458
                threaded_h = roi->height;
 
459
                y_per_thread = (threaded_h + threads-1)/threads;
 
460
                y_offset = roi->y;
 
461
 
 
462
                for (i = 0; i < threads; i++)
 
463
                {
 
464
                        t[i].input = input_image;
 
465
                        t[i].output = output_image;
 
466
                        t[i].start_y = y_offset;
 
467
                        t[i].start_x = roi->x;
 
468
                        t[i].end_x = roi->x + roi->width;
 
469
                        t[i].cst = colorspace_transform;
 
470
                        t[i].input_space = input_space;
 
471
                        t[i].output_space = output_space;
 
472
                        y_offset += y_per_thread;
 
473
                        y_offset = MIN(input_image->h, y_offset);
 
474
                        t[i].end_y = y_offset;
 
475
                        t[i].matrix = &mat;
 
476
                        t[i].table8 = NULL;
 
477
 
 
478
                        t[i].threadid = g_thread_create(start_single_cs8_transform_thread, &t[i], TRUE, NULL);
 
479
                }
 
480
 
 
481
                /* Wait for threads to finish */
 
482
                for(i = 0; i < threads; i++)
 
483
                        g_thread_join(t[i].threadid);
 
484
 
 
485
                g_free(t);
 
486
        }
 
487
        /* If we created the ROI here, free it */
 
488
        if (!_roi) 
 
489
                g_free(roi);
 
490
}