Fixed README
[liblqr.git] / lqr / lqr_carver.c
bloba645b157e2cd1eede4f783efab49c72c4fcc6de1
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->en);
175 g_free(r->bias);
176 g_free(r->m);
177 g_free(r->rcache);
178 g_free(r->least);
179 lqr_cursor_destroy(r->c);
180 g_free(r->vpath);
181 g_free(r->vpath_x);
182 if (r->rigidity_map != NULL) {
183 r->rigidity_map -= r->delta_x;
184 g_free(r->rigidity_map);
186 g_free(r->rigidity_mask);
187 lqr_rwindow_destroy(r->rwindow);
188 g_free(r->nrg_xmin);
189 g_free(r->nrg_xmax);
190 lqr_vmap_list_destroy(r->flushed_vs);
191 lqr_carver_list_destroy(r->attached_list);
192 g_free(r->progress);
193 g_free(r->_raw);
194 g_free(r->raw);
195 g_free(r);
198 /*** initialization ***/
200 LqrRetVal
201 lqr_carver_init_energy_related(LqrCarver *r)
203 gint y, x;
205 LQR_CATCH_F(r->active == FALSE);
206 LQR_CATCH_F(r->nrg_active == FALSE);
208 LQR_CATCH_MEM(r->en = g_try_new(gfloat, r->w * r->h));
209 LQR_CATCH_MEM(r->_raw = g_try_new(gint, r->h_start * r->w_start));
210 LQR_CATCH_MEM(r->raw = g_try_new(gint *, r->h_start));
212 for (y = 0; y < r->h; y++) {
213 r->raw[y] = r->_raw + y * r->w_start;
214 for (x = 0; x < r->w_start; x++) {
215 r->raw[y][x] = y * r->w_start + x;
219 r->nrg_active = TRUE;
221 return LQR_OK;
224 /* LQR_PUBLIC */
225 LqrRetVal
226 lqr_carver_init(LqrCarver *r, gint delta_x, gfloat rigidity)
228 gint x;
230 LQR_CATCH_CANC(r);
232 LQR_CATCH_F(r->active == FALSE);
234 if (r->nrg_active == FALSE) {
235 LQR_CATCH(lqr_carver_init_energy_related(r));
238 /* LQR_CATCH_MEM (r->bias = g_try_new0 (gfloat, r->w * r->h)); */
239 LQR_CATCH_MEM(r->m = g_try_new(gfloat, r->w * r->h));
240 LQR_CATCH_MEM(r->least = g_try_new(gint, r->w * r->h));
242 LQR_CATCH_MEM(r->vpath = g_try_new(gint, r->h));
243 LQR_CATCH_MEM(r->vpath_x = g_try_new(gint, r->h));
245 LQR_CATCH_MEM(r->nrg_xmin = g_try_new(gint, r->h));
246 LQR_CATCH_MEM(r->nrg_xmax = g_try_new(gint, r->h));
248 /* set rigidity map */
249 r->delta_x = delta_x;
250 r->rigidity = rigidity;
252 r->rigidity_map = g_try_new0(gfloat, 2 * r->delta_x + 1);
253 r->rigidity_map += r->delta_x;
254 for (x = -r->delta_x; x <= r->delta_x; x++) {
255 r->rigidity_map[x] = r->rigidity * powf(fabsf(x), 1.5) / r->h;
258 r->active = TRUE;
260 return LQR_OK;
263 /*** set attributes ***/
265 /* LQR_PUBLIC */
266 LqrRetVal
267 lqr_carver_set_image_type(LqrCarver *r, LqrImageType image_type)
269 LQR_CATCH_CANC(r);
271 switch (image_type) {
272 case LQR_GREY_IMAGE:
273 if (r->channels != 1) {
274 return LQR_ERROR;
276 r->alpha_channel = -1;
277 r->black_channel = -1;
278 break;
279 case LQR_GREYA_IMAGE:
280 if (r->channels != 2) {
281 return LQR_ERROR;
283 r->alpha_channel = 1;
284 r->black_channel = -1;
285 break;
286 case LQR_CMY_IMAGE:
287 case LQR_RGB_IMAGE:
288 if (r->channels != 3) {
289 return LQR_ERROR;
291 r->alpha_channel = -1;
292 r->black_channel = -1;
293 break;
294 case LQR_CMYK_IMAGE:
295 if (r->channels != 4) {
296 return LQR_ERROR;
298 r->alpha_channel = -1;
299 r->black_channel = 3;
300 break;
301 case LQR_RGBA_IMAGE:
302 if (r->channels != 4) {
303 return LQR_ERROR;
305 r->alpha_channel = 3;
306 r->black_channel = -1;
307 break;
308 case LQR_CMYKA_IMAGE:
309 if (r->channels != 5) {
310 return LQR_ERROR;
312 r->alpha_channel = 4;
313 r->black_channel = 3;
314 break;
315 case LQR_CUSTOM_IMAGE:
316 r->alpha_channel = -1;
317 r->black_channel = -1;
318 break;
319 default:
320 return LQR_ERROR;
322 r->image_type = image_type;
324 g_free(r->rcache);
325 r->rcache = NULL;
326 r->nrg_uptodate = FALSE;
328 return LQR_OK;
331 /* LQR_PUBLIC */
332 LqrRetVal
333 lqr_carver_set_alpha_channel(LqrCarver *r, gint channel_index)
335 gboolean changed = TRUE;
336 LQR_CATCH_CANC(r);
338 if (channel_index < 0) {
339 if (r->alpha_channel != -1) {
340 r->alpha_channel = -1;
341 } else {
342 changed = FALSE;
344 } else if (channel_index < r->channels) {
345 if (r->alpha_channel != channel_index) {
346 if (r->black_channel == channel_index) {
347 r->black_channel = -1;
349 r->alpha_channel = channel_index;
350 } else {
351 changed = FALSE;
353 } else {
354 return LQR_ERROR;
357 if (r->image_type != LQR_CUSTOM_IMAGE) {
358 r->image_type = LQR_CUSTOM_IMAGE;
359 changed = TRUE;
362 if (changed) {
363 g_free(r->rcache);
364 r->rcache = NULL;
365 r->nrg_uptodate = FALSE;
368 return LQR_OK;
371 /* LQR_PUBLIC */
372 LqrRetVal
373 lqr_carver_set_black_channel(LqrCarver *r, gint channel_index)
375 gboolean changed = TRUE;
376 LQR_CATCH_CANC(r);
378 if (channel_index < 0) {
379 if (r->black_channel != -1) {
380 r->black_channel = -1;
381 } else {
382 changed = FALSE;
384 } else if (channel_index < r->channels) {
385 if (r->black_channel != channel_index) {
386 if (r->alpha_channel == channel_index) {
387 r->alpha_channel = -1;
389 r->black_channel = channel_index;
390 } else {
391 changed = FALSE;
393 } else {
394 return LQR_ERROR;
397 if (r->image_type != LQR_CUSTOM_IMAGE) {
398 r->image_type = LQR_CUSTOM_IMAGE;
399 changed = TRUE;
402 if (changed) {
403 g_free(r->rcache);
404 r->rcache = NULL;
405 r->nrg_uptodate = FALSE;
408 return LQR_OK;
411 /* set gradient function */
412 /* WARNING: THIS FUNCTION IS ONLY MAINTAINED FOR BACK-COMPATIBILITY PURPOSES */
413 /* lqr_carver_set_energy_function_builtin() should be used in newly written code instead */
414 /* LQR_PUBLIC */
415 void
416 lqr_carver_set_gradient_function(LqrCarver *r, LqrGradFuncType gf_ind)
418 switch (gf_ind) {
419 case LQR_GF_NORM:
420 lqr_carver_set_energy_function_builtin(r, LQR_EF_GRAD_NORM);
421 break;
422 case LQR_GF_SUMABS:
423 lqr_carver_set_energy_function_builtin(r, LQR_EF_GRAD_SUMABS);
424 break;
425 case LQR_GF_XABS:
426 lqr_carver_set_energy_function_builtin(r, LQR_EF_GRAD_XABS);
427 break;
428 case LQR_GF_NULL:
429 lqr_carver_set_energy_function_builtin(r, LQR_EF_NULL);
430 break;
431 case LQR_GF_NORM_BIAS:
432 case LQR_GF_YABS:
433 lqr_carver_set_energy_function_builtin(r, LQR_EF_NULL);
434 break;
435 #ifdef __LQR_DEBUG__
436 default:
437 assert(0);
438 #endif /* __LQR_DEBUG__ */
442 /* attach carvers to be scaled along with the main one */
443 /* LQR_PUBLIC */
444 LqrRetVal
445 lqr_carver_attach(LqrCarver *r, LqrCarver *aux)
447 LQR_CATCH_F(r->w0 == aux->w0);
448 LQR_CATCH_F(r->h0 == aux->h0);
449 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
450 LQR_CATCH_F(g_atomic_int_get(&aux->state) == LQR_CARVER_STATE_STD);
451 LQR_CATCH_MEM(r->attached_list = lqr_carver_list_append(r->attached_list, aux));
452 g_free(aux->vs);
453 aux->vs = r->vs;
454 aux->root = r;
456 return LQR_OK;
459 /* set the seam output flag */
460 /* LQR_PUBLIC */
461 void
462 lqr_carver_set_dump_vmaps(LqrCarver *r)
464 r->dump_vmaps = TRUE;
467 /* unset the seam output flag */
468 /* LQR_PUBLIC */
469 void
470 lqr_carver_set_no_dump_vmaps(LqrCarver *r)
472 r->dump_vmaps = FALSE;
475 /* set order if rescaling in both directions */
476 /* LQR_PUBLIC */
477 void
478 lqr_carver_set_resize_order(LqrCarver *r, LqrResizeOrder resize_order)
480 r->resize_order = resize_order;
483 /* set leftright switch interval */
484 /* LQR_PUBLIC */
485 void
486 lqr_carver_set_side_switch_frequency(LqrCarver *r, guint switch_frequency)
488 r->lr_switch_frequency = switch_frequency;
491 /* set enlargement step */
492 /* LQR_PUBLIC */
493 LqrRetVal
494 lqr_carver_set_enl_step(LqrCarver *r, gfloat enl_step)
496 LQR_CATCH_F((enl_step > 1) && (enl_step <= 2));
497 LQR_CATCH_CANC(r);
498 r->enl_step = enl_step;
499 return LQR_OK;
502 /* LQR_PUBLIC */
503 void
504 lqr_carver_set_use_cache(LqrCarver *r, gboolean use_cache)
506 if (!use_cache) {
507 g_free(r->rcache);
508 r->rcache = NULL;
510 r->use_rcache = use_cache;
511 r->rwindow->use_rcache = use_cache;
514 /* set progress reprot */
515 /* LQR_PUBLIC */
516 void
517 lqr_carver_set_progress(LqrCarver *r, LqrProgress * p)
519 g_free(r->progress);
520 r->progress = p;
523 /* flag the input buffer to avoid destruction */
524 /* LQR_PUBLIC */
525 void
526 lqr_carver_set_preserve_input_image(LqrCarver *r)
528 r->preserve_in_buffer = TRUE;
531 /*** compute maps (energy, minpath & visibility) ***/
533 /* build multisize image up to given depth
534 * it is progressive (can be called multilple times) */
535 LqrRetVal
536 lqr_carver_build_maps(LqrCarver *r, gint depth)
538 #ifdef __LQR_DEBUG__
539 assert(depth <= r->w_start);
540 assert(depth >= 1);
541 #endif /* __LQR_DEBUG__ */
543 LQR_CATCH_CANC(r);
545 /* only go deeper if needed */
546 if (depth > r->max_level) {
547 LQR_CATCH_F(r->active);
548 LQR_CATCH_F(r->root == NULL);
550 /* set to minimum width reached so far */
551 lqr_carver_set_width(r, r->w_start - r->max_level + 1);
553 /* compute energy & minpath maps */
554 LQR_CATCH(lqr_carver_build_emap(r));
555 LQR_CATCH(lqr_carver_build_mmap(r));
557 /* compute visibility map */
558 LQR_CATCH(lqr_carver_build_vsmap(r, depth));
560 return LQR_OK;
563 /* compute energy map */
564 LqrRetVal
565 lqr_carver_build_emap(LqrCarver *r)
567 gint x, y;
569 LQR_CATCH_CANC(r);
571 if (r->nrg_uptodate) {
572 return LQR_OK;
575 if (r->use_rcache && r->rcache == NULL) {
576 LQR_CATCH_MEM(r->rcache = lqr_carver_generate_rcache(r));
579 for (y = 0; y < r->h; y++) {
580 LQR_CATCH_CANC(r);
581 /* r->nrg_xmin[y] = 0; */
582 /* r->nrg_xmax[y] = r->w - 1; */
583 for (x = 0; x < r->w; x++) {
584 LQR_CATCH(lqr_carver_compute_e(r, x, y));
588 r->nrg_uptodate = TRUE;
590 return LQR_OK;
593 LqrRetVal
594 lqr_carver_compute_e(LqrCarver *r, gint x, gint y)
596 gint data;
597 gfloat b_add = 0;
599 /* removed CANC check for performance reasons */
600 /* LQR_CATCH_CANC (r); */
602 data = r->raw[y][x];
604 LQR_CATCH(lqr_rwindow_fill(r->rwindow, r, x, y));
605 if (r->bias != NULL) {
606 b_add = r->bias[data] / r->w_start;
608 r->en[data] = r->nrg(x, y, r->w, r->h, r->rwindow, r->nrg_extra_data) + b_add;
610 return LQR_OK;
613 /* compute auxiliary minpath map
614 * defined as
615 * y = 1 : m(x,y) = e(x,y)
616 * y > 1 : m(x,y) = min_{x'=-dx,..,dx} ( m(x-x',y-1) + rig(x') ) + e(x,y)
617 * where
618 * e(x,y) is the energy at point (x,y)
619 * dx is the max seam step delta_x
620 * rig(x') is the rigidity for step x'
622 LqrRetVal
623 lqr_carver_build_mmap(LqrCarver *r)
625 gint x, y;
626 gint data;
627 gint data_down;
628 gint x1_min, x1_max, x1;
629 gfloat m, m1, r_fact;
631 LQR_CATCH_CANC(r);
633 /* span first row */
634 for (x = 0; x < r->w; x++) {
635 data = r->raw[0][x];
636 #ifdef __LQR_DEBUG__
637 assert(r->vs[data] == 0);
638 #endif /* __LQR_DEBUG__ */
639 r->m[data] = r->en[data];
642 /* span all other rows */
643 for (y = 1; y < r->h; y++) {
644 for (x = 0; x < r->w; x++) {
645 LQR_CATCH_CANC(r);
647 data = r->raw[y][x];
648 #ifdef __LQR_DEBUG__
649 assert(r->vs[data] == 0);
650 #endif /* __LQR_DEBUG__ */
651 /* watch for boundaries */
652 x1_min = MAX(-x, -r->delta_x);
653 x1_max = MIN(r->w - 1 - x, r->delta_x);
654 if (r->rigidity_mask) {
655 r_fact = r->rigidity_mask[data];
656 } else {
657 r_fact = 1;
660 /* we use the data_down pointer to be able to
661 * track the seams later (needed for rigidity) */
662 data_down = r->raw[y - 1][x + x1_min];
663 r->least[data] = data_down;
664 if (r->rigidity) {
665 m = r->m[data_down] + r_fact * r->rigidity_map[x1_min];
666 for (x1 = x1_min + 1; x1 <= x1_max; x1++) {
667 data_down = r->raw[y - 1][x + x1];
668 /* find the min among the neighbors
669 * in the last row */
670 m1 = r->m[data_down] + r_fact * r->rigidity_map[x1];
671 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
672 m = m1;
673 r->least[data] = data_down;
675 /* m = MIN(m, r->m[data_down] + r->rigidity_map[x1]); */
677 } else {
678 m = r->m[data_down];
679 for (x1 = x1_min + 1; x1 <= x1_max; x1++) {
680 data_down = r->raw[y - 1][x + x1];
681 /* find the min among the neighbors
682 * in the last row */
683 m1 = r->m[data_down];
684 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
685 m = m1;
686 r->least[data] = data_down;
688 m = MIN(m, r->m[data_down]);
692 /* set current m */
693 r->m[data] = r->en[data] + m;
696 return LQR_OK;
699 /* compute (vertical) visibility map up to given depth
700 * (it also calls inflate() to add image enlargment information) */
701 LqrRetVal
702 lqr_carver_build_vsmap(LqrCarver *r, gint depth)
704 gint l;
705 gint lr_switch_interval = 0;
706 LqrDataTok data_tok;
708 #ifdef __LQR_VERBOSE__
709 printf("[ building visibility map ]\n");
710 fflush(stdout);
711 #endif /* __LQR_VERBOSE__ */
713 #ifdef __LQR_DEBUG__
714 assert(depth <= r->w_start + 1);
715 assert(depth >= 1);
716 #endif /* __LQR_DEBUG__ */
718 /* default behaviour : compute all possible levels
719 * (complete map) */
720 if (depth == 0) {
721 depth = r->w_start + 1;
724 /* here we assume that
725 * lqr_carver_set_width(w_start - max_level + 1);
726 * has been given */
728 /* left-right switch interval */
729 if (r->lr_switch_frequency) {
730 lr_switch_interval = (depth - r->max_level - 1) / r->lr_switch_frequency + 1;
733 /* cycle over levels */
734 for (l = r->max_level; l < depth; l++) {
735 LQR_CATCH_CANC(r);
737 if ((l - r->max_level + r->session_rescale_current) % r->session_update_step == 0) {
738 lqr_progress_update(r->progress, (gdouble) (l - r->max_level + r->session_rescale_current) /
739 (gdouble) (r->session_rescale_total));
741 #ifdef __LQR_DEBUG__
742 /* check raw rows */
743 lqr_carver_debug_check_rows(r);
744 #endif /* __LQR_DEBUG__ */
746 /* compute vertical seam */
747 lqr_carver_build_vpath(r);
749 /* update visibility map
750 * (assign level to the seam) */
751 lqr_carver_update_vsmap(r, l + r->max_level - 1);
753 /* increase (in)visibility level
754 * (make the last seam invisible) */
755 r->level++;
756 r->w--;
758 /* update raw data */
759 lqr_carver_carve(r);
761 if (r->w > 1) {
762 /* update the energy */
763 /* LQR_CATCH (lqr_carver_build_emap (r)); */
764 LQR_CATCH(lqr_carver_update_emap(r));
766 /* recalculate the minpath map */
767 if ((r->lr_switch_frequency) && (((l - r->max_level + lr_switch_interval / 2) % lr_switch_interval) == 0)) {
768 r->leftright ^= 1;
769 LQR_CATCH(lqr_carver_build_mmap(r));
770 } else {
771 /* lqr_carver_build_mmap (r); */
772 LQR_CATCH(lqr_carver_update_mmap(r));
774 } else {
775 /* complete the map (last seam) */
776 lqr_carver_finish_vsmap(r);
780 /* insert seams for image enlargement */
781 LQR_CATCH(lqr_carver_inflate(r, depth - 1));
783 /* reset image size */
784 lqr_carver_set_width(r, r->w_start);
785 /* repeat for auxiliary layers */
786 data_tok.integer = r->w_start;
787 LQR_CATCH(lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_width_attached, data_tok));
789 #ifdef __LQR_VERBOSE__
790 printf("[ visibility map OK ]\n");
791 fflush(stdout);
792 #endif /* __LQR_VERBOSE__ */
794 return LQR_OK;
797 /* enlarge the image by seam insertion
798 * visibility map is updated and the resulting multisize image
799 * is complete in both directions */
800 LqrRetVal
801 lqr_carver_inflate(LqrCarver *r, gint l)
803 gint w1, z0, vs, k;
804 gint x, y;
805 gint c_left;
806 void *new_rgb = NULL;
807 gint *new_vs = NULL;
808 gdouble tmp_rgb;
809 gfloat *new_bias = NULL;
810 gfloat *new_rigmask = NULL;
811 LqrDataTok data_tok;
812 LqrCarverState prev_state = LQR_CARVER_STATE_STD;
814 #ifdef __LQR_VERBOSE__
815 printf(" [ inflating (active=%i) ]\n", r->active);
816 fflush(stdout);
817 #endif /* __LQR_VERBOSE__ */
819 #ifdef __LQR_DEBUG__
820 assert(l + 1 > r->max_level); /* otherwise is useless */
821 #endif /* __LQR_DEBUG__ */
823 LQR_CATCH_CANC(r);
825 if (r->root == NULL) {
826 prev_state = g_atomic_int_get(&r->state);
827 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_INFLATING, TRUE));
830 /* first iterate on attached carvers */
831 data_tok.integer = l;
832 LQR_CATCH(lqr_carver_list_foreach(r->attached_list, lqr_carver_inflate_attached, data_tok));
834 /* scale to current maximum size
835 * (this is the original size the first time) */
836 lqr_carver_set_width(r, r->w0);
838 /* final width */
839 w1 = r->w0 + l - r->max_level + 1;
841 /* allocate room for new maps */
842 BUF_TRY_NEW0_RET_LQR(new_rgb, w1 * r->h0 * r->channels, r->col_depth);
844 if (r->root == NULL) {
845 LQR_CATCH_MEM(new_vs = g_try_new0(gint, w1 * r->h0));
847 if (r->active) {
848 if (r->bias) {
849 LQR_CATCH_MEM(new_bias = g_try_new0(gfloat, w1 * r->h0));
851 if (r->rigidity_mask) {
852 LQR_CATCH_MEM(new_rigmask = g_try_new(gfloat, w1 * r->h0));
856 /* span the image with a cursor
857 * and build the new image */
858 lqr_cursor_reset(r->c);
859 x = 0;
860 y = 0;
861 for (z0 = 0; z0 < w1 * r->h0; z0++, lqr_cursor_next(r->c)) {
863 LQR_CATCH_CANC(r);
865 /* read visibility */
866 vs = r->vs[r->c->now];
867 if ((vs != 0) && (vs <= l + r->max_level - 1)
868 && (vs >= 2 * r->max_level - 1)) {
869 /* the point belongs to a previously computed seam
870 * and was not inserted during a previous
871 * inflate() call : insert another seam */
873 /* the new pixel value is equal to the average of its
874 * left and right neighbors */
876 if (r->c->x > 0) {
877 c_left = lqr_cursor_left(r->c);
878 } else {
879 c_left = r->c->now;
882 for (k = 0; k < r->channels; k++) {
883 switch (r->col_depth) {
884 case LQR_COLDEPTH_8I:
885 tmp_rgb = (AS_8I(r->rgb)[c_left * r->channels + k] +
886 AS_8I(r->rgb)[r->c->now * r->channels + k]) / 2;
887 AS_8I(new_rgb)[z0 * r->channels + k] = (lqr_t_8i) (tmp_rgb + 0.499999);
888 break;
889 case LQR_COLDEPTH_16I:
890 tmp_rgb = (AS_16I(r->rgb)[c_left * r->channels + k] +
891 AS_16I(r->rgb)[r->c->now * r->channels + k]) / 2;
892 AS_16I(new_rgb)[z0 * r->channels + k] = (lqr_t_16i) (tmp_rgb + 0.499999);
893 break;
894 case LQR_COLDEPTH_32F:
895 tmp_rgb = (AS_32F(r->rgb)[c_left * r->channels + k] +
896 AS_32F(r->rgb)[r->c->now * r->channels + k]) / 2;
897 AS_32F(new_rgb)[z0 * r->channels + k] = (lqr_t_32f) tmp_rgb;
898 break;
899 case LQR_COLDEPTH_64F:
900 tmp_rgb = (AS_64F(r->rgb)[c_left * r->channels + k] +
901 AS_64F(r->rgb)[r->c->now * r->channels + k]) / 2;
902 AS_64F(new_rgb)[z0 * r->channels + k] = (lqr_t_64f) tmp_rgb;
903 break;
906 if (r->active) {
907 if (r->bias) {
908 new_bias[z0] = (r->bias[c_left] + r->bias[r->c->now]) / 2;
910 if (r->rigidity_mask) {
911 new_rigmask[z0] = (r->rigidity_mask[c_left] + r->rigidity_mask[r->c->now]) / 2;
914 /* the first time inflate() is called
915 * the new visibility should be -vs + 1 but we shift it
916 * so that the final minimum visibiliy will be 1 again
917 * and so that vs=0 still means "uninitialized".
918 * Subsequent inflations account for that */
919 if (r->root == NULL) {
920 new_vs[z0] = l - vs + r->max_level;
922 z0++;
924 for (k = 0; k < r->channels; k++) {
925 PXL_COPY(new_rgb, z0 * r->channels + k, r->rgb, r->c->now * r->channels + k, r->col_depth);
927 if (r->active) {
928 if (r->bias) {
929 new_bias[z0] = r->bias[r->c->now];
931 if (r->rigidity_mask) {
932 new_rigmask[z0] = r->rigidity_mask[r->c->now];
935 if (vs != 0) {
936 /* visibility has to be shifted up */
937 if (r->root == NULL) {
938 new_vs[z0] = vs + l - r->max_level + 1;
940 } else if (r->raw != NULL) {
941 #ifdef __LQR_DEBUG__
942 assert(y < r->h_start);
943 assert(x < r->w_start - l);
944 #endif /* __LQR_DEBUG__ */
945 r->raw[y][x] = z0;
946 x++;
947 if (x >= r->w_start - l) {
948 x = 0;
949 y++;
954 #ifdef __LQR_DEBUG__
955 if (r->raw != NULL) {
956 assert(x == 0);
957 if (w1 != 2 * r->w_start - 1) {
958 assert((y == r->h_start)
959 || (printf("y=%i hst=%i w1=%i\n", y, r->h_start, w1)
960 && fflush(stdout) && 0));
963 #endif /* __LQR_DEBUG__ */
965 /* substitute maps */
966 if (!r->preserve_in_buffer) {
967 g_free(r->rgb);
969 /* g_free (r->vs); */
970 g_free(r->en);
971 g_free(r->m);
972 g_free(r->rcache);
973 g_free(r->least);
974 g_free(r->bias);
975 g_free(r->rigidity_mask);
977 r->bias = NULL;
978 r->rcache = NULL;
979 r->nrg_uptodate = FALSE;
981 r->rgb = new_rgb;
982 r->preserve_in_buffer = FALSE;
984 if (r->root == NULL) {
985 g_free(r->vs);
986 r->vs = new_vs;
987 LQR_CATCH(lqr_carver_propagate_vsmap(r));
988 } else {
989 /* r->vs = NULL; */
991 if (r->nrg_active) {
992 LQR_CATCH_MEM(r->en = g_try_new0(gfloat, w1 * r->h0));
994 if (r->active) {
995 r->bias = new_bias;
996 r->rigidity_mask = new_rigmask;
997 LQR_CATCH_MEM(r->m = g_try_new0(gfloat, w1 * r->h0));
998 LQR_CATCH_MEM(r->least = g_try_new0(gint, w1 * r->h0));
1001 /* set new widths & levels (w_start is kept for reference) */
1002 r->level = l + 1;
1003 r->max_level = l + 1;
1004 r->w0 = w1;
1005 r->w = r->w_start;
1007 /* reset readout buffer */
1008 g_free(r->rgb_ro_buffer);
1009 BUF_TRY_NEW0_RET_LQR(r->rgb_ro_buffer, r->w0 * r->channels, r->col_depth);
1011 #ifdef __LQR_VERBOSE__
1012 printf(" [ inflating OK ]\n");
1013 fflush(stdout);
1014 #endif /* __LQR_VERBOSE__ */
1016 if (r->root == NULL) {
1017 LQR_CATCH(lqr_carver_set_state(r, prev_state, TRUE));
1020 return LQR_OK;
1023 LqrRetVal
1024 lqr_carver_inflate_attached(LqrCarver *r, LqrDataTok data)
1026 return lqr_carver_inflate(r, data.integer);
1029 /*** internal functions for maps computations ***/
1031 /* do the carving
1032 * this actually carves the raw array,
1033 * which holds the indices to be used
1034 * in all the other maps */
1035 void
1036 lqr_carver_carve(LqrCarver *r)
1038 gint x, y;
1040 #ifdef __LQR_DEBUG__
1041 assert(r->root == NULL);
1042 #endif /* __LQR_DEBUG__ */
1044 for (y = 0; y < r->h_start; y++) {
1045 #ifdef __LQR_DEBUG__
1046 assert(r->vs[r->raw[y][r->vpath_x[y]]] != 0);
1047 for (x = 0; x < r->vpath_x[y]; x++) {
1048 assert(r->vs[r->raw[y][x]] == 0);
1050 #endif /* __LQR_DEBUG__ */
1051 for (x = r->vpath_x[y]; x < r->w; x++) {
1052 r->raw[y][x] = r->raw[y][x + 1];
1053 #ifdef __LQR_DEBUG__
1054 assert(r->vs[r->raw[y][x]] == 0);
1055 #endif /* __LQR_DEBUG__ */
1059 r->nrg_uptodate = FALSE;
1062 /* update energy map after seam removal */
1063 LqrRetVal
1064 lqr_carver_update_emap(LqrCarver *r)
1066 gint x, y;
1067 gint y1, y1_min, y1_max;
1069 LQR_CATCH_CANC(r);
1071 if (r->nrg_uptodate) {
1072 return LQR_OK;
1074 if (r->use_rcache) {
1075 LQR_CATCH_F(r->rcache != NULL);
1078 for (y = 0; y < r->h; y++) {
1079 /* note: here the vpath has already
1080 * been carved */
1081 x = r->vpath_x[y];
1082 r->nrg_xmin[y] = x;
1083 r->nrg_xmax[y] = x - 1;
1085 for (y = 0; y < r->h; y++) {
1086 x = r->vpath_x[y];
1087 y1_min = MAX(y - r->nrg_radius, 0);
1088 y1_max = MIN(y + r->nrg_radius, r->h - 1);
1090 for (y1 = y1_min; y1 <= y1_max; y1++) {
1091 r->nrg_xmin[y1] = MIN(r->nrg_xmin[y1], x - r->nrg_radius);
1092 r->nrg_xmin[y1] = MAX(0, r->nrg_xmin[y1]);
1093 /* note: the -1 below is because of the previous carving */
1094 r->nrg_xmax[y1] = MAX(r->nrg_xmax[y1], x + r->nrg_radius - 1);
1095 r->nrg_xmax[y1] = MIN(r->w - 1, r->nrg_xmax[y1]);
1099 for (y = 0; y < r->h; y++) {
1100 LQR_CATCH_CANC(r);
1102 for (x = r->nrg_xmin[y]; x <= r->nrg_xmax[y]; x++) {
1103 LQR_CATCH(lqr_carver_compute_e(r, x, y));
1107 r->nrg_uptodate = TRUE;
1109 return LQR_OK;
1112 /* update the auxiliary minpath map
1113 * this only updates the affected pixels,
1114 * which start form the beginning of the changed
1115 * energy region around the seam and expand
1116 * at most by delta_x (in both directions)
1117 * at each row */
1118 LqrRetVal
1119 lqr_carver_update_mmap(LqrCarver *r)
1121 gint x, y;
1122 gint x_min, x_max;
1123 gint x1, dx;
1124 gint x1_min, x1_max;
1125 gint data, data_down, least;
1126 gfloat m, m1, r_fact;
1127 gfloat new_m;
1128 gfloat *mc = NULL;
1129 gint stop;
1130 gint x_stop;
1132 LQR_CATCH_CANC(r);
1133 LQR_CATCH_F(r->nrg_uptodate);
1135 if (r->rigidity) {
1136 LQR_CATCH_MEM(mc = g_try_new(gfloat, 2 * r->delta_x + 1));
1137 mc += r->delta_x;
1140 /* span first row */
1141 /* x_min = MAX (r->vpath_x[0] - r->delta_x, 0); */
1142 x_min = MAX(r->nrg_xmin[0], 0);
1143 /* x_max = MIN (r->vpath_x[0] + r->delta_x - 1, r->w - 1); */
1144 /* x_max = MIN (r->vpath_x[0] + r->delta_x, r->w - 1); */
1145 x_max = MIN(r->nrg_xmax[0], r->w - 1);
1147 for (x = x_min; x <= x_max; x++) {
1148 data = r->raw[0][x];
1149 r->m[data] = r->en[data];
1152 /* other rows */
1153 for (y = 1; y < r->h; y++) {
1154 LQR_CATCH_CANC(r);
1156 /* make sure to include the changed energy region */
1157 x_min = MIN(x_min, r->nrg_xmin[y]);
1158 x_max = MAX(x_max, r->nrg_xmax[y]);
1160 /* expand the affected region by delta_x */
1161 x_min = MAX(x_min - r->delta_x, 0);
1162 x_max = MIN(x_max + r->delta_x, r->w - 1);
1164 /* span the affected region */
1165 stop = 0;
1166 x_stop = 0;
1167 for (x = x_min; x <= x_max; x++) {
1168 data = r->raw[y][x];
1169 if (r->rigidity_mask) {
1170 r_fact = r->rigidity_mask[data];
1171 } else {
1172 r_fact = 1;
1175 /* find the minimum in the previous rows
1176 * as in build_mmap() */
1177 x1_min = MAX(0, x - r->delta_x);
1178 x1_max = MIN(r->w - 1, x + r->delta_x);
1180 if (r->rigidity) {
1181 dx = x1_min - x;
1182 switch (x1_max - x1_min + 1) {
1183 UPDATE_MMAP_OPTIMISED_CASES_RIG
1184 default:
1185 data_down = r->raw[y - 1][x1_min];
1186 least = data_down;
1187 m = r->m[data_down] + r_fact * r->rigidity_map[dx++];
1188 /* 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); */
1189 for (x1 = x1_min + 1; x1 <= x1_max; x1++, dx++) {
1190 data_down = r->raw[y - 1][x1];
1191 m1 = r->m[data_down] + r_fact * r->rigidity_map[dx];
1192 /* 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); */
1193 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
1194 m = m1;
1195 least = data_down;
1199 /* 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); */
1200 } else {
1201 switch (x1_max - x1_min + 1) {
1202 UPDATE_MMAP_OPTIMISED_CASES
1203 default:
1204 data_down = r->raw[y - 1][x1_min];
1205 least = data_down;
1206 m = r->m[data_down];
1207 for (x1 = x1_min + 1; x1 <= x1_max; x1++) {
1208 data_down = r->raw[y - 1][x1];
1209 m1 = r->m[data_down];
1210 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
1211 m = m1;
1212 least = data_down;
1216 /* 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); */
1219 new_m = r->en[data] + m;
1221 /* reduce the range if there's no (relevant) difference
1222 * with the previous map */
1223 if (r->least[data] == least) {
1224 if (fabsf(r->m[data] - new_m) < UPDATE_TOLERANCE) {
1225 if (stop == 0) {
1226 x_stop = x;
1228 stop = 1;
1229 new_m = r->m[data];
1230 } else {
1231 stop = 0;
1232 r->m[data] = new_m;
1234 if ((x == x_min) && stop) {
1235 x_min++;
1237 } else {
1238 stop = 0;
1239 r->m[data] = new_m;
1242 r->least[data] = least;
1244 if ((x == x_max) && (stop)) {
1245 x_max = x_stop;
1251 if (r->rigidity) {
1252 mc -= r->delta_x;
1253 g_free(mc);
1256 return LQR_OK;
1259 /* compute seam path from minpath map */
1260 void
1261 lqr_carver_build_vpath(LqrCarver *r)
1263 gint x, y, z0;
1264 gfloat m, m1;
1265 gint last = -1;
1266 gint last_x = 0;
1267 gint x_min, x_max;
1269 /* we start at last row */
1270 y = r->h - 1;
1272 /* span the last row for the minimum mmap value */
1273 m = (1 << 29);
1274 for (x = 0, z0 = y * r->w_start; x < r->w; x++, z0++) {
1275 #ifdef __LQR_DEBUG__
1276 assert(r->vs[r->raw[y][x]] == 0);
1277 #endif /* __LQR_DEBUG__ */
1279 m1 = r->m[r->raw[y][x]];
1280 if ((m1 < m) || ((m1 == m) && (r->leftright == 1))) {
1281 last = r->raw[y][x];
1282 last_x = x;
1283 m = m1;
1287 #ifdef __LQR_DEBUG__
1288 assert(last >= 0);
1289 #endif /* __LQR_DEBUG__ */
1291 /* follow the track for the other rows */
1292 for (y = r->h0 - 1; y >= 0; y--) {
1293 #ifdef __LQR_DEBUG__
1294 assert(r->vs[last] == 0);
1295 assert(last_x < r->w);
1296 #endif /* __LQR_DEBUG__ */
1297 r->vpath[y] = last;
1298 r->vpath_x[y] = last_x;
1299 if (y > 0) {
1300 last = r->least[r->raw[y][last_x]];
1301 /* we also need to retrieve the x coordinate */
1302 x_min = MAX(last_x - r->delta_x, 0);
1303 x_max = MIN(last_x + r->delta_x, r->w - 1);
1304 for (x = x_min; x <= x_max; x++) {
1305 if (r->raw[y - 1][x] == last) {
1306 last_x = x;
1307 break;
1310 #ifdef __LQR_DEBUG__
1311 assert(x < x_max + 1);
1312 #endif /* __LQR_DEBUG__ */
1316 #if 0
1317 /* we backtrack the seam following the min mmap */
1318 for (y = r->h0 - 1; y >= 0; y--) {
1319 #ifdef __LQR_DEBUG__
1320 assert(r->vs[last] == 0);
1321 assert(last_x < r->w);
1322 #endif /* __LQR_DEBUG__ */
1324 r->vpath[y] = last;
1325 r->vpath_x[y] = last_x;
1326 if (y > 0) {
1327 m = (1 << 29);
1328 x_min = MAX(0, last_x - r->delta_x);
1329 x_max = MIN(r->w - 1, last_x + r->delta_x);
1330 for (x = x_min; x <= x_max; x++) {
1331 m1 = r->m[r->raw[y - 1][x]];
1332 if (m1 < m) {
1333 last = r->raw[y - 1][x];
1334 last_x = x;
1335 m = m1;
1340 #endif
1343 /* update visibility map after seam computation */
1344 void
1345 lqr_carver_update_vsmap(LqrCarver *r, gint l)
1347 gint y;
1348 #ifdef __LQR_DEBUG__
1349 assert(r->root == NULL);
1350 #endif /* __LQR_DEBUG__ */
1351 for (y = 0; y < r->h; y++) {
1352 #ifdef __LQR_DEBUG__
1353 assert(r->vs[r->vpath[y]] == 0);
1354 assert(r->vpath[y] == r->raw[y][r->vpath_x[y]]);
1355 #endif /* __LQR_DEBUG__ */
1356 r->vs[r->vpath[y]] = l;
1360 /* complete visibility map (last seam) */
1361 /* set the last column of pixels to vis. level w0 */
1362 void
1363 lqr_carver_finish_vsmap(LqrCarver *r)
1365 gint y;
1367 #ifdef __LQR_DEBUG__
1368 assert(r->w == 1);
1369 assert(r->root == NULL);
1370 #endif /* __LQR_DEBUG__ */
1371 lqr_cursor_reset(r->c);
1372 for (y = 1; y <= r->h; y++, lqr_cursor_next(r->c)) {
1373 #ifdef __LQR_DEBUG__
1374 assert(r->vs[r->c->now] == 0);
1375 #endif /* __LQR_DEBUG__ */
1376 r->vs[r->c->now] = r->w0;
1378 lqr_cursor_reset(r->c);
1381 /* propagate the root carver's visibility map */
1382 LqrRetVal
1383 lqr_carver_propagate_vsmap(LqrCarver *r)
1385 LqrDataTok data_tok;
1387 LQR_CATCH_CANC(r);
1389 data_tok.data = NULL;
1390 LQR_CATCH(lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_propagate_vsmap_attached, data_tok));
1391 return LQR_OK;
1394 LqrRetVal
1395 lqr_carver_propagate_vsmap_attached(LqrCarver *r, LqrDataTok data)
1397 LqrDataTok data_tok;
1398 data_tok.data = NULL;
1399 r->vs = r->root->vs;
1400 lqr_carver_scan_reset(r);
1401 /* LQR_CATCH (lqr_carver_list_foreach (r->attached_list, lqr_carver_propagate_vsmap_attached, data_tok)); */
1402 return LQR_OK;
1405 /*** image manipulations ***/
1407 /* set width of the multisize image
1408 * (maps have to be computed already) */
1409 void
1410 lqr_carver_set_width(LqrCarver *r, gint w1)
1412 #ifdef __LQR_DEBUG__
1413 assert(w1 <= r->w0);
1414 assert(w1 >= r->w_start - r->max_level + 1);
1415 #endif /* __LQR_DEBUG__ */
1416 r->w = w1;
1417 r->level = r->w0 - w1 + 1;
1420 LqrRetVal
1421 lqr_carver_set_width_attached(LqrCarver *r, LqrDataTok data)
1423 lqr_carver_set_width(r, data.integer);
1424 return LQR_OK;
1427 /* flatten the image to its current state
1428 * (all maps are reset, invisible points are lost) */
1429 /* LQR_PUBLIC */
1430 LqrRetVal
1431 lqr_carver_flatten(LqrCarver *r)
1433 void *new_rgb = NULL;
1434 gfloat *new_bias = NULL;
1435 gfloat *new_rigmask = NULL;
1436 gint x, y, k;
1437 gint z0;
1438 LqrDataTok data_tok;
1439 LqrCarverState prev_state = LQR_CARVER_STATE_STD;
1441 #ifdef __LQR_VERBOSE__
1442 printf(" [ flattening (active=%i) ]\n", r->active);
1443 fflush(stdout);
1444 #endif /* __LQR_VERBOSE__ */
1446 LQR_CATCH_CANC(r);
1448 if (r->root == NULL) {
1449 prev_state = g_atomic_int_get(&r->state);
1450 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_FLATTENING, TRUE));
1453 /* first iterate on attached carvers */
1454 data_tok.data = NULL;
1455 LQR_CATCH(lqr_carver_list_foreach(r->attached_list, lqr_carver_flatten_attached, data_tok));
1457 /* free non needed maps first */
1458 g_free(r->en);
1459 g_free(r->m);
1460 g_free(r->rcache);
1461 g_free(r->least);
1463 r->rcache = NULL;
1464 r->nrg_uptodate = FALSE;
1466 /* allocate room for new map */
1467 BUF_TRY_NEW0_RET_LQR(new_rgb, r->w * r->h * r->channels, r->col_depth);
1469 if (r->active) {
1470 if (r->rigidity_mask) {
1471 LQR_CATCH_MEM(new_rigmask = g_try_new(gfloat, r->w * r->h));
1474 if (r->nrg_active) {
1475 if (r->bias) {
1476 LQR_CATCH_MEM(new_bias = g_try_new0(gfloat, r->w * r->h));
1478 g_free(r->_raw);
1479 g_free(r->raw);
1480 LQR_CATCH_MEM(r->_raw = g_try_new(gint, r->w * r->h));
1481 LQR_CATCH_MEM(r->raw = g_try_new(gint *, r->h));
1484 /* span the image with the cursor and copy
1485 * it in the new array */
1486 lqr_cursor_reset(r->c);
1487 for (y = 0; y < r->h; y++) {
1488 LQR_CATCH_CANC(r);
1490 if (r->nrg_active) {
1491 r->raw[y] = r->_raw + y * r->w;
1493 for (x = 0; x < r->w; x++) {
1494 z0 = y * r->w + x;
1495 for (k = 0; k < r->channels; k++) {
1496 PXL_COPY(new_rgb, z0 * r->channels + k, r->rgb, r->c->now * r->channels + k, r->col_depth);
1498 if (r->active) {
1499 if (r->rigidity_mask) {
1500 new_rigmask[z0] = r->rigidity_mask[r->c->now];
1503 if (r->nrg_active) {
1504 if (r->bias) {
1505 new_bias[z0] = r->bias[r->c->now];
1507 r->raw[y][x] = z0;
1509 lqr_cursor_next(r->c);
1513 /* substitute the old maps */
1514 if (!r->preserve_in_buffer) {
1515 g_free(r->rgb);
1517 r->rgb = new_rgb;
1518 r->preserve_in_buffer = FALSE;
1519 if (r->nrg_active) {
1520 g_free(r->bias);
1521 r->bias = new_bias;
1523 if (r->active) {
1524 g_free(r->rigidity_mask);
1525 r->rigidity_mask = new_rigmask;
1528 /* init the other maps */
1529 if (r->root == NULL) {
1530 g_free(r->vs);
1531 LQR_CATCH_MEM(r->vs = g_try_new0(gint, r->w * r->h));
1532 LQR_CATCH(lqr_carver_propagate_vsmap(r));
1534 if (r->nrg_active) {
1535 LQR_CATCH_MEM(r->en = g_try_new0(gfloat, r->w * r->h));
1537 if (r->active) {
1538 LQR_CATCH_MEM(r->m = g_try_new0(gfloat, r->w * r->h));
1539 LQR_CATCH_MEM(r->least = g_try_new(gint, r->w * r->h));
1542 /* reset widths, heights & levels */
1543 r->w0 = r->w;
1544 r->h0 = r->h;
1545 r->w_start = r->w;
1546 r->h_start = r->h;
1547 r->level = 1;
1548 r->max_level = 1;
1550 #ifdef __LQR_VERBOSE__
1551 printf(" [ flattening OK ]\n");
1552 fflush(stdout);
1553 #endif /* __LQR_VERBOSE__ */
1555 if (r->root == NULL) {
1556 LQR_CATCH(lqr_carver_set_state(r, prev_state, TRUE));
1559 return LQR_OK;
1562 LqrRetVal
1563 lqr_carver_flatten_attached(LqrCarver *r, LqrDataTok data)
1565 return lqr_carver_flatten(r);
1568 /* transpose the image, in its current state
1569 * (all maps and invisible points are lost) */
1570 LqrRetVal
1571 lqr_carver_transpose(LqrCarver *r)
1573 gint x, y, k;
1574 gint z0, z1;
1575 gint d;
1576 void *new_rgb = NULL;
1577 gfloat *new_bias = NULL;
1578 gfloat *new_rigmask = NULL;
1579 LqrDataTok data_tok;
1580 LqrCarverState prev_state = LQR_CARVER_STATE_STD;
1582 #ifdef __LQR_VERBOSE__
1583 printf("[ transposing (active=%i) ]\n", r->active);
1584 fflush(stdout);
1585 #endif /* __LQR_VERBOSE__ */
1587 LQR_CATCH_CANC(r);
1589 if (r->root == NULL) {
1590 prev_state = g_atomic_int_get(&r->state);
1591 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_TRANSPOSING, TRUE));
1594 if (r->level > 1) {
1595 LQR_CATCH(lqr_carver_flatten(r));
1598 /* first iterate on attached carvers */
1599 data_tok.data = NULL;
1600 LQR_CATCH(lqr_carver_list_foreach(r->attached_list, lqr_carver_transpose_attached, data_tok));
1602 /* free non needed maps first */
1603 if (r->root == NULL) {
1604 g_free(r->vs);
1606 g_free(r->en);
1607 g_free(r->m);
1608 g_free(r->rcache);
1609 g_free(r->least);
1610 g_free(r->rgb_ro_buffer);
1612 r->rcache = NULL;
1613 r->nrg_uptodate = FALSE;
1615 /* allocate room for the new maps */
1616 BUF_TRY_NEW0_RET_LQR(new_rgb, r->w0 * r->h0 * r->channels, r->col_depth);
1618 if (r->active) {
1619 if (r->rigidity_mask) {
1620 LQR_CATCH_MEM(new_rigmask = g_try_new(gfloat, r->w0 * r->h0));
1623 if (r->nrg_active) {
1624 if (r->bias) {
1625 LQR_CATCH_MEM(new_bias = g_try_new0(gfloat, r->w0 * r->h0));
1627 g_free(r->_raw);
1628 g_free(r->raw);
1629 LQR_CATCH_MEM(r->_raw = g_try_new0(gint, r->h0 * r->w0));
1630 LQR_CATCH_MEM(r->raw = g_try_new0(gint *, r->w0));
1633 /* compute trasposed maps */
1634 for (x = 0; x < r->w; x++) {
1635 if (r->nrg_active) {
1636 r->raw[x] = r->_raw + x * r->h0;
1638 for (y = 0; y < r->h; y++) {
1639 z0 = y * r->w0 + x;
1640 z1 = x * r->h0 + y;
1641 for (k = 0; k < r->channels; k++) {
1642 PXL_COPY(new_rgb, z1 * r->channels + k, r->rgb, z0 * r->channels + k, r->col_depth);
1644 if (r->active) {
1645 if (r->rigidity_mask) {
1646 new_rigmask[z1] = r->rigidity_mask[z0];
1649 if (r->nrg_active) {
1650 if (r->bias) {
1651 new_bias[z1] = r->bias[z0];
1653 r->raw[x][y] = z1;
1658 /* substitute the map */
1659 if (!r->preserve_in_buffer) {
1660 g_free(r->rgb);
1662 r->rgb = new_rgb;
1663 r->preserve_in_buffer = FALSE;
1665 if (r->nrg_active) {
1666 g_free(r->bias);
1667 r->bias = new_bias;
1669 if (r->active) {
1670 g_free(r->rigidity_mask);
1671 r->rigidity_mask = new_rigmask;
1674 /* init the other maps */
1675 if (r->root == NULL) {
1676 LQR_CATCH_MEM(r->vs = g_try_new0(gint, r->w0 * r->h0));
1677 LQR_CATCH(lqr_carver_propagate_vsmap(r));
1679 if (r->nrg_active) {
1680 LQR_CATCH_MEM(r->en = g_try_new0(gfloat, r->w0 * r->h0));
1682 if (r->active) {
1683 LQR_CATCH_MEM(r->m = g_try_new0(gfloat, r->w0 * r->h0));
1684 LQR_CATCH_MEM(r->least = g_try_new(gint, r->w0 * r->h0));
1687 /* switch widths & heights */
1688 d = r->w0;
1689 r->w0 = r->h0;
1690 r->h0 = d;
1691 r->w = r->w0;
1692 r->h = r->h0;
1694 /* reset w_start, h_start & levels */
1695 r->w_start = r->w0;
1696 r->h_start = r->h0;
1697 r->level = 1;
1698 r->max_level = 1;
1700 /* reset seam path, cursor and readout buffer */
1701 if (r->active) {
1702 g_free(r->vpath);
1703 LQR_CATCH_MEM(r->vpath = g_try_new(gint, r->h));
1704 g_free(r->vpath_x);
1705 LQR_CATCH_MEM(r->vpath_x = g_try_new(gint, r->h));
1706 g_free(r->nrg_xmin);
1707 LQR_CATCH_MEM(r->nrg_xmin = g_try_new(gint, r->h));
1708 g_free(r->nrg_xmax);
1709 LQR_CATCH_MEM(r->nrg_xmax = g_try_new(gint, r->h));
1712 BUF_TRY_NEW0_RET_LQR(r->rgb_ro_buffer, r->w0 * r->channels, r->col_depth);
1714 /* rescale rigidity */
1716 if (r->active) {
1717 for (x = -r->delta_x; x <= r->delta_x; x++) {
1718 r->rigidity_map[x] = r->rigidity_map[x] * r->w0 / r->h0;
1722 /* set transposed flag */
1723 r->transposed = (r->transposed ? 0 : 1);
1725 #ifdef __LQR_VERBOSE__
1726 printf("[ transpose OK ]\n");
1727 fflush(stdout);
1728 #endif /* __LQR_VERBOSE__ */
1730 if (r->root == NULL) {
1731 LQR_CATCH(lqr_carver_set_state(r, prev_state, TRUE));
1734 return LQR_OK;
1737 LqrRetVal
1738 lqr_carver_transpose_attached(LqrCarver *r, LqrDataTok data)
1740 return lqr_carver_transpose(r);
1743 /* resize w + h: these are the liquid rescale methods.
1744 * They automatically determine the depth of the map
1745 * according to the desired size, can be called multiple
1746 * times, transpose the image as necessasry */
1747 LqrRetVal
1748 lqr_carver_resize_width(LqrCarver *r, gint w1)
1750 LqrDataTok data_tok;
1751 gint delta, gamma;
1752 gint delta_max;
1753 /* delta is used to determine the required depth
1754 * gamma to decide if action is necessary */
1755 if (!r->transposed) {
1756 delta = w1 - r->w_start;
1757 gamma = w1 - r->w;
1758 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1759 } else {
1760 delta = w1 - r->h_start;
1761 gamma = w1 - r->h;
1762 delta_max = (gint) ((r->enl_step - 1) * r->h_start) - 1;
1764 if (delta_max < 1) {
1765 delta_max = 1;
1767 if (delta < 0) {
1768 delta = -delta;
1769 delta_max = delta;
1772 LQR_CATCH_CANC(r);
1773 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
1774 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_RESIZING, TRUE));
1776 /* update step for progress reprt */
1777 r->session_rescale_total = gamma > 0 ? gamma : -gamma;
1778 r->session_rescale_current = 0;
1779 r->session_update_step = (gint) MAX(r->session_rescale_total * r->progress->update_step, 1);
1781 if (r->session_rescale_total) {
1782 lqr_progress_init(r->progress, r->progress->init_width_message);
1785 while (gamma) {
1786 gint delta0 = MIN(delta, delta_max);
1787 gint new_w;
1789 delta -= delta0;
1790 if (r->transposed) {
1791 LQR_CATCH(lqr_carver_transpose(r));
1793 new_w = MIN(w1, r->w_start + delta_max);
1794 gamma = w1 - new_w;
1795 LQR_CATCH(lqr_carver_build_maps(r, delta0 + 1));
1796 lqr_carver_set_width(r, new_w);
1798 data_tok.integer = new_w;
1799 lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_width_attached, data_tok);
1801 r->session_rescale_current = r->session_rescale_total - (gamma > 0 ? gamma : -gamma);
1803 if (r->dump_vmaps) {
1804 LQR_CATCH(lqr_vmap_internal_dump(r));
1806 if (new_w < w1) {
1807 LQR_CATCH(lqr_carver_flatten(r));
1808 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1809 if (delta_max < 1) {
1810 delta_max = 1;
1815 if (r->session_rescale_total) {
1816 lqr_progress_end(r->progress, r->progress->end_width_message);
1819 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_STD, TRUE));
1821 return LQR_OK;
1824 LqrRetVal
1825 lqr_carver_resize_height(LqrCarver *r, gint h1)
1827 LqrDataTok data_tok;
1828 gint delta, gamma;
1829 gint delta_max;
1830 /* delta is used to determine the required depth
1831 * gamma to decide if action is necessary */
1832 if (!r->transposed) {
1833 delta = h1 - r->h_start;
1834 gamma = h1 - r->h;
1835 delta_max = (gint) ((r->enl_step - 1) * r->h_start) - 1;
1836 } else {
1837 delta = h1 - r->w_start;
1838 gamma = h1 - r->w;
1839 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1841 if (delta_max < 1) {
1842 delta_max = 1;
1844 if (delta < 0) {
1845 delta_max = -delta;
1847 delta = delta > 0 ? delta : -delta;
1849 LQR_CATCH_CANC(r);
1850 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
1851 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_RESIZING, TRUE));
1853 /* update step for progress reprt */
1854 r->session_rescale_total = gamma > 0 ? gamma : -gamma;
1855 r->session_rescale_current = 0;
1856 r->session_update_step = (gint) MAX(r->session_rescale_total * r->progress->update_step, 1);
1858 if (r->session_rescale_total) {
1859 lqr_progress_init(r->progress, r->progress->init_height_message);
1862 while (gamma) {
1863 gint delta0 = MIN(delta, delta_max);
1864 gint new_w;
1865 delta -= delta0;
1866 if (!r->transposed) {
1867 LQR_CATCH(lqr_carver_transpose(r));
1869 new_w = MIN(h1, r->w_start + delta_max);
1870 gamma = h1 - new_w;
1871 LQR_CATCH(lqr_carver_build_maps(r, delta0 + 1));
1872 lqr_carver_set_width(r, new_w);
1874 data_tok.integer = new_w;
1875 lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_width_attached, data_tok);
1877 r->session_rescale_current = r->session_rescale_total - (gamma > 0 ? gamma : -gamma);
1879 if (r->dump_vmaps) {
1880 LQR_CATCH(lqr_vmap_internal_dump(r));
1882 if (new_w < h1) {
1883 LQR_CATCH(lqr_carver_flatten(r));
1884 delta_max = (gint) ((r->enl_step - 1) * r->w_start) - 1;
1885 if (delta_max < 1) {
1886 delta_max = 1;
1891 if (r->session_rescale_total) {
1892 lqr_progress_end(r->progress, r->progress->end_height_message);
1895 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_STD, TRUE));
1897 return LQR_OK;
1900 /* liquid rescale public method */
1901 /* LQR_PUBLIC */
1902 LqrRetVal
1903 lqr_carver_resize(LqrCarver *r, gint w1, gint h1)
1905 #ifdef __LQR_VERBOSE__
1906 printf("[ Rescale from %i,%i to %i,%i ]\n", (r->transposed ? r->h : r->w), (r->transposed ? r->w : r->h), w1, h1);
1907 fflush(stdout);
1908 #endif /* __LQR_VERBOSE__ */
1909 LQR_CATCH_F((w1 >= 1) && (h1 >= 1));
1910 LQR_CATCH_F(r->root == NULL);
1912 LQR_CATCH_CANC(r);
1913 LQR_CATCH_F(g_atomic_int_get(&r->state) == LQR_CARVER_STATE_STD);
1915 switch (r->resize_order) {
1916 case LQR_RES_ORDER_HOR:
1917 LQR_CATCH(lqr_carver_resize_width(r, w1));
1918 LQR_CATCH(lqr_carver_resize_height(r, h1));
1919 break;
1920 case LQR_RES_ORDER_VERT:
1921 LQR_CATCH(lqr_carver_resize_height(r, h1));
1922 LQR_CATCH(lqr_carver_resize_width(r, w1));
1923 break;
1924 #ifdef __LQR_DEBUG__
1925 default:
1926 assert(0);
1927 #endif /* __LQR_DEBUG__ */
1929 lqr_carver_scan_reset_all(r);
1931 #ifdef __LQR_VERBOSE__
1932 printf("[ Rescale OK ]\n");
1933 fflush(stdout);
1934 #endif /* __LQR_VERBOSE__ */
1935 return LQR_OK;
1938 LqrRetVal
1939 lqr_carver_set_state(LqrCarver *r, LqrCarverState state, gboolean skip_canceled)
1941 LqrDataTok data_tok;
1942 gint lock_pos;
1944 LQR_CATCH_F(r->root == NULL);
1946 lock_pos = g_atomic_int_exchange_and_add(&r->state_lock_queue, 1);
1948 while (g_atomic_int_get(&r->state_lock) != lock_pos) {
1949 g_usleep(10000);
1952 if (skip_canceled && g_atomic_int_get(&r->state) == LQR_CARVER_STATE_CANCELLED) {
1953 g_atomic_int_inc(&r->state_lock);
1954 return LQR_OK;
1957 g_atomic_int_set(&r->state, state);
1959 data_tok.integer = state;
1960 LQR_CATCH(lqr_carver_list_foreach_recursive(r->attached_list, lqr_carver_set_state_attached, data_tok));
1962 g_atomic_int_inc(&r->state_lock);
1964 return LQR_OK;
1967 LqrRetVal
1968 lqr_carver_set_state_attached(LqrCarver *r, LqrDataTok data)
1970 g_atomic_int_set(&r->state, data.integer);
1971 return LQR_OK;
1974 /* cancel the current action from a different thread */
1975 /* LQR_PUBLIC */
1976 LqrRetVal
1977 lqr_carver_cancel(LqrCarver *r)
1979 LqrCarverState curr_state;
1981 LQR_CATCH_F(r->root == NULL);
1983 curr_state = g_atomic_int_get(&r->state);
1985 if ((curr_state == LQR_CARVER_STATE_RESIZING) ||
1986 (curr_state == LQR_CARVER_STATE_INFLATING) ||
1987 (curr_state == LQR_CARVER_STATE_TRANSPOSING) || (curr_state == LQR_CARVER_STATE_FLATTENING)) {
1988 LQR_CATCH(lqr_carver_set_state(r, LQR_CARVER_STATE_CANCELLED, TRUE));
1990 return LQR_OK;
1993 /* get current size */
1994 /* LQR_PUBLIC */
1995 gint
1996 lqr_carver_get_width(LqrCarver *r)
1998 return (r->transposed ? r->h : r->w);
2001 /* LQR_PUBLIC */
2002 gint
2003 lqr_carver_get_height(LqrCarver *r)
2005 return (r->transposed ? r->w : r->h);
2008 /* get reference size */
2009 /* LQR_PUBLIC */
2010 gint
2011 lqr_carver_get_ref_width(LqrCarver *r)
2013 return (r->transposed ? r->h_start : r->w_start);
2016 /* LQR_PUBLIC */
2017 gint
2018 lqr_carver_get_ref_height(LqrCarver *r)
2020 return (r->transposed ? r->w_start : r->h_start);
2023 /* get colour channels */
2024 /* LQR_PUBLIC */
2025 gint
2026 lqr_carver_get_channels(LqrCarver *r)
2028 return r->channels;
2031 /* LQR_PUBLIC */
2032 gint
2033 lqr_carver_get_bpp(LqrCarver *r)
2035 return lqr_carver_get_channels(r);
2038 /* get colour depth */
2039 /* LQR_PUBLIC */
2040 LqrColDepth
2041 lqr_carver_get_col_depth(LqrCarver *r)
2043 return r->col_depth;
2046 /* get image type */
2047 /* LQR_PUBLIC */
2048 LqrImageType
2049 lqr_carver_get_image_type(LqrCarver *r)
2051 return r->image_type;
2054 /* get enlargement step */
2055 /* LQR_PUBLIC */
2056 gfloat
2057 lqr_carver_get_enl_step(LqrCarver *r)
2059 return r->enl_step;
2062 /* get orientation */
2063 /* LQR_PUBLIC */
2064 gint
2065 lqr_carver_get_orientation(LqrCarver *r)
2067 return (r->transposed ? 1 : 0);
2070 /* get depth */
2071 /* LQR_PUBLIC */
2072 gint
2073 lqr_carver_get_depth(LqrCarver *r)
2075 return r->w0 - r->w_start;
2078 /* readout reset */
2079 /* LQR_PUBLIC */
2080 void
2081 lqr_carver_scan_reset(LqrCarver *r)
2083 lqr_cursor_reset(r->c);
2086 LqrRetVal
2087 lqr_carver_scan_reset_attached(LqrCarver *r, LqrDataTok data)
2089 lqr_carver_scan_reset(r);
2090 return lqr_carver_list_foreach(r->attached_list, lqr_carver_scan_reset_attached, data);
2093 void
2094 lqr_carver_scan_reset_all(LqrCarver *r)
2096 LqrDataTok data;
2097 data.data = NULL;
2098 lqr_carver_scan_reset(r);
2099 lqr_carver_list_foreach(r->attached_list, lqr_carver_scan_reset_attached, data);
2102 /* readout all, pixel by bixel */
2103 /* LQR_PUBLIC */
2104 gboolean
2105 lqr_carver_scan(LqrCarver *r, gint *x, gint *y, guchar **rgb)
2107 gint k;
2108 if (r->col_depth != LQR_COLDEPTH_8I) {
2109 return FALSE;
2111 if (r->c->eoc) {
2112 lqr_carver_scan_reset(r);
2113 return FALSE;
2115 (*x) = (r->transposed ? r->c->y : r->c->x);
2116 (*y) = (r->transposed ? r->c->x : r->c->y);
2117 for (k = 0; k < r->channels; k++) {
2118 AS_8I(r->rgb_ro_buffer)[k] = AS_8I(r->rgb)[r->c->now * r->channels + k];
2120 (*rgb) = AS_8I(r->rgb_ro_buffer);
2121 lqr_cursor_next(r->c);
2122 return TRUE;
2125 /* LQR_PUBLIC */
2126 gboolean
2127 lqr_carver_scan_ext(LqrCarver *r, gint *x, gint *y, void **rgb)
2129 gint k;
2130 if (r->c->eoc) {
2131 lqr_carver_scan_reset(r);
2132 return FALSE;
2134 (*x) = (r->transposed ? r->c->y : r->c->x);
2135 (*y) = (r->transposed ? r->c->x : r->c->y);
2136 for (k = 0; k < r->channels; k++) {
2137 PXL_COPY(r->rgb_ro_buffer, k, r->rgb, r->c->now * r->channels + k, r->col_depth);
2140 BUF_POINTER_COPY(rgb, r->rgb_ro_buffer, r->col_depth);
2142 lqr_cursor_next(r->c);
2143 return TRUE;
2146 /* readout all, by line */
2147 /* LQR_PUBLIC */
2148 gboolean
2149 lqr_carver_scan_by_row(LqrCarver *r)
2151 return r->transposed ? FALSE : TRUE;
2154 /* LQR_PUBLIC */
2155 gboolean
2156 lqr_carver_scan_line(LqrCarver *r, gint *n, guchar **rgb)
2158 if (r->col_depth != LQR_COLDEPTH_8I) {
2159 return FALSE;
2161 return lqr_carver_scan_line_ext(r, n, (void **) rgb);
2164 /* LQR_PUBLIC */
2165 gboolean
2166 lqr_carver_scan_line_ext(LqrCarver *r, gint *n, void **rgb)
2168 gint k, x;
2169 if (r->c->eoc) {
2170 lqr_carver_scan_reset(r);
2171 return FALSE;
2173 x = r->c->x;
2174 (*n) = r->c->y;
2175 while (x > 0) {
2176 lqr_cursor_prev(r->c);
2177 x = r->c->x;
2179 for (x = 0; x < r->w; x++) {
2180 for (k = 0; k < r->channels; k++) {
2181 PXL_COPY(r->rgb_ro_buffer, x * r->channels + k, r->rgb, r->c->now * r->channels + k, r->col_depth);
2183 lqr_cursor_next(r->c);
2186 BUF_POINTER_COPY(rgb, r->rgb_ro_buffer, r->col_depth);
2188 return TRUE;
2191 #ifdef __LQR_DEBUG__
2192 void
2193 lqr_carver_debug_check_rows(LqrCarver *r)
2195 int x, y;
2196 int data;
2197 for (y = 0; y < r->h; y++) {
2198 for (x = 0; x < r->w; x++) {
2199 data = r->raw[y][x];
2200 if (data / r->w0 != y) {
2201 fflush(stderr);
2203 assert(data / r->w0 == y);
2207 #endif /* __LQR_DEBUG__ */
2209 /**** END OF LQR_CARVER CLASS FUNCTIONS ****/