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);
57 r
->resize_aux_layers
= FALSE
;
58 r
->dump_vmaps
= FALSE
;
59 r
->resize_order
= LQR_RES_ORDER_HOR
;
60 r
->attached_list
= NULL
;
62 r
->preserve_in_buffer
= FALSE
;
63 TRY_N_N (r
->progress
= lqr_progress_new());
73 r
->rigidity_map
= NULL
;
74 r
->rigidity_mask
= NULL
;
79 r
->channels
= channels
;
90 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_XABS
);
95 r
->lr_switch_frequency
= 0;
99 TRY_N_N (r
->vs
= g_try_new0 (gint
, r
->w
* r
->h
));
101 /* initialize cursor */
103 TRY_N_N (r
->c
= lqr_cursor_create (r
));
108 lqr_carver_set_image_type (r
, LQR_GREY_IMAGE
);
111 lqr_carver_set_image_type (r
, LQR_GREYA_IMAGE
);
114 lqr_carver_set_image_type (r
, LQR_RGB_IMAGE
);
117 lqr_carver_set_image_type (r
, LQR_RGBA_IMAGE
);
120 lqr_carver_set_image_type (r
, LQR_CMYKA_IMAGE
);
123 lqr_carver_set_image_type (r
, LQR_CUSTOM_IMAGE
);
132 lqr_carver_new (guchar
* buffer
, gint width
, gint height
, gint channels
)
134 return lqr_carver_new_ext (buffer
, width
, height
, channels
, LQR_COLDEPTH_8I
);
139 lqr_carver_new_ext (void * buffer
, gint width
, gint height
, gint channels
, LqrColDepth colour_depth
)
143 TRY_N_N (r
= lqr_carver_new_common (width
, height
, channels
));
145 r
->rgb
= (void*) buffer
;
147 BUF_TRY_NEW_RET_POINTER(r
->rgb_ro_buffer
, r
->channels
* r
->w
, colour_depth
);
149 r
->col_depth
= colour_depth
;
157 lqr_carver_destroy (LqrCarver
* r
)
159 if (!r
->preserve_in_buffer
)
172 lqr_cursor_destroy (r
->c
);
175 if (r
->rigidity_map
!= NULL
)
177 r
->rigidity_map
-= r
->delta_x
;
178 g_free (r
->rigidity_map
);
180 g_free (r
->rigidity_mask
);
181 lqr_rwindow_destroy (r
->rwindow
);
182 g_free (r
->nrg_xmin
);
183 g_free (r
->nrg_xmax
);
184 lqr_vmap_list_destroy(r
->flushed_vs
);
185 lqr_carver_list_destroy(r
->attached_list
);
186 g_free (r
->progress
);
192 /*** initialization ***/
196 lqr_carver_init (LqrCarver
*r
, gint delta_x
, gfloat rigidity
)
202 CATCH_F (r
->active
== FALSE
);
204 CATCH_MEM (r
->en
= g_try_new (gfloat
, r
->w
* r
->h
));
205 CATCH_MEM (r
->bias
= g_try_new0 (gfloat
, r
->w
* r
->h
));
206 CATCH_MEM (r
->m
= g_try_new (gfloat
, r
->w
* r
->h
));
207 CATCH_MEM (r
->least
= g_try_new (gint
, r
->w
* r
->h
));
209 CATCH_MEM (r
->_raw
= g_try_new (gint
, r
->h_start
* r
->w_start
));
210 CATCH_MEM (r
->raw
= g_try_new (gint
*, r
->h_start
));
212 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
++)
217 r
->raw
[y
][x
] = y
* r
->w_start
+ x
;
221 CATCH_MEM (r
->vpath
= g_try_new (gint
, r
->h
));
222 CATCH_MEM (r
->vpath_x
= g_try_new (gint
, r
->h
));
224 CATCH_MEM (r
->nrg_xmin
= g_try_new (gint
, r
->h
));
225 CATCH_MEM (r
->nrg_xmax
= g_try_new (gint
, r
->h
));
227 /* set rigidity map */
228 r
->delta_x
= delta_x
;
229 r
->rigidity
= rigidity
;
231 r
->rigidity_map
= g_try_new0 (gfloat
, 2 * r
->delta_x
+ 1);
232 r
->rigidity_map
+= r
->delta_x
;
233 for (x
= -r
->delta_x
; x
<= r
->delta_x
; x
++)
236 r
->rigidity
* powf(fabsf(x
), 1.5) / r
->h
;
244 /*** set attributes ***/
248 lqr_carver_set_image_type (LqrCarver
* r
, LqrImageType image_type
)
252 switch (image_type
) {
254 if (r
->channels
!= 1) {
257 r
->alpha_channel
= -1;
258 r
->black_channel
= -1;
260 case LQR_GREYA_IMAGE
:
261 if (r
->channels
!= 2)
265 r
->alpha_channel
= 1;
266 r
->black_channel
= -1;
270 if (r
->channels
!= 3)
274 r
->alpha_channel
= -1;
275 r
->black_channel
= -1;
278 if (r
->channels
!= 4)
282 r
->alpha_channel
= -1;
283 r
->black_channel
= 3;
286 if (r
->channels
!= 4)
290 r
->alpha_channel
= 3;
291 r
->black_channel
= -1;
293 case LQR_CMYKA_IMAGE
:
294 if (r
->channels
!= 5)
298 r
->alpha_channel
= 4;
299 r
->black_channel
= 3;
301 case LQR_CUSTOM_IMAGE
:
302 r
->alpha_channel
= r
->channels
- 1;
303 r
->black_channel
= -1;
308 r
->image_type
= image_type
;
315 lqr_carver_set_alpha_channel (LqrCarver
* r
, gint channel_index
)
319 if (channel_index
< 0) {
320 r
->alpha_channel
= -1;
321 } else if (channel_index
< r
->channels
) {
322 r
->alpha_channel
= channel_index
;
326 r
->image_type
= LQR_CUSTOM_IMAGE
;
332 lqr_carver_set_black_channel (LqrCarver
* r
, gint channel_index
)
336 if (channel_index
< 0) {
337 r
->black_channel
= -1;
338 } else if (channel_index
< r
->channels
) {
339 r
->black_channel
= channel_index
;
343 r
->image_type
= LQR_CUSTOM_IMAGE
;
347 /* set gradient function */
348 /* WARNING: THIS FUNCTION IS ONLY MAINTAINED FOR BACK-COMPATIBILITY PURPOSES */
349 /* lqr_carver_set_energy_function_builtin() should be used in newly written code instead */
352 lqr_carver_set_gradient_function (LqrCarver
* r
, LqrGradFuncType gf_ind
)
357 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_NORM
);
360 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_SUMABS
);
363 lqr_carver_set_energy_function_builtin(r
, LQR_EF_GRAD_XABS
);
366 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
368 case LQR_GF_NORM_BIAS
:
370 lqr_carver_set_energy_function_builtin(r
, LQR_EF_NULL
);
375 #endif /* __LQR_DEBUG__ */
379 /* attach carvers to be scaled along with the main one */
382 lqr_carver_attach (LqrCarver
* r
, LqrCarver
* aux
)
384 CATCH_F (r
->w0
== aux
->w0
);
385 CATCH_F (r
->h0
== aux
->h0
);
386 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
387 CATCH_F (g_atomic_int_get(&aux
->state
) == LQR_CARVER_STATE_STD
);
388 CATCH_MEM (r
->attached_list
= lqr_carver_list_append (r
->attached_list
, aux
));
396 /* set the seam output flag */
399 lqr_carver_set_dump_vmaps (LqrCarver
*r
)
401 r
->dump_vmaps
= TRUE
;
404 /* unset the seam output flag */
407 lqr_carver_set_no_dump_vmaps (LqrCarver
*r
)
409 r
->dump_vmaps
= FALSE
;
412 /* set order if rescaling in both directions */
415 lqr_carver_set_resize_order (LqrCarver
*r
, LqrResizeOrder resize_order
)
417 r
->resize_order
= resize_order
;
420 /* set leftright switch interval */
423 lqr_carver_set_side_switch_frequency (LqrCarver
*r
, guint switch_frequency
)
425 r
->lr_switch_frequency
= switch_frequency
;
428 /* set enlargement step */
431 lqr_carver_set_enl_step (LqrCarver
*r
, gfloat enl_step
)
433 CATCH_F ((enl_step
> 1) && (enl_step
<= 2));
435 r
->enl_step
= enl_step
;
441 lqr_carver_set_use_cache (LqrCarver
*r
, gboolean use_cache
)
448 r
->use_rcache
= use_cache
;
449 r
->rwindow
->use_rcache
= use_cache
;
452 /* set progress reprot */
455 lqr_carver_set_progress (LqrCarver
*r
, LqrProgress
*p
)
461 /* flag the input buffer to avoid destruction */
464 lqr_carver_set_preserve_input_image(LqrCarver
*r
)
466 r
->preserve_in_buffer
= TRUE
;
470 /*** compute maps (energy, minpath & visibility) ***/
472 /* build multisize image up to given depth
473 * it is progressive (can be called multilple times) */
475 lqr_carver_build_maps (LqrCarver
* r
, gint depth
)
478 assert (depth
<= r
->w_start
);
480 #endif /* __LQR_DEBUG__ */
484 /* only go deeper if needed */
485 if (depth
> r
->max_level
)
488 CATCH_F (r
->root
== NULL
);
490 /* set to minimum width reached so far */
491 lqr_carver_set_width (r
, r
->w_start
- r
->max_level
+ 1);
493 /* compute energy & minpath maps */
494 CATCH (lqr_carver_build_emap (r
));
495 CATCH (lqr_carver_build_mmap (r
));
497 /* compute visibility map */
498 CATCH (lqr_carver_build_vsmap (r
, depth
));
503 /* compute energy map */
505 lqr_carver_build_emap (LqrCarver
* r
)
511 if (r
->use_rcache
&& r
->rcache
== NULL
)
513 CATCH_MEM (r
->rcache
= lqr_carver_generate_rcache (r
));
516 for (y
= 0; y
< r
->h
; y
++)
519 for (x
= 0; x
< r
->w
; x
++)
521 CATCH (lqr_carver_compute_e(r
, x
, y
));
529 lqr_carver_compute_e (LqrCarver
* r
, gint x
, gint y
)
533 /* removed CANC check for performance reasons */
534 /* CATCH_CANC (r); */
538 CATCH (lqr_rwindow_fill (r
->rwindow
, r
, x
, y
));
539 r
->en
[data
] = r
->nrg(x
, y
, r
->w
, r
->h
, r
->rwindow
, r
->nrg_extra_data
) + r
->bias
[data
] / r
->w_start
;
544 /* compute auxiliary minpath map
546 * y = 1 : m(x,y) = e(x,y)
547 * y > 1 : m(x,y) = min_{x'=-dx,..,dx} ( m(x-x',y-1) + rig(x') ) + e(x,y)
549 * e(x,y) is the energy at point (x,y)
550 * dx is the max seam step delta_x
551 * rig(x') is the rigidity for step x'
554 lqr_carver_build_mmap (LqrCarver
* r
)
559 gint x1_min
, x1_max
, x1
;
560 gfloat m
, m1
, r_fact
;
566 for (x
= 0; x
< r
->w
; x
++)
570 assert (r
->vs
[data
] == 0);
571 #endif /* __LQR_DEBUG__ */
572 r
->m
[data
] = r
->en
[data
];
575 /* span all other rows */
576 for (y
= 1; y
< r
->h
; y
++)
578 for (x
= 0; x
< r
->w
; x
++)
584 assert (r
->vs
[data
] == 0);
585 #endif /* __LQR_DEBUG__ */
586 /* watch for boundaries */
587 x1_min
= MAX (-x
, -r
->delta_x
);
588 x1_max
= MIN (r
->w
- 1 - x
, r
->delta_x
);
589 if (r
->rigidity_mask
) {
590 r_fact
= r
->rigidity_mask
[data
];
595 /* we use the data_down pointer to be able to
596 * track the seams later (needed for rigidity) */
597 data_down
= r
->raw
[y
- 1][x
+ x1_min
];
598 r
->least
[data
] = data_down
;
601 m
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1_min
];
602 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++)
604 data_down
= r
->raw
[y
- 1][x
+ x1
];
605 /* find the min among the neighbors
607 m1
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1
];
608 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
611 r
->least
[data
] = data_down
;
613 /* m = MIN(m, r->m[data_down] + r->rigidity_map[x1]); */
619 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++)
621 data_down
= r
->raw
[y
- 1][x
+ x1
];
622 /* find the min among the neighbors
624 m1
= r
->m
[data_down
];
625 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
628 r
->least
[data
] = data_down
;
630 m
= MIN (m
, r
->m
[data_down
]);
635 r
->m
[data
] = r
->en
[data
] + m
;
641 /* compute (vertical) visibility map up to given depth
642 * (it also calls inflate() to add image enlargment information) */
644 lqr_carver_build_vsmap (LqrCarver
* r
, gint depth
)
648 gint lr_switch_interval
= 0;
651 #ifdef __LQR_VERBOSE__
652 printf ("[ building visibility map ]\n");
654 #endif /* __LQR_VERBOSE__ */
658 assert (depth
<= r
->w_start
+ 1);
660 #endif /* __LQR_DEBUG__ */
662 /* default behaviour : compute all possible levels
666 depth
= r
->w_start
+ 1;
669 /* here we assume that
670 * lqr_carver_set_width(w_start - max_level + 1);
673 /* update step for progress reprt*/
674 update_step
= (gint
) MAX ((depth
- r
->max_level
) * r
->progress
->update_step
, 1);
676 /* left-right switch interval */
677 if (r
->lr_switch_frequency
)
679 lr_switch_interval
= (depth
- r
->max_level
- 1) / r
->lr_switch_frequency
+ 1;
682 /* cycle over levels */
683 for (l
= r
->max_level
; l
< depth
; l
++)
687 if ((l
- r
->max_level
) % update_step
== 0)
689 lqr_progress_update (r
->progress
, (gdouble
) (l
- r
->max_level
) /
690 (gdouble
) (depth
- r
->max_level
));
695 lqr_carver_debug_check_rows (r
);
696 #endif /* __LQR_DEBUG__ */
698 /* compute vertical seam */
699 lqr_carver_build_vpath (r
);
701 /* update visibility map
702 * (assign level to the seam) */
703 lqr_carver_update_vsmap (r
, l
+ r
->max_level
- 1);
705 /* increase (in)visibility level
706 * (make the last seam invisible) */
710 /* update raw data */
711 lqr_carver_carve (r
);
715 /* update the energy */
716 /* CATCH (lqr_carver_build_emap (r)); */
717 CATCH (lqr_carver_update_emap (r
));
719 if (r
->nrg_builtin_flag
)
721 /* recalculate the minpath map */
722 if ((r
->lr_switch_frequency
) && (((l
- r
->max_level
+ lr_switch_interval
/ 2) % lr_switch_interval
) == 0))
725 CATCH (lqr_carver_build_mmap (r
));
729 /* lqr_carver_build_mmap (r); */
730 CATCH (lqr_carver_update_mmap (r
));
733 if ((r
->lr_switch_frequency
) && (((l
- r
->max_level
+ lr_switch_interval
/ 2) % lr_switch_interval
) == 0))
737 CATCH (lqr_carver_build_mmap (r
));
742 /* complete the map (last seam) */
743 lqr_carver_finish_vsmap (r
);
747 /* insert seams for image enlargement */
748 CATCH (lqr_carver_inflate (r
, depth
- 1));
750 /* reset image size */
751 lqr_carver_set_width (r
, r
->w_start
);
752 /* repeat for auxiliary layers */
753 data_tok
.integer
= r
->w_start
;
754 CATCH (lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_width_attached
, data_tok
));
756 #ifdef __LQR_VERBOSE__
757 printf ("[ visibility map OK ]\n");
759 #endif /* __LQR_VERBOSE__ */
764 /* enlarge the image by seam insertion
765 * visibility map is updated and the resulting multisize image
766 * is complete in both directions */
768 lqr_carver_inflate (LqrCarver
* r
, gint l
)
773 void *new_rgb
= NULL
;
776 gfloat
*new_bias
= NULL
;
777 gfloat
*new_rigmask
= NULL
;
779 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
781 #ifdef __LQR_VERBOSE__
782 printf (" [ inflating (active=%i) ]\n", r
->active
);
784 #endif /* __LQR_VERBOSE__ */
787 assert (l
+ 1 > r
->max_level
); /* otherwise is useless */
788 #endif /* __LQR_DEBUG__ */
794 prev_state
= g_atomic_int_get(&r
->state
);
795 CATCH (lqr_carver_set_state(r
, LQR_CARVER_STATE_INFLATING
, TRUE
));
798 /* first iterate on attached carvers */
799 data_tok
.integer
= l
;
800 CATCH (lqr_carver_list_foreach (r
->attached_list
, lqr_carver_inflate_attached
, data_tok
));
802 /* scale to current maximum size
803 * (this is the original size the first time) */
804 lqr_carver_set_width (r
, r
->w0
);
807 w1
= r
->w0
+ l
- r
->max_level
+ 1;
809 /* allocate room for new maps */
810 BUF_TRY_NEW0_RET_LQR(new_rgb
, w1
* r
->h0
* r
->channels
, r
->col_depth
);
814 CATCH_MEM (new_vs
= g_try_new0 (gint
, w1
* r
->h0
));
818 CATCH_MEM (new_bias
= g_try_new0 (gfloat
, w1
* r
->h0
));
819 if (r
->rigidity_mask
)
821 CATCH_MEM (new_rigmask
= g_try_new (gfloat
, w1
* r
->h0
));
825 /* span the image with a cursor
826 * and build the new image */
827 lqr_cursor_reset (r
->c
);
830 for (z0
= 0; z0
< w1
* r
->h0
; z0
++, lqr_cursor_next (r
->c
))
835 /* read visibility */
836 vs
= r
->vs
[r
->c
->now
];
837 if ((vs
!= 0) && (vs
<= l
+ r
->max_level
- 1)
838 && (vs
>= 2 * r
->max_level
- 1))
840 /* the point belongs to a previously computed seam
841 * and was not inserted during a previous
842 * inflate() call : insert another seam */
844 /* the new pixel value is equal to the average of its
845 * left and right neighbors */
849 c_left
= lqr_cursor_left (r
->c
);
856 for (k
= 0; k
< r
->channels
; k
++)
858 switch (r
->col_depth
)
860 case LQR_COLDEPTH_8I
:
861 tmp_rgb
= (AS_8I(r
->rgb
)[c_left
* r
->channels
+ k
] +
862 AS_8I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
863 AS_8I(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_8i
) (tmp_rgb
+ 0.499999);
865 case LQR_COLDEPTH_16I
:
866 tmp_rgb
= (AS_16I(r
->rgb
)[c_left
* r
->channels
+ k
] +
867 AS_16I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
868 AS_16I(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_16i
) (tmp_rgb
+ 0.499999);
870 case LQR_COLDEPTH_32F
:
871 tmp_rgb
= (AS_32F(r
->rgb
)[c_left
* r
->channels
+ k
] +
872 AS_32F(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
873 AS_32F(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_32f
) tmp_rgb
;
875 case LQR_COLDEPTH_64F
:
876 tmp_rgb
= (AS_64F(r
->rgb
)[c_left
* r
->channels
+ k
] +
877 AS_64F(r
->rgb
)[r
->c
->now
* r
->channels
+ k
]) / 2;
878 AS_64F(new_rgb
)[z0
* r
->channels
+ k
] = (lqr_t_64f
) tmp_rgb
;
884 new_bias
[z0
] = (r
->bias
[c_left
] + r
->bias
[r
->c
->now
]) / 2;
885 if (r
->rigidity_mask
)
887 new_rigmask
[z0
] = (r
->rigidity_mask
[c_left
] + r
->rigidity_mask
[r
->c
->now
]) / 2;
890 /* the first time inflate() is called
891 * the new visibility should be -vs + 1 but we shift it
892 * so that the final minimum visibiliy will be 1 again
893 * and so that vs=0 still means "uninitialized".
894 * Subsequent inflations account for that */
897 new_vs
[z0
] = l
- vs
+ r
->max_level
;
901 for (k
= 0; k
< r
->channels
; k
++)
903 PXL_COPY(new_rgb
, z0
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
907 new_bias
[z0
] = r
->bias
[r
->c
->now
];
908 if (r
->rigidity_mask
)
910 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
915 /* visibility has to be shifted up */
918 new_vs
[z0
] = vs
+ l
- r
->max_level
+ 1;
921 else if (r
->raw
!= NULL
)
924 assert (y
< r
->h_start
);
925 assert (x
< r
->w_start
- l
);
926 #endif /* __LQR_DEBUG__ */
929 if (x
>= r
->w_start
- l
)
941 if (w1
!= 2 * r
->w_start
- 1)
943 assert ((y
== r
->h_start
)
944 || (printf ("y=%i hst=%i w1=%i\n", y
, r
->h_start
, w1
)
945 && fflush (stdout
) && 0));
948 #endif /* __LQR_DEBUG__ */
950 /* substitute maps */
951 if (!r
->preserve_in_buffer
)
955 /* g_free (r->vs); */
961 g_free (r
->rigidity_mask
);
966 r
->preserve_in_buffer
= FALSE
;
972 CATCH (lqr_carver_propagate_vsmap(r
));
981 r
->rigidity_mask
= new_rigmask
;
982 CATCH_MEM (r
->en
= g_try_new0 (gfloat
, w1
* r
->h0
));
983 CATCH_MEM (r
->m
= g_try_new0 (gfloat
, w1
* r
->h0
));
984 CATCH_MEM (r
->least
= g_try_new0 (gint
, w1
* r
->h0
));
987 /* set new widths & levels (w_start is kept for reference) */
989 r
->max_level
= l
+ 1;
993 /* reset readout buffer */
994 g_free (r
->rgb_ro_buffer
);
995 BUF_TRY_NEW0_RET_LQR(r
->rgb_ro_buffer
, r
->w0
* r
->channels
, r
->col_depth
);
997 #ifdef __LQR_VERBOSE__
998 printf (" [ inflating OK ]\n");
1000 #endif /* __LQR_VERBOSE__ */
1002 if (r
->root
== NULL
)
1004 CATCH (lqr_carver_set_state(r
, prev_state
, TRUE
));
1011 lqr_carver_inflate_attached (LqrCarver
* r
, LqrDataTok data
)
1013 return lqr_carver_inflate (r
, data
.integer
);
1017 /*** internal functions for maps computations ***/
1020 /* read average pixel value at x, y
1021 * for energy computation */
1023 lqr_carver_read (LqrCarver
* r
, gint x
, gint y
)
1027 gint now
= r
->raw
[y
][x
];
1028 for (k
= 0; k
< r
->channels
; k
++)
1030 switch (r
->col_depth
)
1032 case LQR_COLDEPTH_8I
:
1033 sum
+= AS_8I(r
->rgb
)[now
* r
->channels
+ k
];
1035 case LQR_COLDEPTH_16I
:
1036 sum
+= AS_16I(r
->rgb
)[now
* r
->channels
+ k
];
1038 case LQR_COLDEPTH_32F
:
1039 sum
+= AS_32F(r
->rgb
)[now
* r
->channels
+ k
];
1041 case LQR_COLDEPTH_64F
:
1042 sum
+= AS_64F(r
->rgb
)[now
* r
->channels
+ k
];
1046 switch (r
->col_depth
)
1048 case LQR_COLDEPTH_8I
:
1049 sum
/= (255 * r
->channels
);
1051 case LQR_COLDEPTH_16I
:
1052 sum
/= ((gdouble
)(0xFFFF) * r
->channels
);
1054 case LQR_COLDEPTH_32F
:
1055 case LQR_COLDEPTH_64F
:
1064 /* compute energy at x, y */
1066 lqr_carver_compute_e (LqrCarver
* r
, gint x
, gint y
)
1073 gy
= lqr_carver_read (r
, x
, y
+ 1) - lqr_carver_read (r
, x
, y
);
1075 else if (y
< r
->h
- 1)
1078 (lqr_carver_read (r
, x
, y
+ 1) - lqr_carver_read (r
, x
, y
- 1)) / 2;
1082 gy
= lqr_carver_read (r
, x
, y
) - lqr_carver_read (r
, x
, y
- 1);
1087 gx
= lqr_carver_read (r
, x
+ 1, y
) - lqr_carver_read (r
, x
, y
);
1089 else if (x
< r
->w
- 1)
1092 (lqr_carver_read (r
, x
+ 1, y
) - lqr_carver_read (r
, x
- 1, y
)) / 2;
1096 gx
= lqr_carver_read (r
, x
, y
) - lqr_carver_read (r
, x
- 1, y
);
1098 data
= r
->raw
[y
][x
];
1099 r
->en
[data
] = (*(r
->gf
)) (gx
, gy
) + r
->bias
[data
] / r
->w_start
;
1104 * this actually carves the raw array,
1105 * which holds the indices to be used
1106 * in all the other maps */
1108 lqr_carver_carve (LqrCarver
* r
)
1112 #ifdef __LQR_DEBUG__
1113 assert (r
->root
== NULL
);
1114 #endif /* __LQR_DEBUG__ */
1116 for (y
= 0; y
< r
->h_start
; y
++)
1118 #ifdef __LQR_DEBUG__
1119 assert (r
->vs
[r
->raw
[y
][r
->vpath_x
[y
]]] != 0);
1120 for (x
= 0; x
< r
->vpath_x
[y
]; x
++)
1122 assert (r
->vs
[r
->raw
[y
][x
]] == 0);
1124 #endif /* __LQR_DEBUG__ */
1125 for (x
= r
->vpath_x
[y
]; x
< r
->w
; x
++)
1127 r
->raw
[y
][x
] = r
->raw
[y
][x
+ 1];
1128 #ifdef __LQR_DEBUG__
1129 assert (r
->vs
[r
->raw
[y
][x
]] == 0);
1130 #endif /* __LQR_DEBUG__ */
1136 /* update energy map after seam removal */
1138 lqr_carver_update_emap (LqrCarver
* r
)
1141 gint x1
, y1
, y1_min
, y1_max
;
1145 CATCH_F (r
->rcache
!= NULL
);
1150 for (y
= 0; y
< r
->h
; y
++)
1152 /* note: here the vpath has already
1156 r
->nrg_xmax
[y
] = x
- 1;
1158 for (y
= 0; y
< r
->h
; y
++)
1161 y1_min
= MAX (y
- r
->nrg_radius
, 0);
1162 y1_max
= MIN (y
+ r
->nrg_radius
, r
->h
- 1);
1164 for (y1
= y1_min
; y1
<= y1_max
; y1
++)
1166 r
->nrg_xmin
[y1
] = MIN (r
->nrg_xmin
[y1
], x
- r
->nrg_radius
);
1167 r
->nrg_xmin
[y1
] = MAX (0, r
->nrg_xmin
[y1
]);
1168 /* note: the -1 below is because of the previous carving */
1169 r
->nrg_xmax
[y1
] = MAX (r
->nrg_xmax
[y1
], x
+ r
->nrg_radius
- 1);
1170 r
->nrg_xmax
[y1
] = MIN (r
->w
- 1, r
->nrg_xmax
[y1
]);
1174 for (y
= 0; y
< r
->h
; y
++)
1178 for (x1
= r
->nrg_xmin
[y
]; x1
<= r
->nrg_xmax
[y
]; x1
++)
1180 CATCH (lqr_carver_compute_e (r
, x1
, y
));
1187 /* update the auxiliary minpath map
1188 * this only updates the affected pixels,
1189 * which start form the beginning of the seam
1190 * and expand at most by delta_x (in both
1191 * directions) at each row */
1193 lqr_carver_update_mmap (LqrCarver
* r
)
1198 gint x1_min
, x1_max
;
1199 gint data
, data_down
, least
;
1200 gfloat m
, m1
, r_fact
;
1206 /* span first row */
1207 /* x_min = MAX (r->vpath_x[0] - r->delta_x, 0); */
1208 x_min
= MAX (r
->vpath_x
[0] - 1, 0);
1209 /* x_max = MIN (r->vpath_x[0] + r->delta_x - 1, r->w - 1); */
1210 /* x_max = MIN (r->vpath_x[0] + r->delta_x, r->w - 1); */
1211 x_max
= MIN (r
->vpath_x
[0], r
->w
- 1);
1213 for (x
= x_min
; x
<= x_max
; x
++)
1215 data
= r
->raw
[0][x
];
1216 r
->m
[data
] = r
->en
[data
];
1220 for (y
= 1; y
< r
->h
; y
++)
1224 /* make sure to include the seam */
1225 x_min
= MIN (x_min
, MAX(r
->vpath_x
[y
] - 1, 0));
1226 x_max
= MAX (x_max
, MIN(r
->vpath_x
[y
], r
->w
- 1));
1227 /* x_max = MAX (x_max, r->vpath_x[y] - 1); */
1229 /* expand the affected region by delta_x */
1230 x_min
= MAX (x_min
- r
->delta_x
, 0);
1231 x_max
= MIN (x_max
+ r
->delta_x
, r
->w
- 1);
1233 /* span the affected region */
1236 for (x
= x_min
; x
<= x_max
; x
++)
1238 data
= r
->raw
[y
][x
];
1239 if (r
->rigidity_mask
) {
1240 r_fact
= r
->rigidity_mask
[data
];
1245 /* find the minimum in the previous rows
1246 * as in build_mmap() */
1247 x1_min
= MAX (-x
, -r
->delta_x
);
1248 x1_max
= MIN (r
->w
- 1 - x
, r
->delta_x
);
1249 data_down
= r
->raw
[y
- 1][x
+ x1_min
];
1253 m
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1_min
];
1254 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++)
1256 data_down
= r
->raw
[y
- 1][x
+ x1
];
1257 m1
= r
->m
[data_down
] + r_fact
* r
->rigidity_map
[x1
];
1258 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
1267 m
= r
->m
[data_down
];
1268 for (x1
= x1_min
+ 1; x1
<= x1_max
; x1
++)
1270 data_down
= r
->raw
[y
- 1][x
+ x1
];
1271 m1
= r
->m
[data_down
];
1272 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
1280 /* reduce the range if there's no difference
1281 * with the previous map */
1282 if (r
->least
[data
] == least
)
1284 if ((x
== x_min
) && (x
< r
->vpath_x
[y
] - 1)
1285 && (r
->m
[data
] == r
->en
[data
] + m
))
1289 if ((x
> r
->vpath_x
[y
]) && (r
->m
[data
] == r
->en
[data
] + m
))
1308 r
->m
[data
] = r
->en
[data
] + m
;
1309 r
->least
[data
] = least
;
1311 if ((x
== x_max
) && (stop
))
1322 /* compute seam path from minpath map */
1324 lqr_carver_build_vpath (LqrCarver
* r
)
1332 /* we start at last row */
1335 /* span the last row for the minimum mmap value */
1337 for (x
= 0, z0
= y
* r
->w_start
; x
< r
->w
; x
++, z0
++)
1339 #ifdef __LQR_DEBUG__
1340 assert (r
->vs
[r
->raw
[y
][x
]] == 0);
1341 #endif /* __LQR_DEBUG__ */
1343 m1
= r
->m
[r
->raw
[y
][x
]];
1344 if ((m1
< m
) || ((m1
== m
) && (r
->leftright
== 1)))
1346 last
= r
->raw
[y
][x
];
1352 #ifdef __LQR_DEBUG__
1354 #endif /* __LQR_DEBUG__ */
1356 /* follow the track for the other rows */
1357 for (y
= r
->h0
- 1; y
>= 0; y
--)
1359 #ifdef __LQR_DEBUG__
1360 assert (r
->vs
[last
] == 0);
1361 assert (last_x
< r
->w
);
1362 #endif /* __LQR_DEBUG__ */
1364 r
->vpath_x
[y
] = last_x
;
1367 last
= r
->least
[r
->raw
[y
][last_x
]];
1368 /* we also need to retrieve the x coordinate */
1369 x_min
= MAX (last_x
- r
->delta_x
, 0);
1370 x_max
= MIN (last_x
+ r
->delta_x
, r
->w
- 1);
1371 for (x
= x_min
; x
<= x_max
; x
++)
1373 if (r
->raw
[y
- 1][x
] == last
)
1379 #ifdef __LQR_DEBUG__
1380 assert (x
< x_max
+ 1);
1381 #endif /* __LQR_DEBUG__ */
1387 /* we backtrack the seam following the min mmap */
1388 for (y
= r
->h0
- 1; y
>= 0; y
--)
1390 #ifdef __LQR_DEBUG__
1391 assert (r
->vs
[last
] == 0);
1392 assert (last_x
< r
->w
);
1393 #endif /* __LQR_DEBUG__ */
1396 r
->vpath_x
[y
] = last_x
;
1400 x_min
= MAX (0, last_x
- r
->delta_x
);
1401 x_max
= MIN (r
->w
- 1, last_x
+ r
->delta_x
);
1402 for (x
= x_min
; x
<= x_max
; x
++)
1404 m1
= r
->m
[r
->raw
[y
- 1][x
]];
1407 last
= r
->raw
[y
- 1][x
];
1417 /* update visibility map after seam computation */
1419 lqr_carver_update_vsmap (LqrCarver
* r
, gint l
)
1422 #ifdef __LQR_DEBUG__
1423 assert(r
->root
== NULL
);
1424 #endif /* __LQR_DEBUG__ */
1425 for (y
= 0; y
< r
->h
; y
++)
1427 #ifdef __LQR_DEBUG__
1428 assert (r
->vs
[r
->vpath
[y
]] == 0);
1429 assert (r
->vpath
[y
] == r
->raw
[y
][r
->vpath_x
[y
]]);
1430 #endif /* __LQR_DEBUG__ */
1431 r
->vs
[r
->vpath
[y
]] = l
;
1435 /* complete visibility map (last seam) */
1436 /* set the last column of pixels to vis. level w0 */
1438 lqr_carver_finish_vsmap (LqrCarver
* r
)
1442 #ifdef __LQR_DEBUG__
1444 assert (r
->root
== NULL
);
1445 #endif /* __LQR_DEBUG__ */
1446 lqr_cursor_reset (r
->c
);
1447 for (y
= 1; y
<= r
->h
; y
++, lqr_cursor_next (r
->c
))
1449 #ifdef __LQR_DEBUG__
1450 assert (r
->vs
[r
->c
->now
] == 0);
1451 #endif /* __LQR_DEBUG__ */
1452 r
->vs
[r
->c
->now
] = r
->w0
;
1454 lqr_cursor_reset (r
->c
);
1457 /* propagate the root carver's visibility map */
1459 lqr_carver_propagate_vsmap (LqrCarver
* r
)
1461 LqrDataTok data_tok
;
1465 data_tok
.data
= NULL
;
1466 CATCH (lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_propagate_vsmap_attached
, data_tok
));
1471 lqr_carver_propagate_vsmap_attached (LqrCarver
* r
, LqrDataTok data
)
1473 LqrDataTok data_tok
;
1474 data_tok
.data
= NULL
;
1475 r
->vs
= r
->root
->vs
;
1476 lqr_carver_scan_reset(r
);
1477 /* CATCH (lqr_carver_list_foreach (r->attached_list, lqr_carver_propagate_vsmap_attached, data_tok)); */
1481 /*** image manipulations ***/
1483 /* set width of the multisize image
1484 * (maps have to be computed already) */
1486 lqr_carver_set_width (LqrCarver
* r
, gint w1
)
1488 #ifdef __LQR_DEBUG__
1489 assert (w1
<= r
->w0
);
1490 assert (w1
>= r
->w_start
- r
->max_level
+ 1);
1491 #endif /* __LQR_DEBUG__ */
1493 r
->level
= r
->w0
- w1
+ 1;
1497 lqr_carver_set_width_attached (LqrCarver
* r
, LqrDataTok data
)
1499 lqr_carver_set_width (r
, data
.integer
);
1505 /* flatten the image to its current state
1506 * (all maps are reset, invisible points are lost) */
1509 lqr_carver_flatten (LqrCarver
* r
)
1511 void *new_rgb
= NULL
;
1512 gfloat
*new_bias
= NULL
;
1513 gfloat
*new_rigmask
= NULL
;
1516 LqrDataTok data_tok
;
1517 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
1519 #ifdef __LQR_VERBOSE__
1520 printf (" [ flattening (active=%i) ]\n", r
->active
);
1522 #endif /* __LQR_VERBOSE__ */
1526 if (r
->root
== NULL
)
1528 prev_state
= g_atomic_int_get(&r
->state
);
1529 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_FLATTENING
, TRUE
));
1532 /* first iterate on attached carvers */
1533 CATCH (lqr_carver_list_foreach (r
->attached_list
, lqr_carver_flatten_attached
, data_tok
));
1535 /* free non needed maps first */
1543 /* allocate room for new map */
1544 BUF_TRY_NEW0_RET_LQR(new_rgb
, r
->w
* r
->h
* r
->channels
, r
->col_depth
);
1548 CATCH_MEM (new_bias
= g_try_new0 (gfloat
, r
->w
* r
->h
));
1549 if (r
->rigidity_mask
) {
1550 CATCH_MEM (new_rigmask
= g_try_new (gfloat
, r
->w
* r
->h
));
1555 CATCH_MEM (r
->_raw
= g_try_new (gint
, r
->w
* r
->h
));
1556 CATCH_MEM (r
->raw
= g_try_new (gint
*, r
->h
));
1559 /* span the image with the cursor and copy
1560 * it in the new array */
1561 lqr_cursor_reset (r
->c
);
1562 for (y
= 0; y
< r
->h
; y
++)
1568 r
->raw
[y
] = r
->_raw
+ y
* r
->w
;
1570 for (x
= 0; x
< r
->w
; x
++)
1573 for (k
= 0; k
< r
->channels
; k
++)
1575 PXL_COPY(new_rgb
, z0
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
1579 new_bias
[z0
] = r
->bias
[r
->c
->now
];
1580 if (r
->rigidity_mask
) {
1581 new_rigmask
[z0
] = r
->rigidity_mask
[r
->c
->now
];
1585 lqr_cursor_next (r
->c
);
1589 /* substitute the old maps */
1590 if (!r
->preserve_in_buffer
)
1595 r
->preserve_in_buffer
= FALSE
;
1600 if (r
->rigidity_mask
) {
1601 g_free (r
->rigidity_mask
);
1602 r
->rigidity_mask
= new_rigmask
;
1606 /* init the other maps */
1607 if (r
->root
== NULL
)
1610 CATCH_MEM (r
->vs
= g_try_new0 (gint
, r
->w
* r
->h
));
1611 CATCH (lqr_carver_propagate_vsmap(r
));
1615 CATCH_MEM (r
->en
= g_try_new0 (gfloat
, r
->w
* r
->h
));
1616 CATCH_MEM (r
->m
= g_try_new0 (gfloat
, r
->w
* r
->h
));
1617 CATCH_MEM (r
->least
= g_try_new (gint
, r
->w
* r
->h
));
1620 /* reset widths, heights & levels */
1628 #ifdef __LQR_VERBOSE__
1629 printf (" [ flattening OK ]\n");
1631 #endif /* __LQR_VERBOSE__ */
1633 if (r
->root
== NULL
)
1635 CATCH (lqr_carver_set_state (r
, prev_state
, TRUE
));
1642 lqr_carver_flatten_attached(LqrCarver
*r
, LqrDataTok data
)
1644 return lqr_carver_flatten(r
);
1647 /* transpose the image, in its current state
1648 * (all maps and invisible points are lost) */
1650 lqr_carver_transpose (LqrCarver
* r
)
1655 void *new_rgb
= NULL
;
1656 gfloat
*new_bias
= NULL
;
1657 gfloat
*new_rigmask
= NULL
;
1658 LqrDataTok data_tok
;
1659 LqrCarverState prev_state
= LQR_CARVER_STATE_STD
;
1661 #ifdef __LQR_VERBOSE__
1662 printf ("[ transposing (active=%i) ]\n", r
->active
);
1664 #endif /* __LQR_VERBOSE__ */
1668 if (r
->root
== NULL
)
1670 prev_state
= g_atomic_int_get(&r
->state
);
1671 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_TRANSPOSING
, TRUE
));
1676 CATCH (lqr_carver_flatten (r
));
1679 /* first iterate on attached carvers */
1680 CATCH (lqr_carver_list_foreach (r
->attached_list
, lqr_carver_transpose_attached
, data_tok
));
1682 /* free non needed maps first */
1683 if (r
->root
== NULL
)
1691 g_free (r
->rgb_ro_buffer
);
1695 /* allocate room for the new maps */
1696 BUF_TRY_NEW0_RET_LQR(new_rgb
, r
->w0
* r
->h0
* r
->channels
, r
->col_depth
);
1700 CATCH_MEM (new_bias
= g_try_new0 (gfloat
, r
->w0
* r
->h0
));
1701 if (r
->rigidity_mask
)
1703 CATCH_MEM (new_rigmask
= g_try_new (gfloat
, r
->w0
* r
->h0
));
1707 CATCH_MEM (r
->_raw
= g_try_new0 (gint
, r
->h0
* r
->w0
));
1708 CATCH_MEM (r
->raw
= g_try_new0 (gint
*, r
->w0
));
1711 /* compute trasposed maps */
1712 for (x
= 0; x
< r
->w
; x
++)
1716 r
->raw
[x
] = r
->_raw
+ x
* r
->h0
;
1718 for (y
= 0; y
< r
->h
; y
++)
1722 for (k
= 0; k
< r
->channels
; k
++)
1724 PXL_COPY(new_rgb
, z1
* r
->channels
+ k
, r
->rgb
, z0
* r
->channels
+ k
, r
->col_depth
);
1728 new_bias
[z1
] = r
->bias
[z0
];
1729 if (r
->rigidity_mask
) {
1730 new_rigmask
[z1
] = r
->rigidity_mask
[z0
];
1737 /* substitute the map */
1738 if (!r
->preserve_in_buffer
)
1743 r
->preserve_in_buffer
= FALSE
;
1749 if (r
->rigidity_mask
) {
1750 g_free (r
->rigidity_mask
);
1751 r
->rigidity_mask
= new_rigmask
;
1755 /* init the other maps */
1756 if (r
->root
== NULL
)
1758 CATCH_MEM (r
->vs
= g_try_new0 (gint
, r
->w0
* r
->h0
));
1759 CATCH (lqr_carver_propagate_vsmap(r
));
1763 CATCH_MEM (r
->en
= g_try_new0 (gfloat
, r
->w0
* r
->h0
));
1764 CATCH_MEM (r
->m
= g_try_new0 (gfloat
, r
->w0
* r
->h0
));
1765 CATCH_MEM (r
->least
= g_try_new (gint
, r
->w0
* r
->h0
));
1768 /* switch widths & heights */
1775 /* reset w_start, h_start & levels */
1781 /* reset seam path, cursor and readout buffer */
1785 CATCH_MEM (r
->vpath
= g_try_new (gint
, r
->h
));
1786 g_free (r
->vpath_x
);
1787 CATCH_MEM (r
->vpath_x
= g_try_new (gint
, r
->h
));
1788 g_free (r
->nrg_xmin
);
1789 CATCH_MEM (r
->nrg_xmin
= g_try_new (gint
, r
->h
));
1790 g_free (r
->nrg_xmax
);
1791 CATCH_MEM (r
->nrg_xmax
= g_try_new (gint
, r
->h
));
1794 BUF_TRY_NEW0_RET_LQR(r
->rgb_ro_buffer
, r
->w0
* r
->channels
, r
->col_depth
);
1796 /* rescale rigidity */
1800 for (x
= -r
->delta_x
; x
<= r
->delta_x
; x
++)
1802 r
->rigidity_map
[x
] = r
->rigidity_map
[x
] * r
->w0
/ r
->h0
;
1806 /* set transposed flag */
1807 r
->transposed
= (r
->transposed
? 0 : 1);
1809 #ifdef __LQR_VERBOSE__
1810 printf ("[ transpose OK ]\n");
1812 #endif /* __LQR_VERBOSE__ */
1814 if (r
->root
== NULL
)
1816 CATCH (lqr_carver_set_state (r
, prev_state
, TRUE
));
1823 lqr_carver_transpose_attached (LqrCarver
* r
, LqrDataTok data
)
1825 return lqr_carver_transpose(r
);
1828 /* resize w + h: these are the liquid rescale methods.
1829 * They automatically determine the depth of the map
1830 * according to the desired size, can be called multiple
1831 * times, transpose the image as necessasry */
1833 lqr_carver_resize_width (LqrCarver
* r
, gint w1
)
1835 LqrDataTok data_tok
;
1838 /* delta is used to determine the required depth
1839 * gamma to decide if action is necessary */
1842 delta
= w1
- r
->w_start
;
1844 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1848 delta
= w1
- r
->h_start
;
1850 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
1863 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
1864 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_RESIZING
, TRUE
));
1868 gint delta0
= MIN (delta
, delta_max
);
1874 CATCH (lqr_carver_transpose (r
));
1876 new_w
= MIN (w1
, r
->w_start
+ delta_max
);
1878 lqr_progress_init (r
->progress
, r
->progress
->init_width_message
);
1879 CATCH (lqr_carver_build_maps (r
, delta0
+ 1));
1880 lqr_carver_set_width (r
, new_w
);
1882 data_tok
.integer
= new_w
;
1883 lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_width_attached
, data_tok
);
1887 CATCH (lqr_vmap_internal_dump (r
));
1889 lqr_progress_end (r
->progress
, r
->progress
->end_width_message
);
1892 CATCH (lqr_carver_flatten(r
));
1893 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1901 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_STD
, TRUE
));
1907 lqr_carver_resize_height (LqrCarver
* r
, gint h1
)
1909 LqrDataTok data_tok
;
1912 /* delta is used to determine the required depth
1913 * gamma to decide if action is necessary */
1916 delta
= h1
- r
->h_start
;
1918 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->h_start
) - 1;
1922 delta
= h1
- r
->w_start
;
1924 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1934 delta
= delta
> 0 ? delta
: -delta
;
1937 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
1938 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_RESIZING
, TRUE
));
1942 gint delta0
= MIN (delta
, delta_max
);
1947 CATCH (lqr_carver_transpose (r
));
1949 new_w
= MIN (h1
, r
->w_start
+ delta_max
);
1951 lqr_progress_init (r
->progress
, r
->progress
->init_height_message
);
1952 CATCH (lqr_carver_build_maps (r
, delta0
+ 1));
1953 lqr_carver_set_width (r
, new_w
);
1955 data_tok
.integer
= new_w
;
1956 lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_width_attached
, data_tok
);
1960 CATCH (lqr_vmap_internal_dump (r
));
1962 lqr_progress_end (r
->progress
, r
->progress
->end_height_message
);
1965 CATCH (lqr_carver_flatten(r
));
1966 delta_max
= (gint
) ((r
->enl_step
- 1) * r
->w_start
) - 1;
1974 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_STD
, TRUE
));
1979 /* liquid rescale public method */
1982 lqr_carver_resize (LqrCarver
* r
, gint w1
, gint h1
)
1984 #ifdef __LQR_VERBOSE__
1985 printf("[ Rescale from %i,%i to %i,%i ]\n", (r
->transposed
? r
->h
: r
->w
), (r
->transposed
? r
->w
: r
->h
), w1
, h1
);
1987 #endif /* __LQR_VERBOSE__ */
1988 CATCH_F ((w1
>= 1) && (h1
>= 1));
1989 CATCH_F (r
->root
== NULL
);
1992 CATCH_F (g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_STD
);
1994 switch (r
->resize_order
)
1996 case LQR_RES_ORDER_HOR
:
1997 CATCH (lqr_carver_resize_width(r
, w1
));
1998 CATCH (lqr_carver_resize_height(r
, h1
));
2000 case LQR_RES_ORDER_VERT
:
2001 CATCH (lqr_carver_resize_height(r
, h1
));
2002 CATCH (lqr_carver_resize_width(r
, w1
));
2004 #ifdef __LQR_DEBUG__
2007 #endif /* __LQR_DEBUG__ */
2009 lqr_carver_scan_reset_all(r
);
2011 #ifdef __LQR_VERBOSE__
2012 printf("[ Rescale OK ]\n");
2014 #endif /* __LQR_VERBOSE__ */
2019 lqr_carver_set_state (LqrCarver
* r
, LqrCarverState state
, gboolean skip_canceled
)
2021 LqrDataTok data_tok
;
2024 CATCH_F(r
->root
== NULL
);
2026 lock_pos
= g_atomic_int_exchange_and_add(&r
->state_lock_queue
, 1);
2028 while (g_atomic_int_get(&r
->state_lock
) != lock_pos
)
2033 if (skip_canceled
&& g_atomic_int_get(&r
->state
) == LQR_CARVER_STATE_CANCELLED
) {
2034 g_atomic_int_inc(&r
->state_lock
);
2038 g_atomic_int_set(&r
->state
, state
);
2040 data_tok
.integer
= state
;
2041 CATCH (lqr_carver_list_foreach_recursive (r
->attached_list
, lqr_carver_set_state_attached
, data_tok
));
2043 g_atomic_int_inc(&r
->state_lock
);
2049 lqr_carver_set_state_attached (LqrCarver
* r
, LqrDataTok data
)
2051 g_atomic_int_set (&r
->state
, data
.integer
);
2055 /* cancel the current action from a different thread */
2058 lqr_carver_cancel (LqrCarver
* r
)
2060 LqrCarverState curr_state
;
2062 CATCH_F (r
->root
== NULL
);
2064 curr_state
= g_atomic_int_get (&r
->state
);
2066 if ((curr_state
== LQR_CARVER_STATE_RESIZING
) ||
2067 (curr_state
== LQR_CARVER_STATE_INFLATING
) ||
2068 (curr_state
== LQR_CARVER_STATE_TRANSPOSING
) ||
2069 (curr_state
== LQR_CARVER_STATE_FLATTENING
))
2071 CATCH (lqr_carver_set_state (r
, LQR_CARVER_STATE_CANCELLED
, TRUE
));
2076 /* get current size */
2079 lqr_carver_get_width(LqrCarver
* r
)
2081 return (r
->transposed
? r
->h
: r
->w
);
2086 lqr_carver_get_height(LqrCarver
* r
)
2088 return (r
->transposed
? r
->w
: r
->h
);
2091 /* get reference size */
2094 lqr_carver_get_ref_width(LqrCarver
* r
)
2096 return (r
->transposed
? r
->h_start
: r
->w_start
);
2101 lqr_carver_get_ref_height(LqrCarver
* r
)
2103 return (r
->transposed
? r
->w_start
: r
->h_start
);
2106 /* get colour channels */
2109 lqr_carver_get_channels (LqrCarver
* r
)
2116 lqr_carver_get_bpp (LqrCarver
* r
)
2118 return lqr_carver_get_channels(r
);
2121 /* get colour depth */
2124 lqr_carver_get_col_depth (LqrCarver
* r
)
2126 return r
->col_depth
;
2129 /* get enlargement step */
2132 lqr_carver_get_enl_step (LqrCarver
* r
)
2137 /* get orientation */
2140 lqr_carver_get_orientation (LqrCarver
* r
)
2142 return (r
->transposed
? 1 : 0);
2148 lqr_carver_get_depth (LqrCarver
*r
)
2150 return r
->w0
- r
->w_start
;
2157 lqr_carver_scan_reset (LqrCarver
* r
)
2159 lqr_cursor_reset (r
->c
);
2163 lqr_carver_scan_reset_attached (LqrCarver
* r
, LqrDataTok data
)
2165 lqr_carver_scan_reset(r
);
2166 return lqr_carver_list_foreach(r
->attached_list
, lqr_carver_scan_reset_attached
, data
);
2170 lqr_carver_scan_reset_all (LqrCarver
*r
)
2174 lqr_carver_scan_reset(r
);
2175 lqr_carver_list_foreach(r
->attached_list
, lqr_carver_scan_reset_attached
, data
);
2180 /* readout all, pixel by bixel */
2183 lqr_carver_scan (LqrCarver
* r
, gint
* x
, gint
* y
, guchar
** rgb
)
2186 if (r
->col_depth
!= LQR_COLDEPTH_8I
)
2192 lqr_carver_scan_reset (r
);
2195 (*x
) = (r
->transposed
? r
->c
->y
: r
->c
->x
);
2196 (*y
) = (r
->transposed
? r
->c
->x
: r
->c
->y
);
2197 for (k
= 0; k
< r
->channels
; k
++)
2199 AS_8I(r
->rgb_ro_buffer
)[k
] = AS_8I(r
->rgb
)[r
->c
->now
* r
->channels
+ k
];
2201 (*rgb
) = AS_8I(r
->rgb_ro_buffer
);
2202 lqr_cursor_next(r
->c
);
2208 lqr_carver_scan_ext (LqrCarver
* r
, gint
* x
, gint
* y
, void ** rgb
)
2213 lqr_carver_scan_reset (r
);
2216 (*x
) = (r
->transposed
? r
->c
->y
: r
->c
->x
);
2217 (*y
) = (r
->transposed
? r
->c
->x
: r
->c
->y
);
2218 for (k
= 0; k
< r
->channels
; k
++)
2220 PXL_COPY(r
->rgb_ro_buffer
, k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
2223 BUF_POINTER_COPY(rgb
, r
->rgb_ro_buffer
, r
->col_depth
);
2225 lqr_cursor_next(r
->c
);
2229 /* readout all, by line */
2232 lqr_carver_scan_by_row (LqrCarver
*r
)
2234 return r
->transposed
? FALSE
: TRUE
;
2239 lqr_carver_scan_line (LqrCarver
* r
, gint
* n
, guchar
** rgb
)
2241 if (r
->col_depth
!= LQR_COLDEPTH_8I
)
2245 return lqr_carver_scan_line_ext (r
, n
, (void**) rgb
);
2250 lqr_carver_scan_line_ext (LqrCarver
* r
, gint
* n
, void ** rgb
)
2255 lqr_carver_scan_reset (r
);
2262 lqr_cursor_prev(r
->c
);
2265 for (x
= 0; x
< r
->w
; x
++)
2267 for (k
= 0; k
< r
->channels
; k
++)
2269 PXL_COPY(r
->rgb_ro_buffer
, x
* r
->channels
+ k
, r
->rgb
, r
->c
->now
* r
->channels
+ k
, r
->col_depth
);
2271 lqr_cursor_next(r
->c
);
2275 BUF_POINTER_COPY(rgb
, r
->rgb_ro_buffer
, r
->col_depth
);
2280 #ifdef __LQR_DEBUG__
2281 void lqr_carver_debug_check_rows(LqrCarver
* r
)
2285 for (y
= 0; y
< r
->h
; y
++)
2287 for (x
= 0; x
< r
->w
; x
++)
2289 data
= r
->raw
[y
][x
];
2290 if (data
/ r
->w0
!= y
)
2294 assert(data
/ r
->w0
== y
);
2298 #endif /* __LQR_DEBUG__ */
2301 /**** END OF LQR_CARVER CLASS FUNCTIONS ****/