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
) {
174 g_free(r
->rgb_ro_buffer
);
180 lqr_cursor_destroy(r
->c
);
183 if (r
->rigidity_map
!= NULL
) {
184 r
->rigidity_map
-= r
->delta_x
;
185 g_free(r
->rigidity_map
);
187 g_free(r
->rigidity_mask
);
188 lqr_rwindow_destroy(r
->rwindow
);
191 lqr_vmap_list_destroy(r
->flushed_vs
);
192 lqr_carver_list_destroy(r
->attached_list
);
199 /*** initialization ***/
202 lqr_carver_init_energy_related(LqrCarver
*r
)
206 LQR_CATCH_F(r
->active
== FALSE
);
207 LQR_CATCH_F(r
->nrg_active
== FALSE
);
209 LQR_CATCH_MEM(r
->en
= g_try_new(gfloat
, r
->w
* r
->h
));
210 LQR_CATCH_MEM(r
->_raw
= g_try_new(gint
, r
->h_start
* r
->w_start
));
211 LQR_CATCH_MEM(r
->raw
= g_try_new(gint
*, r
->h_start
));
213 for (y
= 0; y
< r
->h
; y
++) {
214 r
->raw
[y
] = r
->_raw
+ y
* r
->w_start
;
215 for (x
= 0; x
< r
->w_start
; x
++) {
216 r
->raw
[y
][x
] = y
* r
->w_start
+ x
;
220 r
->nrg_active
= TRUE
;
227 lqr_carver_init(LqrCarver
*r
, gint delta_x
, gfloat rigidity
)
233 LQR_CATCH_F(r
->active
== FALSE
);
235 if (r
->nrg_active
== FALSE
) {
236 LQR_CATCH(lqr_carver_init_energy_related(r
));
239 /* LQR_CATCH_MEM (r->bias = g_try_new0 (gfloat, r->w * r->h)); */
240 LQR_CATCH_MEM(r
->m
= g_try_new(gfloat
, r
->w
* r
->h
));
241 LQR_CATCH_MEM(r
->least
= g_try_new(gint
, r
->w
* r
->h
));
243 LQR_CATCH_MEM(r
->vpath
= g_try_new(gint
, r
->h
));
244 LQR_CATCH_MEM(r
->vpath_x
= g_try_new(gint
, r
->h
));
246 LQR_CATCH_MEM(r
->nrg_xmin
= g_try_new(gint
, r
->h
));
247 LQR_CATCH_MEM(r
->nrg_xmax
= g_try_new(gint
, r
->h
));
249 /* set rigidity map */
250 r
->delta_x
= delta_x
;
251 r
->rigidity
= rigidity
;
253 r
->rigidity_map
= g_try_new0(gfloat
, 2 * r
->delta_x
+ 1);
254 r
->rigidity_map
+= r
->delta_x
;
255 for (x
= -r
->delta_x
; x
<= r
->delta_x
; x
++) {
256 r
->rigidity_map
[x
] = r
->rigidity
* powf(fabsf(x
), 1.5) / r
->h
;
264 /*** set attributes ***/
268 lqr_carver_set_image_type(LqrCarver
*r
, LqrImageType image_type
)
272 switch (image_type
) {
274 if (r
->channels
!= 1) {
277 r
->alpha_channel
= -1;
278 r
->black_channel
= -1;
280 case LQR_GREYA_IMAGE
:
281 if (r
->channels
!= 2) {
284 r
->alpha_channel
= 1;
285 r
->black_channel
= -1;
289 if (r
->channels
!= 3) {
292 r
->alpha_channel
= -1;
293 r
->black_channel
= -1;
296 if (r
->channels
!= 4) {
299 r
->alpha_channel
= -1;
300 r
->black_channel
= 3;
303 if (r
->channels
!= 4) {
306 r
->alpha_channel
= 3;
307 r
->black_channel
= -1;
309 case LQR_CMYKA_IMAGE
:
310 if (r
->channels
!= 5) {
313 r
->alpha_channel
= 4;
314 r
->black_channel
= 3;
316 case LQR_CUSTOM_IMAGE
:
317 r
->alpha_channel
= -1;
318 r
->black_channel
= -1;
323 r
->image_type
= image_type
;
327 r
->nrg_uptodate
= FALSE
;
334 lqr_carver_set_alpha_channel(LqrCarver
*r
, gint channel_index
)
336 gboolean changed
= TRUE
;
339 if (channel_index
< 0) {
340 if (r
->alpha_channel
!= -1) {
341 r
->alpha_channel
= -1;
345 } else if (channel_index
< r
->channels
) {
346 if (r
->alpha_channel
!= channel_index
) {
347 if (r
->black_channel
== channel_index
) {
348 r
->black_channel
= -1;
350 r
->alpha_channel
= channel_index
;
358 if (r
->image_type
!= LQR_CUSTOM_IMAGE
) {
359 r
->image_type
= LQR_CUSTOM_IMAGE
;
366 r
->nrg_uptodate
= FALSE
;
374 lqr_carver_set_black_channel(LqrCarver
*r
, gint channel_index
)
376 gboolean changed
= TRUE
;
379 if (channel_index
< 0) {
380 if (r
->black_channel
!= -1) {
381 r
->black_channel
= -1;
385 } else if (channel_index
< r
->channels
) {
386 if (r
->black_channel
!= channel_index
) {
387 if (r
->alpha_channel
== channel_index
) {
388 r
->alpha_channel
= -1;
390 r
->black_channel
= channel_index
;
398 if (r
->image_type
!= LQR_CUSTOM_IMAGE
) {
399 r
->image_type
= LQR_CUSTOM_IMAGE
;
406 r
->nrg_uptodate
= FALSE
;
412 /* set gradient function */
413 /* WARNING: THIS FUNCTION IS ONLY MAINTAINED FOR BACK-COMPATIBILITY PURPOSES */
414 /* lqr_carver_set_energy_function_builtin() should be used in newly written code instead */
417 lqr_carver_set_gradient_function(LqrCarver
*r
, LqrGradFuncType gf_ind
)
421 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_NORM
);
424 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_SUMABS
);
427 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_XABS
);
430 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
432 case LQR_GF_NORM_BIAS
:
434 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
439 #endif /* __LQR_DEBUG__ */
443 /* attach carvers to be scaled along with the main one */
446 lqr_carver_attach(LqrCarver
*r
, LqrCarver
*aux
)
448 LQR_CATCH_F(r
->w0
== aux
->w0
);
449 LQR_CATCH_F(r
->h0
== aux
->h0
);
450 LQR_CATCH_F(g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
451 LQR_CATCH_F(g_atomic_int_get(&aux
->state
) == LQR_CARVER_STATE_STD
);
452 LQR_CATCH_MEM(r
->attached_list
= lqr_carver_list_append(r
->attached_list
, aux
));
460 /* set the seam output flag */
463 lqr_carver_set_dump_vmaps(LqrCarver
*r
)
465 r
->dump_vmaps
= TRUE
;
468 /* unset the seam output flag */
471 lqr_carver_set_no_dump_vmaps(LqrCarver
*r
)
473 r
->dump_vmaps
= FALSE
;
476 /* set order if rescaling in both directions */
479 lqr_carver_set_resize_order(LqrCarver
*r
, LqrResizeOrder resize_order
)
481 r
->resize_order
= resize_order
;
484 /* set leftright switch interval */
487 lqr_carver_set_side_switch_frequency(LqrCarver
*r
, guint switch_frequency
)
489 r
->lr_switch_frequency
= switch_frequency
;
492 /* set enlargement step */
495 lqr_carver_set_enl_step(LqrCarver
*r
, gfloat enl_step
)
497 LQR_CATCH_F((enl_step
> 1) && (enl_step
<= 2));
499 r
->enl_step
= enl_step
;
505 lqr_carver_set_use_cache(LqrCarver
*r
, gboolean use_cache
)
511 r
->use_rcache
= use_cache
;
512 r
->rwindow
->use_rcache
= use_cache
;
515 /* set progress reprot */
518 lqr_carver_set_progress(LqrCarver
*r
, LqrProgress
* p
)
524 /* flag the input buffer to avoid destruction */
527 lqr_carver_set_preserve_input_image(LqrCarver
*r
)
529 r
->preserve_in_buffer
= TRUE
;
532 /*** compute maps (energy, minpath & visibility) ***/
534 /* build multisize image up to given depth
535 * it is progressive (can be called multilple times) */
537 lqr_carver_build_maps(LqrCarver
*r
, gint depth
)
540 assert(depth
<= r
->w_start
);
542 #endif /* __LQR_DEBUG__ */
546 /* only go deeper if needed */
547 if (depth
> r
->max_level
) {
548 LQR_CATCH_F(r
->active
);
549 LQR_CATCH_F(r
->root
== NULL
);
551 /* set to minimum width reached so far */
552 lqr_carver_set_width(r
, r
->w_start
- r
->max_level
+ 1);
554 /* compute energy & minpath maps */
555 LQR_CATCH(lqr_carver_build_emap(r
));
556 LQR_CATCH(lqr_carver_build_mmap(r
));
558 /* compute visibility map */
559 LQR_CATCH(lqr_carver_build_vsmap(r
, depth
));
564 /* compute energy map */
566 lqr_carver_build_emap(LqrCarver
*r
)
572 if (r
->nrg_uptodate
) {
576 if (r
->use_rcache
&& r
->rcache
== NULL
) {
577 LQR_CATCH_MEM(r
->rcache
= lqr_carver_generate_rcache(r
));
580 for (y
= 0; y
< r
->h
; y
++) {
582 /* r->nrg_xmin[y] = 0; */
583 /* r->nrg_xmax[y] = r->w - 1; */
584 for (x
= 0; x
< r
->w
; x
++) {
585 LQR_CATCH(lqr_carver_compute_e(r
, x
, y
));
589 r
->nrg_uptodate
= TRUE
;
595 lqr_carver_compute_e(LqrCarver
*r
, gint x
, gint y
)
600 /* removed CANC check for performance reasons */
601 /* LQR_CATCH_CANC (r); */
605 LQR_CATCH(lqr_rwindow_fill(r
->rwindow
, r
, x
, y
));
606 if (r
->bias
!= NULL
) {
607 b_add
= r
->bias
[data
] / r
->w_start
;
609 r
->en
[data
] = r
->nrg(x
, y
, r
->w
, r
->h
, r
->rwindow
, r
->nrg_extra_data
) + b_add
;
614 /* compute auxiliary minpath map
616 * y = 1 : m(x,y) = e(x,y)
617 * y > 1 : m(x,y) = min_{x'=-dx,..,dx} ( m(x-x',y-1) + rig(x') ) + e(x,y)
619 * e(x,y) is the energy at point (x,y)
620 * dx is the max seam step delta_x
621 * rig(x') is the rigidity for step x'
624 lqr_carver_build_mmap(LqrCarver
*r
)
629 gint x1_min
, x1_max
, x1
;
630 gfloat m
, m1
, r_fact
;
635 for (x
= 0; x
< r
->w
; x
++) {
638 assert(r
->vs
[data
] == 0);
639 #endif /* __LQR_DEBUG__ */
640 r
->m
[data
] = r
->en
[data
];
643 /* span all other rows */
644 for (y
= 1; y
< r
->h
; y
++) {
645 for (x
= 0; x
< r
->w
; x
++) {
650 assert(r
->vs
[data
] == 0);
651 #endif /* __LQR_DEBUG__ */
652 /* watch for boundaries */
653 x1_min
= MAX(-x
, -r
->delta_x
);
654 x1_max
= MIN(r
->w
- 1 - x
, r
->delta_x
);
655 if (r
->rigidity_mask
) {
656 r_fact
= r
->rigidity_mask
[data
];
661 /* we use the data_down pointer to be able to
662 * track the seams later (needed for rigidity) */
663 data_down
= r
->raw
[y
- 1][x
+ x1_min
];
664 r
->least
[data
] = data_down
;
666 m
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1_min
];
667 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++) {
668 data_down
= r
->raw
[y
- 1][x
+ x1
];
669 /* find the min among the neighbors
671 m1
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1
];
672 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1))) {
674 r
->least
[data
] = data_down
;
676 /* m = MIN(m, r->m[data_down] + r->rigidity_map[x1]); */
680 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++) {
681 data_down
= r
->raw
[y
- 1][x
+ x1
];
682 /* find the min among the neighbors
684 m1
= r
->m
[data_down
];
685 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1))) {
687 r
->least
[data
] = data_down
;
689 m
= MIN(m
, r
->m
[data_down
]);
694 r
->m
[data
] = r
->en
[data
] + m
;
700 /* compute (vertical) visibility map up to given depth
701 * (it also calls inflate() to add image enlargment information) */
703 lqr_carver_build_vsmap(LqrCarver
*r
, gint depth
)
706 gint lr_switch_interval
= 0;
709 #ifdef __LQR_VERBOSE__
710 printf("[ building visibility map ]\n");
712 #endif /* __LQR_VERBOSE__ */
715 assert(depth
<= r
->w_start
+ 1);
717 #endif /* __LQR_DEBUG__ */
719 /* default behaviour : compute all possible levels
722 depth
= r
->w_start
+ 1;
725 /* here we assume that
726 * lqr_carver_set_width(w_start - max_level + 1);
729 /* left-right switch interval */
730 if (r
->lr_switch_frequency
) {
731 lr_switch_interval
= (depth
- r
->max_level
- 1) / r
->lr_switch_frequency
+ 1;
734 /* cycle over levels */
735 for (l
= r
->max_level
; l
< depth
; l
++) {
738 if ((l
- r
->max_level
+ r
->session_rescale_current
) % r
->session_update_step
== 0) {
739 lqr_progress_update(r
->progress
, (gdouble
) (l
- r
->max_level
+ r
->session_rescale_current
) /
740 (gdouble
) (r
->session_rescale_total
));
744 lqr_carver_debug_check_rows(r
);
745 #endif /* __LQR_DEBUG__ */
747 /* compute vertical seam */
748 lqr_carver_build_vpath(r
);
750 /* update visibility map
751 * (assign level to the seam) */
752 lqr_carver_update_vsmap(r
, l
+ r
->max_level
- 1);
754 /* increase (in)visibility level
755 * (make the last seam invisible) */
759 /* update raw data */
763 /* update the energy */
764 /* LQR_CATCH (lqr_carver_build_emap (r)); */
765 LQR_CATCH(lqr_carver_update_emap(r
));
767 /* recalculate the minpath map */
768 if ((r
->lr_switch_frequency
) && (((l
- r
->max_level
+ lr_switch_interval
/ 2) % lr_switch_interval
) == 0)) {
770 LQR_CATCH(lqr_carver_build_mmap(r
));
772 /* lqr_carver_build_mmap (r); */
773 LQR_CATCH(lqr_carver_update_mmap(r
));
776 /* complete the map (last seam) */
777 lqr_carver_finish_vsmap(r
);
781 /* insert seams for image enlargement */
782 LQR_CATCH(lqr_carver_inflate(r
, depth
- 1));
784 /* reset image size */
785 lqr_carver_set_width(r
, r
->w_start
);
786 /* repeat for auxiliary layers */
787 data_tok
.integer
= r
->w_start
;
788 LQR_CATCH(lqr_carver_list_foreach_recursive(r
->attached_list
, lqr_carver_set_width_attached
, data_tok
));
790 #ifdef __LQR_VERBOSE__
791 printf("[ visibility map OK ]\n");
793 #endif /* __LQR_VERBOSE__ */
798 /* enlarge the image by seam insertion
799 * visibility map is updated and the resulting multisize image
800 * is complete in both directions */
802 lqr_carver_inflate(LqrCarver
*r
, gint l
)
807 void *new_rgb
= NULL
;
810 gfloat
*new_bias
= NULL
;
811 gfloat
*new_rigmask
= NULL
;
813 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
815 #ifdef __LQR_VERBOSE__
816 printf(" [ inflating (active=%i) ]\n", r
->active
);
818 #endif /* __LQR_VERBOSE__ */
821 assert(l
+ 1 > r
->max_level
); /* otherwise is useless */
822 #endif /* __LQR_DEBUG__ */
826 if (r
->root
== NULL
) {
827 prev_state
= g_atomic_int_get(&r
->state
);
828 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_INFLATING
, TRUE
));
831 /* first iterate on attached carvers */
832 data_tok
.integer
= l
;
833 LQR_CATCH(lqr_carver_list_foreach(r
->attached_list
, lqr_carver_inflate_attached
, data_tok
));
835 /* scale to current maximum size
836 * (this is the original size the first time) */
837 lqr_carver_set_width(r
, r
->w0
);
840 w1
= r
->w0
+ l
- r
->max_level
+ 1;
842 /* allocate room for new maps */
843 BUF_TRY_NEW0_RET_LQR(new_rgb
, w1
* r
->h0
* r
->channels
, r
->col_depth
);
845 if (r
->root
== NULL
) {
846 LQR_CATCH_MEM(new_vs
= g_try_new0(gint
, w1
* r
->h0
));
850 LQR_CATCH_MEM(new_bias
= g_try_new0(gfloat
, w1
* r
->h0
));
852 if (r
->rigidity_mask
) {
853 LQR_CATCH_MEM(new_rigmask
= g_try_new(gfloat
, w1
* r
->h0
));
857 /* span the image with a cursor
858 * and build the new image */
859 lqr_cursor_reset(r
->c
);
862 for (z0
= 0; z0
< w1
* r
->h0
; z0
++, lqr_cursor_next(r
->c
)) {
866 /* read visibility */
867 vs
= r
->vs
[r
->c
->now
];
868 if ((vs
!= 0) && (vs
<= l
+ r
->max_level
- 1)
869 && (vs
>= 2 * r
->max_level
- 1)) {
870 /* the point belongs to a previously computed seam
871 * and was not inserted during a previous
872 * inflate() call : insert another seam */
874 /* the new pixel value is equal to the average of its
875 * left and right neighbors */
878 c_left
= lqr_cursor_left(r
->c
);
883 for (k
= 0; k
< r
->channels
; k
++) {
884 switch (r
->col_depth
) {
885 case LQR_COLDEPTH_8I
:
886 tmp_rgb
= (AS_8I(r
->rgb
)[c_left
* r
->channels
+ k
] +
887 AS_8I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
888 AS_8I(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_8i
) (tmp_rgb
+ 0.499999);
890 case LQR_COLDEPTH_16I
:
891 tmp_rgb
= (AS_16I(r
->rgb
)[c_left
* r
->channels
+ k
] +
892 AS_16I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
893 AS_16I(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_16i
) (tmp_rgb
+ 0.499999);
895 case LQR_COLDEPTH_32F
:
896 tmp_rgb
= (AS_32F(r
->rgb
)[c_left
* r
->channels
+ k
] +
897 AS_32F(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
898 AS_32F(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_32f
) tmp_rgb
;
900 case LQR_COLDEPTH_64F
:
901 tmp_rgb
= (AS_64F(r
->rgb
)[c_left
* r
->channels
+ k
] +
902 AS_64F(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
903 AS_64F(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_64f
) tmp_rgb
;
909 new_bias
[z0
] = (r
->bias
[c_left
] + r
->bias
[r
->c
->now
]) / 2;
911 if (r
->rigidity_mask
) {
912 new_rigmask
[z0
] = (r
->rigidity_mask
[c_left
] + r
->rigidity_mask
[r
->c
->now
]) / 2;
915 /* the first time inflate() is called
916 * the new visibility should be -vs + 1 but we shift it
917 * so that the final minimum visibiliy will be 1 again
918 * and so that vs=0 still means "uninitialized".
919 * Subsequent inflations account for that */
920 if (r
->root
== NULL
) {
921 new_vs
[z0
] = l
- vs
+ r
->max_level
;
925 for (k
= 0; k
< r
->channels
; k
++) {
926 PXL_COPY(new_rgb
, z0
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
930 new_bias
[z0
] = r
->bias
[r
->c
->now
];
932 if (r
->rigidity_mask
) {
933 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
937 /* visibility has to be shifted up */
938 if (r
->root
== NULL
) {
939 new_vs
[z0
] = vs
+ l
- r
->max_level
+ 1;
941 } else if (r
->raw
!= NULL
) {
943 assert(y
< r
->h_start
);
944 assert(x
< r
->w_start
- l
);
945 #endif /* __LQR_DEBUG__ */
948 if (x
>= r
->w_start
- l
) {
956 if (r
->raw
!= NULL
) {
958 if (w1
!= 2 * r
->w_start
- 1) {
959 assert((y
== r
->h_start
)
960 || (printf("y=%i hst=%i w1=%i\n", y
, r
->h_start
, w1
)
961 && fflush(stdout
) && 0));
964 #endif /* __LQR_DEBUG__ */
966 /* substitute maps */
967 if (!r
->preserve_in_buffer
) {
970 /* g_free (r->vs); */
976 g_free(r
->rigidity_mask
);
980 r
->nrg_uptodate
= FALSE
;
983 r
->preserve_in_buffer
= FALSE
;
985 if (r
->root
== NULL
) {
988 LQR_CATCH(lqr_carver_propagate_vsmap(r
));
993 LQR_CATCH_MEM(r
->en
= g_try_new0(gfloat
, w1
* r
->h0
));
997 r
->rigidity_mask
= new_rigmask
;
998 LQR_CATCH_MEM(r
->m
= g_try_new0(gfloat
, w1
* r
->h0
));
999 LQR_CATCH_MEM(r
->least
= g_try_new0(gint
, w1
* r
->h0
));
1002 /* set new widths & levels (w_start is kept for reference) */
1004 r
->max_level
= l
+ 1;
1008 /* reset readout buffer */
1009 g_free(r
->rgb_ro_buffer
);
1010 BUF_TRY_NEW0_RET_LQR(r
->rgb_ro_buffer
, r
->w0
* r
->channels
, r
->col_depth
);
1012 #ifdef __LQR_VERBOSE__
1013 printf(" [ inflating OK ]\n");
1015 #endif /* __LQR_VERBOSE__ */
1017 if (r
->root
== NULL
) {
1018 LQR_CATCH(lqr_carver_set_state(r
, prev_state
, TRUE
));
1025 lqr_carver_inflate_attached(LqrCarver
*r
, LqrDataTok data
)
1027 return lqr_carver_inflate(r
, data
.integer
);
1030 /*** internal functions for maps computations ***/
1033 * this actually carves the raw array,
1034 * which holds the indices to be used
1035 * in all the other maps */
1037 lqr_carver_carve(LqrCarver
*r
)
1041 #ifdef __LQR_DEBUG__
1042 assert(r
->root
== NULL
);
1043 #endif /* __LQR_DEBUG__ */
1045 for (y
= 0; y
< r
->h_start
; y
++) {
1046 #ifdef __LQR_DEBUG__
1047 assert(r
->vs
[r
->raw
[y
][r
->vpath_x
[y
]]] != 0);
1048 for (x
= 0; x
< r
->vpath_x
[y
]; x
++) {
1049 assert(r
->vs
[r
->raw
[y
][x
]] == 0);
1051 #endif /* __LQR_DEBUG__ */
1052 for (x
= r
->vpath_x
[y
]; x
< r
->w
; x
++) {
1053 r
->raw
[y
][x
] = r
->raw
[y
][x
+ 1];
1054 #ifdef __LQR_DEBUG__
1055 assert(r
->vs
[r
->raw
[y
][x
]] == 0);
1056 #endif /* __LQR_DEBUG__ */
1060 r
->nrg_uptodate
= FALSE
;
1063 /* update energy map after seam removal */
1065 lqr_carver_update_emap(LqrCarver
*r
)
1068 gint y1
, y1_min
, y1_max
;
1072 if (r
->nrg_uptodate
) {
1075 if (r
->use_rcache
) {
1076 LQR_CATCH_F(r
->rcache
!= NULL
);
1079 for (y
= 0; y
< r
->h
; y
++) {
1080 /* note: here the vpath has already
1084 r
->nrg_xmax
[y
] = x
- 1;
1086 for (y
= 0; y
< r
->h
; y
++) {
1088 y1_min
= MAX(y
- r
->nrg_radius
, 0);
1089 y1_max
= MIN(y
+ r
->nrg_radius
, r
->h
- 1);
1091 for (y1
= y1_min
; y1
<= y1_max
; y1
++) {
1092 r
->nrg_xmin
[y1
] = MIN(r
->nrg_xmin
[y1
], x
- r
->nrg_radius
);
1093 r
->nrg_xmin
[y1
] = MAX(0, r
->nrg_xmin
[y1
]);
1094 /* note: the -1 below is because of the previous carving */
1095 r
->nrg_xmax
[y1
] = MAX(r
->nrg_xmax
[y1
], x
+ r
->nrg_radius
- 1);
1096 r
->nrg_xmax
[y1
] = MIN(r
->w
- 1, r
->nrg_xmax
[y1
]);
1100 for (y
= 0; y
< r
->h
; y
++) {
1103 for (x
= r
->nrg_xmin
[y
]; x
<= r
->nrg_xmax
[y
]; x
++) {
1104 LQR_CATCH(lqr_carver_compute_e(r
, x
, y
));
1108 r
->nrg_uptodate
= TRUE
;
1113 /* update the auxiliary minpath map
1114 * this only updates the affected pixels,
1115 * which start form the beginning of the changed
1116 * energy region around the seam and expand
1117 * at most by delta_x (in both directions)
1120 lqr_carver_update_mmap(LqrCarver
*r
)
1125 gint x1_min
, x1_max
;
1126 gint data
, data_down
, least
;
1127 gfloat m
, m1
, r_fact
;
1134 LQR_CATCH_F(r
->nrg_uptodate
);
1137 LQR_CATCH_MEM(mc
= g_try_new(gfloat
, 2 * r
->delta_x
+ 1));
1141 /* span first row */
1142 /* x_min = MAX (r->vpath_x[0] - r->delta_x, 0); */
1143 x_min
= MAX(r
->nrg_xmin
[0], 0);
1144 /* x_max = MIN (r->vpath_x[0] + r->delta_x - 1, r->w - 1); */
1145 /* x_max = MIN (r->vpath_x[0] + r->delta_x, r->w - 1); */
1146 x_max
= MIN(r
->nrg_xmax
[0], r
->w
- 1);
1148 for (x
= x_min
; x
<= x_max
; x
++) {
1149 data
= r
->raw
[0][x
];
1150 r
->m
[data
] = r
->en
[data
];
1154 for (y
= 1; y
< r
->h
; y
++) {
1157 /* make sure to include the changed energy region */
1158 x_min
= MIN(x_min
, r
->nrg_xmin
[y
]);
1159 x_max
= MAX(x_max
, r
->nrg_xmax
[y
]);
1161 /* expand the affected region by delta_x */
1162 x_min
= MAX(x_min
- r
->delta_x
, 0);
1163 x_max
= MIN(x_max
+ r
->delta_x
, r
->w
- 1);
1165 /* span the affected region */
1168 for (x
= x_min
; x
<= x_max
; x
++) {
1169 data
= r
->raw
[y
][x
];
1170 if (r
->rigidity_mask
) {
1171 r_fact
= r
->rigidity_mask
[data
];
1176 /* find the minimum in the previous rows
1177 * as in build_mmap() */
1178 x1_min
= MAX(0, x
- r
->delta_x
);
1179 x1_max
= MIN(r
->w
- 1, x
+ r
->delta_x
);
1183 switch (x1_max
- x1_min
+ 1) {
1184 UPDATE_MMAP_OPTIMISED_CASES_RIG
1186 data_down
= r
->raw
[y
- 1][x1_min
];
1188 m
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[dx
++];
1189 /* fprintf(stderr, "y,x=%i,%i x1=%i dx=%i mr=%g MR=%g m=%g M=%g\n", y, x, x1_min, dx, m, MRDOWN(y, x1_min, dx), r->m[data_down], MDOWN(y, x1_min)); fflush(stderr); */
1190 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++, dx
++) {
1191 data_down
= r
->raw
[y
- 1][x1
];
1192 m1
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[dx
];
1193 /* fprintf(stderr, "y,x=%i,%i x1=%i dx=%i mr=%g MR=%g m=%g M=%g\n", y, x, x1, dx, m1, MRDOWN(y, x1, dx), r->m[data_down], MDOWN(y, x1)); fflush(stderr); */
1194 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1))) {
1200 /* fprintf(stderr, "y,x=%i,%i x1_min,max=%i,%i least=%i m=%g\n", y, x, x1_min, x1_max, least, m); fflush(stderr); */
1202 switch (x1_max
- x1_min
+ 1) {
1203 UPDATE_MMAP_OPTIMISED_CASES
1205 data_down
= r
->raw
[y
- 1][x1_min
];
1207 m
= r
->m
[data_down
];
1208 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++) {
1209 data_down
= r
->raw
[y
- 1][x1
];
1210 m1
= r
->m
[data_down
];
1211 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1))) {
1217 /* fprintf(stderr, "y,x=%i,%i x1_min,max=%i,%i least=%i m=%g\n", y, x, x1_min, x1_max, least, m); fflush(stderr); */
1220 new_m
= r
->en
[data
] + m
;
1222 /* reduce the range if there's no (relevant) difference
1223 * with the previous map */
1224 if (r
->least
[data
] == least
) {
1225 if (fabsf(r
->m
[data
] - new_m
) < UPDATE_TOLERANCE
) {
1235 if ((x
== x_min
) && stop
) {
1243 r
->least
[data
] = least
;
1245 if ((x
== x_max
) && (stop
)) {
1260 /* compute seam path from minpath map */
1262 lqr_carver_build_vpath(LqrCarver
*r
)
1270 /* we start at last row */
1273 /* span the last row for the minimum mmap value */
1275 for (x
= 0, z0
= y
* r
->w_start
; x
< r
->w
; x
++, z0
++) {
1276 #ifdef __LQR_DEBUG__
1277 assert(r
->vs
[r
->raw
[y
][x
]] == 0);
1278 #endif /* __LQR_DEBUG__ */
1280 m1
= r
->m
[r
->raw
[y
][x
]];
1281 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1))) {
1282 last
= r
->raw
[y
][x
];
1288 #ifdef __LQR_DEBUG__
1290 #endif /* __LQR_DEBUG__ */
1292 /* follow the track for the other rows */
1293 for (y
= r
->h0
- 1; y
>= 0; y
--) {
1294 #ifdef __LQR_DEBUG__
1295 assert(r
->vs
[last
] == 0);
1296 assert(last_x
< r
->w
);
1297 #endif /* __LQR_DEBUG__ */
1299 r
->vpath_x
[y
] = last_x
;
1301 last
= r
->least
[r
->raw
[y
][last_x
]];
1302 /* we also need to retrieve the x coordinate */
1303 x_min
= MAX(last_x
- r
->delta_x
, 0);
1304 x_max
= MIN(last_x
+ r
->delta_x
, r
->w
- 1);
1305 for (x
= x_min
; x
<= x_max
; x
++) {
1306 if (r
->raw
[y
- 1][x
] == last
) {
1311 #ifdef __LQR_DEBUG__
1312 assert(x
< x_max
+ 1);
1313 #endif /* __LQR_DEBUG__ */
1318 /* we backtrack the seam following the min mmap */
1319 for (y
= r
->h0
- 1; y
>= 0; y
--) {
1320 #ifdef __LQR_DEBUG__
1321 assert(r
->vs
[last
] == 0);
1322 assert(last_x
< r
->w
);
1323 #endif /* __LQR_DEBUG__ */
1326 r
->vpath_x
[y
] = last_x
;
1329 x_min
= MAX(0, last_x
- r
->delta_x
);
1330 x_max
= MIN(r
->w
- 1, last_x
+ r
->delta_x
);
1331 for (x
= x_min
; x
<= x_max
; x
++) {
1332 m1
= r
->m
[r
->raw
[y
- 1][x
]];
1334 last
= r
->raw
[y
- 1][x
];
1344 /* update visibility map after seam computation */
1346 lqr_carver_update_vsmap(LqrCarver
*r
, gint l
)
1349 #ifdef __LQR_DEBUG__
1350 assert(r
->root
== NULL
);
1351 #endif /* __LQR_DEBUG__ */
1352 for (y
= 0; y
< r
->h
; y
++) {
1353 #ifdef __LQR_DEBUG__
1354 assert(r
->vs
[r
->vpath
[y
]] == 0);
1355 assert(r
->vpath
[y
] == r
->raw
[y
][r
->vpath_x
[y
]]);
1356 #endif /* __LQR_DEBUG__ */
1357 r
->vs
[r
->vpath
[y
]] = l
;
1361 /* complete visibility map (last seam) */
1362 /* set the last column of pixels to vis. level w0 */
1364 lqr_carver_finish_vsmap(LqrCarver
*r
)
1368 #ifdef __LQR_DEBUG__
1370 assert(r
->root
== NULL
);
1371 #endif /* __LQR_DEBUG__ */
1372 lqr_cursor_reset(r
->c
);
1373 for (y
= 1; y
<= r
->h
; y
++, lqr_cursor_next(r
->c
)) {
1374 #ifdef __LQR_DEBUG__
1375 assert(r
->vs
[r
->c
->now
] == 0);
1376 #endif /* __LQR_DEBUG__ */
1377 r
->vs
[r
->c
->now
] = r
->w0
;
1379 lqr_cursor_reset(r
->c
);
1382 /* propagate the root carver's visibility map */
1384 lqr_carver_propagate_vsmap(LqrCarver
*r
)
1386 LqrDataTok data_tok
;
1390 data_tok
.data
= NULL
;
1391 LQR_CATCH(lqr_carver_list_foreach_recursive(r
->attached_list
, lqr_carver_propagate_vsmap_attached
, data_tok
));
1396 lqr_carver_propagate_vsmap_attached(LqrCarver
*r
, LqrDataTok data
)
1398 /* LqrDataTok data_tok;
1399 data_tok.data = NULL; */
1400 r
->vs
= r
->root
->vs
;
1401 lqr_carver_scan_reset(r
);
1402 /* LQR_CATCH (lqr_carver_list_foreach (r->attached_list, lqr_carver_propagate_vsmap_attached, data_tok)); */
1406 /*** image manipulations ***/
1408 /* set width of the multisize image
1409 * (maps have to be computed already) */
1411 lqr_carver_set_width(LqrCarver
*r
, gint w1
)
1413 #ifdef __LQR_DEBUG__
1414 assert(w1
<= r
->w0
);
1415 assert(w1
>= r
->w_start
- r
->max_level
+ 1);
1416 #endif /* __LQR_DEBUG__ */
1418 r
->level
= r
->w0
- w1
+ 1;
1422 lqr_carver_set_width_attached(LqrCarver
*r
, LqrDataTok data
)
1424 lqr_carver_set_width(r
, data
.integer
);
1428 /* flatten the image to its current state
1429 * (all maps are reset, invisible points are lost) */
1432 lqr_carver_flatten(LqrCarver
*r
)
1434 void *new_rgb
= NULL
;
1435 gfloat
*new_bias
= NULL
;
1436 gfloat
*new_rigmask
= NULL
;
1439 LqrDataTok data_tok
;
1440 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
1442 #ifdef __LQR_VERBOSE__
1443 printf(" [ flattening (active=%i) ]\n", r
->active
);
1445 #endif /* __LQR_VERBOSE__ */
1449 if (r
->root
== NULL
) {
1450 prev_state
= g_atomic_int_get(&r
->state
);
1451 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_FLATTENING
, TRUE
));
1454 /* first iterate on attached carvers */
1455 data_tok
.data
= NULL
;
1456 LQR_CATCH(lqr_carver_list_foreach(r
->attached_list
, lqr_carver_flatten_attached
, data_tok
));
1458 /* free non needed maps first */
1465 r
->nrg_uptodate
= FALSE
;
1467 /* allocate room for new map */
1468 BUF_TRY_NEW0_RET_LQR(new_rgb
, r
->w
* r
->h
* r
->channels
, r
->col_depth
);
1471 if (r
->rigidity_mask
) {
1472 LQR_CATCH_MEM(new_rigmask
= g_try_new(gfloat
, r
->w
* r
->h
));
1475 if (r
->nrg_active
) {
1477 LQR_CATCH_MEM(new_bias
= g_try_new0(gfloat
, r
->w
* r
->h
));
1481 LQR_CATCH_MEM(r
->_raw
= g_try_new(gint
, r
->w
* r
->h
));
1482 LQR_CATCH_MEM(r
->raw
= g_try_new(gint
*, r
->h
));
1485 /* span the image with the cursor and copy
1486 * it in the new array */
1487 lqr_cursor_reset(r
->c
);
1488 for (y
= 0; y
< r
->h
; y
++) {
1491 if (r
->nrg_active
) {
1492 r
->raw
[y
] = r
->_raw
+ y
* r
->w
;
1494 for (x
= 0; x
< r
->w
; x
++) {
1496 for (k
= 0; k
< r
->channels
; k
++) {
1497 PXL_COPY(new_rgb
, z0
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
1500 if (r
->rigidity_mask
) {
1501 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
1504 if (r
->nrg_active
) {
1506 new_bias
[z0
] = r
->bias
[r
->c
->now
];
1510 lqr_cursor_next(r
->c
);
1514 /* substitute the old maps */
1515 if (!r
->preserve_in_buffer
) {
1519 r
->preserve_in_buffer
= FALSE
;
1520 if (r
->nrg_active
) {
1525 g_free(r
->rigidity_mask
);
1526 r
->rigidity_mask
= new_rigmask
;
1529 /* init the other maps */
1530 if (r
->root
== NULL
) {
1532 LQR_CATCH_MEM(r
->vs
= g_try_new0(gint
, r
->w
* r
->h
));
1533 LQR_CATCH(lqr_carver_propagate_vsmap(r
));
1535 if (r
->nrg_active
) {
1536 LQR_CATCH_MEM(r
->en
= g_try_new0(gfloat
, r
->w
* r
->h
));
1539 LQR_CATCH_MEM(r
->m
= g_try_new0(gfloat
, r
->w
* r
->h
));
1540 LQR_CATCH_MEM(r
->least
= g_try_new(gint
, r
->w
* r
->h
));
1543 /* reset widths, heights & levels */
1551 #ifdef __LQR_VERBOSE__
1552 printf(" [ flattening OK ]\n");
1554 #endif /* __LQR_VERBOSE__ */
1556 if (r
->root
== NULL
) {
1557 LQR_CATCH(lqr_carver_set_state(r
, prev_state
, TRUE
));
1564 lqr_carver_flatten_attached(LqrCarver
*r
, LqrDataTok data
)
1566 return lqr_carver_flatten(r
);
1569 /* transpose the image, in its current state
1570 * (all maps and invisible points are lost) */
1572 lqr_carver_transpose(LqrCarver
*r
)
1577 void *new_rgb
= NULL
;
1578 gfloat
*new_bias
= NULL
;
1579 gfloat
*new_rigmask
= NULL
;
1580 LqrDataTok data_tok
;
1581 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
1583 #ifdef __LQR_VERBOSE__
1584 printf("[ transposing (active=%i) ]\n", r
->active
);
1586 #endif /* __LQR_VERBOSE__ */
1590 if (r
->root
== NULL
) {
1591 prev_state
= g_atomic_int_get(&r
->state
);
1592 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_TRANSPOSING
, TRUE
));
1596 LQR_CATCH(lqr_carver_flatten(r
));
1599 /* first iterate on attached carvers */
1600 data_tok
.data
= NULL
;
1601 LQR_CATCH(lqr_carver_list_foreach(r
->attached_list
, lqr_carver_transpose_attached
, data_tok
));
1603 /* free non needed maps first */
1604 if (r
->root
== NULL
) {
1611 g_free(r
->rgb_ro_buffer
);
1614 r
->nrg_uptodate
= FALSE
;
1616 /* allocate room for the new maps */
1617 BUF_TRY_NEW0_RET_LQR(new_rgb
, r
->w0
* r
->h0
* r
->channels
, r
->col_depth
);
1620 if (r
->rigidity_mask
) {
1621 LQR_CATCH_MEM(new_rigmask
= g_try_new(gfloat
, r
->w0
* r
->h0
));
1624 if (r
->nrg_active
) {
1626 LQR_CATCH_MEM(new_bias
= g_try_new0(gfloat
, r
->w0
* r
->h0
));
1630 LQR_CATCH_MEM(r
->_raw
= g_try_new0(gint
, r
->h0
* r
->w0
));
1631 LQR_CATCH_MEM(r
->raw
= g_try_new0(gint
*, r
->w0
));
1634 /* compute trasposed maps */
1635 for (x
= 0; x
< r
->w
; x
++) {
1636 if (r
->nrg_active
) {
1637 r
->raw
[x
] = r
->_raw
+ x
* r
->h0
;
1639 for (y
= 0; y
< r
->h
; y
++) {
1642 for (k
= 0; k
< r
->channels
; k
++) {
1643 PXL_COPY(new_rgb
, z1
* r
->channels
+ k
, r
->rgb
, z0
* r
->channels
+ k
, r
->col_depth
);
1646 if (r
->rigidity_mask
) {
1647 new_rigmask
[z1
] = r
->rigidity_mask
[z0
];
1650 if (r
->nrg_active
) {
1652 new_bias
[z1
] = r
->bias
[z0
];
1659 /* substitute the map */
1660 if (!r
->preserve_in_buffer
) {
1664 r
->preserve_in_buffer
= FALSE
;
1666 if (r
->nrg_active
) {
1671 g_free(r
->rigidity_mask
);
1672 r
->rigidity_mask
= new_rigmask
;
1675 /* init the other maps */
1676 if (r
->root
== NULL
) {
1677 LQR_CATCH_MEM(r
->vs
= g_try_new0(gint
, r
->w0
* r
->h0
));
1678 LQR_CATCH(lqr_carver_propagate_vsmap(r
));
1680 if (r
->nrg_active
) {
1681 LQR_CATCH_MEM(r
->en
= g_try_new0(gfloat
, r
->w0
* r
->h0
));
1684 LQR_CATCH_MEM(r
->m
= g_try_new0(gfloat
, r
->w0
* r
->h0
));
1685 LQR_CATCH_MEM(r
->least
= g_try_new(gint
, r
->w0
* r
->h0
));
1688 /* switch widths & heights */
1695 /* reset w_start, h_start & levels */
1701 /* reset seam path, cursor and readout buffer */
1704 LQR_CATCH_MEM(r
->vpath
= g_try_new(gint
, r
->h
));
1706 LQR_CATCH_MEM(r
->vpath_x
= g_try_new(gint
, r
->h
));
1707 g_free(r
->nrg_xmin
);
1708 LQR_CATCH_MEM(r
->nrg_xmin
= g_try_new(gint
, r
->h
));
1709 g_free(r
->nrg_xmax
);
1710 LQR_CATCH_MEM(r
->nrg_xmax
= g_try_new(gint
, r
->h
));
1713 BUF_TRY_NEW0_RET_LQR(r
->rgb_ro_buffer
, r
->w0
* r
->channels
, r
->col_depth
);
1715 /* rescale rigidity */
1718 for (x
= -r
->delta_x
; x
<= r
->delta_x
; x
++) {
1719 r
->rigidity_map
[x
] = r
->rigidity_map
[x
] * r
->w0
/ r
->h0
;
1723 /* set transposed flag */
1724 r
->transposed
= (r
->transposed
? 0 : 1);
1726 #ifdef __LQR_VERBOSE__
1727 printf("[ transpose OK ]\n");
1729 #endif /* __LQR_VERBOSE__ */
1731 if (r
->root
== NULL
) {
1732 LQR_CATCH(lqr_carver_set_state(r
, prev_state
, TRUE
));
1739 lqr_carver_transpose_attached(LqrCarver
*r
, LqrDataTok data
)
1741 return lqr_carver_transpose(r
);
1744 /* resize w + h: these are the liquid rescale methods.
1745 * They automatically determine the depth of the map
1746 * according to the desired size, can be called multiple
1747 * times, transpose the image as necessasry */
1749 lqr_carver_resize_width(LqrCarver
*r
, gint w1
)
1751 LqrDataTok data_tok
;
1754 /* delta is used to determine the required depth
1755 * gamma to decide if action is necessary */
1756 if (!r
->transposed
) {
1757 delta
= w1
- r
->w_start
;
1759 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1761 delta
= w1
- r
->h_start
;
1763 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
1765 if (delta_max
< 1) {
1774 LQR_CATCH_F(g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
1775 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_RESIZING
, TRUE
));
1777 /* update step for progress reprt */
1778 r
->session_rescale_total
= gamma
> 0 ? gamma
: -gamma
;
1779 r
->session_rescale_current
= 0;
1780 r
->session_update_step
= (gint
) MAX(r
->session_rescale_total
* r
->progress
->update_step
, 1);
1782 if (r
->session_rescale_total
) {
1783 lqr_progress_init(r
->progress
, r
->progress
->init_width_message
);
1787 gint delta0
= MIN(delta
, delta_max
);
1791 if (r
->transposed
) {
1792 LQR_CATCH(lqr_carver_transpose(r
));
1794 new_w
= MIN(w1
, r
->w_start
+ delta_max
);
1796 LQR_CATCH(lqr_carver_build_maps(r
, delta0
+ 1));
1797 lqr_carver_set_width(r
, new_w
);
1799 data_tok
.integer
= new_w
;
1800 lqr_carver_list_foreach_recursive(r
->attached_list
, lqr_carver_set_width_attached
, data_tok
);
1802 r
->session_rescale_current
= r
->session_rescale_total
- (gamma
> 0 ? gamma
: -gamma
);
1804 if (r
->dump_vmaps
) {
1805 LQR_CATCH(lqr_vmap_internal_dump(r
));
1808 LQR_CATCH(lqr_carver_flatten(r
));
1809 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1810 if (delta_max
< 1) {
1816 if (r
->session_rescale_total
) {
1817 lqr_progress_end(r
->progress
, r
->progress
->end_width_message
);
1820 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_STD
, TRUE
));
1826 lqr_carver_resize_height(LqrCarver
*r
, gint h1
)
1828 LqrDataTok data_tok
;
1831 /* delta is used to determine the required depth
1832 * gamma to decide if action is necessary */
1833 if (!r
->transposed
) {
1834 delta
= h1
- r
->h_start
;
1836 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
1838 delta
= h1
- r
->w_start
;
1840 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1842 if (delta_max
< 1) {
1848 delta
= delta
> 0 ? delta
: -delta
;
1851 LQR_CATCH_F(g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
1852 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_RESIZING
, TRUE
));
1854 /* update step for progress reprt */
1855 r
->session_rescale_total
= gamma
> 0 ? gamma
: -gamma
;
1856 r
->session_rescale_current
= 0;
1857 r
->session_update_step
= (gint
) MAX(r
->session_rescale_total
* r
->progress
->update_step
, 1);
1859 if (r
->session_rescale_total
) {
1860 lqr_progress_init(r
->progress
, r
->progress
->init_height_message
);
1864 gint delta0
= MIN(delta
, delta_max
);
1867 if (!r
->transposed
) {
1868 LQR_CATCH(lqr_carver_transpose(r
));
1870 new_w
= MIN(h1
, r
->w_start
+ delta_max
);
1872 LQR_CATCH(lqr_carver_build_maps(r
, delta0
+ 1));
1873 lqr_carver_set_width(r
, new_w
);
1875 data_tok
.integer
= new_w
;
1876 lqr_carver_list_foreach_recursive(r
->attached_list
, lqr_carver_set_width_attached
, data_tok
);
1878 r
->session_rescale_current
= r
->session_rescale_total
- (gamma
> 0 ? gamma
: -gamma
);
1880 if (r
->dump_vmaps
) {
1881 LQR_CATCH(lqr_vmap_internal_dump(r
));
1884 LQR_CATCH(lqr_carver_flatten(r
));
1885 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1886 if (delta_max
< 1) {
1892 if (r
->session_rescale_total
) {
1893 lqr_progress_end(r
->progress
, r
->progress
->end_height_message
);
1896 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_STD
, TRUE
));
1901 /* liquid rescale public method */
1904 lqr_carver_resize(LqrCarver
*r
, gint w1
, gint h1
)
1906 #ifdef __LQR_VERBOSE__
1907 printf("[ Rescale from %i,%i to %i,%i ]\n", (r
->transposed
? r
->h
: r
->w
), (r
->transposed
? r
->w
: r
->h
), w1
, h1
);
1909 #endif /* __LQR_VERBOSE__ */
1910 LQR_CATCH_F((w1
>= 1) && (h1
>= 1));
1911 LQR_CATCH_F(r
->root
== NULL
);
1914 LQR_CATCH_F(g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
1916 switch (r
->resize_order
) {
1917 case LQR_RES_ORDER_HOR
:
1918 LQR_CATCH(lqr_carver_resize_width(r
, w1
));
1919 LQR_CATCH(lqr_carver_resize_height(r
, h1
));
1921 case LQR_RES_ORDER_VERT
:
1922 LQR_CATCH(lqr_carver_resize_height(r
, h1
));
1923 LQR_CATCH(lqr_carver_resize_width(r
, w1
));
1925 #ifdef __LQR_DEBUG__
1928 #endif /* __LQR_DEBUG__ */
1930 lqr_carver_scan_reset_all(r
);
1932 #ifdef __LQR_VERBOSE__
1933 printf("[ Rescale OK ]\n");
1935 #endif /* __LQR_VERBOSE__ */
1940 lqr_carver_set_state(LqrCarver
*r
, LqrCarverState state
, gboolean skip_canceled
)
1942 LqrDataTok data_tok
;
1945 LQR_CATCH_F(r
->root
== NULL
);
1947 #if (GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 30)
1948 lock_pos
= g_atomic_int_exchange_and_add(&r
->state_lock_queue
, 1);
1950 lock_pos
= g_atomic_int_add(&r
->state_lock_queue
, 1);
1951 #endif /* GLIB_VERSION < 2.30 */
1953 while (g_atomic_int_get(&r
->state_lock
) != lock_pos
) {
1957 if (skip_canceled
&& g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_CANCELLED
) {
1958 g_atomic_int_inc(&r
->state_lock
);
1962 g_atomic_int_set(&r
->state
, state
);
1964 data_tok
.integer
= state
;
1965 LQR_CATCH(lqr_carver_list_foreach_recursive(r
->attached_list
, lqr_carver_set_state_attached
, data_tok
));
1967 g_atomic_int_inc(&r
->state_lock
);
1973 lqr_carver_set_state_attached(LqrCarver
*r
, LqrDataTok data
)
1975 g_atomic_int_set(&r
->state
, data
.integer
);
1979 /* cancel the current action from a different thread */
1982 lqr_carver_cancel(LqrCarver
*r
)
1984 LqrCarverState curr_state
;
1986 LQR_CATCH_F(r
->root
== NULL
);
1988 curr_state
= g_atomic_int_get(&r
->state
);
1990 if ((curr_state
== LQR_CARVER_STATE_RESIZING
) ||
1991 (curr_state
== LQR_CARVER_STATE_INFLATING
) ||
1992 (curr_state
== LQR_CARVER_STATE_TRANSPOSING
) || (curr_state
== LQR_CARVER_STATE_FLATTENING
)) {
1993 LQR_CATCH(lqr_carver_set_state(r
, LQR_CARVER_STATE_CANCELLED
, TRUE
));
1998 /* get current size */
2001 lqr_carver_get_width(LqrCarver
*r
)
2003 return (r
->transposed
? r
->h
: r
->w
);
2008 lqr_carver_get_height(LqrCarver
*r
)
2010 return (r
->transposed
? r
->w
: r
->h
);
2013 /* get reference size */
2016 lqr_carver_get_ref_width(LqrCarver
*r
)
2018 return (r
->transposed
? r
->h_start
: r
->w_start
);
2023 lqr_carver_get_ref_height(LqrCarver
*r
)
2025 return (r
->transposed
? r
->w_start
: r
->h_start
);
2028 /* get colour channels */
2031 lqr_carver_get_channels(LqrCarver
*r
)
2038 lqr_carver_get_bpp(LqrCarver
*r
)
2040 return lqr_carver_get_channels(r
);
2043 /* get colour depth */
2046 lqr_carver_get_col_depth(LqrCarver
*r
)
2048 return r
->col_depth
;
2051 /* get image type */
2054 lqr_carver_get_image_type(LqrCarver
*r
)
2056 return r
->image_type
;
2059 /* get enlargement step */
2062 lqr_carver_get_enl_step(LqrCarver
*r
)
2067 /* get orientation */
2070 lqr_carver_get_orientation(LqrCarver
*r
)
2072 return (r
->transposed
? 1 : 0);
2078 lqr_carver_get_depth(LqrCarver
*r
)
2080 return r
->w0
- r
->w_start
;
2086 lqr_carver_scan_reset(LqrCarver
*r
)
2088 lqr_cursor_reset(r
->c
);
2092 lqr_carver_scan_reset_attached(LqrCarver
*r
, LqrDataTok data
)
2094 lqr_carver_scan_reset(r
);
2095 return lqr_carver_list_foreach(r
->attached_list
, lqr_carver_scan_reset_attached
, data
);
2099 lqr_carver_scan_reset_all(LqrCarver
*r
)
2103 lqr_carver_scan_reset(r
);
2104 lqr_carver_list_foreach(r
->attached_list
, lqr_carver_scan_reset_attached
, data
);
2107 /* readout all, pixel by bixel */
2110 lqr_carver_scan(LqrCarver
*r
, gint
*x
, gint
*y
, guchar
**rgb
)
2113 if (r
->col_depth
!= LQR_COLDEPTH_8I
) {
2117 lqr_carver_scan_reset(r
);
2120 (*x
) = (r
->transposed
? r
->c
->y
: r
->c
->x
);
2121 (*y
) = (r
->transposed
? r
->c
->x
: r
->c
->y
);
2122 for (k
= 0; k
< r
->channels
; k
++) {
2123 AS_8I(r
->rgb_ro_buffer
)[k
] = AS_8I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
];
2125 (*rgb
) = AS_8I(r
->rgb_ro_buffer
);
2126 lqr_cursor_next(r
->c
);
2132 lqr_carver_scan_ext(LqrCarver
*r
, gint
*x
, gint
*y
, void **rgb
)
2136 lqr_carver_scan_reset(r
);
2139 (*x
) = (r
->transposed
? r
->c
->y
: r
->c
->x
);
2140 (*y
) = (r
->transposed
? r
->c
->x
: r
->c
->y
);
2141 for (k
= 0; k
< r
->channels
; k
++) {
2142 PXL_COPY(r
->rgb_ro_buffer
, k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
2145 BUF_POINTER_COPY(rgb
, r
->rgb_ro_buffer
, r
->col_depth
);
2147 lqr_cursor_next(r
->c
);
2151 /* readout all, by line */
2154 lqr_carver_scan_by_row(LqrCarver
*r
)
2156 return r
->transposed
? FALSE
: TRUE
;
2161 lqr_carver_scan_line(LqrCarver
*r
, gint
*n
, guchar
**rgb
)
2163 if (r
->col_depth
!= LQR_COLDEPTH_8I
) {
2166 return lqr_carver_scan_line_ext(r
, n
, (void **) rgb
);
2171 lqr_carver_scan_line_ext(LqrCarver
*r
, gint
*n
, void **rgb
)
2175 lqr_carver_scan_reset(r
);
2181 lqr_cursor_prev(r
->c
);
2184 for (x
= 0; x
< r
->w
; x
++) {
2185 for (k
= 0; k
< r
->channels
; k
++) {
2186 PXL_COPY(r
->rgb_ro_buffer
, x
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
2188 lqr_cursor_next(r
->c
);
2191 BUF_POINTER_COPY(rgb
, r
->rgb_ro_buffer
, r
->col_depth
);
2196 #ifdef __LQR_DEBUG__
2198 lqr_carver_debug_check_rows(LqrCarver
*r
)
2202 for (y
= 0; y
< r
->h
; y
++) {
2203 for (x
= 0; x
< r
->w
; x
++) {
2204 data
= r
->raw
[y
][x
];
2205 if (data
/ r
->w0
!= y
) {
2208 assert(data
/ r
->w0
== y
);
2212 #endif /* __LQR_DEBUG__ */
2214 /**** END OF LQR_CARVER CLASS FUNCTIONS ****/