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/>
25 #include <lqr/lqr_all.h>
27 #ifdef __LQR_VERBOSE__
29 #endif /* __LQR_VERBOSE__ */
34 #endif /* __LQR_DEBUG__ */
36 /**** LQR_CARVER CLASS FUNCTIONS ****/
38 /*** constructor & destructor ***/
41 LqrCarver
* lqr_carver_new_common (gint width
, gint height
, gint channels
)
45 TRY_N_N (r
= g_try_new (LqrCarver
, 1));
47 g_atomic_int_set(&r
->state
, LQR_CARVER_STATE_STD
);
48 g_atomic_int_set(&r
->state_lock
, 0);
49 g_atomic_int_set(&r
->state_lock_queue
, 0);
55 r
->nrg_active
= FALSE
;
58 r
->resize_aux_layers
= FALSE
;
59 r
->dump_vmaps
= FALSE
;
60 r
->resize_order
= LQR_RES_ORDER_HOR
;
61 r
->attached_list
= NULL
;
63 r
->preserve_in_buffer
= FALSE
;
64 TRY_N_N (r
->progress
= lqr_progress_new());
65 r
->session_update_step
= 1;
66 r
->session_rescale_total
= 0;
67 r
->session_rescale_current
= 0;
77 r
->rigidity_map
= NULL
;
78 r
->rigidity_mask
= NULL
;
83 r
->channels
= channels
;
94 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_XABS
);
97 r
->nrg_uptodate
= FALSE
;
100 r
->lr_switch_frequency
= 0;
104 TRY_N_N (r
->vs
= g_try_new0 (gint
, r
->w
* r
->h
));
106 /* initialize cursor */
108 TRY_N_N (r
->c
= lqr_cursor_create (r
));
113 lqr_carver_set_image_type (r
, LQR_GREY_IMAGE
);
116 lqr_carver_set_image_type (r
, LQR_GREYA_IMAGE
);
119 lqr_carver_set_image_type (r
, LQR_RGB_IMAGE
);
122 lqr_carver_set_image_type (r
, LQR_RGBA_IMAGE
);
125 lqr_carver_set_image_type (r
, LQR_CMYKA_IMAGE
);
128 lqr_carver_set_image_type (r
, LQR_CUSTOM_IMAGE
);
137 lqr_carver_new (guchar
* buffer
, gint width
, gint height
, gint channels
)
139 return lqr_carver_new_ext (buffer
, width
, height
, channels
, LQR_COLDEPTH_8I
);
144 lqr_carver_new_ext (void * buffer
, gint width
, gint height
, gint channels
, LqrColDepth colour_depth
)
148 TRY_N_N (r
= lqr_carver_new_common (width
, height
, channels
));
150 r
->rgb
= (void*) buffer
;
152 BUF_TRY_NEW_RET_POINTER(r
->rgb_ro_buffer
, r
->channels
* r
->w
, colour_depth
);
154 r
->col_depth
= colour_depth
;
162 lqr_carver_destroy (LqrCarver
* r
)
164 if (!r
->preserve_in_buffer
)
177 lqr_cursor_destroy (r
->c
);
180 if (r
->rigidity_map
!= NULL
)
182 r
->rigidity_map
-= r
->delta_x
;
183 g_free (r
->rigidity_map
);
185 g_free (r
->rigidity_mask
);
186 lqr_rwindow_destroy (r
->rwindow
);
187 g_free (r
->nrg_xmin
);
188 g_free (r
->nrg_xmax
);
189 lqr_vmap_list_destroy(r
->flushed_vs
);
190 lqr_carver_list_destroy(r
->attached_list
);
191 g_free (r
->progress
);
197 /*** initialization ***/
200 lqr_carver_init_energy_related (LqrCarver
*r
)
204 CATCH_F (r
->active
== FALSE
);
205 CATCH_F (r
->nrg_active
== FALSE
);
207 CATCH_MEM (r
->en
= g_try_new (gfloat
, r
->w
* r
->h
));
208 CATCH_MEM (r
->_raw
= g_try_new (gint
, r
->h_start
* r
->w_start
));
209 CATCH_MEM (r
->raw
= g_try_new (gint
*, r
->h_start
));
211 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
++)
216 r
->raw
[y
][x
] = y
* r
->w_start
+ x
;
220 r
->nrg_active
= TRUE
;
228 lqr_carver_init (LqrCarver
*r
, gint delta_x
, gfloat rigidity
)
234 CATCH_F (r
->active
== FALSE
);
236 if (r
->nrg_active
== FALSE
)
238 CATCH (lqr_carver_init_energy_related (r
));
241 /* CATCH_MEM (r->bias = g_try_new0 (gfloat, r->w * r->h)); */
242 CATCH_MEM (r
->m
= g_try_new (gfloat
, r
->w
* r
->h
));
243 CATCH_MEM (r
->least
= g_try_new (gint
, r
->w
* r
->h
));
245 CATCH_MEM (r
->vpath
= g_try_new (gint
, r
->h
));
246 CATCH_MEM (r
->vpath_x
= g_try_new (gint
, r
->h
));
248 CATCH_MEM (r
->nrg_xmin
= g_try_new (gint
, r
->h
));
249 CATCH_MEM (r
->nrg_xmax
= g_try_new (gint
, r
->h
));
251 /* set rigidity map */
252 r
->delta_x
= delta_x
;
253 r
->rigidity
= rigidity
;
255 r
->rigidity_map
= g_try_new0 (gfloat
, 2 * r
->delta_x
+ 1);
256 r
->rigidity_map
+= r
->delta_x
;
257 for (x
= -r
->delta_x
; x
<= r
->delta_x
; x
++)
260 r
->rigidity
* powf(fabsf(x
), 1.5) / r
->h
;
268 /*** set attributes ***/
272 lqr_carver_set_image_type (LqrCarver
* r
, LqrImageType image_type
)
276 switch (image_type
) {
278 if (r
->channels
!= 1) {
281 r
->alpha_channel
= -1;
282 r
->black_channel
= -1;
284 case LQR_GREYA_IMAGE
:
285 if (r
->channels
!= 2)
289 r
->alpha_channel
= 1;
290 r
->black_channel
= -1;
294 if (r
->channels
!= 3)
298 r
->alpha_channel
= -1;
299 r
->black_channel
= -1;
302 if (r
->channels
!= 4)
306 r
->alpha_channel
= -1;
307 r
->black_channel
= 3;
310 if (r
->channels
!= 4)
314 r
->alpha_channel
= 3;
315 r
->black_channel
= -1;
317 case LQR_CMYKA_IMAGE
:
318 if (r
->channels
!= 5)
322 r
->alpha_channel
= 4;
323 r
->black_channel
= 3;
325 case LQR_CUSTOM_IMAGE
:
326 r
->alpha_channel
= r
->channels
- 1;
327 r
->black_channel
= -1;
332 r
->image_type
= image_type
;
336 r
->nrg_uptodate
= FALSE
;
343 lqr_carver_set_alpha_channel (LqrCarver
* r
, gint channel_index
)
347 if (channel_index
< 0) {
348 r
->alpha_channel
= -1;
349 } else if (channel_index
< r
->channels
) {
350 r
->alpha_channel
= channel_index
;
354 r
->image_type
= LQR_CUSTOM_IMAGE
;
358 r
->nrg_uptodate
= FALSE
;
365 lqr_carver_set_black_channel (LqrCarver
* r
, gint channel_index
)
369 if (channel_index
< 0) {
370 r
->black_channel
= -1;
371 } else if (channel_index
< r
->channels
) {
372 r
->black_channel
= channel_index
;
376 r
->image_type
= LQR_CUSTOM_IMAGE
;
380 r
->nrg_uptodate
= FALSE
;
385 /* set gradient function */
386 /* WARNING: THIS FUNCTION IS ONLY MAINTAINED FOR BACK-COMPATIBILITY PURPOSES */
387 /* lqr_carver_set_energy_function_builtin() should be used in newly written code instead */
390 lqr_carver_set_gradient_function (LqrCarver
* r
, LqrGradFuncType gf_ind
)
395 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_NORM
);
398 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_SUMABS
);
401 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_XABS
);
404 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
406 case LQR_GF_NORM_BIAS
:
408 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
413 #endif /* __LQR_DEBUG__ */
417 /* attach carvers to be scaled along with the main one */
420 lqr_carver_attach (LqrCarver
* r
, LqrCarver
* aux
)
422 CATCH_F (r
->w0
== aux
->w0
);
423 CATCH_F (r
->h0
== aux
->h0
);
424 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
425 CATCH_F (g_atomic_int_get(&aux
->state
) == LQR_CARVER_STATE_STD
);
426 CATCH_MEM (r
->attached_list
= lqr_carver_list_append (r
->attached_list
, aux
));
434 /* set the seam output flag */
437 lqr_carver_set_dump_vmaps (LqrCarver
*r
)
439 r
->dump_vmaps
= TRUE
;
442 /* unset the seam output flag */
445 lqr_carver_set_no_dump_vmaps (LqrCarver
*r
)
447 r
->dump_vmaps
= FALSE
;
450 /* set order if rescaling in both directions */
453 lqr_carver_set_resize_order (LqrCarver
*r
, LqrResizeOrder resize_order
)
455 r
->resize_order
= resize_order
;
458 /* set leftright switch interval */
461 lqr_carver_set_side_switch_frequency (LqrCarver
*r
, guint switch_frequency
)
463 r
->lr_switch_frequency
= switch_frequency
;
466 /* set enlargement step */
469 lqr_carver_set_enl_step (LqrCarver
*r
, gfloat enl_step
)
471 CATCH_F ((enl_step
> 1) && (enl_step
<= 2));
473 r
->enl_step
= enl_step
;
479 lqr_carver_set_use_cache (LqrCarver
*r
, gboolean use_cache
)
486 r
->use_rcache
= use_cache
;
487 r
->rwindow
->use_rcache
= use_cache
;
490 /* set progress reprot */
493 lqr_carver_set_progress (LqrCarver
*r
, LqrProgress
*p
)
499 /* flag the input buffer to avoid destruction */
502 lqr_carver_set_preserve_input_image(LqrCarver
*r
)
504 r
->preserve_in_buffer
= TRUE
;
508 /*** compute maps (energy, minpath & visibility) ***/
510 /* build multisize image up to given depth
511 * it is progressive (can be called multilple times) */
513 lqr_carver_build_maps (LqrCarver
* r
, gint depth
)
516 assert (depth
<= r
->w_start
);
518 #endif /* __LQR_DEBUG__ */
522 /* only go deeper if needed */
523 if (depth
> r
->max_level
)
526 CATCH_F (r
->root
== NULL
);
528 /* set to minimum width reached so far */
529 lqr_carver_set_width (r
, r
->w_start
- r
->max_level
+ 1);
531 /* compute energy & minpath maps */
532 CATCH (lqr_carver_build_emap (r
));
533 CATCH (lqr_carver_build_mmap (r
));
535 /* compute visibility map */
536 CATCH (lqr_carver_build_vsmap (r
, depth
));
541 /* compute energy map */
543 lqr_carver_build_emap (LqrCarver
* r
)
554 if (r
->use_rcache
&& r
->rcache
== NULL
)
556 CATCH_MEM (r
->rcache
= lqr_carver_generate_rcache (r
));
559 for (y
= 0; y
< r
->h
; y
++)
562 /* r->nrg_xmin[y] = 0; */
563 /* r->nrg_xmax[y] = r->w - 1; */
564 for (x
= 0; x
< r
->w
; x
++)
566 CATCH (lqr_carver_compute_e(r
, x
, y
));
570 r
->nrg_uptodate
= TRUE
;
576 lqr_carver_compute_e (LqrCarver
* r
, gint x
, gint y
)
581 /* removed CANC check for performance reasons */
582 /* CATCH_CANC (r); */
586 CATCH (lqr_rwindow_fill (r
->rwindow
, r
, x
, y
));
589 b_add
= r
->bias
[data
] / r
->w_start
;
591 r
->en
[data
] = r
->nrg(x
, y
, r
->w
, r
->h
, r
->rwindow
, r
->nrg_extra_data
) + b_add
;
596 /* compute auxiliary minpath map
598 * y = 1 : m(x,y) = e(x,y)
599 * y > 1 : m(x,y) = min_{x'=-dx,..,dx} ( m(x-x',y-1) + rig(x') ) + e(x,y)
601 * e(x,y) is the energy at point (x,y)
602 * dx is the max seam step delta_x
603 * rig(x') is the rigidity for step x'
606 lqr_carver_build_mmap (LqrCarver
* r
)
611 gint x1_min
, x1_max
, x1
;
612 gfloat m
, m1
, r_fact
;
618 for (x
= 0; x
< r
->w
; x
++)
622 assert (r
->vs
[data
] == 0);
623 #endif /* __LQR_DEBUG__ */
624 r
->m
[data
] = r
->en
[data
];
627 /* span all other rows */
628 for (y
= 1; y
< r
->h
; y
++)
630 for (x
= 0; x
< r
->w
; x
++)
636 assert (r
->vs
[data
] == 0);
637 #endif /* __LQR_DEBUG__ */
638 /* watch for boundaries */
639 x1_min
= MAX (-x
, -r
->delta_x
);
640 x1_max
= MIN (r
->w
- 1 - x
, r
->delta_x
);
641 if (r
->rigidity_mask
) {
642 r_fact
= r
->rigidity_mask
[data
];
647 /* we use the data_down pointer to be able to
648 * track the seams later (needed for rigidity) */
649 data_down
= r
->raw
[y
- 1][x
+ x1_min
];
650 r
->least
[data
] = data_down
;
653 m
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1_min
];
654 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++)
656 data_down
= r
->raw
[y
- 1][x
+ x1
];
657 /* find the min among the neighbors
659 m1
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1
];
660 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
663 r
->least
[data
] = data_down
;
665 /* m = MIN(m, r->m[data_down] + r->rigidity_map[x1]); */
671 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++)
673 data_down
= r
->raw
[y
- 1][x
+ x1
];
674 /* find the min among the neighbors
676 m1
= r
->m
[data_down
];
677 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
680 r
->least
[data
] = data_down
;
682 m
= MIN (m
, r
->m
[data_down
]);
687 r
->m
[data
] = r
->en
[data
] + m
;
693 /* compute (vertical) visibility map up to given depth
694 * (it also calls inflate() to add image enlargment information) */
696 lqr_carver_build_vsmap (LqrCarver
* r
, gint depth
)
699 gint lr_switch_interval
= 0;
702 #ifdef __LQR_VERBOSE__
703 printf ("[ building visibility map ]\n");
705 #endif /* __LQR_VERBOSE__ */
709 assert (depth
<= r
->w_start
+ 1);
711 #endif /* __LQR_DEBUG__ */
713 /* default behaviour : compute all possible levels
717 depth
= r
->w_start
+ 1;
720 /* here we assume that
721 * lqr_carver_set_width(w_start - max_level + 1);
724 /* left-right switch interval */
725 if (r
->lr_switch_frequency
)
727 lr_switch_interval
= (depth
- r
->max_level
- 1) / r
->lr_switch_frequency
+ 1;
730 /* cycle over levels */
731 for (l
= r
->max_level
; l
< depth
; l
++)
735 if ((l
- r
->max_level
+ r
->session_rescale_current
) % r
->session_update_step
== 0)
737 lqr_progress_update (r
->progress
, (gdouble
) (l
- r
->max_level
+ r
->session_rescale_current
) /
738 (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 */
759 lqr_carver_carve (r
);
763 /* update the energy */
764 /* CATCH (lqr_carver_build_emap (r)); */
765 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))
771 CATCH (lqr_carver_build_mmap (r
));
775 /* lqr_carver_build_mmap (r); */
776 CATCH (lqr_carver_update_mmap (r
));
781 /* complete the map (last seam) */
782 lqr_carver_finish_vsmap (r
);
786 /* insert seams for image enlargement */
787 CATCH (lqr_carver_inflate (r
, depth
- 1));
789 /* reset image size */
790 lqr_carver_set_width (r
, r
->w_start
);
791 /* repeat for auxiliary layers */
792 data_tok
.integer
= r
->w_start
;
793 CATCH (lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_width_attached
, data_tok
));
795 #ifdef __LQR_VERBOSE__
796 printf ("[ visibility map OK ]\n");
798 #endif /* __LQR_VERBOSE__ */
803 /* enlarge the image by seam insertion
804 * visibility map is updated and the resulting multisize image
805 * is complete in both directions */
807 lqr_carver_inflate (LqrCarver
* r
, gint l
)
812 void *new_rgb
= NULL
;
815 gfloat
*new_bias
= NULL
;
816 gfloat
*new_rigmask
= NULL
;
818 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
820 #ifdef __LQR_VERBOSE__
821 printf (" [ inflating (active=%i) ]\n", r
->active
);
823 #endif /* __LQR_VERBOSE__ */
826 assert (l
+ 1 > r
->max_level
); /* otherwise is useless */
827 #endif /* __LQR_DEBUG__ */
833 prev_state
= g_atomic_int_get(&r
->state
);
834 CATCH (lqr_carver_set_state(r
, LQR_CARVER_STATE_INFLATING
, TRUE
));
837 /* first iterate on attached carvers */
838 data_tok
.integer
= l
;
839 CATCH (lqr_carver_list_foreach (r
->attached_list
, lqr_carver_inflate_attached
, data_tok
));
841 /* scale to current maximum size
842 * (this is the original size the first time) */
843 lqr_carver_set_width (r
, r
->w0
);
846 w1
= r
->w0
+ l
- r
->max_level
+ 1;
848 /* allocate room for new maps */
849 BUF_TRY_NEW0_RET_LQR(new_rgb
, w1
* r
->h0
* r
->channels
, r
->col_depth
);
853 CATCH_MEM (new_vs
= g_try_new0 (gint
, w1
* r
->h0
));
859 CATCH_MEM (new_bias
= g_try_new0 (gfloat
, w1
* r
->h0
));
861 if (r
->rigidity_mask
)
863 CATCH_MEM (new_rigmask
= g_try_new (gfloat
, w1
* r
->h0
));
867 /* span the image with a cursor
868 * and build the new image */
869 lqr_cursor_reset (r
->c
);
872 for (z0
= 0; z0
< w1
* r
->h0
; z0
++, lqr_cursor_next (r
->c
))
877 /* read visibility */
878 vs
= r
->vs
[r
->c
->now
];
879 if ((vs
!= 0) && (vs
<= l
+ r
->max_level
- 1)
880 && (vs
>= 2 * r
->max_level
- 1))
882 /* the point belongs to a previously computed seam
883 * and was not inserted during a previous
884 * inflate() call : insert another seam */
886 /* the new pixel value is equal to the average of its
887 * left and right neighbors */
891 c_left
= lqr_cursor_left (r
->c
);
898 for (k
= 0; k
< r
->channels
; k
++)
900 switch (r
->col_depth
)
902 case LQR_COLDEPTH_8I
:
903 tmp_rgb
= (AS_8I(r
->rgb
)[c_left
* r
->channels
+ k
] +
904 AS_8I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
905 AS_8I(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_8i
) (tmp_rgb
+ 0.499999);
907 case LQR_COLDEPTH_16I
:
908 tmp_rgb
= (AS_16I(r
->rgb
)[c_left
* r
->channels
+ k
] +
909 AS_16I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
910 AS_16I(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_16i
) (tmp_rgb
+ 0.499999);
912 case LQR_COLDEPTH_32F
:
913 tmp_rgb
= (AS_32F(r
->rgb
)[c_left
* r
->channels
+ k
] +
914 AS_32F(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
915 AS_32F(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_32f
) tmp_rgb
;
917 case LQR_COLDEPTH_64F
:
918 tmp_rgb
= (AS_64F(r
->rgb
)[c_left
* r
->channels
+ k
] +
919 AS_64F(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
920 AS_64F(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_64f
) tmp_rgb
;
928 new_bias
[z0
] = (r
->bias
[c_left
] + r
->bias
[r
->c
->now
]) / 2;
930 if (r
->rigidity_mask
)
932 new_rigmask
[z0
] = (r
->rigidity_mask
[c_left
] + r
->rigidity_mask
[r
->c
->now
]) / 2;
935 /* the first time inflate() is called
936 * the new visibility should be -vs + 1 but we shift it
937 * so that the final minimum visibiliy will be 1 again
938 * and so that vs=0 still means "uninitialized".
939 * Subsequent inflations account for that */
942 new_vs
[z0
] = l
- vs
+ r
->max_level
;
946 for (k
= 0; k
< r
->channels
; k
++)
948 PXL_COPY(new_rgb
, z0
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
954 new_bias
[z0
] = r
->bias
[r
->c
->now
];
956 if (r
->rigidity_mask
)
958 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
963 /* visibility has to be shifted up */
966 new_vs
[z0
] = vs
+ l
- r
->max_level
+ 1;
969 else if (r
->raw
!= NULL
)
972 assert (y
< r
->h_start
);
973 assert (x
< r
->w_start
- l
);
974 #endif /* __LQR_DEBUG__ */
977 if (x
>= r
->w_start
- l
)
989 if (w1
!= 2 * r
->w_start
- 1)
991 assert ((y
== r
->h_start
)
992 || (printf ("y=%i hst=%i w1=%i\n", y
, r
->h_start
, w1
)
993 && fflush (stdout
) && 0));
996 #endif /* __LQR_DEBUG__ */
998 /* substitute maps */
999 if (!r
->preserve_in_buffer
)
1003 /* g_free (r->vs); */
1009 g_free (r
->rigidity_mask
);
1013 r
->nrg_uptodate
= FALSE
;
1016 r
->preserve_in_buffer
= FALSE
;
1018 if (r
->root
== NULL
)
1022 CATCH (lqr_carver_propagate_vsmap(r
));
1030 CATCH_MEM (r
->en
= g_try_new0 (gfloat
, w1
* r
->h0
));
1035 r
->rigidity_mask
= new_rigmask
;
1036 CATCH_MEM (r
->m
= g_try_new0 (gfloat
, w1
* r
->h0
));
1037 CATCH_MEM (r
->least
= g_try_new0 (gint
, w1
* r
->h0
));
1040 /* set new widths & levels (w_start is kept for reference) */
1042 r
->max_level
= l
+ 1;
1046 /* reset readout buffer */
1047 g_free (r
->rgb_ro_buffer
);
1048 BUF_TRY_NEW0_RET_LQR(r
->rgb_ro_buffer
, r
->w0
* r
->channels
, r
->col_depth
);
1050 #ifdef __LQR_VERBOSE__
1051 printf (" [ inflating OK ]\n");
1053 #endif /* __LQR_VERBOSE__ */
1055 if (r
->root
== NULL
)
1057 CATCH (lqr_carver_set_state(r
, prev_state
, TRUE
));
1064 lqr_carver_inflate_attached (LqrCarver
* r
, LqrDataTok data
)
1066 return lqr_carver_inflate (r
, data
.integer
);
1070 /*** internal functions for maps computations ***/
1073 * this actually carves the raw array,
1074 * which holds the indices to be used
1075 * in all the other maps */
1077 lqr_carver_carve (LqrCarver
* r
)
1081 #ifdef __LQR_DEBUG__
1082 assert (r
->root
== NULL
);
1083 #endif /* __LQR_DEBUG__ */
1085 for (y
= 0; y
< r
->h_start
; y
++)
1087 #ifdef __LQR_DEBUG__
1088 assert (r
->vs
[r
->raw
[y
][r
->vpath_x
[y
]]] != 0);
1089 for (x
= 0; x
< r
->vpath_x
[y
]; x
++)
1091 assert (r
->vs
[r
->raw
[y
][x
]] == 0);
1093 #endif /* __LQR_DEBUG__ */
1094 for (x
= r
->vpath_x
[y
]; x
< r
->w
; x
++)
1096 r
->raw
[y
][x
] = r
->raw
[y
][x
+ 1];
1097 #ifdef __LQR_DEBUG__
1098 assert (r
->vs
[r
->raw
[y
][x
]] == 0);
1099 #endif /* __LQR_DEBUG__ */
1103 r
->nrg_uptodate
= FALSE
;
1107 /* update energy map after seam removal */
1109 lqr_carver_update_emap (LqrCarver
* r
)
1112 gint y1
, y1_min
, y1_max
;
1116 if (r
->nrg_uptodate
)
1122 CATCH_F (r
->rcache
!= NULL
);
1125 for (y
= 0; y
< r
->h
; y
++)
1127 /* note: here the vpath has already
1131 r
->nrg_xmax
[y
] = x
- 1;
1133 for (y
= 0; y
< r
->h
; y
++)
1136 y1_min
= MAX (y
- r
->nrg_radius
, 0);
1137 y1_max
= MIN (y
+ r
->nrg_radius
, r
->h
- 1);
1139 for (y1
= y1_min
; y1
<= y1_max
; y1
++)
1141 r
->nrg_xmin
[y1
] = MIN (r
->nrg_xmin
[y1
], x
- r
->nrg_radius
);
1142 r
->nrg_xmin
[y1
] = MAX (0, r
->nrg_xmin
[y1
]);
1143 /* note: the -1 below is because of the previous carving */
1144 r
->nrg_xmax
[y1
] = MAX (r
->nrg_xmax
[y1
], x
+ r
->nrg_radius
- 1);
1145 r
->nrg_xmax
[y1
] = MIN (r
->w
- 1, r
->nrg_xmax
[y1
]);
1149 for (y
= 0; y
< r
->h
; y
++)
1153 for (x
= r
->nrg_xmin
[y
]; x
<= r
->nrg_xmax
[y
]; x
++)
1155 CATCH (lqr_carver_compute_e (r
, x
, y
));
1159 r
->nrg_uptodate
= TRUE
;
1164 /* update the auxiliary minpath map
1165 * this only updates the affected pixels,
1166 * which start form the beginning of the changed
1167 * energy region around the seam and expand
1168 * at most by delta_x (in both directions)
1171 lqr_carver_update_mmap (LqrCarver
* r
)
1176 gint x1_min
, x1_max
;
1177 gint data
, data_down
, least
;
1178 gfloat m
, m1
, r_fact
;
1185 CATCH_F (r
->nrg_uptodate
);
1189 CATCH_MEM (mc
= g_try_new(gfloat
, 2 * r
->delta_x
+ 1));
1193 /* span first row */
1194 /* x_min = MAX (r->vpath_x[0] - r->delta_x, 0); */
1195 x_min
= MAX (r
->nrg_xmin
[0], 0);
1196 /* x_max = MIN (r->vpath_x[0] + r->delta_x - 1, r->w - 1); */
1197 /* x_max = MIN (r->vpath_x[0] + r->delta_x, r->w - 1); */
1198 x_max
= MIN (r
->nrg_xmax
[0], r
->w
- 1);
1200 for (x
= x_min
; x
<= x_max
; x
++)
1202 data
= r
->raw
[0][x
];
1203 r
->m
[data
] = r
->en
[data
];
1208 for (y
= 1; y
< r
->h
; y
++)
1212 /* make sure to include the changed energy region */
1213 x_min
= MIN (x_min
, r
->nrg_xmin
[y
]);
1214 x_max
= MAX (x_max
, r
->nrg_xmax
[y
]);
1216 /* expand the affected region by delta_x */
1217 x_min
= MAX (x_min
- r
->delta_x
, 0);
1218 x_max
= MIN (x_max
+ r
->delta_x
, r
->w
- 1);
1220 /* span the affected region */
1223 for (x
= x_min
; x
<= x_max
; x
++)
1225 data
= r
->raw
[y
][x
];
1226 if (r
->rigidity_mask
) {
1227 r_fact
= r
->rigidity_mask
[data
];
1232 /* find the minimum in the previous rows
1233 * as in build_mmap() */
1234 x1_min
= MAX (0, x
- r
->delta_x
);
1235 x1_max
= MIN (r
->w
- 1, x
+ r
->delta_x
);
1240 switch (x1_max
- x1_min
+ 1)
1243 MRSET1(y
, x1_min
, dx
);
1244 m
= r
->leftright
? MRMINR1(y
, x1_min
, dx
) : MRMINL1(y
, x1_min
, dx
);
1247 MRSET2(y
, x1_min
, dx
);
1248 m
= r
->leftright
? MRMINR2(y
, x1_min
, dx
) : MRMINL2(y
, x1_min
, dx
);
1251 MRSET3(y
, x1_min
, dx
);
1252 m
= r
->leftright
? MRMINR3(y
, x1_min
, dx
) : MRMINL3(y
, x1_min
, dx
);
1255 MRSET4(y
, x1_min
, dx
);
1256 m
= r
->leftright
? MRMINR4(y
, x1_min
, dx
) : MRMINL4(y
, x1_min
, dx
);
1259 MRSET5(y
, x1_min
, dx
);
1260 m
= r
->leftright
? MRMINR5(y
, x1_min
, dx
) : MRMINL5(y
, x1_min
, dx
);
1263 data_down
= r
->raw
[y
- 1][x1_min
];
1265 m
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[dx
++];
1266 /* 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); */
1267 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++, dx
++)
1269 data_down
= r
->raw
[y
- 1][x1
];
1270 m1
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[dx
];
1271 /* 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); */
1272 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
1279 /* 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); */
1283 switch (x1_max
- x1_min
+ 1)
1286 m
= r
->leftright
? MMINR1(y
, x1_min
) : MMINL1(y
, x1_min
);
1289 m
= r
->leftright
? MMINR2(y
, x1_min
) : MMINL2(y
, x1_min
);
1292 m
= r
->leftright
? MMINR3(y
, x1_min
) : MMINL3(y
, x1_min
);
1295 m
= r
->leftright
? MMINR4(y
, x1_min
) : MMINL4(y
, x1_min
);
1298 m
= r
->leftright
? MMINR5(y
, x1_min
) : MMINL5(y
, x1_min
);
1301 data_down
= r
->raw
[y
- 1][x1_min
];
1303 m
= r
->m
[data_down
];
1304 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++)
1306 data_down
= r
->raw
[y
- 1][x1
];
1307 m1
= r
->m
[data_down
];
1308 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
1315 /* 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); */
1318 new_m
= r
->en
[data
] + m
;
1320 /* reduce the range if there's no (relevant) difference
1321 * with the previous map */
1322 if (r
->least
[data
] == least
)
1324 if (fabsf(r
->m
[data
] - new_m
) < UPDATE_TOLERANCE
)
1338 if ((x
== x_min
) && stop
)
1349 r
->least
[data
] = least
;
1351 if ((x
== x_max
) && (stop
))
1370 /* compute seam path from minpath map */
1372 lqr_carver_build_vpath (LqrCarver
* r
)
1380 /* we start at last row */
1383 /* span the last row for the minimum mmap value */
1385 for (x
= 0, z0
= y
* r
->w_start
; x
< r
->w
; x
++, z0
++)
1387 #ifdef __LQR_DEBUG__
1388 assert (r
->vs
[r
->raw
[y
][x
]] == 0);
1389 #endif /* __LQR_DEBUG__ */
1391 m1
= r
->m
[r
->raw
[y
][x
]];
1392 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
1394 last
= r
->raw
[y
][x
];
1400 #ifdef __LQR_DEBUG__
1402 #endif /* __LQR_DEBUG__ */
1404 /* follow the track for the other rows */
1405 for (y
= r
->h0
- 1; y
>= 0; y
--)
1407 #ifdef __LQR_DEBUG__
1408 assert (r
->vs
[last
] == 0);
1409 assert (last_x
< r
->w
);
1410 #endif /* __LQR_DEBUG__ */
1412 r
->vpath_x
[y
] = last_x
;
1415 last
= r
->least
[r
->raw
[y
][last_x
]];
1416 /* we also need to retrieve the x coordinate */
1417 x_min
= MAX (last_x
- r
->delta_x
, 0);
1418 x_max
= MIN (last_x
+ r
->delta_x
, r
->w
- 1);
1419 for (x
= x_min
; x
<= x_max
; x
++)
1421 if (r
->raw
[y
- 1][x
] == last
)
1427 #ifdef __LQR_DEBUG__
1428 assert (x
< x_max
+ 1);
1429 #endif /* __LQR_DEBUG__ */
1435 /* we backtrack the seam following the min mmap */
1436 for (y
= r
->h0
- 1; y
>= 0; y
--)
1438 #ifdef __LQR_DEBUG__
1439 assert (r
->vs
[last
] == 0);
1440 assert (last_x
< r
->w
);
1441 #endif /* __LQR_DEBUG__ */
1444 r
->vpath_x
[y
] = last_x
;
1448 x_min
= MAX (0, last_x
- r
->delta_x
);
1449 x_max
= MIN (r
->w
- 1, last_x
+ r
->delta_x
);
1450 for (x
= x_min
; x
<= x_max
; x
++)
1452 m1
= r
->m
[r
->raw
[y
- 1][x
]];
1455 last
= r
->raw
[y
- 1][x
];
1465 /* update visibility map after seam computation */
1467 lqr_carver_update_vsmap (LqrCarver
* r
, gint l
)
1470 #ifdef __LQR_DEBUG__
1471 assert(r
->root
== NULL
);
1472 #endif /* __LQR_DEBUG__ */
1473 for (y
= 0; y
< r
->h
; y
++)
1475 #ifdef __LQR_DEBUG__
1476 assert (r
->vs
[r
->vpath
[y
]] == 0);
1477 assert (r
->vpath
[y
] == r
->raw
[y
][r
->vpath_x
[y
]]);
1478 #endif /* __LQR_DEBUG__ */
1479 r
->vs
[r
->vpath
[y
]] = l
;
1483 /* complete visibility map (last seam) */
1484 /* set the last column of pixels to vis. level w0 */
1486 lqr_carver_finish_vsmap (LqrCarver
* r
)
1490 #ifdef __LQR_DEBUG__
1492 assert (r
->root
== NULL
);
1493 #endif /* __LQR_DEBUG__ */
1494 lqr_cursor_reset (r
->c
);
1495 for (y
= 1; y
<= r
->h
; y
++, lqr_cursor_next (r
->c
))
1497 #ifdef __LQR_DEBUG__
1498 assert (r
->vs
[r
->c
->now
] == 0);
1499 #endif /* __LQR_DEBUG__ */
1500 r
->vs
[r
->c
->now
] = r
->w0
;
1502 lqr_cursor_reset (r
->c
);
1505 /* propagate the root carver's visibility map */
1507 lqr_carver_propagate_vsmap (LqrCarver
* r
)
1509 LqrDataTok data_tok
;
1513 data_tok
.data
= NULL
;
1514 CATCH (lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_propagate_vsmap_attached
, data_tok
));
1519 lqr_carver_propagate_vsmap_attached (LqrCarver
* r
, LqrDataTok data
)
1521 LqrDataTok data_tok
;
1522 data_tok
.data
= NULL
;
1523 r
->vs
= r
->root
->vs
;
1524 lqr_carver_scan_reset(r
);
1525 /* CATCH (lqr_carver_list_foreach (r->attached_list, lqr_carver_propagate_vsmap_attached, data_tok)); */
1529 /*** image manipulations ***/
1531 /* set width of the multisize image
1532 * (maps have to be computed already) */
1534 lqr_carver_set_width (LqrCarver
* r
, gint w1
)
1536 #ifdef __LQR_DEBUG__
1537 assert (w1
<= r
->w0
);
1538 assert (w1
>= r
->w_start
- r
->max_level
+ 1);
1539 #endif /* __LQR_DEBUG__ */
1541 r
->level
= r
->w0
- w1
+ 1;
1545 lqr_carver_set_width_attached (LqrCarver
* r
, LqrDataTok data
)
1547 lqr_carver_set_width (r
, data
.integer
);
1553 /* flatten the image to its current state
1554 * (all maps are reset, invisible points are lost) */
1557 lqr_carver_flatten (LqrCarver
* r
)
1559 void *new_rgb
= NULL
;
1560 gfloat
*new_bias
= NULL
;
1561 gfloat
*new_rigmask
= NULL
;
1564 LqrDataTok data_tok
;
1565 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
1567 #ifdef __LQR_VERBOSE__
1568 printf (" [ flattening (active=%i) ]\n", r
->active
);
1570 #endif /* __LQR_VERBOSE__ */
1574 if (r
->root
== NULL
)
1576 prev_state
= g_atomic_int_get(&r
->state
);
1577 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_FLATTENING
, TRUE
));
1580 /* first iterate on attached carvers */
1581 CATCH (lqr_carver_list_foreach (r
->attached_list
, lqr_carver_flatten_attached
, data_tok
));
1583 /* free non needed maps first */
1590 r
->nrg_uptodate
= FALSE
;
1592 /* allocate room for new map */
1593 BUF_TRY_NEW0_RET_LQR(new_rgb
, r
->w
* r
->h
* r
->channels
, r
->col_depth
);
1597 if (r
->rigidity_mask
)
1599 CATCH_MEM (new_rigmask
= g_try_new (gfloat
, r
->w
* r
->h
));
1606 CATCH_MEM (new_bias
= g_try_new0 (gfloat
, r
->w
* r
->h
));
1610 CATCH_MEM (r
->_raw
= g_try_new (gint
, r
->w
* r
->h
));
1611 CATCH_MEM (r
->raw
= g_try_new (gint
*, r
->h
));
1614 /* span the image with the cursor and copy
1615 * it in the new array */
1616 lqr_cursor_reset (r
->c
);
1617 for (y
= 0; y
< r
->h
; y
++)
1623 r
->raw
[y
] = r
->_raw
+ y
* r
->w
;
1625 for (x
= 0; x
< r
->w
; x
++)
1628 for (k
= 0; k
< r
->channels
; k
++)
1630 PXL_COPY(new_rgb
, z0
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
1634 if (r
->rigidity_mask
)
1636 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
1643 new_bias
[z0
] = r
->bias
[r
->c
->now
];
1647 lqr_cursor_next (r
->c
);
1651 /* substitute the old maps */
1652 if (!r
->preserve_in_buffer
)
1657 r
->preserve_in_buffer
= FALSE
;
1665 g_free (r
->rigidity_mask
);
1666 r
->rigidity_mask
= new_rigmask
;
1669 /* init the other maps */
1670 if (r
->root
== NULL
)
1673 CATCH_MEM (r
->vs
= g_try_new0 (gint
, r
->w
* r
->h
));
1674 CATCH (lqr_carver_propagate_vsmap(r
));
1678 CATCH_MEM (r
->en
= g_try_new0 (gfloat
, r
->w
* r
->h
));
1682 CATCH_MEM (r
->m
= g_try_new0 (gfloat
, r
->w
* r
->h
));
1683 CATCH_MEM (r
->least
= g_try_new (gint
, r
->w
* r
->h
));
1686 /* reset widths, heights & levels */
1694 #ifdef __LQR_VERBOSE__
1695 printf (" [ flattening OK ]\n");
1697 #endif /* __LQR_VERBOSE__ */
1699 if (r
->root
== NULL
)
1701 CATCH (lqr_carver_set_state (r
, prev_state
, TRUE
));
1708 lqr_carver_flatten_attached(LqrCarver
*r
, LqrDataTok data
)
1710 return lqr_carver_flatten(r
);
1713 /* transpose the image, in its current state
1714 * (all maps and invisible points are lost) */
1716 lqr_carver_transpose (LqrCarver
* r
)
1721 void *new_rgb
= NULL
;
1722 gfloat
*new_bias
= NULL
;
1723 gfloat
*new_rigmask
= NULL
;
1724 LqrDataTok data_tok
;
1725 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
1727 #ifdef __LQR_VERBOSE__
1728 printf ("[ transposing (active=%i) ]\n", r
->active
);
1730 #endif /* __LQR_VERBOSE__ */
1734 if (r
->root
== NULL
)
1736 prev_state
= g_atomic_int_get(&r
->state
);
1737 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_TRANSPOSING
, TRUE
));
1742 CATCH (lqr_carver_flatten (r
));
1745 /* first iterate on attached carvers */
1746 CATCH (lqr_carver_list_foreach (r
->attached_list
, lqr_carver_transpose_attached
, data_tok
));
1748 /* free non needed maps first */
1749 if (r
->root
== NULL
)
1757 g_free (r
->rgb_ro_buffer
);
1760 r
->nrg_uptodate
= FALSE
;
1762 /* allocate room for the new maps */
1763 BUF_TRY_NEW0_RET_LQR(new_rgb
, r
->w0
* r
->h0
* r
->channels
, r
->col_depth
);
1767 if (r
->rigidity_mask
)
1769 CATCH_MEM (new_rigmask
= g_try_new (gfloat
, r
->w0
* r
->h0
));
1776 CATCH_MEM (new_bias
= g_try_new0 (gfloat
, r
->w0
* r
->h0
));
1780 CATCH_MEM (r
->_raw
= g_try_new0 (gint
, r
->h0
* r
->w0
));
1781 CATCH_MEM (r
->raw
= g_try_new0 (gint
*, r
->w0
));
1784 /* compute trasposed maps */
1785 for (x
= 0; x
< r
->w
; x
++)
1789 r
->raw
[x
] = r
->_raw
+ x
* r
->h0
;
1791 for (y
= 0; y
< r
->h
; y
++)
1795 for (k
= 0; k
< r
->channels
; k
++)
1797 PXL_COPY(new_rgb
, z1
* r
->channels
+ k
, r
->rgb
, z0
* r
->channels
+ k
, r
->col_depth
);
1801 if (r
->rigidity_mask
)
1803 new_rigmask
[z1
] = r
->rigidity_mask
[z0
];
1810 new_bias
[z1
] = r
->bias
[z0
];
1817 /* substitute the map */
1818 if (!r
->preserve_in_buffer
)
1823 r
->preserve_in_buffer
= FALSE
;
1832 g_free (r
->rigidity_mask
);
1833 r
->rigidity_mask
= new_rigmask
;
1836 /* init the other maps */
1837 if (r
->root
== NULL
)
1839 CATCH_MEM (r
->vs
= g_try_new0 (gint
, r
->w0
* r
->h0
));
1840 CATCH (lqr_carver_propagate_vsmap(r
));
1844 CATCH_MEM (r
->en
= g_try_new0 (gfloat
, r
->w0
* r
->h0
));
1848 CATCH_MEM (r
->m
= g_try_new0 (gfloat
, r
->w0
* r
->h0
));
1849 CATCH_MEM (r
->least
= g_try_new (gint
, r
->w0
* r
->h0
));
1852 /* switch widths & heights */
1859 /* reset w_start, h_start & levels */
1865 /* reset seam path, cursor and readout buffer */
1869 CATCH_MEM (r
->vpath
= g_try_new (gint
, r
->h
));
1870 g_free (r
->vpath_x
);
1871 CATCH_MEM (r
->vpath_x
= g_try_new (gint
, r
->h
));
1872 g_free (r
->nrg_xmin
);
1873 CATCH_MEM (r
->nrg_xmin
= g_try_new (gint
, r
->h
));
1874 g_free (r
->nrg_xmax
);
1875 CATCH_MEM (r
->nrg_xmax
= g_try_new (gint
, r
->h
));
1878 BUF_TRY_NEW0_RET_LQR(r
->rgb_ro_buffer
, r
->w0
* r
->channels
, r
->col_depth
);
1880 /* rescale rigidity */
1884 for (x
= -r
->delta_x
; x
<= r
->delta_x
; x
++)
1886 r
->rigidity_map
[x
] = r
->rigidity_map
[x
] * r
->w0
/ r
->h0
;
1890 /* set transposed flag */
1891 r
->transposed
= (r
->transposed
? 0 : 1);
1893 #ifdef __LQR_VERBOSE__
1894 printf ("[ transpose OK ]\n");
1896 #endif /* __LQR_VERBOSE__ */
1898 if (r
->root
== NULL
)
1900 CATCH (lqr_carver_set_state (r
, prev_state
, TRUE
));
1907 lqr_carver_transpose_attached (LqrCarver
* r
, LqrDataTok data
)
1909 return lqr_carver_transpose(r
);
1912 /* resize w + h: these are the liquid rescale methods.
1913 * They automatically determine the depth of the map
1914 * according to the desired size, can be called multiple
1915 * times, transpose the image as necessasry */
1917 lqr_carver_resize_width (LqrCarver
* r
, gint w1
)
1919 LqrDataTok data_tok
;
1922 /* delta is used to determine the required depth
1923 * gamma to decide if action is necessary */
1926 delta
= w1
- r
->w_start
;
1928 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1932 delta
= w1
- r
->h_start
;
1934 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
1947 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
1948 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_RESIZING
, TRUE
));
1950 /* update step for progress reprt*/
1951 r
->session_rescale_total
= gamma
> 0 ? gamma
: -gamma
;
1952 r
->session_rescale_current
= 0;
1953 r
->session_update_step
= (gint
) MAX (r
->session_rescale_total
* r
->progress
->update_step
, 1);
1955 if (r
->session_rescale_total
)
1957 lqr_progress_init (r
->progress
, r
->progress
->init_width_message
);
1962 gint delta0
= MIN (delta
, delta_max
);
1968 CATCH (lqr_carver_transpose (r
));
1970 new_w
= MIN (w1
, r
->w_start
+ delta_max
);
1972 CATCH (lqr_carver_build_maps (r
, delta0
+ 1));
1973 lqr_carver_set_width (r
, new_w
);
1975 data_tok
.integer
= new_w
;
1976 lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_width_attached
, data_tok
);
1978 r
->session_rescale_current
= r
->session_rescale_total
- (gamma
> 0 ? gamma
: -gamma
);
1982 CATCH (lqr_vmap_internal_dump (r
));
1986 CATCH (lqr_carver_flatten(r
));
1987 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1995 if (r
->session_rescale_total
)
1997 lqr_progress_end (r
->progress
, r
->progress
->end_width_message
);
2000 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_STD
, TRUE
));
2006 lqr_carver_resize_height (LqrCarver
* r
, gint h1
)
2008 LqrDataTok data_tok
;
2011 /* delta is used to determine the required depth
2012 * gamma to decide if action is necessary */
2015 delta
= h1
- r
->h_start
;
2017 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
2021 delta
= h1
- r
->w_start
;
2023 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
2033 delta
= delta
> 0 ? delta
: -delta
;
2036 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
2037 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_RESIZING
, TRUE
));
2039 /* update step for progress reprt*/
2040 r
->session_rescale_total
= gamma
> 0 ? gamma
: -gamma
;
2041 r
->session_rescale_current
= 0;
2042 r
->session_update_step
= (gint
) MAX (r
->session_rescale_total
* r
->progress
->update_step
, 1);
2044 if (r
->session_rescale_total
)
2046 lqr_progress_init (r
->progress
, r
->progress
->init_height_message
);
2051 gint delta0
= MIN (delta
, delta_max
);
2056 CATCH (lqr_carver_transpose (r
));
2058 new_w
= MIN (h1
, r
->w_start
+ delta_max
);
2060 CATCH (lqr_carver_build_maps (r
, delta0
+ 1));
2061 lqr_carver_set_width (r
, new_w
);
2063 data_tok
.integer
= new_w
;
2064 lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_width_attached
, data_tok
);
2066 r
->session_rescale_current
= r
->session_rescale_total
- (gamma
> 0 ? gamma
: - gamma
);
2070 CATCH (lqr_vmap_internal_dump (r
));
2074 CATCH (lqr_carver_flatten(r
));
2075 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
2083 if (r
->session_rescale_total
)
2085 lqr_progress_end (r
->progress
, r
->progress
->end_height_message
);
2088 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_STD
, TRUE
));
2093 /* liquid rescale public method */
2096 lqr_carver_resize (LqrCarver
* r
, gint w1
, gint h1
)
2098 #ifdef __LQR_VERBOSE__
2099 printf("[ Rescale from %i,%i to %i,%i ]\n", (r
->transposed
? r
->h
: r
->w
), (r
->transposed
? r
->w
: r
->h
), w1
, h1
);
2101 #endif /* __LQR_VERBOSE__ */
2102 CATCH_F ((w1
>= 1) && (h1
>= 1));
2103 CATCH_F (r
->root
== NULL
);
2106 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
2108 switch (r
->resize_order
)
2110 case LQR_RES_ORDER_HOR
:
2111 CATCH (lqr_carver_resize_width(r
, w1
));
2112 CATCH (lqr_carver_resize_height(r
, h1
));
2114 case LQR_RES_ORDER_VERT
:
2115 CATCH (lqr_carver_resize_height(r
, h1
));
2116 CATCH (lqr_carver_resize_width(r
, w1
));
2118 #ifdef __LQR_DEBUG__
2121 #endif /* __LQR_DEBUG__ */
2123 lqr_carver_scan_reset_all(r
);
2125 #ifdef __LQR_VERBOSE__
2126 printf("[ Rescale OK ]\n");
2128 #endif /* __LQR_VERBOSE__ */
2133 lqr_carver_set_state (LqrCarver
* r
, LqrCarverState state
, gboolean skip_canceled
)
2135 LqrDataTok data_tok
;
2138 CATCH_F(r
->root
== NULL
);
2140 lock_pos
= g_atomic_int_exchange_and_add(&r
->state_lock_queue
, 1);
2142 while (g_atomic_int_get(&r
->state_lock
) != lock_pos
)
2147 if (skip_canceled
&& g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_CANCELLED
) {
2148 g_atomic_int_inc(&r
->state_lock
);
2152 g_atomic_int_set(&r
->state
, state
);
2154 data_tok
.integer
= state
;
2155 CATCH (lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_state_attached
, data_tok
));
2157 g_atomic_int_inc(&r
->state_lock
);
2163 lqr_carver_set_state_attached (LqrCarver
* r
, LqrDataTok data
)
2165 g_atomic_int_set (&r
->state
, data
.integer
);
2169 /* cancel the current action from a different thread */
2172 lqr_carver_cancel (LqrCarver
* r
)
2174 LqrCarverState curr_state
;
2176 CATCH_F (r
->root
== NULL
);
2178 curr_state
= g_atomic_int_get (&r
->state
);
2180 if ((curr_state
== LQR_CARVER_STATE_RESIZING
) ||
2181 (curr_state
== LQR_CARVER_STATE_INFLATING
) ||
2182 (curr_state
== LQR_CARVER_STATE_TRANSPOSING
) ||
2183 (curr_state
== LQR_CARVER_STATE_FLATTENING
))
2185 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_CANCELLED
, TRUE
));
2190 /* get current size */
2193 lqr_carver_get_width(LqrCarver
* r
)
2195 return (r
->transposed
? r
->h
: r
->w
);
2200 lqr_carver_get_height(LqrCarver
* r
)
2202 return (r
->transposed
? r
->w
: r
->h
);
2205 /* get reference size */
2208 lqr_carver_get_ref_width(LqrCarver
* r
)
2210 return (r
->transposed
? r
->h_start
: r
->w_start
);
2215 lqr_carver_get_ref_height(LqrCarver
* r
)
2217 return (r
->transposed
? r
->w_start
: r
->h_start
);
2220 /* get colour channels */
2223 lqr_carver_get_channels (LqrCarver
* r
)
2230 lqr_carver_get_bpp (LqrCarver
* r
)
2232 return lqr_carver_get_channels(r
);
2235 /* get colour depth */
2238 lqr_carver_get_col_depth (LqrCarver
* r
)
2240 return r
->col_depth
;
2243 /* get enlargement step */
2246 lqr_carver_get_enl_step (LqrCarver
* r
)
2251 /* get orientation */
2254 lqr_carver_get_orientation (LqrCarver
* r
)
2256 return (r
->transposed
? 1 : 0);
2262 lqr_carver_get_depth (LqrCarver
*r
)
2264 return r
->w0
- r
->w_start
;
2271 lqr_carver_scan_reset (LqrCarver
* r
)
2273 lqr_cursor_reset (r
->c
);
2277 lqr_carver_scan_reset_attached (LqrCarver
* r
, LqrDataTok data
)
2279 lqr_carver_scan_reset(r
);
2280 return lqr_carver_list_foreach(r
->attached_list
, lqr_carver_scan_reset_attached
, data
);
2284 lqr_carver_scan_reset_all (LqrCarver
*r
)
2288 lqr_carver_scan_reset(r
);
2289 lqr_carver_list_foreach(r
->attached_list
, lqr_carver_scan_reset_attached
, data
);
2294 /* readout all, pixel by bixel */
2297 lqr_carver_scan (LqrCarver
* r
, gint
* x
, gint
* y
, guchar
** rgb
)
2300 if (r
->col_depth
!= LQR_COLDEPTH_8I
)
2306 lqr_carver_scan_reset (r
);
2309 (*x
) = (r
->transposed
? r
->c
->y
: r
->c
->x
);
2310 (*y
) = (r
->transposed
? r
->c
->x
: r
->c
->y
);
2311 for (k
= 0; k
< r
->channels
; k
++)
2313 AS_8I(r
->rgb_ro_buffer
)[k
] = AS_8I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
];
2315 (*rgb
) = AS_8I(r
->rgb_ro_buffer
);
2316 lqr_cursor_next(r
->c
);
2322 lqr_carver_scan_ext (LqrCarver
* r
, gint
* x
, gint
* y
, void ** rgb
)
2327 lqr_carver_scan_reset (r
);
2330 (*x
) = (r
->transposed
? r
->c
->y
: r
->c
->x
);
2331 (*y
) = (r
->transposed
? r
->c
->x
: r
->c
->y
);
2332 for (k
= 0; k
< r
->channels
; k
++)
2334 PXL_COPY(r
->rgb_ro_buffer
, k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
2337 BUF_POINTER_COPY(rgb
, r
->rgb_ro_buffer
, r
->col_depth
);
2339 lqr_cursor_next(r
->c
);
2343 /* readout all, by line */
2346 lqr_carver_scan_by_row (LqrCarver
*r
)
2348 return r
->transposed
? FALSE
: TRUE
;
2353 lqr_carver_scan_line (LqrCarver
* r
, gint
* n
, guchar
** rgb
)
2355 if (r
->col_depth
!= LQR_COLDEPTH_8I
)
2359 return lqr_carver_scan_line_ext (r
, n
, (void**) rgb
);
2364 lqr_carver_scan_line_ext (LqrCarver
* r
, gint
* n
, void ** rgb
)
2369 lqr_carver_scan_reset (r
);
2376 lqr_cursor_prev(r
->c
);
2379 for (x
= 0; x
< r
->w
; x
++)
2381 for (k
= 0; k
< r
->channels
; k
++)
2383 PXL_COPY(r
->rgb_ro_buffer
, x
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
2385 lqr_cursor_next(r
->c
);
2389 BUF_POINTER_COPY(rgb
, r
->rgb_ro_buffer
, r
->col_depth
);
2394 #ifdef __LQR_DEBUG__
2395 void lqr_carver_debug_check_rows(LqrCarver
* r
)
2399 for (y
= 0; y
< r
->h
; y
++)
2401 for (x
= 0; x
< r
->w
; x
++)
2403 data
= r
->raw
[y
][x
];
2404 if (data
/ r
->w0
!= y
)
2408 assert(data
/ r
->w0
== y
);
2412 #endif /* __LQR_DEBUG__ */
2415 /**** END OF LQR_CARVER CLASS FUNCTIONS ****/