1 /* LiquidRescaling Library
2 * Copyright (C) 2007-2009 Carlo Baldassi (the "Author") <carlobaldassi@gmail.com>.
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/>
29 #include <lqr/lqr_all.h>
31 #ifdef __LQR_VERBOSE__
33 #endif /* __LQR_VERBOSE__ */
38 #endif /* __LQR_DEBUG__ */
40 /**** LQR_CARVER CLASS FUNCTIONS ****/
42 /*** constructor & destructor ***/
46 lqr_carver_new_common(gint width
, gint height
, gint channels
)
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);
60 r
->nrg_active
= FALSE
;
63 r
->resize_aux_layers
= FALSE
;
64 r
->dump_vmaps
= FALSE
;
65 r
->resize_order
= LQR_RES_ORDER_HOR
;
66 r
->attached_list
= 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;
82 r
->rigidity_map
= NULL
;
83 r
->rigidity_mask
= NULL
;
88 r
->channels
= channels
;
99 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_XABS
);
102 r
->nrg_uptodate
= FALSE
;
105 r
->lr_switch_frequency
= 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
));
117 lqr_carver_set_image_type(r
, LQR_GREY_IMAGE
);
120 lqr_carver_set_image_type(r
, LQR_GREYA_IMAGE
);
123 lqr_carver_set_image_type(r
, LQR_RGB_IMAGE
);
126 lqr_carver_set_image_type(r
, LQR_RGBA_IMAGE
);
129 lqr_carver_set_image_type(r
, LQR_CMYKA_IMAGE
);
132 lqr_carver_set_image_type(r
, LQR_CUSTOM_IMAGE
);
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
);
148 lqr_carver_new_ext(void *buffer
, gint width
, gint height
, gint channels
, LqrColDepth colour_depth
)
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
;
166 lqr_carver_destroy(LqrCarver
*r
)
168 if (!r
->preserve_in_buffer
) {
171 if (r
->root
== NULL
) {
179 lqr_cursor_destroy(r
->c
);
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
);
190 lqr_vmap_list_destroy(r
->flushed_vs
);
191 lqr_carver_list_destroy(r
->attached_list
);
198 /*** initialization ***/
201 lqr_carver_init_energy_related(LqrCarver
*r
)
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
;
226 lqr_carver_init(LqrCarver
*r
, gint delta_x
, gfloat rigidity
)
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
;
263 /*** set attributes ***/
267 lqr_carver_set_image_type(LqrCarver
*r
, LqrImageType image_type
)
271 switch (image_type
) {
273 if (r
->channels
!= 1) {
276 r
->alpha_channel
= -1;
277 r
->black_channel
= -1;
279 case LQR_GREYA_IMAGE
:
280 if (r
->channels
!= 2) {
283 r
->alpha_channel
= 1;
284 r
->black_channel
= -1;
288 if (r
->channels
!= 3) {
291 r
->alpha_channel
= -1;
292 r
->black_channel
= -1;
295 if (r
->channels
!= 4) {
298 r
->alpha_channel
= -1;
299 r
->black_channel
= 3;
302 if (r
->channels
!= 4) {
305 r
->alpha_channel
= 3;
306 r
->black_channel
= -1;
308 case LQR_CMYKA_IMAGE
:
309 if (r
->channels
!= 5) {
312 r
->alpha_channel
= 4;
313 r
->black_channel
= 3;
315 case LQR_CUSTOM_IMAGE
:
316 r
->alpha_channel
= -1;
317 r
->black_channel
= -1;
322 r
->image_type
= image_type
;
326 r
->nrg_uptodate
= FALSE
;
333 lqr_carver_set_alpha_channel(LqrCarver
*r
, gint channel_index
)
335 gboolean changed
= TRUE
;
338 if (channel_index
< 0) {
339 if (r
->alpha_channel
!= -1) {
340 r
->alpha_channel
= -1;
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
;
357 if (r
->image_type
!= LQR_CUSTOM_IMAGE
) {
358 r
->image_type
= LQR_CUSTOM_IMAGE
;
365 r
->nrg_uptodate
= FALSE
;
373 lqr_carver_set_black_channel(LqrCarver
*r
, gint channel_index
)
375 gboolean changed
= TRUE
;
378 if (channel_index
< 0) {
379 if (r
->black_channel
!= -1) {
380 r
->black_channel
= -1;
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
;
397 if (r
->image_type
!= LQR_CUSTOM_IMAGE
) {
398 r
->image_type
= LQR_CUSTOM_IMAGE
;
405 r
->nrg_uptodate
= FALSE
;
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 */
416 lqr_carver_set_gradient_function(LqrCarver
*r
, LqrGradFuncType gf_ind
)
420 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_NORM
);
423 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_SUMABS
);
426 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_XABS
);
429 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
431 case LQR_GF_NORM_BIAS
:
433 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
438 #endif /* __LQR_DEBUG__ */
442 /* attach carvers to be scaled along with the main one */
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
));
459 /* set the seam output flag */
462 lqr_carver_set_dump_vmaps(LqrCarver
*r
)
464 r
->dump_vmaps
= TRUE
;
467 /* unset the seam output flag */
470 lqr_carver_set_no_dump_vmaps(LqrCarver
*r
)
472 r
->dump_vmaps
= FALSE
;
475 /* set order if rescaling in both directions */
478 lqr_carver_set_resize_order(LqrCarver
*r
, LqrResizeOrder resize_order
)
480 r
->resize_order
= resize_order
;
483 /* set leftright switch interval */
486 lqr_carver_set_side_switch_frequency(LqrCarver
*r
, guint switch_frequency
)
488 r
->lr_switch_frequency
= switch_frequency
;
491 /* set enlargement step */
494 lqr_carver_set_enl_step(LqrCarver
*r
, gfloat enl_step
)
496 LQR_CATCH_F((enl_step
> 1) && (enl_step
<= 2));
498 r
->enl_step
= enl_step
;
504 lqr_carver_set_use_cache(LqrCarver
*r
, gboolean use_cache
)
510 r
->use_rcache
= use_cache
;
511 r
->rwindow
->use_rcache
= use_cache
;
514 /* set progress reprot */
517 lqr_carver_set_progress(LqrCarver
*r
, LqrProgress
* p
)
523 /* flag the input buffer to avoid destruction */
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) */
536 lqr_carver_build_maps(LqrCarver
*r
, gint depth
)
539 assert(depth
<= r
->w_start
);
541 #endif /* __LQR_DEBUG__ */
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
));
563 /* compute energy map */
565 lqr_carver_build_emap(LqrCarver
*r
)
571 if (r
->nrg_uptodate
) {
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
++) {
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
;
594 lqr_carver_compute_e(LqrCarver
*r
, gint x
, gint y
)
599 /* removed CANC check for performance reasons */
600 /* LQR_CATCH_CANC (r); */
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
;
613 /* compute auxiliary minpath map
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)
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'
623 lqr_carver_build_mmap(LqrCarver
*r
)
628 gint x1_min
, x1_max
, x1
;
629 gfloat m
, m1
, r_fact
;
634 for (x
= 0; x
< r
->w
; x
++) {
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
++) {
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
];
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
;
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
670 m1
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1
];
671 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1))) {
673 r
->least
[data
] = data_down
;
675 /* m = MIN(m, r->m[data_down] + r->rigidity_map[x1]); */
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
683 m1
= r
->m
[data_down
];
684 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1))) {
686 r
->least
[data
] = data_down
;
688 m
= MIN(m
, r
->m
[data_down
]);
693 r
->m
[data
] = r
->en
[data
] + m
;
699 /* compute (vertical) visibility map up to given depth
700 * (it also calls inflate() to add image enlargment information) */
702 lqr_carver_build_vsmap(LqrCarver
*r
, gint depth
)
705 gint lr_switch_interval
= 0;
708 #ifdef __LQR_VERBOSE__
709 printf("[ building visibility map ]\n");
711 #endif /* __LQR_VERBOSE__ */
714 assert(depth
<= r
->w_start
+ 1);
716 #endif /* __LQR_DEBUG__ */
718 /* default behaviour : compute all possible levels
721 depth
= r
->w_start
+ 1;
724 /* here we assume that
725 * lqr_carver_set_width(w_start - max_level + 1);
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
++) {
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
));
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) */
758 /* update raw data */
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)) {
769 LQR_CATCH(lqr_carver_build_mmap(r
));
771 /* lqr_carver_build_mmap (r); */
772 LQR_CATCH(lqr_carver_update_mmap(r
));
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");
792 #endif /* __LQR_VERBOSE__ */
797 /* enlarge the image by seam insertion
798 * visibility map is updated and the resulting multisize image
799 * is complete in both directions */
801 lqr_carver_inflate(LqrCarver
*r
, gint l
)
806 void *new_rgb
= NULL
;
809 gfloat
*new_bias
= NULL
;
810 gfloat
*new_rigmask
= NULL
;
812 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
814 #ifdef __LQR_VERBOSE__
815 printf(" [ inflating (active=%i) ]\n", r
->active
);
817 #endif /* __LQR_VERBOSE__ */
820 assert(l
+ 1 > r
->max_level
); /* otherwise is useless */
821 #endif /* __LQR_DEBUG__ */
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
);
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
));
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
);
861 for (z0
= 0; z0
< w1
* r
->h0
; z0
++, lqr_cursor_next(r
->c
)) {
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 */
877 c_left
= lqr_cursor_left(r
->c
);
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);
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);
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
;
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
;
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
;
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
);
929 new_bias
[z0
] = r
->bias
[r
->c
->now
];
931 if (r
->rigidity_mask
) {
932 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
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
) {
942 assert(y
< r
->h_start
);
943 assert(x
< r
->w_start
- l
);
944 #endif /* __LQR_DEBUG__ */
947 if (x
>= r
->w_start
- l
) {
955 if (r
->raw
!= NULL
) {
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
) {
969 /* g_free (r->vs); */
975 g_free(r
->rigidity_mask
);
979 r
->nrg_uptodate
= FALSE
;
982 r
->preserve_in_buffer
= FALSE
;
984 if (r
->root
== NULL
) {
987 LQR_CATCH(lqr_carver_propagate_vsmap(r
));
992 LQR_CATCH_MEM(r
->en
= g_try_new0(gfloat
, w1
* r
->h0
));
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) */
1003 r
->max_level
= l
+ 1;
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");
1014 #endif /* __LQR_VERBOSE__ */
1016 if (r
->root
== NULL
) {
1017 LQR_CATCH(lqr_carver_set_state(r
, prev_state
, TRUE
));
1024 lqr_carver_inflate_attached(LqrCarver
*r
, LqrDataTok data
)
1026 return lqr_carver_inflate(r
, data
.integer
);
1029 /*** internal functions for maps computations ***/
1032 * this actually carves the raw array,
1033 * which holds the indices to be used
1034 * in all the other maps */
1036 lqr_carver_carve(LqrCarver
*r
)
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 */
1064 lqr_carver_update_emap(LqrCarver
*r
)
1067 gint y1
, y1_min
, y1_max
;
1071 if (r
->nrg_uptodate
) {
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
1083 r
->nrg_xmax
[y
] = x
- 1;
1085 for (y
= 0; y
< r
->h
; 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
++) {
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
;
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)
1119 lqr_carver_update_mmap(LqrCarver
*r
)
1124 gint x1_min
, x1_max
;
1125 gint data
, data_down
, least
;
1126 gfloat m
, m1
, r_fact
;
1133 LQR_CATCH_F(r
->nrg_uptodate
);
1136 LQR_CATCH_MEM(mc
= g_try_new(gfloat
, 2 * r
->delta_x
+ 1));
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
];
1153 for (y
= 1; y
< r
->h
; y
++) {
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 */
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
];
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
);
1182 switch (x1_max
- x1_min
+ 1) {
1183 UPDATE_MMAP_OPTIMISED_CASES_RIG
1185 data_down
= r
->raw
[y
- 1][x1_min
];
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))) {
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); */
1201 switch (x1_max
- x1_min
+ 1) {
1202 UPDATE_MMAP_OPTIMISED_CASES
1204 data_down
= r
->raw
[y
- 1][x1_min
];
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))) {
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
) {
1234 if ((x
== x_min
) && stop
) {
1242 r
->least
[data
] = least
;
1244 if ((x
== x_max
) && (stop
)) {
1259 /* compute seam path from minpath map */
1261 lqr_carver_build_vpath(LqrCarver
*r
)
1269 /* we start at last row */
1272 /* span the last row for the minimum mmap value */
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
];
1287 #ifdef __LQR_DEBUG__
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__ */
1298 r
->vpath_x
[y
] = last_x
;
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
) {
1310 #ifdef __LQR_DEBUG__
1311 assert(x
< x_max
+ 1);
1312 #endif /* __LQR_DEBUG__ */
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__ */
1325 r
->vpath_x
[y
] = last_x
;
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
]];
1333 last
= r
->raw
[y
- 1][x
];
1343 /* update visibility map after seam computation */
1345 lqr_carver_update_vsmap(LqrCarver
*r
, gint l
)
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 */
1363 lqr_carver_finish_vsmap(LqrCarver
*r
)
1367 #ifdef __LQR_DEBUG__
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 */
1383 lqr_carver_propagate_vsmap(LqrCarver
*r
)
1385 LqrDataTok data_tok
;
1389 data_tok
.data
= NULL
;
1390 LQR_CATCH(lqr_carver_list_foreach_recursive(r
->attached_list
, lqr_carver_propagate_vsmap_attached
, data_tok
));
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)); */
1405 /*** image manipulations ***/
1407 /* set width of the multisize image
1408 * (maps have to be computed already) */
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__ */
1417 r
->level
= r
->w0
- w1
+ 1;
1421 lqr_carver_set_width_attached(LqrCarver
*r
, LqrDataTok data
)
1423 lqr_carver_set_width(r
, data
.integer
);
1427 /* flatten the image to its current state
1428 * (all maps are reset, invisible points are lost) */
1431 lqr_carver_flatten(LqrCarver
*r
)
1433 void *new_rgb
= NULL
;
1434 gfloat
*new_bias
= NULL
;
1435 gfloat
*new_rigmask
= NULL
;
1438 LqrDataTok data_tok
;
1439 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
1441 #ifdef __LQR_VERBOSE__
1442 printf(" [ flattening (active=%i) ]\n", r
->active
);
1444 #endif /* __LQR_VERBOSE__ */
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 */
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
);
1470 if (r
->rigidity_mask
) {
1471 LQR_CATCH_MEM(new_rigmask
= g_try_new(gfloat
, r
->w
* r
->h
));
1474 if (r
->nrg_active
) {
1476 LQR_CATCH_MEM(new_bias
= g_try_new0(gfloat
, r
->w
* r
->h
));
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
++) {
1490 if (r
->nrg_active
) {
1491 r
->raw
[y
] = r
->_raw
+ y
* r
->w
;
1493 for (x
= 0; x
< 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
);
1499 if (r
->rigidity_mask
) {
1500 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
1503 if (r
->nrg_active
) {
1505 new_bias
[z0
] = r
->bias
[r
->c
->now
];
1509 lqr_cursor_next(r
->c
);
1513 /* substitute the old maps */
1514 if (!r
->preserve_in_buffer
) {
1518 r
->preserve_in_buffer
= FALSE
;
1519 if (r
->nrg_active
) {
1524 g_free(r
->rigidity_mask
);
1525 r
->rigidity_mask
= new_rigmask
;
1528 /* init the other maps */
1529 if (r
->root
== NULL
) {
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
));
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 */
1550 #ifdef __LQR_VERBOSE__
1551 printf(" [ flattening OK ]\n");
1553 #endif /* __LQR_VERBOSE__ */
1555 if (r
->root
== NULL
) {
1556 LQR_CATCH(lqr_carver_set_state(r
, prev_state
, TRUE
));
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) */
1571 lqr_carver_transpose(LqrCarver
*r
)
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
);
1585 #endif /* __LQR_VERBOSE__ */
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
));
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
) {
1610 g_free(r
->rgb_ro_buffer
);
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
);
1619 if (r
->rigidity_mask
) {
1620 LQR_CATCH_MEM(new_rigmask
= g_try_new(gfloat
, r
->w0
* r
->h0
));
1623 if (r
->nrg_active
) {
1625 LQR_CATCH_MEM(new_bias
= g_try_new0(gfloat
, r
->w0
* r
->h0
));
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
++) {
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
);
1645 if (r
->rigidity_mask
) {
1646 new_rigmask
[z1
] = r
->rigidity_mask
[z0
];
1649 if (r
->nrg_active
) {
1651 new_bias
[z1
] = r
->bias
[z0
];
1658 /* substitute the map */
1659 if (!r
->preserve_in_buffer
) {
1663 r
->preserve_in_buffer
= FALSE
;
1665 if (r
->nrg_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
));
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 */
1694 /* reset w_start, h_start & levels */
1700 /* reset seam path, cursor and readout buffer */
1703 LQR_CATCH_MEM(r
->vpath
= g_try_new(gint
, r
->h
));
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 */
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");
1728 #endif /* __LQR_VERBOSE__ */
1730 if (r
->root
== NULL
) {
1731 LQR_CATCH(lqr_carver_set_state(r
, prev_state
, TRUE
));
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 */
1748 lqr_carver_resize_width(LqrCarver
*r
, gint w1
)
1750 LqrDataTok data_tok
;
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
;
1758 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1760 delta
= w1
- r
->h_start
;
1762 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
1764 if (delta_max
< 1) {
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
);
1786 gint delta0
= MIN(delta
, delta_max
);
1790 if (r
->transposed
) {
1791 LQR_CATCH(lqr_carver_transpose(r
));
1793 new_w
= MIN(w1
, r
->w_start
+ delta_max
);
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
));
1807 LQR_CATCH(lqr_carver_flatten(r
));
1808 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1809 if (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
));
1825 lqr_carver_resize_height(LqrCarver
*r
, gint h1
)
1827 LqrDataTok data_tok
;
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
;
1835 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
1837 delta
= h1
- r
->w_start
;
1839 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1841 if (delta_max
< 1) {
1847 delta
= delta
> 0 ? delta
: -delta
;
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
);
1863 gint delta0
= MIN(delta
, delta_max
);
1866 if (!r
->transposed
) {
1867 LQR_CATCH(lqr_carver_transpose(r
));
1869 new_w
= MIN(h1
, r
->w_start
+ delta_max
);
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
));
1883 LQR_CATCH(lqr_carver_flatten(r
));
1884 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1885 if (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
));
1900 /* liquid rescale public method */
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
);
1908 #endif /* __LQR_VERBOSE__ */
1909 LQR_CATCH_F((w1
>= 1) && (h1
>= 1));
1910 LQR_CATCH_F(r
->root
== NULL
);
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
));
1920 case LQR_RES_ORDER_VERT
:
1921 LQR_CATCH(lqr_carver_resize_height(r
, h1
));
1922 LQR_CATCH(lqr_carver_resize_width(r
, w1
));
1924 #ifdef __LQR_DEBUG__
1927 #endif /* __LQR_DEBUG__ */
1929 lqr_carver_scan_reset_all(r
);
1931 #ifdef __LQR_VERBOSE__
1932 printf("[ Rescale OK ]\n");
1934 #endif /* __LQR_VERBOSE__ */
1939 lqr_carver_set_state(LqrCarver
*r
, LqrCarverState state
, gboolean skip_canceled
)
1941 LqrDataTok data_tok
;
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
) {
1952 if (skip_canceled
&& g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_CANCELLED
) {
1953 g_atomic_int_inc(&r
->state_lock
);
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
);
1968 lqr_carver_set_state_attached(LqrCarver
*r
, LqrDataTok data
)
1970 g_atomic_int_set(&r
->state
, data
.integer
);
1974 /* cancel the current action from a different thread */
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
));
1993 /* get current size */
1996 lqr_carver_get_width(LqrCarver
*r
)
1998 return (r
->transposed
? r
->h
: r
->w
);
2003 lqr_carver_get_height(LqrCarver
*r
)
2005 return (r
->transposed
? r
->w
: r
->h
);
2008 /* get reference size */
2011 lqr_carver_get_ref_width(LqrCarver
*r
)
2013 return (r
->transposed
? r
->h_start
: r
->w_start
);
2018 lqr_carver_get_ref_height(LqrCarver
*r
)
2020 return (r
->transposed
? r
->w_start
: r
->h_start
);
2023 /* get colour channels */
2026 lqr_carver_get_channels(LqrCarver
*r
)
2033 lqr_carver_get_bpp(LqrCarver
*r
)
2035 return lqr_carver_get_channels(r
);
2038 /* get colour depth */
2041 lqr_carver_get_col_depth(LqrCarver
*r
)
2043 return r
->col_depth
;
2046 /* get image type */
2049 lqr_carver_get_image_type(LqrCarver
*r
)
2051 return r
->image_type
;
2054 /* get enlargement step */
2057 lqr_carver_get_enl_step(LqrCarver
*r
)
2062 /* get orientation */
2065 lqr_carver_get_orientation(LqrCarver
*r
)
2067 return (r
->transposed
? 1 : 0);
2073 lqr_carver_get_depth(LqrCarver
*r
)
2075 return r
->w0
- r
->w_start
;
2081 lqr_carver_scan_reset(LqrCarver
*r
)
2083 lqr_cursor_reset(r
->c
);
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
);
2094 lqr_carver_scan_reset_all(LqrCarver
*r
)
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 */
2105 lqr_carver_scan(LqrCarver
*r
, gint
*x
, gint
*y
, guchar
**rgb
)
2108 if (r
->col_depth
!= LQR_COLDEPTH_8I
) {
2112 lqr_carver_scan_reset(r
);
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
);
2127 lqr_carver_scan_ext(LqrCarver
*r
, gint
*x
, gint
*y
, void **rgb
)
2131 lqr_carver_scan_reset(r
);
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
);
2146 /* readout all, by line */
2149 lqr_carver_scan_by_row(LqrCarver
*r
)
2151 return r
->transposed
? FALSE
: TRUE
;
2156 lqr_carver_scan_line(LqrCarver
*r
, gint
*n
, guchar
**rgb
)
2158 if (r
->col_depth
!= LQR_COLDEPTH_8I
) {
2161 return lqr_carver_scan_line_ext(r
, n
, (void **) rgb
);
2166 lqr_carver_scan_line_ext(LqrCarver
*r
, gint
*n
, void **rgb
)
2170 lqr_carver_scan_reset(r
);
2176 lqr_cursor_prev(r
->c
);
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
);
2191 #ifdef __LQR_DEBUG__
2193 lqr_carver_debug_check_rows(LqrCarver
*r
)
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
) {
2203 assert(data
/ r
->w0
== y
);
2207 #endif /* __LQR_DEBUG__ */
2209 /**** END OF LQR_CARVER CLASS FUNCTIONS ****/