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 4 */
21 
22 #include <rawstudio.h>
23 #include <string.h>
24 
25 #define RS_TYPE_DEMOSAIC (rs_demosaic_type)
26 #define RS_DEMOSAIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RS_TYPE_DEMOSAIC, RSDemosaic))
27 #define RS_DEMOSAIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RS_TYPE_DEMOSAIC, RSDemosaicClass))
28 #define RS_IS_DEMOSAIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RS_TYPE_DEMOSAIC))
29 
30 typedef struct {
31 	gint start_y;
32 	gint end_y;
33 	RS_IMAGE16 *image;
34 	RS_IMAGE16 *output;
35 	guint filters;
36 	GThread *threadid;
37 } ThreadInfo;
38 
39 typedef enum {
40 	RS_DEMOSAIC_NONE,
41 	RS_DEMOSAIC_BILINEAR,
42 	RS_DEMOSAIC_PPG,
43 	RS_DEMOSAIC_MAX,
44 	RS_DEMOSAIC_NONE_HALF
45 } RS_DEMOSAIC;
46 
47 const static gchar *rs_demosaic_ascii[RS_DEMOSAIC_MAX] = {
48 	"none",
49 	"bilinear",
50 	"pixel-grouping"
51 };
52 
53 typedef struct _RSDemosaic RSDemosaic;
54 typedef struct _RSDemosaicClass RSDemosaicClass;
55 
56 struct _RSDemosaic {
57 	RSFilter parent;
58 
59 	RS_DEMOSAIC method;
60 	gboolean allow_half;
61 };
62 
63 struct _RSDemosaicClass {
64 	RSFilterClass parent_class;
65 };
66 
67 RS_DEFINE_FILTER(rs_demosaic, RSDemosaic)
68 
69 enum {
70 	PROP_0,
71 	PROP_METHOD,
72 	PROP_ALLOW_HALF,
73 };
74 
75 static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
76 static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
77 static RSFilterResponse *get_image(RSFilter *filter, const RSFilterRequest *request);
78 static inline int fc_INDI (const unsigned int filters, const int row, const int col);
79 static void border_interpolate_INDI (const ThreadInfo* t, int colors, int border);
80 static void lin_interpolate_INDI(RS_IMAGE16 *image, RS_IMAGE16 *output, const unsigned int filters, const int colors);
81 static void ppg_interpolate_INDI(RS_IMAGE16 *image, RS_IMAGE16 *output, const unsigned int filters, const int colors);
82 static void none_interpolate_INDI(RS_IMAGE16 *in, RS_IMAGE16 *out, const unsigned int filters, const int colors, gboolean half_size);
83 static void hotpixel_detect(const ThreadInfo* t);
84 static void expand_cfa_data(const ThreadInfo* t);
85 
86 
87 static RSFilterClass *rs_demosaic_parent_class = NULL;
88 
89 G_MODULE_EXPORT void
rs_plugin_load(RSPlugin * plugin)90 rs_plugin_load(RSPlugin *plugin)
91 {
92 	rs_demosaic_get_type(G_TYPE_MODULE(plugin));
93 }
94 
95 static void
rs_demosaic_class_init(RSDemosaicClass * klass)96 rs_demosaic_class_init(RSDemosaicClass *klass)
97 {
98 	RSFilterClass *filter_class = RS_FILTER_CLASS (klass);
99 	GObjectClass *object_class = G_OBJECT_CLASS(klass);
100 
101 	rs_demosaic_parent_class = g_type_class_peek_parent (klass);
102 
103 	object_class->get_property = get_property;
104 	object_class->set_property = set_property;
105 
106 	g_object_class_install_property(object_class,
107 		PROP_METHOD, g_param_spec_string(
108 			"method", "demosaic method", "The demosaic algorithm to use (\"bilinear\" or \"pixel-grouping\")",
109 			rs_demosaic_ascii[RS_DEMOSAIC_PPG], G_PARAM_READWRITE)
110 	);
111 
112 	g_object_class_install_property(object_class,
113 		PROP_ALLOW_HALF, g_param_spec_boolean(
114 			"demosaic-allow-downscale", "demosaic-allow-downscale", "Allow demosaic to return half size image",
115 			FALSE, G_PARAM_READWRITE)
116 	);
117 
118 	filter_class->name = "Demosaic filter";
119 	filter_class->get_image = get_image;
120 }
121 
122 static void
rs_demosaic_init(RSDemosaic * demosaic)123 rs_demosaic_init(RSDemosaic *demosaic)
124 {
125 	demosaic->method = RS_DEMOSAIC_PPG;
126 }
127 
128 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)129 get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
130 {
131 	RSDemosaic *demosaic = RS_DEMOSAIC(object);
132 
133 	switch (property_id)
134 	{
135 		case PROP_METHOD:
136 			g_value_set_string(value, rs_demosaic_ascii[demosaic->method]);
137 			break;
138 		case PROP_ALLOW_HALF:
139 			g_value_set_boolean(value, demosaic->allow_half);
140 			break;
141 		default:
142 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
143 	}
144 }
145 
146 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)147 set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
148 {
149 	gint i;
150 	const gchar *str;
151 	RSDemosaic *demosaic = RS_DEMOSAIC(object);
152 
153 	switch (property_id)
154 	{
155 		case PROP_METHOD:
156 			str = g_value_get_string(value);
157 			for(i=0;i<RS_DEMOSAIC_MAX;i++)
158 			{
159 				if (g_str_equal(rs_demosaic_ascii[i], str))
160 					demosaic->method = i;
161 			}
162 			break;
163 		case PROP_ALLOW_HALF:
164 			demosaic->allow_half = g_value_get_boolean(value);
165 			break;
166 		default:
167 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
168 	}
169 }
170 
171 /*
172    In order to inline this calculation, I make the risky
173    assumption that all filter patterns can be described
174    by a repeating pattern of eight rows and two columns
175 
176    Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2
177  */
178 #define FC(row,col) \
179   (int)(filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
180 
181 static RSFilterResponse *
get_image(RSFilter * filter,const RSFilterRequest * request)182 get_image(RSFilter *filter, const RSFilterRequest *request)
183 {
184 	RSDemosaic *demosaic = RS_DEMOSAIC(filter);
185 	RSFilterResponse *previous_response;
186 	RSFilterResponse *response;
187 	RS_IMAGE16 *input;
188 	RS_IMAGE16 *output = NULL;
189 	guint filters;
190 	RS_DEMOSAIC method;
191 
192 	previous_response = rs_filter_get_image(filter->previous, request);
193 
194 	input = rs_filter_response_get_image(previous_response);
195 
196 	if (!RS_IS_IMAGE16(input))
197 		return previous_response;
198 
199 	/* Just pass on output from previous filter if the image is not CFA */
200 	if (input->filters == 0)
201 	{
202 		g_object_unref(input);
203 		return previous_response;
204 	}
205 
206 	g_assert(input->channels == 1);
207 	g_assert(input->filters != 0);
208 
209 	response = rs_filter_response_clone(previous_response);
210 	g_object_unref(previous_response);
211 
212 	gint fuji_width;
213 	if (rs_filter_param_get_integer(RS_FILTER_PARAM(response), "fuji-width", &fuji_width) && (fuji_width > 0))
214 		demosaic->allow_half = FALSE;
215 
216 	method = demosaic->method;
217 	if (rs_filter_request_get_quick(request))
218 	{
219 		method = RS_DEMOSAIC_NONE;
220 		rs_filter_response_set_quick(response);
221 	}
222 
223 	/* Magic - Ask Dave ;) */
224 	filters = input->filters;
225 	filters &= ~((filters & 0x55555555) << 1);
226 
227 	/* Check if pattern is 2x2, otherwise we cannot do "none" demosaic */
228 	if (method == RS_DEMOSAIC_NONE)
229 		if (! ( (filters & 0xff ) == ((filters >> 8) & 0xff) &&
230 			((filters >> 16) & 0xff) == ((filters >> 24) & 0xff) &&
231 			(filters & 0xff) == ((filters >> 24) &0xff)))
232 				method = RS_DEMOSAIC_PPG;
233 
234 	if (method == RS_DEMOSAIC_NONE)
235 	{
236 		if (demosaic->allow_half)
237 		{
238 			output = rs_image16_new(input->w/2, input->h/2, 3, 4);
239 			rs_filter_param_set_boolean(RS_FILTER_PARAM(response), "half-size", TRUE);
240 			method = RS_DEMOSAIC_NONE_HALF;
241 		}
242 		else
243 		{
244 			output = rs_image16_new(input->w, input->h, 3, 4);
245 		}
246 	}
247 	else
248 		output = rs_image16_new(input->w, input->h, 3, 4);
249 
250 	rs_filter_response_set_image(response, output);
251 	g_object_unref(output);
252 
253 
254 	switch (method)
255 	{
256 	  case RS_DEMOSAIC_BILINEAR:
257 			lin_interpolate_INDI(input, output, filters, 3);
258 			break;
259 	  case RS_DEMOSAIC_PPG:
260 			ppg_interpolate_INDI(input,output, filters, 3);
261 			break;
262 		case RS_DEMOSAIC_NONE:
263 			none_interpolate_INDI(input, output, filters, 3, FALSE);
264 			break;
265 		case RS_DEMOSAIC_NONE_HALF:
266 			none_interpolate_INDI(input, output, filters, 3, TRUE);
267 			break;
268 		default:
269 			/* Do nothing */
270 			break;
271 		}
272 
273 	g_object_unref(input);
274 	return response;
275 }
276 
277 /*
278 The rest of this file is pretty much copied verbatim from dcraw/ufraw
279 */
280 
281 #define FORCC for (c=0; c < colors; c++)
282 
283 
284 #define BAYER(row,col) \
285 	image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)]
286 
287 static inline int
fc_INDI(const unsigned int filters,const int row,const int col)288 fc_INDI (const unsigned int filters, const int row, const int col)
289 {
290   static const char filter[16][16] =
291   { { 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 },
292     { 0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2 },
293     { 2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3 },
294     { 0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1 },
295     { 3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0 },
296     { 2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1 },
297     { 2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1 },
298     { 1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3 },
299     { 2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3 },
300     { 1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2 },
301     { 0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2 },
302     { 2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0 },
303     { 1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0 },
304     { 0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1 },
305     { 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 },
306     { 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 } };
307 
308   if (filters != 1) return FC(row,col);
309   /* Assume that we are handling the Leaf CatchLight with
310    * top_margin = 8; left_margin = 18; */
311 //  return filter[(row+top_margin) & 15][(col+left_margin) & 15];
312   return filter[(row+8) & 15][(col+18) & 15];
313 }
314 
315 
316 static void
border_interpolate_INDI(const ThreadInfo * t,int colors,int border)317 border_interpolate_INDI (const ThreadInfo* t, int colors, int border)
318 {
319 	int row, col, y, x, f, c, sum[8];
320 	RS_IMAGE16* image = t->output;
321 	guint filters = t->filters;
322 
323 	for (row=t->start_y; row < t->end_y; row++)
324 		for (col=0; col < image->w; col++)
325 		{
326 			if (col==border && row >= border && row < image->h-border)
327 				col = image->w-border;
328 			memset (sum, 0, sizeof sum);
329 			for (y=row-1; y != row+2; y++)
330 				for (x=col-1; x != col+2; x++)
331 					if (y >= 0 && y < image->h && x >= 0 && x < image->w)
332 					{
333 						f = FC(y, x);
334 						sum[f] += GET_PIXEL(image, x, y)[f];
335 						sum[f+4]++;
336 					}
337 			f = FC(row,col);
338 			for (c=0; c < colors; c++)
339 				if (c != f && sum[c+4])
340 					image->pixels[row*image->rowstride+col*4+c] = sum[c] / sum[c+4];
341 		}
342 }
343 
344 static void
lin_interpolate_INDI(RS_IMAGE16 * input,RS_IMAGE16 * output,const unsigned int filters,const int colors)345 lin_interpolate_INDI(RS_IMAGE16 *input, RS_IMAGE16 *output, const unsigned int filters, const int colors) /*UF*/
346 {
347 	ThreadInfo *t = g_new(ThreadInfo, 1);
348 	t->image = input;
349 	t->output = output;
350 	t->filters = filters;
351 	t->start_y = 0;
352 	t->end_y = input->w;
353 
354 	expand_cfa_data(t);
355 	RS_IMAGE16* image = output;
356 
357   int code[16][16][32], *ip, sum[4];
358   int c, i, x, y, row, col, shift, color;
359   gushort *pix;
360 
361   border_interpolate_INDI(t, colors, 1);
362   for (row=0; row < 16; row++)
363     for (col=0; col < 16; col++) {
364       ip = code[row][col];
365       memset (sum, 0, sizeof sum);
366       for (y=-1; y <= 1; y++)
367 	for (x=-1; x <= 1; x++) {
368 	  shift = (y==0) + (x==0);
369 	  if (shift == 2) continue;
370 	  color = fc_INDI(filters,row+y,col+x);
371 	  *ip++ = (image->pitch*y + x)*4 + color;
372 	  *ip++ = shift;
373 	  *ip++ = color;
374 	  sum[color] += 1 << shift;
375 	}
376       FORCC
377 	if (c != fc_INDI(filters,row,col)) {
378 	  *ip++ = c;
379 	  *ip++ = 256 / sum[c];
380 	}
381     }
382   for (row=1; row < image->h-1; row++)
383     for (col=1; col < image->w-1; col++) {
384       pix = GET_PIXEL(image, col, row);
385       ip = code[row & 15][col & 15];
386       memset (sum, 0, sizeof sum);
387       for (i=8; i--; ip+=3)
388 	sum[ip[2]] += pix[ip[0]] << ip[1];
389       for (i=colors; --i; ip+=2)
390 	pix[ip[0]] = sum[ip[0]] * ip[1] >> 8;
391     }
392 }
393 
394 static void
expand_cfa_data(const ThreadInfo * t)395 expand_cfa_data(const ThreadInfo* t) {
396 
397 	RS_IMAGE16* input  = t->image;
398 	RS_IMAGE16* output = t->output;
399 	guint filters = t->filters;
400 	guint col, row;
401 
402 	/* Populate new image with bayer data */
403 	for(row=t->start_y; row<t->end_y; row++)
404 	{
405 		gushort* src = GET_PIXEL(input, 0, row);
406 		gushort* dest = GET_PIXEL(output, 0, row);
407 		for(col=0;col<output->w;col++)
408 		{
409 			dest[fc_INDI(filters, row, col)] = *src;
410 			dest += output->pixelsize;
411 			src++;
412 		}
413 	}
414 }
415 
416 /*
417    Patterned Pixel Grouping Interpolation by Alain Desbiolles
418 */
clampbits16(gint x)419 inline guint clampbits16(gint x) { guint32 _y_temp; if( (_y_temp=x>>16) ) x = ~_y_temp >> 16; return x;}
420 
421 #define CLIP(x) clampbits16(x)
422 #define ULIM(x,y,z) ((y) < (z) ? CLAMP(x,y,z) : CLAMP(x,z,y))
423 
424 static void
interpolate_INDI_part(ThreadInfo * t)425 interpolate_INDI_part(ThreadInfo *t)
426 {
427   RS_IMAGE16 *image = t->output;
428   const unsigned int filters = t->filters;
429 
430   /* Subtract 3 from top and bottom  */
431   const int start_y = MAX(3, t->start_y);
432   const int end_y = MIN(image->h-3, t->end_y);
433   int row, col, c, d;
434 	int diffA, diffB, guessA, guessB;
435 	int p = image->pitch;
436 	int p3 = p*3;
437   gushort (*pix)[4];
438 
439   {
440 /*  Fill in the green layer with gradients and pattern recognition: */
441   for (row=start_y; row < end_y; row++)
442     for (col=3+(FC(row,3) & 1), c=FC(row,col); col < image->w-3; col+=2) {
443       pix = (gushort (*)[4])GET_PIXEL(image, col, row);
444 
445 	guessA = (pix[-1][1] + pix[0][c] + pix[1][1]) * 2
446 		      - pix[-2*1][c] - pix[2*1][c];
447 	diffA = ( ABS(pix[-2*1][c] - pix[ 0][c]) +
448 		    ABS(pix[ 2*1][c] - pix[ 0][c]) +
449 		    ABS(pix[  -1][1] - pix[ 1][1]) ) * 3 +
450 		  ( ABS(pix[ 3*1][1] - pix[ 1][1]) +
451 		    ABS(pix[-3*1][1] - pix[-1][1]) ) * 2;
452 
453 	guessB = (pix[-p][1] + pix[0][c] + pix[p][1]) * 2
454 		      - pix[-2*p][c] - pix[2*p][c];
455 	diffB = ( ABS(pix[-2*p][c] - pix[ 0][c]) +
456 		    ABS(pix[ 2*p][c] - pix[ 0][c]) +
457 		    ABS(pix[  -p][1] - pix[ p][1]) ) * 3 +
458 		  ( ABS(pix[ p3][1] - pix[ p][1]) +
459 		    ABS(pix[-p3][1] - pix[-p][1]) ) * 2;
460 
461 		if (diffA > diffB)
462 			pix[0][1] = ULIM(guessB >> 2, pix[p][1], pix[-p][1]);
463 		else
464 			pix[0][1] = ULIM(guessA >> 2, pix[1][1], pix[-1][1]);
465     }
466 /*  Calculate red and blue for each green pixel:		*/
467   for (row=start_y-2; row < end_y+2; row++)
468     for (col=1+(FC(row,2) & 1), c=FC(row,col+1); col < image->w-1; col+=2) {
469       pix = (gushort (*)[4])GET_PIXEL(image, col, row);
470       pix[0][c] = CLIP((pix[-1][c] + pix[1][c] + 2*pix[0][1]
471           - pix[-1][1] - pix[1][1]) >> 1);
472       c=2-c;
473       pix[0][c] = CLIP((pix[-p][c] + pix[p][c] + 2*pix[0][1]
474           - pix[-p][1] - pix[p][1]) >> 1);
475       c=2-c;
476     }
477 
478 /*  Calculate blue for red pixels and vice versa:		*/
479 	for (row=start_y-2; row < end_y+2; row++)
480 		for (col=1+(FC(row,1) & 1), c=2-FC(row,col); col < image->w-1; col+=2) {
481 			pix = (gushort (*)[4])GET_PIXEL(image, col, row);
482 			d = 1 + p;
483 			diffA = ABS(pix[-d][c] - pix[d][c]) +
484 				ABS(pix[-d][1] - pix[0][1]) +
485 				ABS(pix[ d][1] - pix[0][1]);
486 			guessA = pix[-d][c] + pix[d][c] + 2*pix[0][1]
487 			- pix[-d][1] - pix[d][1];
488 
489 			d = p - 1;
490 			diffB = ABS(pix[-d][c] - pix[d][c]) +
491 				ABS(pix[-d][1] - pix[0][1]) +
492 				ABS(pix[ d][1] - pix[0][1]);
493 			guessB = pix[-d][c] + pix[d][c] + 2*pix[0][1]
494 				- pix[-d][1] - pix[d][1];
495 
496 			if (diffA > diffB)
497 				pix[0][c] = CLIP(guessB >> 1);
498 			else
499 				pix[0][c] = CLIP(guessA >> 1);
500 		}
501 	}
502 }
503 
504 gpointer
start_interp_thread(gpointer _thread_info)505 start_interp_thread(gpointer _thread_info)
506 {
507 	ThreadInfo* t = _thread_info;
508 	hotpixel_detect(t);
509 	expand_cfa_data(t);
510 	border_interpolate_INDI (t, 3, 3);
511 	interpolate_INDI_part(t);
512 	g_thread_exit(NULL);
513 
514 	return NULL; /* Make the compiler shut up - we'll never return */
515 }
516 
517 static void
ppg_interpolate_INDI(RS_IMAGE16 * image,RS_IMAGE16 * output,const unsigned int filters,const int colors)518 ppg_interpolate_INDI(RS_IMAGE16 *image, RS_IMAGE16 *output, const unsigned int filters, const int colors)
519 {
520 	guint i, y_offset, y_per_thread, threaded_h;
521 	const guint threads = rs_get_number_of_processor_cores();
522 	ThreadInfo *t = g_new(ThreadInfo, threads);
523 
524 	threaded_h = image->h;
525 	y_per_thread = (threaded_h + threads-1)/threads;
526 	y_offset = 0;
527 
528 	for (i = 0; i < threads; i++)
529 	{
530 		t[i].image = image;
531 		t[i].output = output;
532 		t[i].filters = filters;
533 		t[i].start_y = y_offset;
534 		y_offset += y_per_thread;
535 		y_offset = MIN(image->h, y_offset);
536 		t[i].end_y = y_offset;
537 
538 		t[i].threadid = g_thread_create(start_interp_thread, &t[i], TRUE, NULL);
539 	}
540 
541 	/* Wait for threads to finish */
542 	for(i = 0; i < threads; i++)
543 		g_thread_join(t[i].threadid);
544 
545 	g_free(t);
546 }
547 
548 
549 gpointer
start_none_thread(gpointer _thread_info)550 start_none_thread(gpointer _thread_info)
551 {
552 	gint row, col;
553 	gushort *src;
554 	gushort *dest;
555 
556 	ThreadInfo* t = _thread_info;
557 	gint ops = t->output->pixelsize;
558 	gint ors = t->output->rowstride;
559 	guint filters = t->filters;
560 
561 	for(row=t->start_y; row < t->end_y; row++)
562 	{
563 		src = GET_PIXEL(t->image, 0, row);
564 		dest = GET_PIXEL(t->output, 0, row);
565 		guint first = FC(row, 0);
566 		guint second = FC(row, 1);
567 		gint col_end = t->output->w & 0xfffe;
568 
569 		/* Green first or second?*/
570 		if (first == 1) {
571 			/* Green first, then red or blue */
572 			/* Copy non-green to this and pixel below */
573 			dest[second] = dest[second+ors] = src[1];
574 			/* Copy green down */
575 			dest[1+ors] = *src;
576 			for(col=0 ; col < col_end; col += 2)
577 			{
578 				dest[1] = dest[1+ops]= *src;
579 				/* Move to next pixel */
580 				src++;
581 				dest += ops;
582 
583 				dest[second] = dest[second+ops] =
584 				dest[second+ors] = dest[second+ops+ors] = *src;
585 
586 				/* Move to next pixel */
587 				dest += ops;
588 				src++;
589 			}
590 			/* If uneven pixel width, copy last pixel */
591 			if (t->output->w & 1) {
592 				dest[0] = dest[-ops];
593 				dest[1] = dest[-ops+1];
594 				dest[2] = dest[-ops+2];
595 			}
596 		} else {
597 			for(col=0 ; col < col_end; col += 2)
598 			{
599 				dest[first] = dest[first+ops] =
600 				dest[first+ors] = dest[first+ops+ors] = *src;
601 
602 				dest += ops;
603 				src++;
604 
605 				dest[1] = dest[1+ops]= *src;
606 
607 				dest += ops;
608 				src++;
609 			}
610 			/* If uneven pixel width, copy last pixel */
611 			if (t->output->w & 1) {
612 				dest[0] = dest[-ops];
613 				dest[1] = dest[-ops+1];
614 				dest[2] = dest[-ops+2];
615 			}
616 		}
617 		/*  Duplicate first & last line */
618 		if (t->end_y == t->output->h - 1)
619 		{
620 			memcpy(GET_PIXEL(t->output, 0, t->end_y), GET_PIXEL(t->output, 0, t->end_y - 1), t->output->rowstride * 2);
621 			memcpy(GET_PIXEL(t->output, 0, 0), GET_PIXEL(t->output, 0, 1), t->output->rowstride * 2);
622 		}
623 	}
624 	g_thread_exit(NULL);
625 
626 	return NULL; /* Make the compiler shut up - we'll never return */
627 }
628 
629 
630 gpointer
start_none_thread_half(gpointer _thread_info)631 start_none_thread_half(gpointer _thread_info)
632 {
633 	gint row, col, i, j;
634 	gushort *src;
635 	gushort *dest;
636 
637 	ThreadInfo* t = _thread_info;
638 	guint filters = t->filters;
639 	gint col_end = t->output->w;
640 
641 	for(row=t->start_y; row < t->end_y; row++)
642 	{
643 		gint src_row = row*2;
644 		src = GET_PIXEL(t->image, 0, src_row);
645 		dest = GET_PIXEL(t->output, 0, row);
646 		gushort *g_src = (FC(src_row, 0) == 1) ? &src[0] : &src[1];
647 		gushort *r_src = NULL;
648 		gushort *b_src = NULL;
649 		for(i = src_row; i < src_row + 2; i++)
650 		{
651 			for( j = 0; j < 2; j ++)
652 			{
653 				if (FC(i, j) == 0)
654 					r_src = GET_PIXEL(t->image, j, i);
655 				if (FC(i, j) == 2)
656 					b_src = GET_PIXEL(t->image, j, i);
657 			}
658 		}
659 
660 		g_assert(r_src);
661 		g_assert(b_src);
662 		for(col=0 ; col < col_end; col++)
663 		{
664 			*dest++ = *r_src;
665 			*dest++ = *g_src;
666 			*dest++ = *b_src;
667 			dest++;
668 			r_src+=2;
669 			g_src+=2;
670 			b_src+=2;
671 		}
672 
673 	}
674 	g_thread_exit(NULL);
675 
676 	return NULL; /* Make the compiler shut up - we'll never return */
677 }
678 
679 
680 static void
none_interpolate_INDI(RS_IMAGE16 * in,RS_IMAGE16 * out,const unsigned int filters,const int colors,gboolean half_size)681 none_interpolate_INDI(RS_IMAGE16 *in, RS_IMAGE16 *out, const unsigned int filters, const int colors, gboolean half_size)
682 {
683 	guint i, y_offset, y_per_thread, threaded_h;
684 	const guint threads = rs_get_number_of_processor_cores();
685 	ThreadInfo *t = g_new(ThreadInfo, threads);
686 
687 	/* Subtract 1 from bottom  */
688 	threaded_h = out->h-1;
689 	y_per_thread = (threaded_h + threads-1)/threads;
690 	y_offset = 0;
691 
692 	for (i = 0; i < threads; i++)
693 	{
694 		t[i].image = in;
695 		t[i].filters = filters;
696 		t[i].start_y = y_offset;
697 		t[i].output = out;
698 		y_offset += y_per_thread;
699 		y_offset = MIN(out->h-1, y_offset);
700 		t[i].end_y = y_offset;
701 
702 		if (half_size)
703 			t[i].threadid = g_thread_create(start_none_thread_half, &t[i], TRUE, NULL);
704 		else
705 			t[i].threadid = g_thread_create(start_none_thread, &t[i], TRUE, NULL);
706 	}
707 
708 	/* Wait for threads to finish */
709 	for(i = 0; i < threads; i++)
710 		g_thread_join(t[i].threadid);
711 
712 	g_free(t);
713 }
714 
715 static void
hotpixel_detect(const ThreadInfo * t)716 hotpixel_detect(const ThreadInfo* t)
717 {
718 
719 	RS_IMAGE16 *image = t->image;
720 
721 	gint x, y, end_y;
722 	y = MAX( 4, t->start_y);
723 	end_y = MIN(t->end_y, image->h - 4);
724 
725 	for(; y < end_y; y++)
726 	{
727 		gint col_end = image->w - 4;
728 		gushort* img = GET_PIXEL(image, 0, y);
729 		gint p = image->rowstride * 2;
730 		gint p_one = image->rowstride;
731 		for (x = 4; x < col_end ; x++) {
732 			/* Calculate minimum difference to surrounding pixels */
733 			gint left = (int)img[x - 2];
734 			gint c = (int)img[x];
735 			gint right = (int)img[x + 2];
736 			gint up = (int)img[x - p];
737 			gint down = (int)img[x + p];
738 
739 			gint d = ABS(c - left);
740 			d = MIN(d, ABS(c - right));
741 			d = MIN(d, ABS(c - up));
742 			d = MIN(d, ABS(c - down));
743 
744 			/* Also calculate maximum difference between surrounding pixels themselves */
745 			gint d2 = ABS(left - right);
746 			d2 = MAX(d2, ABS(up - down));
747 
748 			/* If difference larger than surrounding pixels by a factor of 4,
749 				replace with left/right pixel interpolation */
750 
751 			if ((d > d2 * 8) && (d > 2000)) {
752 				/* Do extended test! */
753 				left = (int)img[x - 4];
754 				right = (int)img[x + 4];
755 				up = (int)img[x - p * 2];
756 				down = (int)img[x + p * 2];
757 
758 				d = MIN(d, ABS(c - left));
759 				d = MIN(d, ABS(c - right));
760 				d = MIN(d, ABS(c - up));
761 				d = MIN(d, ABS(c - down));
762 
763 				/* Create threshold for surrounding pixels - also include other colors */
764 				d2 = MAX(d2, ABS(left - right));
765 				d2 = MAX(d2, ABS(up - down));
766 				d = MIN(d, ABS(c - (int)img[x - 2 - p]));
767 				d = MIN(d, ABS(c - (int)img[x + 2 - p]));
768 				d = MIN(d, ABS(c - (int)img[x - 2 + p]));
769 				d = MIN(d, ABS(c - (int)img[x + 2 + p]));
770 				d2 = MAX(d2, ABS((int)img[x - 1] - (int)img[x + 1]));
771 				d2 = MAX(d2, ABS((int)img[x - p_one] - (int)img[x + p_one]));
772 				d2 = MAX(d2, ABS((int)img[x - 1 - p_one] - (int)img[x + 1 + p_one]));
773 				d2 = MAX(d2, ABS((int)img[x - 1 + p_one] - (int)img[x + 1 - p_one]));
774 				d2 = MAX(d2, ABS((int)img[x - 2 - p] - (int)img[x + 2 + p]));
775 				d2 = MAX(d2, ABS((int)img[x - 2 + p] - (int)img[x + 2 - p]));
776 
777 				if ((d > d2 * 4) && (d > 1600)) {
778 					img[x] = (gushort)(((gint)img[x-2] + (gint)img[x+2] + 1) >> 1);
779 				}
780 			}
781 
782 		}
783 	}
784 }
785