Fixed minor compilation warnings
[liblqr.git] / lqr / lqr_carver.c
blob50a578b68b8a1285b01c411b7885e7897be9b879
1 /* LiquidRescaling Library
2 * Copyright (C) 2007-2009 Carlo Baldassi (the "Author") <carlobaldassi@gmail.com>.
3 * All Rights Reserved.
5 * This library implements the algorithm described in the paper
6 * "Seam Carving for Content-Aware Image Resizing"
7 * by Shai Avidan and Ariel Shamir
8 * which can be found at http://www.faculty.idc.ac.il/arik/imret.pdf
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; version 3 dated June, 2007.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, see <http://www.gnu.org/licenses/>
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
27 #include <math.h>
29 #include <lqr/lqr_all.h>
31 #ifdef __LQR_VERBOSE__
32 #include <stdio.h>
33 #endif /* __LQR_VERBOSE__ */
35 #ifdef __LQR_DEBUG__
36 #include <stdio.h>
37 #include <assert.h>
38 #endif /* __LQR_DEBUG__ */
40 /**** LQR_CARVER CLASS FUNCTIONS ****/
42 /*** constructor & destructor ***/
44 /* constructors */
45 LqrCarver *
46 lqr_carver_new_common(gint width, gint height, gint channels)
48 LqrCarver *r;
50 LQR_TRY_N_N(r = g_try_new(LqrCarver, 1));
52 g_atomic_int_set(&r->state, LQR_CARVER_STATE_STD);
53 g_atomic_int_set(&r->state_lock, 0);
54 g_atomic_int_set(&r->state_lock_queue, 0);
56 r->level = 1;
57 r->max_level = 1;
58 r->transposed = 0;
59 r->active = FALSE;
60 r->nrg_active = FALSE;
61 r->root = NULL;
62 r->rigidity = 0;
63 r->resize_aux_layers = FALSE;
64 r->dump_vmaps = FALSE;
65 r->resize_order = LQR_RES_ORDER_HOR;
66 r->attached_list = NULL;
67 r->flushed_vs = NULL;
68 r->preserve_in_buffer = FALSE;
69 LQR_TRY_N_N(r->progress = lqr_progress_new());
70 r->session_update_step = 1;
71 r->session_rescale_total = 0;
72 r->session_rescale_current = 0;
74 r->en = NULL;
75 r->bias = NULL;
76 r->m = NULL;
77 r->least = NULL;
78 r->_raw = NULL;
79 r->raw = NULL;
80 r->vpath = NULL;
81 r->vpath_x = NULL;
82 r->rigidity_map = NULL;
83 r->rigidity_mask = NULL;
84 r->delta_x = 1;
86 r->h = height;
87 r->w = width;
88 r->channels = channels;
90 r->w0 = r->w;
91 r->h0 = r->h;
92 r->w_start = r->w;
93 r->h_start = r->h;
95 r->rcache = NULL;
96 r->use_rcache = TRUE;
98 r->rwindow = NULL;
99 lqr_carver_set_energy_function_builtin(r, LQR_EF_GRAD_XABS);
100 r->nrg_xmin = NULL;
101 r->nrg_xmax = NULL;
102 r->nrg_uptodate = FALSE;
104 r->leftright = 0;
105 r->lr_switch_frequency = 0;
107 r->enl_step = 2.0;
109 LQR_TRY_N_N(r->vs = g_try_new0(gint, r->w * r->h));
111 /* initialize cursor */
113 LQR_TRY_N_N(r->c = lqr_cursor_create(r));
115 switch (channels) {
116 case 1:
117 lqr_carver_set_image_type(r, LQR_GREY_IMAGE);
118 break;
119 case 2:
120 lqr_carver_set_image_type(r, LQR_GREYA_IMAGE);
121 break;
122 case 3:
123 lqr_carver_set_image_type(r, LQR_RGB_IMAGE);
124 break;
125 case 4:
126 lqr_carver_set_image_type(r, LQR_RGBA_IMAGE);
127 break;
128 case 5:
129 lqr_carver_set_image_type(r, LQR_CMYKA_IMAGE);
130 break;
131 default:
132 lqr_carver_set_image_type(r, LQR_CUSTOM_IMAGE);
133 break;
136 return r;
139 /* LQR_PUBLIC */
140 LqrCarver *
141 lqr_carver_new(guchar *buffer, gint width, gint height, gint channels)
143 return lqr_carver_new_ext(buffer, width, height, channels, LQR_COLDEPTH_8I);
146 /* LQR_PUBLIC */
147 LqrCarver *
148 lqr_carver_new_ext(void *buffer, gint width, gint height, gint channels, LqrColDepth colour_depth)
150 LqrCarver *r;
152 LQR_TRY_N_N(r = lqr_carver_new_common(width, height, channels));
154 r->rgb = (void *) buffer;
156 BUF_TRY_NEW_RET_POINTER(r->rgb_ro_buffer, r->channels * r->w, colour_depth);
158 r->col_depth = colour_depth;
160 return r;
163 /* destructor */
164 /* LQR_PUBLIC */
165 void
166 lqr_carver_destroy(LqrCarver *r)
168 if (!r->preserve_in_buffer) {
169 g_free(r->rgb);
171 if (r->root == NULL) {
172 g_free(r->vs);
174 g_free(r->rgb_ro_buffer);
175 g_free(r->en);
176 g_free(r->bias);
177 g_free(r->m);
178 g_free(r->rcache);
179 g_free(r->least);
180 lqr_cursor_destroy(r->c);
181 g_free(r->vpath);
182 g_free(r->vpath_x);
183 if (r->rigidity_map != NULL) {
184 r->rigidity_map -= r->delta_x;
185 g_free(r->rigidity_map);
187 g_free(r->rigidity_mask);
188 lqr_rwindow_destroy(r->rwindow);
189 g_free(r->nrg_xmin);
190 g_free(r->nrg_xmax);
191 lqr_vmap_list_destroy(r->flushed_vs);
192 lqr_carver_list_destroy(r->attached_list);
193 g_free(r->progress);
194 g_free(r->_raw);
195 g_free(r->raw);
196 g_free(r);
199 /*** initialization ***/
201 LqrRetVal
202 lqr_carver_init_energy_related(LqrCarver *r)
204 gint y, x;
206 LQR_CATCH_F(r->active == FALSE);
207 LQR_CATCH_F(r->nrg_active == FALSE);
209 LQR_CATCH_MEM(r->en = g_try_new(gfloat, r->w * r->h));
210 LQR_CATCH_MEM(r->_raw = g_try_new(gint, r->h_start * r->w_start));
211 LQR_CATCH_MEM(r->raw = g_try_new(gint *, r->h_start));
213 for (y = 0; y < r->h; y++) {
214 r->raw[y] = r->_raw + y * r->w_start;
215 for (x = 0; x < r->w_start; x++) {
216 r->raw[y][x] = y * r->w_start + x;
220 r->nrg_active = TRUE;
222 return LQR_OK;
225 /* LQR_PUBLIC */
226 LqrRetVal
227 lqr_carver_init(LqrCarver *r, gint delta_x, gfloat rigidity)
229 gint x;
231 LQR_CATCH_CANC(r);
233 LQR_CATCH_F(r->active == FALSE);
235 if (r->nrg_active == FALSE) {
236 LQR_CATCH(lqr_carver_init_energy_related(r));
239 /* LQR_CATCH_MEM (r->bias = g_try_new0 (gfloat, r->w * r->h)); */
240 LQR_CATCH_MEM(r->m = g_try_new(gfloat, r->w * r->h));
241 LQR_CATCH_MEM(r->least = g_try_new(gint, r->w * r->h));
243 LQR_CATCH_MEM(r->vpath = g_try_new(gint, r->h));
244 LQR_CATCH_MEM(r->vpath_x = g_try_new(gint, r->h));
246 LQR_CATCH_MEM(r->nrg_xmin = g_try_new(gint, r->h));
247 LQR_CATCH_MEM(r->nrg_xmax = g_try_new(gint, r->h));
249 /* set rigidity map */
250 r->delta_x = delta_x;
251 r->rigidity = rigidity;
253 r->rigidity_map = g_try_new0(gfloat, 2 * r->delta_x + 1);
254 r->rigidity_map += r->delta_x;
255 for (x = -r->delta_x; x <= r->delta_x; x++) {
256 r->rigidity_map[x] = r->rigidity * powf(fabsf(x), 1.5) / r->h;
259 r->active = TRUE;
261 return LQR_OK;
264 /*** set attributes ***/
266 /* LQR_PUBLIC */
267 LqrRetVal
268 lqr_carver_set_image_type(LqrCarver *r, LqrImageType image_type)
270 LQR_CATCH_CANC(r);
272 switch (image_type) {
273 case LQR_GREY_IMAGE:
274 if (r->channels != 1) {
275 return LQR_ERROR;
277 r->alpha_channel = -1;
278 r->black_channel = -1;
279 break;
280 case LQR_GREYA_IMAGE:
281 if (r->channels != 2) {
282 return LQR_ERROR;
284 r->alpha_channel = 1;
285 r->black_channel = -1;
286 break;
287 case LQR_CMY_IMAGE:
288 case LQR_RGB_IMAGE:
289 if (r->channels != 3) {
290 return LQR_ERROR;
292 r->alpha_channel = -1;
293 r->black_channel = -1;
294 break;
295 case LQR_CMYK_IMAGE:
296 if (r->channels != 4) {
297 return LQR_ERROR;
299 r->alpha_channel = -1;
300 r->black_channel = 3;
301 break;
302 case LQR_RGBA_IMAGE:
303 if (r->channels != 4) {
304 return LQR_ERROR;
306 r->alpha_channel = 3;
307 r->black_channel = -1;
308 break;
309 case LQR_CMYKA_IMAGE:
310 if (r->channels != 5) {
311 return LQR_ERROR;
313 r->alpha_channel = 4;
314 r->black_channel = 3;
315 break;
316 case LQR_CUSTOM_IMAGE:
317 r->alpha_channel = -1;
318 r->black_channel = -1;
319 break;
320 default:
321 return LQR_ERROR;
323 r->image_type = image_type;
325 g_free(r->rcache);
326 r->rcache = NULL;
327 r->nrg_uptodate = FALSE;
329 return LQR_OK;
332 /* LQR_PUBLIC */
333 LqrRetVal
334 lqr_carver_set_alpha_channel(LqrCarver *r, gint channel_index)
336 gboolean changed = TRUE;
337 LQR_CATCH_CANC(r);
339 if (channel_index < 0) {
340 if (r->alpha_channel != -1) {
341 r->alpha_channel = -1;
342 } else {
343 changed = FALSE;
345 } else if (channel_index < r->channels) {
346 if (r->alpha_channel != channel_index) {
347 if (r->black_channel == channel_index) {
348 r->black_channel = -1;
350 r->alpha_channel = channel_index;
351 } else {
352 changed = FALSE;
354 } else {
355 return LQR_ERROR;
358 if (r->image_type != LQR_CUSTOM_IMAGE) {
359 r->image_type = LQR_CUSTOM_IMAGE;
360 changed = TRUE;
363 if (changed) {
364 g_free(r->rcache);
365 r->rcache = NULL;
366 r->nrg_uptodate = FALSE;
369 return LQR_OK;
372 /* LQR_PUBLIC */
373 LqrRetVal
374 lqr_carver_set_black_channel(LqrCarver *r, gint channel_index)
376 gboolean changed = TRUE;
377 LQR_CATCH_CANC(r);
379 if (channel_index < 0) {
380 if (r->black_channel != -1) {
381 r->black_channel = -1;
382 } else {
383 changed = FALSE;
385 } else if (channel_index < r->channels) {
386 if (r->black_channel != channel_index) {
387 if (r->alpha_channel == channel_index) {
388 r->alpha_channel = -1;
390 r->black_channel = channel_index;
391 } else {
392 changed = FALSE;
394 } else {
395 return LQR_ERROR;
398 if (r->image_type != LQR_CUSTOM_IMAGE) {
399 r->image_type = LQR_CUSTOM_IMAGE;
400 changed = TRUE;
403 if (changed) {
404 g_free(r->rcache);
405 r->rcache = NULL;
406 r->nrg_uptodate = FALSE;
409 return LQR_OK;
412 /* set gradient function */
413 /* WARNING: THIS FUNCTION IS ONLY MAINTAINED FOR BACK-COMPATIBILITY PURPOSES */
414 /* lqr_carver_set_energy_function_builtin() should be used in newly written code instead */
415 /* LQR_PUBLIC */
416 void
417 lqr_carver_set_gradient_function(LqrCarver *r, LqrGradFuncType gf_ind)
419 switch (gf_ind) {
420 case LQR_GF_NORM:
421 lqr_carver_set_energy_function_builtin(r, LQR_EF_GRAD_NORM);
422 break;
423 case LQR_GF_SUMABS:
424 lqr_carver_set_energy_function_builtin(r, LQR_EF_GRAD_SUMABS);
425 break;
426 case LQR_GF_XABS:
427 lqr_carver_set_energy_function_builtin(r, LQR_EF_GRAD_XABS);
428 break;
429 case LQR_GF_NULL:
430 lqr_carver_set_energy_function_builtin(r, LQR_EF_NULL);
431 break;
432 case LQR_GF_NORM_BIAS:
433 case LQR_GF_YABS:
434 lqr_carver_set_energy_function_builtin(r, LQR_EF_NULL);
435 break;
436 #ifdef __LQR_DEBUG__
437 default:
438 assert(0);
439 #endif /* __LQR_DEBUG__ */
443 /* attach carvers to be scaled along with the main one */
444 /* LQR_PUBLIC */
445 LqrRetVal
446 lqr_carver_attach(LqrCarver *r, LqrCarver *aux)
448 LQR_CATCH_F(r->w0 == aux->w0);
449 LQR_CATCH_F(r->h0 == aux->h0);
450 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
451 LQR_CATCH_F(g_atomic_int_get(&aux->state) == LQR_CARVER_STATE_STD);
452 LQR_CATCH_MEM(r->attached_list = lqr_carver_list_append(r->attached_list, aux));
453 g_free(aux->vs);
454 aux->vs = r->vs;
455 aux->root = r;
457 return LQR_OK;
460 /* set the seam output flag */
461 /* LQR_PUBLIC */
462 void
463 lqr_carver_set_dump_vmaps(LqrCarver *r)
465 r->dump_vmaps = TRUE;
468 /* unset the seam output flag */
469 /* LQR_PUBLIC */
470 void
471 lqr_carver_set_no_dump_vmaps(LqrCarver *r)
473 r->dump_vmaps = FALSE;
476 /* set order if rescaling in both directions */
477 /* LQR_PUBLIC */
478 void
479 lqr_carver_set_resize_order(LqrCarver *r, LqrResizeOrder resize_order)
481 r->resize_order = resize_order;
484 /* set leftright switch interval */
485 /* LQR_PUBLIC */
486 void
487 lqr_carver_set_side_switch_frequency(LqrCarver *r, guint switch_frequency)
489 r->lr_switch_frequency = switch_frequency;
492 /* set enlargement step */
493 /* LQR_PUBLIC */
494 LqrRetVal
495 lqr_carver_set_enl_step(LqrCarver *r, gfloat enl_step)
497 LQR_CATCH_F((enl_step > 1) && (enl_step <= 2));
498 LQR_CATCH_CANC(r);
499 r->enl_step = enl_step;
500 return LQR_OK;
503 /* LQR_PUBLIC */
504 void
505 lqr_carver_set_use_cache(LqrCarver *r, gboolean use_cache)
507 if (!use_cache) {
508 g_free(r->rcache);
509 r->rcache = NULL;
511 r->use_rcache = use_cache;
512 r->rwindow->use_rcache = use_cache;
515 /* set progress reprot */
516 /* LQR_PUBLIC */
517 void
518 lqr_carver_set_progress(LqrCarver *r, LqrProgress * p)
520 g_free(r->progress);
521 r->progress = p;
524 /* flag the input buffer to avoid destruction */
525 /* LQR_PUBLIC */
526 void
527 lqr_carver_set_preserve_input_image(LqrCarver *r)
529 r->preserve_in_buffer = TRUE;
532 /*** compute maps (energy, minpath & visibility) ***/
534 /* build multisize image up to given depth
535 * it is progressive (can be called multilple times) */
536 LqrRetVal
537 lqr_carver_build_maps(LqrCarver *r, gint depth)
539 #ifdef __LQR_DEBUG__
540 assert(depth <= r->w_start);
541 assert(depth >= 1);
542 #endif /* __LQR_DEBUG__ */
544 LQR_CATCH_CANC(r);
546 /* only go deeper if needed */
547 if (depth > r->max_level) {
548 LQR_CATCH_F(r->active);
549 LQR_CATCH_F(r->root == NULL);
551 /* set to minimum width reached so far */
552 lqr_carver_set_width(r, r->w_start - r->max_level + 1);
554 /* compute energy & minpath maps */
555 LQR_CATCH(lqr_carver_build_emap(r));
556 LQR_CATCH(lqr_carver_build_mmap(r));
558 /* compute visibility map */
559 LQR_CATCH(lqr_carver_build_vsmap(r, depth));
561 return LQR_OK;
564 /* compute energy map */
565 LqrRetVal
566 lqr_carver_build_emap(LqrCarver *r)
568 gint x, y;
570 LQR_CATCH_CANC(r);
572 if (r->nrg_uptodate) {
573 return LQR_OK;
576 if (r->use_rcache && r->rcache == NULL) {
577 LQR_CATCH_MEM(r->rcache = lqr_carver_generate_rcache(r));
580 for (y = 0; y < r->h; y++) {
581 LQR_CATCH_CANC(r);
582 /* r->nrg_xmin[y] = 0; */
583 /* r->nrg_xmax[y] = r->w - 1; */
584 for (x = 0; x < r->w; x++) {
585 LQR_CATCH(lqr_carver_compute_e(r, x, y));
589 r->nrg_uptodate = TRUE;
591 return LQR_OK;
594 LqrRetVal
595 lqr_carver_compute_e(LqrCarver *r, gint x, gint y)
597 gint data;
598 gfloat b_add = 0;
600 /* removed CANC check for performance reasons */
601 /* LQR_CATCH_CANC (r); */
603 data = r->raw[y][x];
605 LQR_CATCH(lqr_rwindow_fill(r->rwindow, r, x, y));
606 if (r->bias != NULL) {
607 b_add = r->bias[data] / r->w_start;
609 r->en[data] = r->nrg(x, y, r->w, r->h, r->rwindow, r->nrg_extra_data) + b_add;
611 return LQR_OK;
614 /* compute auxiliary minpath map
615 * defined as
616 * y = 1 : m(x,y) = e(x,y)
617 * y > 1 : m(x,y) = min_{x'=-dx,..,dx} ( m(x-x',y-1) + rig(x') ) + e(x,y)
618 * where
619 * e(x,y) is the energy at point (x,y)
620 * dx is the max seam step delta_x
621 * rig(x') is the rigidity for step x'
623 LqrRetVal
624 lqr_carver_build_mmap(LqrCarver *r)
626 gint x, y;
627 gint data;
628 gint data_down;
629 gint x1_min, x1_max, x1;
630 gfloat m, m1, r_fact;
632 LQR_CATCH_CANC(r);
634 /* span first row */
635 for (x = 0; x < r->w; x++) {
636 data = r->raw[0][x];
637 #ifdef __LQR_DEBUG__
638 assert(r->vs[data] == 0);
639 #endif /* __LQR_DEBUG__ */
640 r->m[data] = r->en[data];
643 /* span all other rows */
644 for (y = 1; y < r->h; y++) {
645 for (x = 0; x < r->w; x++) {
646 LQR_CATCH_CANC(r);
648 data = r->raw[y][x];
649 #ifdef __LQR_DEBUG__
650 assert(r->vs[data] == 0);
651 #endif /* __LQR_DEBUG__ */
652 /* watch for boundaries */
653 x1_min = MAX(-x, -r->delta_x);
654 x1_max = MIN(r->w - 1 - x, r->delta_x);
655 if (r->rigidity_mask) {
656 r_fact = r->rigidity_mask[data];
657 } else {
658 r_fact = 1;
661 /* we use the data_down pointer to be able to
662 * track the seams later (needed for rigidity) */
663 data_down = r->raw[y - 1][x + x1_min];
664 r->least[data] = data_down;
665 if (r->rigidity) {
666 m = r->m[data_down] + r_fact * r->rigidity_map[x1_min];
667 for (x1 = x1_min + 1; x1 <= x1_max; x1++) {
668 data_down = r->raw[y - 1][x + x1];
669 /* find the min among the neighbors
670 * in the last row */
671 m1 = r->m[data_down] + r_fact * r->rigidity_map[x1];
672 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
673 m = m1;
674 r->least[data] = data_down;
676 /* m = MIN(m, r->m[data_down] + r->rigidity_map[x1]); */
678 } else {
679 m = r->m[data_down];
680 for (x1 = x1_min + 1; x1 <= x1_max; x1++) {
681 data_down = r->raw[y - 1][x + x1];
682 /* find the min among the neighbors
683 * in the last row */
684 m1 = r->m[data_down];
685 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
686 m = m1;
687 r->least[data] = data_down;
689 m = MIN(m, r->m[data_down]);
693 /* set current m */
694 r->m[data] = r->en[data] + m;
697 return LQR_OK;
700 /* compute (vertical) visibility map up to given depth
701 * (it also calls inflate() to add image enlargment information) */
702 LqrRetVal
703 lqr_carver_build_vsmap(LqrCarver *r, gint depth)
705 gint l;
706 gint lr_switch_interval = 0;
707 LqrDataTok data_tok;
709 #ifdef __LQR_VERBOSE__
710 printf("[ building visibility map ]\n");
711 fflush(stdout);
712 #endif /* __LQR_VERBOSE__ */
714 #ifdef __LQR_DEBUG__
715 assert(depth <= r->w_start + 1);
716 assert(depth >= 1);
717 #endif /* __LQR_DEBUG__ */
719 /* default behaviour : compute all possible levels
720 * (complete map) */
721 if (depth == 0) {
722 depth = r->w_start + 1;
725 /* here we assume that
726 * lqr_carver_set_width(w_start - max_level + 1);
727 * has been given */
729 /* left-right switch interval */
730 if (r->lr_switch_frequency) {
731 lr_switch_interval = (depth - r->max_level - 1) / r->lr_switch_frequency + 1;
734 /* cycle over levels */
735 for (l = r->max_level; l < depth; l++) {
736 LQR_CATCH_CANC(r);
738 if ((l - r->max_level + r->session_rescale_current) % r->session_update_step == 0) {
739 lqr_progress_update(r->progress, (gdouble) (l - r->max_level + r->session_rescale_current) /
740 (gdouble) (r->session_rescale_total));
742 #ifdef __LQR_DEBUG__
743 /* check raw rows */
744 lqr_carver_debug_check_rows(r);
745 #endif /* __LQR_DEBUG__ */
747 /* compute vertical seam */
748 lqr_carver_build_vpath(r);
750 /* update visibility map
751 * (assign level to the seam) */
752 lqr_carver_update_vsmap(r, l + r->max_level - 1);
754 /* increase (in)visibility level
755 * (make the last seam invisible) */
756 r->level++;
757 r->w--;
759 /* update raw data */
760 lqr_carver_carve(r);
762 if (r->w > 1) {
763 /* update the energy */
764 /* LQR_CATCH (lqr_carver_build_emap (r)); */
765 LQR_CATCH(lqr_carver_update_emap(r));
767 /* recalculate the minpath map */
768 if ((r->lr_switch_frequency) && (((l - r->max_level + lr_switch_interval / 2) % lr_switch_interval) == 0)) {
769 r->leftright ^= 1;
770 LQR_CATCH(lqr_carver_build_mmap(r));
771 } else {
772 /* lqr_carver_build_mmap (r); */
773 LQR_CATCH(lqr_carver_update_mmap(r));
775 } else {
776 /* complete the map (last seam) */
777 lqr_carver_finish_vsmap(r);
781 /* insert seams for image enlargement */
782 LQR_CATCH(lqr_carver_inflate(r, depth - 1));
784 /* reset image size */
785 lqr_carver_set_width(r, r->w_start);
786 /* repeat for auxiliary layers */
787 data_tok.integer = r->w_start;
788 LQR_CATCH(lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_width_attached, data_tok));
790 #ifdef __LQR_VERBOSE__
791 printf("[ visibility map OK ]\n");
792 fflush(stdout);
793 #endif /* __LQR_VERBOSE__ */
795 return LQR_OK;
798 /* enlarge the image by seam insertion
799 * visibility map is updated and the resulting multisize image
800 * is complete in both directions */
801 LqrRetVal
802 lqr_carver_inflate(LqrCarver *r, gint l)
804 gint w1, z0, vs, k;
805 gint x, y;
806 gint c_left;
807 void *new_rgb = NULL;
808 gint *new_vs = NULL;
809 gdouble tmp_rgb;
810 gfloat *new_bias = NULL;
811 gfloat *new_rigmask = NULL;
812 LqrDataTok data_tok;
813 LqrCarverState prev_state = LQR_CARVER_STATE_STD;
815 #ifdef __LQR_VERBOSE__
816 printf(" [ inflating (active=%i) ]\n", r->active);
817 fflush(stdout);
818 #endif /* __LQR_VERBOSE__ */
820 #ifdef __LQR_DEBUG__
821 assert(l + 1 > r->max_level); /* otherwise is useless */
822 #endif /* __LQR_DEBUG__ */
824 LQR_CATCH_CANC(r);
826 if (r->root == NULL) {
827 prev_state = g_atomic_int_get(&r->state);
828 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_INFLATING, TRUE));
831 /* first iterate on attached carvers */
832 data_tok.integer = l;
833 LQR_CATCH(lqr_carver_list_foreach(r->attached_list, lqr_carver_inflate_attached, data_tok));
835 /* scale to current maximum size
836 * (this is the original size the first time) */
837 lqr_carver_set_width(r, r->w0);
839 /* final width */
840 w1 = r->w0 + l - r->max_level + 1;
842 /* allocate room for new maps */
843 BUF_TRY_NEW0_RET_LQR(new_rgb, w1 * r->h0 * r->channels, r->col_depth);
845 if (r->root == NULL) {
846 LQR_CATCH_MEM(new_vs = g_try_new0(gint, w1 * r->h0));
848 if (r->active) {
849 if (r->bias) {
850 LQR_CATCH_MEM(new_bias = g_try_new0(gfloat, w1 * r->h0));
852 if (r->rigidity_mask) {
853 LQR_CATCH_MEM(new_rigmask = g_try_new(gfloat, w1 * r->h0));
857 /* span the image with a cursor
858 * and build the new image */
859 lqr_cursor_reset(r->c);
860 x = 0;
861 y = 0;
862 for (z0 = 0; z0 < w1 * r->h0; z0++, lqr_cursor_next(r->c)) {
864 LQR_CATCH_CANC(r);
866 /* read visibility */
867 vs = r->vs[r->c->now];
868 if ((vs != 0) && (vs <= l + r->max_level - 1)
869 && (vs >= 2 * r->max_level - 1)) {
870 /* the point belongs to a previously computed seam
871 * and was not inserted during a previous
872 * inflate() call : insert another seam */
874 /* the new pixel value is equal to the average of its
875 * left and right neighbors */
877 if (r->c->x > 0) {
878 c_left = lqr_cursor_left(r->c);
879 } else {
880 c_left = r->c->now;
883 for (k = 0; k < r->channels; k++) {
884 switch (r->col_depth) {
885 case LQR_COLDEPTH_8I:
886 tmp_rgb = (AS_8I(r->rgb)[c_left * r->channels + k] +
887 AS_8I(r->rgb)[r->c->now * r->channels + k]) / 2;
888 AS_8I(new_rgb)[z0 * r->channels + k] = (lqr_t_8i) (tmp_rgb + 0.499999);
889 break;
890 case LQR_COLDEPTH_16I:
891 tmp_rgb = (AS_16I(r->rgb)[c_left * r->channels + k] +
892 AS_16I(r->rgb)[r->c->now * r->channels + k]) / 2;
893 AS_16I(new_rgb)[z0 * r->channels + k] = (lqr_t_16i) (tmp_rgb + 0.499999);
894 break;
895 case LQR_COLDEPTH_32F:
896 tmp_rgb = (AS_32F(r->rgb)[c_left * r->channels + k] +
897 AS_32F(r->rgb)[r->c->now * r->channels + k]) / 2;
898 AS_32F(new_rgb)[z0 * r->channels + k] = (lqr_t_32f) tmp_rgb;
899 break;
900 case LQR_COLDEPTH_64F:
901 tmp_rgb = (AS_64F(r->rgb)[c_left * r->channels + k] +
902 AS_64F(r->rgb)[r->c->now * r->channels + k]) / 2;
903 AS_64F(new_rgb)[z0 * r->channels + k] = (lqr_t_64f) tmp_rgb;
904 break;
907 if (r->active) {
908 if (r->bias) {
909 new_bias[z0] = (r->bias[c_left] + r->bias[r->c->now]) / 2;
911 if (r->rigidity_mask) {
912 new_rigmask[z0] = (r->rigidity_mask[c_left] + r->rigidity_mask[r->c->now]) / 2;
915 /* the first time inflate() is called
916 * the new visibility should be -vs + 1 but we shift it
917 * so that the final minimum visibiliy will be 1 again
918 * and so that vs=0 still means "uninitialized".
919 * Subsequent inflations account for that */
920 if (r->root == NULL) {
921 new_vs[z0] = l - vs + r->max_level;
923 z0++;
925 for (k = 0; k < r->channels; k++) {
926 PXL_COPY(new_rgb, z0 * r->channels + k, r->rgb, r->c->now * r->channels + k, r->col_depth);
928 if (r->active) {
929 if (r->bias) {
930 new_bias[z0] = r->bias[r->c->now];
932 if (r->rigidity_mask) {
933 new_rigmask[z0] = r->rigidity_mask[r->c->now];
936 if (vs != 0) {
937 /* visibility has to be shifted up */
938 if (r->root == NULL) {
939 new_vs[z0] = vs + l - r->max_level + 1;
941 } else if (r->raw != NULL) {
942 #ifdef __LQR_DEBUG__
943 assert(y < r->h_start);
944 assert(x < r->w_start - l);
945 #endif /* __LQR_DEBUG__ */
946 r->raw[y][x] = z0;
947 x++;
948 if (x >= r->w_start - l) {
949 x = 0;
950 y++;
955 #ifdef __LQR_DEBUG__
956 if (r->raw != NULL) {
957 assert(x == 0);
958 if (w1 != 2 * r->w_start - 1) {
959 assert((y == r->h_start)
960 || (printf("y=%i hst=%i w1=%i\n", y, r->h_start, w1)
961 && fflush(stdout) && 0));
964 #endif /* __LQR_DEBUG__ */
966 /* substitute maps */
967 if (!r->preserve_in_buffer) {
968 g_free(r->rgb);
970 /* g_free (r->vs); */
971 g_free(r->en);
972 g_free(r->m);
973 g_free(r->rcache);
974 g_free(r->least);
975 g_free(r->bias);
976 g_free(r->rigidity_mask);
978 r->bias = NULL;
979 r->rcache = NULL;
980 r->nrg_uptodate = FALSE;
982 r->rgb = new_rgb;
983 r->preserve_in_buffer = FALSE;
985 if (r->root == NULL) {
986 g_free(r->vs);
987 r->vs = new_vs;
988 LQR_CATCH(lqr_carver_propagate_vsmap(r));
989 } else {
990 /* r->vs = NULL; */
992 if (r->nrg_active) {
993 LQR_CATCH_MEM(r->en = g_try_new0(gfloat, w1 * r->h0));
995 if (r->active) {
996 r->bias = new_bias;
997 r->rigidity_mask = new_rigmask;
998 LQR_CATCH_MEM(r->m = g_try_new0(gfloat, w1 * r->h0));
999 LQR_CATCH_MEM(r->least = g_try_new0(gint, w1 * r->h0));
1002 /* set new widths & levels (w_start is kept for reference) */
1003 r->level = l + 1;
1004 r->max_level = l + 1;
1005 r->w0 = w1;
1006 r->w = r->w_start;
1008 /* reset readout buffer */
1009 g_free(r->rgb_ro_buffer);
1010 BUF_TRY_NEW0_RET_LQR(r->rgb_ro_buffer, r->w0 * r->channels, r->col_depth);
1012 #ifdef __LQR_VERBOSE__
1013 printf(" [ inflating OK ]\n");
1014 fflush(stdout);
1015 #endif /* __LQR_VERBOSE__ */
1017 if (r->root == NULL) {
1018 LQR_CATCH(lqr_carver_set_state(r, prev_state, TRUE));
1021 return LQR_OK;
1024 LqrRetVal
1025 lqr_carver_inflate_attached(LqrCarver *r, LqrDataTok data)
1027 return lqr_carver_inflate(r, data.integer);
1030 /*** internal functions for maps computations ***/
1032 /* do the carving
1033 * this actually carves the raw array,
1034 * which holds the indices to be used
1035 * in all the other maps */
1036 void
1037 lqr_carver_carve(LqrCarver *r)
1039 gint x, y;
1041 #ifdef __LQR_DEBUG__
1042 assert(r->root == NULL);
1043 #endif /* __LQR_DEBUG__ */
1045 for (y = 0; y < r->h_start; y++) {
1046 #ifdef __LQR_DEBUG__
1047 assert(r->vs[r->raw[y][r->vpath_x[y]]] != 0);
1048 for (x = 0; x < r->vpath_x[y]; x++) {
1049 assert(r->vs[r->raw[y][x]] == 0);
1051 #endif /* __LQR_DEBUG__ */
1052 for (x = r->vpath_x[y]; x < r->w; x++) {
1053 r->raw[y][x] = r->raw[y][x + 1];
1054 #ifdef __LQR_DEBUG__
1055 assert(r->vs[r->raw[y][x]] == 0);
1056 #endif /* __LQR_DEBUG__ */
1060 r->nrg_uptodate = FALSE;
1063 /* update energy map after seam removal */
1064 LqrRetVal
1065 lqr_carver_update_emap(LqrCarver *r)
1067 gint x, y;
1068 gint y1, y1_min, y1_max;
1070 LQR_CATCH_CANC(r);
1072 if (r->nrg_uptodate) {
1073 return LQR_OK;
1075 if (r->use_rcache) {
1076 LQR_CATCH_F(r->rcache != NULL);
1079 for (y = 0; y < r->h; y++) {
1080 /* note: here the vpath has already
1081 * been carved */
1082 x = r->vpath_x[y];
1083 r->nrg_xmin[y] = x;
1084 r->nrg_xmax[y] = x - 1;
1086 for (y = 0; y < r->h; y++) {
1087 x = r->vpath_x[y];
1088 y1_min = MAX(y - r->nrg_radius, 0);
1089 y1_max = MIN(y + r->nrg_radius, r->h - 1);
1091 for (y1 = y1_min; y1 <= y1_max; y1++) {
1092 r->nrg_xmin[y1] = MIN(r->nrg_xmin[y1], x - r->nrg_radius);
1093 r->nrg_xmin[y1] = MAX(0, r->nrg_xmin[y1]);
1094 /* note: the -1 below is because of the previous carving */
1095 r->nrg_xmax[y1] = MAX(r->nrg_xmax[y1], x + r->nrg_radius - 1);
1096 r->nrg_xmax[y1] = MIN(r->w - 1, r->nrg_xmax[y1]);
1100 for (y = 0; y < r->h; y++) {
1101 LQR_CATCH_CANC(r);
1103 for (x = r->nrg_xmin[y]; x <= r->nrg_xmax[y]; x++) {
1104 LQR_CATCH(lqr_carver_compute_e(r, x, y));
1108 r->nrg_uptodate = TRUE;
1110 return LQR_OK;
1113 /* update the auxiliary minpath map
1114 * this only updates the affected pixels,
1115 * which start form the beginning of the changed
1116 * energy region around the seam and expand
1117 * at most by delta_x (in both directions)
1118 * at each row */
1119 LqrRetVal
1120 lqr_carver_update_mmap(LqrCarver *r)
1122 gint x, y;
1123 gint x_min, x_max;
1124 gint x1, dx;
1125 gint x1_min, x1_max;
1126 gint data, data_down, least;
1127 gfloat m, m1, r_fact;
1128 gfloat new_m;
1129 gfloat *mc = NULL;
1130 gint stop;
1131 gint x_stop;
1133 LQR_CATCH_CANC(r);
1134 LQR_CATCH_F(r->nrg_uptodate);
1136 if (r->rigidity) {
1137 LQR_CATCH_MEM(mc = g_try_new(gfloat, 2 * r->delta_x + 1));
1138 mc += r->delta_x;
1141 /* span first row */
1142 /* x_min = MAX (r->vpath_x[0] - r->delta_x, 0); */
1143 x_min = MAX(r->nrg_xmin[0], 0);
1144 /* x_max = MIN (r->vpath_x[0] + r->delta_x - 1, r->w - 1); */
1145 /* x_max = MIN (r->vpath_x[0] + r->delta_x, r->w - 1); */
1146 x_max = MIN(r->nrg_xmax[0], r->w - 1);
1148 for (x = x_min; x <= x_max; x++) {
1149 data = r->raw[0][x];
1150 r->m[data] = r->en[data];
1153 /* other rows */
1154 for (y = 1; y < r->h; y++) {
1155 LQR_CATCH_CANC(r);
1157 /* make sure to include the changed energy region */
1158 x_min = MIN(x_min, r->nrg_xmin[y]);
1159 x_max = MAX(x_max, r->nrg_xmax[y]);
1161 /* expand the affected region by delta_x */
1162 x_min = MAX(x_min - r->delta_x, 0);
1163 x_max = MIN(x_max + r->delta_x, r->w - 1);
1165 /* span the affected region */
1166 stop = 0;
1167 x_stop = 0;
1168 for (x = x_min; x <= x_max; x++) {
1169 data = r->raw[y][x];
1170 if (r->rigidity_mask) {
1171 r_fact = r->rigidity_mask[data];
1172 } else {
1173 r_fact = 1;
1176 /* find the minimum in the previous rows
1177 * as in build_mmap() */
1178 x1_min = MAX(0, x - r->delta_x);
1179 x1_max = MIN(r->w - 1, x + r->delta_x);
1181 if (r->rigidity) {
1182 dx = x1_min - x;
1183 switch (x1_max - x1_min + 1) {
1184 UPDATE_MMAP_OPTIMISED_CASES_RIG
1185 default:
1186 data_down = r->raw[y - 1][x1_min];
1187 least = data_down;
1188 m = r->m[data_down] + r_fact * r->rigidity_map[dx++];
1189 /* fprintf(stderr, "y,x=%i,%i x1=%i dx=%i mr=%g MR=%g m=%g M=%g\n", y, x, x1_min, dx, m, MRDOWN(y, x1_min, dx), r->m[data_down], MDOWN(y, x1_min)); fflush(stderr); */
1190 for (x1 = x1_min + 1; x1 <= x1_max; x1++, dx++) {
1191 data_down = r->raw[y - 1][x1];
1192 m1 = r->m[data_down] + r_fact * r->rigidity_map[dx];
1193 /* fprintf(stderr, "y,x=%i,%i x1=%i dx=%i mr=%g MR=%g m=%g M=%g\n", y, x, x1, dx, m1, MRDOWN(y, x1, dx), r->m[data_down], MDOWN(y, x1)); fflush(stderr); */
1194 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
1195 m = m1;
1196 least = data_down;
1200 /* fprintf(stderr, "y,x=%i,%i x1_min,max=%i,%i least=%i m=%g\n", y, x, x1_min, x1_max, least, m); fflush(stderr); */
1201 } else {
1202 switch (x1_max - x1_min + 1) {
1203 UPDATE_MMAP_OPTIMISED_CASES
1204 default:
1205 data_down = r->raw[y - 1][x1_min];
1206 least = data_down;
1207 m = r->m[data_down];
1208 for (x1 = x1_min + 1; x1 <= x1_max; x1++) {
1209 data_down = r->raw[y - 1][x1];
1210 m1 = r->m[data_down];
1211 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
1212 m = m1;
1213 least = data_down;
1217 /* fprintf(stderr, "y,x=%i,%i x1_min,max=%i,%i least=%i m=%g\n", y, x, x1_min, x1_max, least, m); fflush(stderr); */
1220 new_m = r->en[data] + m;
1222 /* reduce the range if there's no (relevant) difference
1223 * with the previous map */
1224 if (r->least[data] == least) {
1225 if (fabsf(r->m[data] - new_m) < UPDATE_TOLERANCE) {
1226 if (stop == 0) {
1227 x_stop = x;
1229 stop = 1;
1230 new_m = r->m[data];
1231 } else {
1232 stop = 0;
1233 r->m[data] = new_m;
1235 if ((x == x_min) && stop) {
1236 x_min++;
1238 } else {
1239 stop = 0;
1240 r->m[data] = new_m;
1243 r->least[data] = least;
1245 if ((x == x_max) && (stop)) {
1246 x_max = x_stop;
1252 if (r->rigidity) {
1253 mc -= r->delta_x;
1254 g_free(mc);
1257 return LQR_OK;
1260 /* compute seam path from minpath map */
1261 void
1262 lqr_carver_build_vpath(LqrCarver *r)
1264 gint x, y, z0;
1265 gfloat m, m1;
1266 gint last = -1;
1267 gint last_x = 0;
1268 gint x_min, x_max;
1270 /* we start at last row */
1271 y = r->h - 1;
1273 /* span the last row for the minimum mmap value */
1274 m = (1 << 29);
1275 for (x = 0, z0 = y * r->w_start; x < r->w; x++, z0++) {
1276 #ifdef __LQR_DEBUG__
1277 assert(r->vs[r->raw[y][x]] == 0);
1278 #endif /* __LQR_DEBUG__ */
1280 m1 = r->m[r->raw[y][x]];
1281 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
1282 last = r->raw[y][x];
1283 last_x = x;
1284 m = m1;
1288 #ifdef __LQR_DEBUG__
1289 assert(last >= 0);
1290 #endif /* __LQR_DEBUG__ */
1292 /* follow the track for the other rows */
1293 for (y = r->h0 - 1; y >= 0; y--) {
1294 #ifdef __LQR_DEBUG__
1295 assert(r->vs[last] == 0);
1296 assert(last_x < r->w);
1297 #endif /* __LQR_DEBUG__ */
1298 r->vpath[y] = last;
1299 r->vpath_x[y] = last_x;
1300 if (y > 0) {
1301 last = r->least[r->raw[y][last_x]];
1302 /* we also need to retrieve the x coordinate */
1303 x_min = MAX(last_x - r->delta_x, 0);
1304 x_max = MIN(last_x + r->delta_x, r->w - 1);
1305 for (x = x_min; x <= x_max; x++) {
1306 if (r->raw[y - 1][x] == last) {
1307 last_x = x;
1308 break;
1311 #ifdef __LQR_DEBUG__
1312 assert(x < x_max + 1);
1313 #endif /* __LQR_DEBUG__ */
1317 #if 0
1318 /* we backtrack the seam following the min mmap */
1319 for (y = r->h0 - 1; y >= 0; y--) {
1320 #ifdef __LQR_DEBUG__
1321 assert(r->vs[last] == 0);
1322 assert(last_x < r->w);
1323 #endif /* __LQR_DEBUG__ */
1325 r->vpath[y] = last;
1326 r->vpath_x[y] = last_x;
1327 if (y > 0) {
1328 m = (1 << 29);
1329 x_min = MAX(0, last_x - r->delta_x);
1330 x_max = MIN(r->w - 1, last_x + r->delta_x);
1331 for (x = x_min; x <= x_max; x++) {
1332 m1 = r->m[r->raw[y - 1][x]];
1333 if (m1 < m) {
1334 last = r->raw[y - 1][x];
1335 last_x = x;
1336 m = m1;
1341 #endif
1344 /* update visibility map after seam computation */
1345 void
1346 lqr_carver_update_vsmap(LqrCarver *r, gint l)
1348 gint y;
1349 #ifdef __LQR_DEBUG__
1350 assert(r->root == NULL);
1351 #endif /* __LQR_DEBUG__ */
1352 for (y = 0; y < r->h; y++) {
1353 #ifdef __LQR_DEBUG__
1354 assert(r->vs[r->vpath[y]] == 0);
1355 assert(r->vpath[y] == r->raw[y][r->vpath_x[y]]);
1356 #endif /* __LQR_DEBUG__ */
1357 r->vs[r->vpath[y]] = l;
1361 /* complete visibility map (last seam) */
1362 /* set the last column of pixels to vis. level w0 */
1363 void
1364 lqr_carver_finish_vsmap(LqrCarver *r)
1366 gint y;
1368 #ifdef __LQR_DEBUG__
1369 assert(r->w == 1);
1370 assert(r->root == NULL);
1371 #endif /* __LQR_DEBUG__ */
1372 lqr_cursor_reset(r->c);
1373 for (y = 1; y <= r->h; y++, lqr_cursor_next(r->c)) {
1374 #ifdef __LQR_DEBUG__
1375 assert(r->vs[r->c->now] == 0);
1376 #endif /* __LQR_DEBUG__ */
1377 r->vs[r->c->now] = r->w0;
1379 lqr_cursor_reset(r->c);
1382 /* propagate the root carver's visibility map */
1383 LqrRetVal
1384 lqr_carver_propagate_vsmap(LqrCarver *r)
1386 LqrDataTok data_tok;
1388 LQR_CATCH_CANC(r);
1390 data_tok.data = NULL;
1391 LQR_CATCH(lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_propagate_vsmap_attached, data_tok));
1392 return LQR_OK;
1395 LqrRetVal
1396 lqr_carver_propagate_vsmap_attached(LqrCarver *r, LqrDataTok data)
1398 /* LqrDataTok data_tok;
1399 data_tok.data = NULL; */
1400 r->vs = r->root->vs;
1401 lqr_carver_scan_reset(r);
1402 /* LQR_CATCH (lqr_carver_list_foreach (r->attached_list, lqr_carver_propagate_vsmap_attached, data_tok)); */
1403 return LQR_OK;
1406 /*** image manipulations ***/
1408 /* set width of the multisize image
1409 * (maps have to be computed already) */
1410 void
1411 lqr_carver_set_width(LqrCarver *r, gint w1)
1413 #ifdef __LQR_DEBUG__
1414 assert(w1 <= r->w0);
1415 assert(w1 >= r->w_start - r->max_level + 1);
1416 #endif /* __LQR_DEBUG__ */
1417 r->w = w1;
1418 r->level = r->w0 - w1 + 1;
1421 LqrRetVal
1422 lqr_carver_set_width_attached(LqrCarver *r, LqrDataTok data)
1424 lqr_carver_set_width(r, data.integer);
1425 return LQR_OK;
1428 /* flatten the image to its current state
1429 * (all maps are reset, invisible points are lost) */
1430 /* LQR_PUBLIC */
1431 LqrRetVal
1432 lqr_carver_flatten(LqrCarver *r)
1434 void *new_rgb = NULL;
1435 gfloat *new_bias = NULL;
1436 gfloat *new_rigmask = NULL;
1437 gint x, y, k;
1438 gint z0;
1439 LqrDataTok data_tok;
1440 LqrCarverState prev_state = LQR_CARVER_STATE_STD;
1442 #ifdef __LQR_VERBOSE__
1443 printf(" [ flattening (active=%i) ]\n", r->active);
1444 fflush(stdout);
1445 #endif /* __LQR_VERBOSE__ */
1447 LQR_CATCH_CANC(r);
1449 if (r->root == NULL) {
1450 prev_state = g_atomic_int_get(&r->state);
1451 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_FLATTENING, TRUE));
1454 /* first iterate on attached carvers */
1455 data_tok.data = NULL;
1456 LQR_CATCH(lqr_carver_list_foreach(r->attached_list, lqr_carver_flatten_attached, data_tok));
1458 /* free non needed maps first */
1459 g_free(r->en);
1460 g_free(r->m);
1461 g_free(r->rcache);
1462 g_free(r->least);
1464 r->rcache = NULL;
1465 r->nrg_uptodate = FALSE;
1467 /* allocate room for new map */
1468 BUF_TRY_NEW0_RET_LQR(new_rgb, r->w * r->h * r->channels, r->col_depth);
1470 if (r->active) {
1471 if (r->rigidity_mask) {
1472 LQR_CATCH_MEM(new_rigmask = g_try_new(gfloat, r->w * r->h));
1475 if (r->nrg_active) {
1476 if (r->bias) {
1477 LQR_CATCH_MEM(new_bias = g_try_new0(gfloat, r->w * r->h));
1479 g_free(r->_raw);
1480 g_free(r->raw);
1481 LQR_CATCH_MEM(r->_raw = g_try_new(gint, r->w * r->h));
1482 LQR_CATCH_MEM(r->raw = g_try_new(gint *, r->h));
1485 /* span the image with the cursor and copy
1486 * it in the new array */
1487 lqr_cursor_reset(r->c);
1488 for (y = 0; y < r->h; y++) {
1489 LQR_CATCH_CANC(r);
1491 if (r->nrg_active) {
1492 r->raw[y] = r->_raw + y * r->w;
1494 for (x = 0; x < r->w; x++) {
1495 z0 = y * r->w + x;
1496 for (k = 0; k < r->channels; k++) {
1497 PXL_COPY(new_rgb, z0 * r->channels + k, r->rgb, r->c->now * r->channels + k, r->col_depth);
1499 if (r->active) {
1500 if (r->rigidity_mask) {
1501 new_rigmask[z0] = r->rigidity_mask[r->c->now];
1504 if (r->nrg_active) {
1505 if (r->bias) {
1506 new_bias[z0] = r->bias[r->c->now];
1508 r->raw[y][x] = z0;
1510 lqr_cursor_next(r->c);
1514 /* substitute the old maps */
1515 if (!r->preserve_in_buffer) {
1516 g_free(r->rgb);
1518 r->rgb = new_rgb;
1519 r->preserve_in_buffer = FALSE;
1520 if (r->nrg_active) {
1521 g_free(r->bias);
1522 r->bias = new_bias;
1524 if (r->active) {
1525 g_free(r->rigidity_mask);
1526 r->rigidity_mask = new_rigmask;
1529 /* init the other maps */
1530 if (r->root == NULL) {
1531 g_free(r->vs);
1532 LQR_CATCH_MEM(r->vs = g_try_new0(gint, r->w * r->h));
1533 LQR_CATCH(lqr_carver_propagate_vsmap(r));
1535 if (r->nrg_active) {
1536 LQR_CATCH_MEM(r->en = g_try_new0(gfloat, r->w * r->h));
1538 if (r->active) {
1539 LQR_CATCH_MEM(r->m = g_try_new0(gfloat, r->w * r->h));
1540 LQR_CATCH_MEM(r->least = g_try_new(gint, r->w * r->h));
1543 /* reset widths, heights & levels */
1544 r->w0 = r->w;
1545 r->h0 = r->h;
1546 r->w_start = r->w;
1547 r->h_start = r->h;
1548 r->level = 1;
1549 r->max_level = 1;
1551 #ifdef __LQR_VERBOSE__
1552 printf(" [ flattening OK ]\n");
1553 fflush(stdout);
1554 #endif /* __LQR_VERBOSE__ */
1556 if (r->root == NULL) {
1557 LQR_CATCH(lqr_carver_set_state(r, prev_state, TRUE));
1560 return LQR_OK;
1563 LqrRetVal
1564 lqr_carver_flatten_attached(LqrCarver *r, LqrDataTok data)
1566 return lqr_carver_flatten(r);
1569 /* transpose the image, in its current state
1570 * (all maps and invisible points are lost) */
1571 LqrRetVal
1572 lqr_carver_transpose(LqrCarver *r)
1574 gint x, y, k;
1575 gint z0, z1;
1576 gint d;
1577 void *new_rgb = NULL;
1578 gfloat *new_bias = NULL;
1579 gfloat *new_rigmask = NULL;
1580 LqrDataTok data_tok;
1581 LqrCarverState prev_state = LQR_CARVER_STATE_STD;
1583 #ifdef __LQR_VERBOSE__
1584 printf("[ transposing (active=%i) ]\n", r->active);
1585 fflush(stdout);
1586 #endif /* __LQR_VERBOSE__ */
1588 LQR_CATCH_CANC(r);
1590 if (r->root == NULL) {
1591 prev_state = g_atomic_int_get(&r->state);
1592 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_TRANSPOSING, TRUE));
1595 if (r->level > 1) {
1596 LQR_CATCH(lqr_carver_flatten(r));
1599 /* first iterate on attached carvers */
1600 data_tok.data = NULL;
1601 LQR_CATCH(lqr_carver_list_foreach(r->attached_list, lqr_carver_transpose_attached, data_tok));
1603 /* free non needed maps first */
1604 if (r->root == NULL) {
1605 g_free(r->vs);
1607 g_free(r->en);
1608 g_free(r->m);
1609 g_free(r->rcache);
1610 g_free(r->least);
1611 g_free(r->rgb_ro_buffer);
1613 r->rcache = NULL;
1614 r->nrg_uptodate = FALSE;
1616 /* allocate room for the new maps */
1617 BUF_TRY_NEW0_RET_LQR(new_rgb, r->w0 * r->h0 * r->channels, r->col_depth);
1619 if (r->active) {
1620 if (r->rigidity_mask) {
1621 LQR_CATCH_MEM(new_rigmask = g_try_new(gfloat, r->w0 * r->h0));
1624 if (r->nrg_active) {
1625 if (r->bias) {
1626 LQR_CATCH_MEM(new_bias = g_try_new0(gfloat, r->w0 * r->h0));
1628 g_free(r->_raw);
1629 g_free(r->raw);
1630 LQR_CATCH_MEM(r->_raw = g_try_new0(gint, r->h0 * r->w0));
1631 LQR_CATCH_MEM(r->raw = g_try_new0(gint *, r->w0));
1634 /* compute trasposed maps */
1635 for (x = 0; x < r->w; x++) {
1636 if (r->nrg_active) {
1637 r->raw[x] = r->_raw + x * r->h0;
1639 for (y = 0; y < r->h; y++) {
1640 z0 = y * r->w0 + x;
1641 z1 = x * r->h0 + y;
1642 for (k = 0; k < r->channels; k++) {
1643 PXL_COPY(new_rgb, z1 * r->channels + k, r->rgb, z0 * r->channels + k, r->col_depth);
1645 if (r->active) {
1646 if (r->rigidity_mask) {
1647 new_rigmask[z1] = r->rigidity_mask[z0];
1650 if (r->nrg_active) {
1651 if (r->bias) {
1652 new_bias[z1] = r->bias[z0];
1654 r->raw[x][y] = z1;
1659 /* substitute the map */
1660 if (!r->preserve_in_buffer) {
1661 g_free(r->rgb);
1663 r->rgb = new_rgb;
1664 r->preserve_in_buffer = FALSE;
1666 if (r->nrg_active) {
1667 g_free(r->bias);
1668 r->bias = new_bias;
1670 if (r->active) {
1671 g_free(r->rigidity_mask);
1672 r->rigidity_mask = new_rigmask;
1675 /* init the other maps */
1676 if (r->root == NULL) {
1677 LQR_CATCH_MEM(r->vs = g_try_new0(gint, r->w0 * r->h0));
1678 LQR_CATCH(lqr_carver_propagate_vsmap(r));
1680 if (r->nrg_active) {
1681 LQR_CATCH_MEM(r->en = g_try_new0(gfloat, r->w0 * r->h0));
1683 if (r->active) {
1684 LQR_CATCH_MEM(r->m = g_try_new0(gfloat, r->w0 * r->h0));
1685 LQR_CATCH_MEM(r->least = g_try_new(gint, r->w0 * r->h0));
1688 /* switch widths & heights */
1689 d = r->w0;
1690 r->w0 = r->h0;
1691 r->h0 = d;
1692 r->w = r->w0;
1693 r->h = r->h0;
1695 /* reset w_start, h_start & levels */
1696 r->w_start = r->w0;
1697 r->h_start = r->h0;
1698 r->level = 1;
1699 r->max_level = 1;
1701 /* reset seam path, cursor and readout buffer */
1702 if (r->active) {
1703 g_free(r->vpath);
1704 LQR_CATCH_MEM(r->vpath = g_try_new(gint, r->h));
1705 g_free(r->vpath_x);
1706 LQR_CATCH_MEM(r->vpath_x = g_try_new(gint, r->h));
1707 g_free(r->nrg_xmin);
1708 LQR_CATCH_MEM(r->nrg_xmin = g_try_new(gint, r->h));
1709 g_free(r->nrg_xmax);
1710 LQR_CATCH_MEM(r->nrg_xmax = g_try_new(gint, r->h));
1713 BUF_TRY_NEW0_RET_LQR(r->rgb_ro_buffer, r->w0 * r->channels, r->col_depth);
1715 /* rescale rigidity */
1717 if (r->active) {
1718 for (x = -r->delta_x; x <= r->delta_x; x++) {
1719 r->rigidity_map[x] = r->rigidity_map[x] * r->w0 / r->h0;
1723 /* set transposed flag */
1724 r->transposed = (r->transposed ? 0 : 1);
1726 #ifdef __LQR_VERBOSE__
1727 printf("[ transpose OK ]\n");
1728 fflush(stdout);
1729 #endif /* __LQR_VERBOSE__ */
1731 if (r->root == NULL) {
1732 LQR_CATCH(lqr_carver_set_state(r, prev_state, TRUE));
1735 return LQR_OK;
1738 LqrRetVal
1739 lqr_carver_transpose_attached(LqrCarver *r, LqrDataTok data)
1741 return lqr_carver_transpose(r);
1744 /* resize w + h: these are the liquid rescale methods.
1745 * They automatically determine the depth of the map
1746 * according to the desired size, can be called multiple
1747 * times, transpose the image as necessasry */
1748 LqrRetVal
1749 lqr_carver_resize_width(LqrCarver *r, gint w1)
1751 LqrDataTok data_tok;
1752 gint delta, gamma;
1753 gint delta_max;
1754 /* delta is used to determine the required depth
1755 * gamma to decide if action is necessary */
1756 if (!r->transposed) {
1757 delta = w1 - r->w_start;
1758 gamma = w1 - r->w;
1759 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1760 } else {
1761 delta = w1 - r->h_start;
1762 gamma = w1 - r->h;
1763 delta_max = (gint) ((r->enl_step - 1) * r->h_start) - 1;
1765 if (delta_max < 1) {
1766 delta_max = 1;
1768 if (delta < 0) {
1769 delta = -delta;
1770 delta_max = delta;
1773 LQR_CATCH_CANC(r);
1774 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
1775 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_RESIZING, TRUE));
1777 /* update step for progress reprt */
1778 r->session_rescale_total = gamma > 0 ? gamma : -gamma;
1779 r->session_rescale_current = 0;
1780 r->session_update_step = (gint) MAX(r->session_rescale_total * r->progress->update_step, 1);
1782 if (r->session_rescale_total) {
1783 lqr_progress_init(r->progress, r->progress->init_width_message);
1786 while (gamma) {
1787 gint delta0 = MIN(delta, delta_max);
1788 gint new_w;
1790 delta -= delta0;
1791 if (r->transposed) {
1792 LQR_CATCH(lqr_carver_transpose(r));
1794 new_w = MIN(w1, r->w_start + delta_max);
1795 gamma = w1 - new_w;
1796 LQR_CATCH(lqr_carver_build_maps(r, delta0 + 1));
1797 lqr_carver_set_width(r, new_w);
1799 data_tok.integer = new_w;
1800 lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_width_attached, data_tok);
1802 r->session_rescale_current = r->session_rescale_total - (gamma > 0 ? gamma : -gamma);
1804 if (r->dump_vmaps) {
1805 LQR_CATCH(lqr_vmap_internal_dump(r));
1807 if (new_w < w1) {
1808 LQR_CATCH(lqr_carver_flatten(r));
1809 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1810 if (delta_max < 1) {
1811 delta_max = 1;
1816 if (r->session_rescale_total) {
1817 lqr_progress_end(r->progress, r->progress->end_width_message);
1820 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_STD, TRUE));
1822 return LQR_OK;
1825 LqrRetVal
1826 lqr_carver_resize_height(LqrCarver *r, gint h1)
1828 LqrDataTok data_tok;
1829 gint delta, gamma;
1830 gint delta_max;
1831 /* delta is used to determine the required depth
1832 * gamma to decide if action is necessary */
1833 if (!r->transposed) {
1834 delta = h1 - r->h_start;
1835 gamma = h1 - r->h;
1836 delta_max = (gint) ((r->enl_step - 1) * r->h_start) - 1;
1837 } else {
1838 delta = h1 - r->w_start;
1839 gamma = h1 - r->w;
1840 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1842 if (delta_max < 1) {
1843 delta_max = 1;
1845 if (delta < 0) {
1846 delta_max = -delta;
1848 delta = delta > 0 ? delta : -delta;
1850 LQR_CATCH_CANC(r);
1851 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
1852 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_RESIZING, TRUE));
1854 /* update step for progress reprt */
1855 r->session_rescale_total = gamma > 0 ? gamma : -gamma;
1856 r->session_rescale_current = 0;
1857 r->session_update_step = (gint) MAX(r->session_rescale_total * r->progress->update_step, 1);
1859 if (r->session_rescale_total) {
1860 lqr_progress_init(r->progress, r->progress->init_height_message);
1863 while (gamma) {
1864 gint delta0 = MIN(delta, delta_max);
1865 gint new_w;
1866 delta -= delta0;
1867 if (!r->transposed) {
1868 LQR_CATCH(lqr_carver_transpose(r));
1870 new_w = MIN(h1, r->w_start + delta_max);
1871 gamma = h1 - new_w;
1872 LQR_CATCH(lqr_carver_build_maps(r, delta0 + 1));
1873 lqr_carver_set_width(r, new_w);
1875 data_tok.integer = new_w;
1876 lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_width_attached, data_tok);
1878 r->session_rescale_current = r->session_rescale_total - (gamma > 0 ? gamma : -gamma);
1880 if (r->dump_vmaps) {
1881 LQR_CATCH(lqr_vmap_internal_dump(r));
1883 if (new_w < h1) {
1884 LQR_CATCH(lqr_carver_flatten(r));
1885 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1886 if (delta_max < 1) {
1887 delta_max = 1;
1892 if (r->session_rescale_total) {
1893 lqr_progress_end(r->progress, r->progress->end_height_message);
1896 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_STD, TRUE));
1898 return LQR_OK;
1901 /* liquid rescale public method */
1902 /* LQR_PUBLIC */
1903 LqrRetVal
1904 lqr_carver_resize(LqrCarver *r, gint w1, gint h1)
1906 #ifdef __LQR_VERBOSE__
1907 printf("[ Rescale from %i,%i to %i,%i ]\n", (r->transposed ? r->h : r->w), (r->transposed ? r->w : r->h), w1, h1);
1908 fflush(stdout);
1909 #endif /* __LQR_VERBOSE__ */
1910 LQR_CATCH_F((w1 >= 1) && (h1 >= 1));
1911 LQR_CATCH_F(r->root == NULL);
1913 LQR_CATCH_CANC(r);
1914 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
1916 switch (r->resize_order) {
1917 case LQR_RES_ORDER_HOR:
1918 LQR_CATCH(lqr_carver_resize_width(r, w1));
1919 LQR_CATCH(lqr_carver_resize_height(r, h1));
1920 break;
1921 case LQR_RES_ORDER_VERT:
1922 LQR_CATCH(lqr_carver_resize_height(r, h1));
1923 LQR_CATCH(lqr_carver_resize_width(r, w1));
1924 break;
1925 #ifdef __LQR_DEBUG__
1926 default:
1927 assert(0);
1928 #endif /* __LQR_DEBUG__ */
1930 lqr_carver_scan_reset_all(r);
1932 #ifdef __LQR_VERBOSE__
1933 printf("[ Rescale OK ]\n");
1934 fflush(stdout);
1935 #endif /* __LQR_VERBOSE__ */
1936 return LQR_OK;
1939 LqrRetVal
1940 lqr_carver_set_state(LqrCarver *r, LqrCarverState state, gboolean skip_canceled)
1942 LqrDataTok data_tok;
1943 gint lock_pos;
1945 LQR_CATCH_F(r->root == NULL);
1947 #if (GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 30)
1948 lock_pos = g_atomic_int_exchange_and_add(&r->state_lock_queue, 1);
1949 #else
1950 lock_pos = g_atomic_int_add(&r->state_lock_queue, 1);
1951 #endif /* GLIB_VERSION < 2.30 */
1953 while (g_atomic_int_get(&r->state_lock) != lock_pos) {
1954 g_usleep(10000);
1957 if (skip_canceled && g_atomic_int_get(&r->state) == LQR_CARVER_STATE_CANCELLED) {
1958 g_atomic_int_inc(&r->state_lock);
1959 return LQR_OK;
1962 g_atomic_int_set(&r->state, state);
1964 data_tok.integer = state;
1965 LQR_CATCH(lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_state_attached, data_tok));
1967 g_atomic_int_inc(&r->state_lock);
1969 return LQR_OK;
1972 LqrRetVal
1973 lqr_carver_set_state_attached(LqrCarver *r, LqrDataTok data)
1975 g_atomic_int_set(&r->state, data.integer);
1976 return LQR_OK;
1979 /* cancel the current action from a different thread */
1980 /* LQR_PUBLIC */
1981 LqrRetVal
1982 lqr_carver_cancel(LqrCarver *r)
1984 LqrCarverState curr_state;
1986 LQR_CATCH_F(r->root == NULL);
1988 curr_state = g_atomic_int_get(&r->state);
1990 if ((curr_state == LQR_CARVER_STATE_RESIZING) ||
1991 (curr_state == LQR_CARVER_STATE_INFLATING) ||
1992 (curr_state == LQR_CARVER_STATE_TRANSPOSING) || (curr_state == LQR_CARVER_STATE_FLATTENING)) {
1993 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_CANCELLED, TRUE));
1995 return LQR_OK;
1998 /* get current size */
1999 /* LQR_PUBLIC */
2000 gint
2001 lqr_carver_get_width(LqrCarver *r)
2003 return (r->transposed ? r->h : r->w);
2006 /* LQR_PUBLIC */
2007 gint
2008 lqr_carver_get_height(LqrCarver *r)
2010 return (r->transposed ? r->w : r->h);
2013 /* get reference size */
2014 /* LQR_PUBLIC */
2015 gint
2016 lqr_carver_get_ref_width(LqrCarver *r)
2018 return (r->transposed ? r->h_start : r->w_start);
2021 /* LQR_PUBLIC */
2022 gint
2023 lqr_carver_get_ref_height(LqrCarver *r)
2025 return (r->transposed ? r->w_start : r->h_start);
2028 /* get colour channels */
2029 /* LQR_PUBLIC */
2030 gint
2031 lqr_carver_get_channels(LqrCarver *r)
2033 return r->channels;
2036 /* LQR_PUBLIC */
2037 gint
2038 lqr_carver_get_bpp(LqrCarver *r)
2040 return lqr_carver_get_channels(r);
2043 /* get colour depth */
2044 /* LQR_PUBLIC */
2045 LqrColDepth
2046 lqr_carver_get_col_depth(LqrCarver *r)
2048 return r->col_depth;
2051 /* get image type */
2052 /* LQR_PUBLIC */
2053 LqrImageType
2054 lqr_carver_get_image_type(LqrCarver *r)
2056 return r->image_type;
2059 /* get enlargement step */
2060 /* LQR_PUBLIC */
2061 gfloat
2062 lqr_carver_get_enl_step(LqrCarver *r)
2064 return r->enl_step;
2067 /* get orientation */
2068 /* LQR_PUBLIC */
2069 gint
2070 lqr_carver_get_orientation(LqrCarver *r)
2072 return (r->transposed ? 1 : 0);
2075 /* get depth */
2076 /* LQR_PUBLIC */
2077 gint
2078 lqr_carver_get_depth(LqrCarver *r)
2080 return r->w0 - r->w_start;
2083 /* readout reset */
2084 /* LQR_PUBLIC */
2085 void
2086 lqr_carver_scan_reset(LqrCarver *r)
2088 lqr_cursor_reset(r->c);
2091 LqrRetVal
2092 lqr_carver_scan_reset_attached(LqrCarver *r, LqrDataTok data)
2094 lqr_carver_scan_reset(r);
2095 return lqr_carver_list_foreach(r->attached_list, lqr_carver_scan_reset_attached, data);
2098 void
2099 lqr_carver_scan_reset_all(LqrCarver *r)
2101 LqrDataTok data;
2102 data.data = NULL;
2103 lqr_carver_scan_reset(r);
2104 lqr_carver_list_foreach(r->attached_list, lqr_carver_scan_reset_attached, data);
2107 /* readout all, pixel by bixel */
2108 /* LQR_PUBLIC */
2109 gboolean
2110 lqr_carver_scan(LqrCarver *r, gint *x, gint *y, guchar **rgb)
2112 gint k;
2113 if (r->col_depth != LQR_COLDEPTH_8I) {
2114 return FALSE;
2116 if (r->c->eoc) {
2117 lqr_carver_scan_reset(r);
2118 return FALSE;
2120 (*x) = (r->transposed ? r->c->y : r->c->x);
2121 (*y) = (r->transposed ? r->c->x : r->c->y);
2122 for (k = 0; k < r->channels; k++) {
2123 AS_8I(r->rgb_ro_buffer)[k] = AS_8I(r->rgb)[r->c->now * r->channels + k];
2125 (*rgb) = AS_8I(r->rgb_ro_buffer);
2126 lqr_cursor_next(r->c);
2127 return TRUE;
2130 /* LQR_PUBLIC */
2131 gboolean
2132 lqr_carver_scan_ext(LqrCarver *r, gint *x, gint *y, void **rgb)
2134 gint k;
2135 if (r->c->eoc) {
2136 lqr_carver_scan_reset(r);
2137 return FALSE;
2139 (*x) = (r->transposed ? r->c->y : r->c->x);
2140 (*y) = (r->transposed ? r->c->x : r->c->y);
2141 for (k = 0; k < r->channels; k++) {
2142 PXL_COPY(r->rgb_ro_buffer, k, r->rgb, r->c->now * r->channels + k, r->col_depth);
2145 BUF_POINTER_COPY(rgb, r->rgb_ro_buffer, r->col_depth);
2147 lqr_cursor_next(r->c);
2148 return TRUE;
2151 /* readout all, by line */
2152 /* LQR_PUBLIC */
2153 gboolean
2154 lqr_carver_scan_by_row(LqrCarver *r)
2156 return r->transposed ? FALSE : TRUE;
2159 /* LQR_PUBLIC */
2160 gboolean
2161 lqr_carver_scan_line(LqrCarver *r, gint *n, guchar **rgb)
2163 if (r->col_depth != LQR_COLDEPTH_8I) {
2164 return FALSE;
2166 return lqr_carver_scan_line_ext(r, n, (void **) rgb);
2169 /* LQR_PUBLIC */
2170 gboolean
2171 lqr_carver_scan_line_ext(LqrCarver *r, gint *n, void **rgb)
2173 gint k, x;
2174 if (r->c->eoc) {
2175 lqr_carver_scan_reset(r);
2176 return FALSE;
2178 x = r->c->x;
2179 (*n) = r->c->y;
2180 while (x > 0) {
2181 lqr_cursor_prev(r->c);
2182 x = r->c->x;
2184 for (x = 0; x < r->w; x++) {
2185 for (k = 0; k < r->channels; k++) {
2186 PXL_COPY(r->rgb_ro_buffer, x * r->channels + k, r->rgb, r->c->now * r->channels + k, r->col_depth);
2188 lqr_cursor_next(r->c);
2191 BUF_POINTER_COPY(rgb, r->rgb_ro_buffer, r->col_depth);
2193 return TRUE;
2196 #ifdef __LQR_DEBUG__
2197 void
2198 lqr_carver_debug_check_rows(LqrCarver *r)
2200 int x, y;
2201 int data;
2202 for (y = 0; y < r->h; y++) {
2203 for (x = 0; x < r->w; x++) {
2204 data = r->raw[y][x];
2205 if (data / r->w0 != y) {
2206 fflush(stderr);
2208 assert(data / r->w0 == y);
2212 #endif /* __LQR_DEBUG__ */
2214 /**** END OF LQR_CARVER CLASS FUNCTIONS ****/